1 /* 2 3 ChartWindow.cpp 4 5 by Pierre Raynaud-Richard. 6 7 Copyright 1998 Be Incorporated, All Rights Reserved. 8 9 */ 10 11 #include "ChartWindow.h" 12 13 #include <AppFileInfo.h> 14 #include <Application.h> 15 #include <Bitmap.h> 16 #include <Box.h> 17 #include <Button.h> 18 #include <ByteOrder.h> 19 #include <CheckBox.h> 20 #include <Directory.h> 21 #include <Entry.h> 22 #include <File.h> 23 #include <FindDirectory.h> 24 #include <Menu.h> 25 #include <MenuBar.h> 26 #include <MenuField.h> 27 #include <MenuItem.h> 28 #include <Path.h> 29 #include <PlaySound.h> 30 #include <PopUpMenu.h> 31 #include <RadioButton.h> 32 #include <Screen.h> 33 #include <Slider.h> 34 #include <TextControl.h> 35 36 #include <math.h> 37 #include <stdio.h> 38 #include <string.h> 39 #include <stdlib.h> 40 41 /* pseudo-random generator parameters (not very good ones, 42 but good enough for what we do here). */ 43 enum { 44 CRC_START = 0x29dec231, 45 CRC_KEY = 0x1789feb3 46 }; 47 48 #define MAX_FONT_SIZE 12.0f 49 50 /* various offse, width, height and position used to align and 51 set the various UI elements. */ 52 enum { 53 TOP_LEFT_LIMIT = 26, 54 H_BORDER = 5, 55 V_BORDER = 2, 56 ANIM_LABEL = 52, 57 ANIM_POPUP = 42, 58 DISP_LABEL = 40, 59 DISP_POPUP = 42, 60 BUTTON_WIDTH = 50, 61 BUTTON_OFFSET = -100, 62 SPACE_LABEL = 40, 63 SPACE_POPUP = 53, 64 INSTANT_LOAD = 205, 65 LEFT_WIDTH = 96, 66 LEFT_OFFSET = 2, 67 STATUS_BOX = 98, 68 STATUS_LABEL = 13, 69 STATUS_EDIT = 25, 70 STATUS_OFFSET = 2, 71 BOX_H_OFFSET = 4, 72 BOX_V_OFFSET = 14, 73 FULL_SCREEN = 16, 74 AUTO_DEMO = 22, 75 SECOND_THREAD = 16, 76 COLORS_BOX = 146, 77 COLORS_LABEL = 16, 78 COLORS_OFFSET = 2, 79 COLOR_CELL = 8, 80 SPECIAL_BOX = 92, 81 SPECIAL_LABEL = 16, 82 SPECIAL_OFFSET = 2, 83 STAR_DENSITY_H = 160, 84 STAR_DENSITY_V = 34, 85 REFRESH_RATE_H = 320, 86 REFRESH_RATE_V = 34 87 }; 88 89 /* min, max and standard setting of the star count, also 90 called starfield density. */ 91 enum { 92 STAR_DENSITY_MIN = 400, 93 STAR_DENSITY_MAX = 20000, 94 STAR_DENSITY_DEFAULT = 2000 95 }; 96 97 /* min, max and standard setting of the refresh rate. */ 98 #define REFRESH_RATE_MIN 0.6 99 #define REFRESH_RATE_MAX 600.0 100 #define REFRESH_RATE_DEFAULT 60.0 101 102 /* private enums used to identify the 3 types of 103 programmable picture buttons. */ 104 enum { 105 COLOR_BUTTON_PICT = 0, 106 DENSITY_BUTTON_PICT = 1, 107 REFRESH_BUTTON_PICT = 2 108 }; 109 110 /* min, max zoom (also default offscreen size), and max dimensions 111 of the content area of the window. */ 112 enum { 113 WINDOW_H_MIN = 220, 114 WINDOW_V_MIN = 146, 115 WINDOW_H_STD = 800, 116 WINDOW_V_STD = 600, 117 WINDOW_H_MAX = 1920, 118 WINDOW_V_MAX = 1440, 119 120 /* increment step used to dynamically resize the offscreen buffer */ 121 WINDOW_H_STEP = 224, 122 WINDOW_V_STEP = 168 123 }; 124 125 /* time delay between refresh of the stat counters */ 126 enum { 127 STAT_DELAY = 1000000 128 }; 129 130 /* ratio between the rear clipping depth and the front clipping 131 depth. */ 132 #define Z_CUT_RATIO 20.0 133 134 /* prefered aspect ratio between horizontal and vertical 135 dimensions of the animation frame. */ 136 #define DH_REF 0.8 137 #define DV_REF 0.6 138 139 /* no comments (almost :-) */ 140 #define abs(x) (((x)>0)?(x):-(x)) 141 142 /* default background color for the UI. */ 143 rgb_color background_color = { 216, 216, 216, 255 }; 144 145 /* the 7 colors for stars. */ 146 static rgb_color color_list[7] = { 147 { 255, 160, 160, 255 }, /* red */ 148 { 160, 255, 160, 255 }, /* green */ 149 { 160, 160, 255, 255 }, /* blue */ 150 { 255, 255, 160, 255 }, /* yellow */ 151 { 255, 208, 160, 255 }, /* orange */ 152 { 255, 160, 255, 255 }, /* pink */ 153 { 255, 255, 255, 255 } /* white */ 154 }; 155 156 /* the 8 levels of lighting, in 1/65536. */ 157 static int32 light_gradient[8] = { 158 0x2000, 159 0x5000, 160 0x7800, 161 0x9800, 162 0xb800, 163 0xd000, 164 0xe800, 165 0x10000 166 }; 167 168 169 // #pragma mark helper classes 170 171 172 /* multiply a vector by a constant */ 173 TPoint 174 TPoint::operator* (const float k) const 175 { 176 TPoint v; 177 178 v.x = x*k; 179 v.y = y*k; 180 v.z = z*k; 181 return v; 182 } 183 184 /* substract 2 vectors */ 185 TPoint 186 TPoint::operator- (const TPoint& v2) const 187 { 188 TPoint v; 189 190 v.x = x-v2.x; 191 v.y = y-v2.y; 192 v.z = z-v2.z; 193 return v; 194 } 195 196 /* add 2 vectors */ 197 TPoint TPoint::operator+ (const TPoint& v2) const { 198 TPoint v; 199 200 v.x = x+v2.x; 201 v.y = y+v2.y; 202 v.z = z+v2.z; 203 return v; 204 } 205 206 /* vectorial product of 2 vectors */ 207 TPoint 208 TPoint::operator^ (const TPoint& v2) const 209 { 210 TPoint v; 211 212 v.x = y*v2.z - z*v2.y; 213 v.y = z*v2.x - x*v2.z; 214 v.z = x*v2.y - y*v2.x; 215 return v; 216 } 217 218 /* length of a vector */ 219 float 220 TPoint::Length() const 221 { 222 return sqrt(x*x + y*y + z*z); 223 } 224 225 /* product of a vector by a matrix */ 226 TPoint 227 TMatrix::operator* (const TPoint& v) const 228 { 229 TPoint res; 230 231 res.x = m[0][0]*v.x + m[1][0]*v.y + m[2][0]*v.z; 232 res.y = m[0][1]*v.x + m[1][1]*v.y + m[2][1]*v.z; 233 res.z = m[0][2]*v.x + m[1][2]*v.y + m[2][2]*v.z; 234 return res; 235 } 236 237 /* extract the Nth vector/column of a matrix. */ 238 TPoint 239 TMatrix::Axis(int32 index) 240 { 241 TPoint v; 242 243 v.x = m[index][0]; 244 v.y = m[index][1]; 245 v.z = m[index][2]; 246 return v; 247 } 248 249 /* as we use rotation matrix, the invert of the matrix 250 is equal to the transpose */ 251 TMatrix 252 TMatrix::Transpose() const 253 { 254 TMatrix inv; 255 256 inv.m[0][0] = m[0][0]; 257 inv.m[0][1] = m[1][0]; 258 inv.m[0][2] = m[2][0]; 259 inv.m[1][0] = m[0][1]; 260 inv.m[1][1] = m[1][1]; 261 inv.m[1][2] = m[2][1]; 262 inv.m[2][0] = m[0][2]; 263 inv.m[2][1] = m[1][2]; 264 inv.m[2][2] = m[2][2]; 265 return inv; 266 } 267 268 /* set a spherical rotation matrix */ 269 void 270 TMatrix::Set(const float alpha, const float theta, const float phi) 271 { 272 float cD,sD,cI,sI,cA,sA; 273 274 /* trigonometry */ 275 cD = cos(alpha); 276 sD = sin(alpha); 277 cI = cos(theta); 278 sI = sin(theta); 279 cA = cos(phi); 280 sA = sin(phi); 281 282 /* rotation matrix */ 283 m[0][0] = cD*cA+sD*sI*sA; 284 m[1][0] = -sA*cI; 285 m[2][0] = sD*cA-cD*sI*sA; 286 m[0][1] = cD*sA-sD*sI*cA; 287 m[1][1] = cI*cA; 288 m[2][1] = sD*sA+cD*cA*sI; 289 m[0][2] = -sD*cI; 290 m[1][2] = -sI; 291 m[2][2] = cD*cI; 292 } 293 294 295 // #pragma mark - 296 297 298 /* this function will play a wav sound file, with the specified 299 following name, in the application folder. This is activated 300 when you press the button "Auto demo". */ 301 void 302 LaunchSound() 303 { 304 /* 305 BEntry soundFile; 306 app_info info; 307 status_t err; 308 entry_ref snd_ref; 309 BDirectory appFolder; 310 sound_handle sndhandle; 311 312 err = be_app->GetAppInfo(&info); 313 BEntry appEntry(&info.ref); 314 if (err != B_NO_ERROR) 315 return; 316 err = appEntry.GetParent(&appFolder); 317 if (err != B_NO_ERROR) 318 return; 319 appFolder.FindEntry("demo.wav", &soundFile); 320 err = soundFile.GetRef(&snd_ref); 321 sndhandle = play_sound(&snd_ref, true, true, true); 322 */ 323 } 324 325 /* return the version_info of a file, described by its name 326 and its generic folder (in find_directory syntax). */ 327 status_t 328 get_file_version_info(directory_which dir, 329 char *filename, version_info *info) 330 { 331 BPath path; 332 BFile file; 333 status_t res; 334 BAppFileInfo appinfo; 335 336 /* find the directory */ 337 if ((res = find_directory(dir, &path)) != B_NO_ERROR) 338 return res; 339 340 /* find the file */ 341 path.Append(filename); 342 file.SetTo(path.Path(), O_RDONLY); 343 if ((res = file.InitCheck()) != B_NO_ERROR) 344 return res; 345 346 /* get the version_info */ 347 if ((res = appinfo.SetTo(&file)) != B_NO_ERROR) 348 return res; 349 return appinfo.GetVersionInfo(info, B_APP_VERSION_KIND); 350 } 351 352 353 // #pragma mark - 354 355 356 ChartWindow::ChartWindow(BRect frame, const char *name) 357 : BDirectWindow(frame, name, B_TITLED_WINDOW, 0) 358 { 359 float h, v, h2, v2; 360 int32 colors[3]; 361 BRect r; 362 BMenu *menu; 363 BButton *button; 364 BCheckBox *check_box, *full_screen; 365 BMenuItem *item; 366 BMenuField *popup; 367 BStringView *string; 368 BRadioButton *radio; 369 370 // we're not font-sensitive, so we make sure we don't look too ugly 371 BFont font; 372 if (font.Size() > MAX_FONT_SIZE) 373 font.SetSize(MAX_FONT_SIZE); 374 BFont boldFont(be_bold_font); 375 if (boldFont.Size() > MAX_FONT_SIZE) 376 boldFont.SetSize(MAX_FONT_SIZE); 377 378 /* Check to see if we need the work-around for the case where 379 DirectConnected is called back with B_BUFFER_RESET not set 380 properly. This happens only with version 1.3.0 of the 381 app_server. */ 382 need_r3_buffer_reset_work_around = false; 383 384 version_info vi; 385 if (get_file_version_info(B_BEOS_SERVERS_DIRECTORY, "app_server", &vi) == B_NO_ERROR 386 && (vi.major == 1) && (vi.middle == 3) && (vi.minor == 0)) 387 need_r3_buffer_reset_work_around = true; 388 389 /* offset the content area frame in window relative coordinate */ 390 frame.OffsetTo(0.0, 0.0); 391 392 /* init the pattern anti-aliasing tables. */ 393 InitPatterns(); 394 395 /* set window size limits */ 396 SetSizeLimits(WINDOW_H_MIN, WINDOW_H_MAX, WINDOW_V_MIN, WINDOW_V_MAX); 397 SetZoomLimits(WINDOW_H_STD, WINDOW_V_STD); 398 399 /* initial bitmap buffer */ 400 fOffscreen = NULL; 401 max_width = WINDOW_H_STD - LEFT_WIDTH; 402 max_height = WINDOW_V_STD - TOP_LEFT_LIMIT; 403 404 /* initialise the default setting state */ 405 for (int32 i = 0; i < 7; i++) 406 fCurrentSettings.colors[i] = false; 407 fCurrentSettings.colors[1] = true; 408 fCurrentSettings.colors[2] = true; 409 fCurrentSettings.colors[3] = true; 410 fCurrentSettings.fullscreen_mode = WINDOW_MODE; 411 fCurrentSettings.special = SPECIAL_NONE; 412 fCurrentSettings.display = DISPLAY_OFF; 413 fCurrentSettings.animation = ANIMATION_OFF; 414 fCurrentSettings.back_color.red = 0; 415 fCurrentSettings.back_color.green = 0; 416 fCurrentSettings.back_color.blue = 0; 417 fCurrentSettings.back_color.alpha = 255; 418 fCurrentSettings.star_density = STAR_DENSITY_DEFAULT; 419 fCurrentSettings.refresh_rate = REFRESH_RATE_DEFAULT; 420 BScreen screen(this); 421 fCurrentSettings.depth = screen.ColorSpace(); 422 fCurrentSettings.width = (int32)frame.right+1-LEFT_WIDTH; 423 fCurrentSettings.height = (int32)frame.bottom+1-TOP_LEFT_LIMIT; 424 previous_fullscreen_mode = WINDOW_MODE; 425 next_set.Set(&fCurrentSettings); 426 427 /* initialise various global parameters */ 428 fInstantLoadLevel = 0; 429 second_thread_threshold = 0.5; 430 last_dynamic_delay = 0.0; 431 crc_alea = CRC_START; 432 433 /* initialise the starfield and the special structs */ 434 stars.list = (star*)malloc(sizeof(star)*STAR_DENSITY_MAX); 435 specials.list = (star*)malloc(sizeof(star)*SPECIAL_COUNT_MAX); 436 special_list = (special*)malloc(sizeof(special)*SPECIAL_COUNT_MAX); 437 InitStars(SPACE_CHAOS); 438 stars.count = fCurrentSettings.star_density; 439 stars.erase_count = 0; 440 InitSpecials(SPECIAL_NONE); 441 specials.erase_count = 0; 442 colors[0] = 1; 443 colors[1] = 2; 444 colors[2] = 3; 445 SetStarColors(colors, 3); 446 447 /* set camera default position and rotation */ 448 camera_alpha = 0.2; 449 camera_theta = 0.0; 450 camera_phi = 0.0; 451 camera.Set(camera_alpha, camera_theta, camera_phi); 452 camera_invert = camera.Transpose(); 453 origin.x = 0.5; 454 origin.y = 0.5; 455 origin.z = 0.1; 456 457 /* initialise camera animation */ 458 tracking_target = -1; 459 speed = 0.0115; 460 target_speed = speed; 461 462 /* initialise the view coordinate system */ 463 InitGeometry(); 464 SetGeometry(fCurrentSettings.width, fCurrentSettings.height); 465 SetCubeOffset(); 466 467 /* init the direct buffer in a valid state */ 468 direct_buffer.buffer_width = fCurrentSettings.width; 469 direct_buffer.buffer_height = fCurrentSettings.height; 470 direct_buffer.clip_list_count = 1; 471 direct_buffer.clip_bounds.top = 0; 472 direct_buffer.clip_bounds.left = 0; 473 direct_buffer.clip_bounds.right = -1; 474 direct_buffer.clip_bounds.bottom = -1; 475 direct_buffer.clip_list[0].top = 0; 476 direct_buffer.clip_list[0].left = 0; 477 direct_buffer.clip_list[0].right = -1; 478 direct_buffer.clip_list[0].bottom = -1; 479 fDirectConnected = false; 480 481 /* build the UI content of the window */ 482 483 /* top line background */ 484 r.Set(0.0, 0.0, frame.right, TOP_LEFT_LIMIT - 1); 485 fTopView = new BView(r, "top view", B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW); 486 fTopView->SetViewColor(background_color); 487 AddChild(fTopView); 488 489 h = 2; 490 v = V_BORDER; 491 492 /* instant load vue-meter */ 493 r.Set(h, v, h+INSTANT_LOAD-1, v + (TOP_LEFT_LIMIT - 1 - 2*V_BORDER)); 494 fInstantLoad = new InstantView(r); 495 fTopView->AddChild(fInstantLoad); 496 fInstantLoad->SetViewColor(0.0, 0.0, 0.0); 497 498 h += INSTANT_LOAD+H_BORDER; 499 500 /* camera animation popup */ 501 menu = new BPopUpMenu("Off"); 502 item = new BMenuItem("Off", new BMessage(ANIM_OFF_MSG)); 503 item->SetTarget(this); 504 menu->AddItem(item); 505 item = new BMenuItem("Slow rotation", new BMessage(ANIM_SLOW_ROT_MSG)); 506 item->SetTarget(this); 507 menu->AddItem(item); 508 item = new BMenuItem("Slow motion", new BMessage(ANIM_SLOW_MOVE_MSG)); 509 item->SetTarget(this); 510 menu->AddItem(item); 511 item = new BMenuItem("Fast motion", new BMessage(ANIM_FAST_MOVE_MSG)); 512 item->SetTarget(this); 513 menu->AddItem(item); 514 item = new BMenuItem("Free motion", new BMessage(ANIM_FREE_MOVE_MSG)); 515 item->SetTarget(this); 516 menu->AddItem(item); 517 518 r.Set(h, v, h+ANIM_LABEL+ANIM_POPUP-1, v + (TOP_LEFT_LIMIT - 1 - 2*V_BORDER)); 519 popup = new BMenuField(r, "", "Animation:", menu); 520 popup->SetFont(&font); 521 popup->MenuBar()->SetFont(&font); 522 popup->Menu()->SetFont(&font); 523 popup->ResizeToPreferred(); 524 popup->SetDivider(popup->StringWidth(popup->Label()) + 4.0f); 525 fTopView->AddChild(popup); 526 527 h += ANIM_LABEL + ANIM_POPUP + popup->StringWidth("Slow rotation"); 528 529 /* display mode popup */ 530 menu = new BPopUpMenu("Off"); 531 item = new BMenuItem("Off", new BMessage(DISP_OFF_MSG)); 532 item->SetTarget(this); 533 menu->AddItem(item); 534 item = new BMenuItem("LineArray", new BMessage(DISP_LINE_MSG)); 535 item->SetTarget(this); 536 item->SetEnabled(false); 537 menu->AddItem(item); 538 item = new BMenuItem("DrawBitmap", new BMessage(DISP_BITMAP_MSG)); 539 item->SetTarget(this); 540 menu->AddItem(item); 541 item = new BMenuItem("DirectWindow", new BMessage(DISP_DIRECT_MSG)); 542 item->SetTarget(this); 543 item->SetEnabled(BDirectWindow::SupportsWindowMode()); 544 menu->AddItem(item); 545 546 r.Set(h, v, h+DISP_LABEL+DISP_POPUP-1, v + (TOP_LEFT_LIMIT - 1 - 2*V_BORDER)); 547 popup = new BMenuField(r, "", "Display:", menu); 548 popup->SetFont(&font); 549 popup->MenuBar()->SetFont(&font); 550 popup->Menu()->SetFont(&font); 551 popup->ResizeToPreferred(); 552 popup->SetDivider(popup->StringWidth(popup->Label()) + 4.0f); 553 fTopView->AddChild(popup); 554 555 h += DISP_LABEL + DISP_POPUP + popup->StringWidth("DirectWindow") + H_BORDER; 556 557 /* create the offwindow (invisible) button on the left side. 558 this will be used to record the content of the Picture 559 button. */ 560 r.Set(0, 0, BUTTON_WIDTH-1, TOP_LEFT_LIMIT - 1 - 2*V_BORDER); 561 offwindow_button = new BButton(r, "", "", NULL); 562 offwindow_button->Hide(); 563 AddChild(offwindow_button); 564 offwindow_button->ResizeTo(r.Width(), r.Height()); 565 566 /* refresh rate picture button */ 567 r.Set(h, v, h+BUTTON_WIDTH-1, v + (TOP_LEFT_LIMIT - 1 - 2*V_BORDER)); 568 refresh_button = new BPictureButton(r, "", 569 ButtonPicture(false, REFRESH_BUTTON_PICT), 570 ButtonPicture(true, REFRESH_BUTTON_PICT), 571 new BMessage(OPEN_REFRESH_MSG)); 572 refresh_button->SetViewColor(B_TRANSPARENT_32_BIT); 573 refresh_button->ResizeToPreferred(); 574 fTopView->AddChild(refresh_button); 575 576 h += BUTTON_WIDTH+2*H_BORDER; 577 578 /* background color button */ 579 r.Set(h, v, h+BUTTON_WIDTH-1, v + (TOP_LEFT_LIMIT - 1 - 2*V_BORDER)); 580 color_button = new BPictureButton(r, "", 581 ButtonPicture(false, COLOR_BUTTON_PICT), 582 ButtonPicture(true, COLOR_BUTTON_PICT), 583 new BMessage(OPEN_COLOR_MSG)); 584 color_button->SetViewColor(B_TRANSPARENT_32_BIT); 585 color_button->ResizeToPreferred(); 586 fTopView->AddChild(color_button); 587 588 h += BUTTON_WIDTH+2*H_BORDER; 589 590 /* star density button */ 591 r.Set(h, v, h+BUTTON_WIDTH-1, v + (TOP_LEFT_LIMIT - 1 - 2*V_BORDER)); 592 density_button = new BPictureButton(r, "", 593 ButtonPicture(false, DENSITY_BUTTON_PICT), 594 ButtonPicture(true, DENSITY_BUTTON_PICT), 595 new BMessage(OPEN_DENSITY_MSG)); 596 density_button->SetViewColor(B_TRANSPARENT_32_BIT); 597 density_button->ResizeToPreferred(); 598 fTopView->AddChild(density_button); 599 600 h += BUTTON_WIDTH+H_BORDER; 601 602 /* starfield type popup */ 603 menu = new BPopUpMenu("Chaos"); 604 item = new BMenuItem("Chaos", new BMessage(SPACE_CHAOS_MSG)); 605 item->SetTarget(this); 606 menu->AddItem(item); 607 item = new BMenuItem("Amas", new BMessage(SPACE_AMAS_MSG)); 608 item->SetTarget(this); 609 menu->AddItem(item); 610 item = new BMenuItem("Spiral", new BMessage(SPACE_SPIRAL_MSG)); 611 item->SetTarget(this); 612 menu->AddItem(item); 613 614 r.Set(h, v, h+SPACE_LABEL+SPACE_POPUP-1, v + (TOP_LEFT_LIMIT - 1 - 2*V_BORDER)); 615 popup = new BMenuField(r, "", "Space:", menu); 616 popup->SetFont(&font); 617 popup->MenuBar()->SetFont(&font); 618 popup->Menu()->SetFont(&font); 619 popup->ResizeToPreferred(); 620 popup->SetDivider(popup->StringWidth(popup->Label()) + 4.0f); 621 fTopView->AddChild(popup); 622 623 h += SPACE_LABEL+SPACE_POPUP+2*H_BORDER; 624 625 /* left column gray background */ 626 r.Set(0.0, TOP_LEFT_LIMIT, LEFT_WIDTH-1, frame.bottom); 627 fLeftView = new BView(r, "top view", B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM, B_WILL_DRAW); 628 fLeftView->SetViewColor(background_color); 629 AddChild(fLeftView); 630 631 h2 = LEFT_OFFSET; 632 v2 = LEFT_OFFSET; 633 h = h2; 634 v = v2; 635 636 /* status box */ 637 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2, v+STATUS_BOX-1); 638 fStatusBox = new BBox(r); 639 fStatusBox->SetFont(&boldFont); 640 fStatusBox->SetLabel("Status"); 641 fLeftView->AddChild(fStatusBox); 642 float boxWidth, boxHeight; 643 fStatusBox->GetPreferredSize(&boxWidth, &boxHeight); 644 boxWidth += r.left; 645 646 h = BOX_H_OFFSET; 647 v = BOX_V_OFFSET; 648 649 /* frames per second title string */ 650 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1, v+STATUS_LABEL-1); 651 string = new BStringView(r, "", "Frames/s"); 652 string->SetFont(&font); 653 string->SetAlignment(B_ALIGN_CENTER); 654 fStatusBox->AddChild(string); 655 656 v += STATUS_LABEL+STATUS_OFFSET; 657 658 /* frames per second display string */ 659 r.Set(h-1, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET, v+STATUS_EDIT-1); 660 frames = new BStringView(r, "", "0.0"); 661 frames->SetAlignment(B_ALIGN_RIGHT); 662 frames->SetFont(be_bold_font); 663 frames->SetFontSize(24.0); 664 frames->SetViewColor(B_TRANSPARENT_32_BIT); 665 fStatusBox->AddChild(frames); 666 667 v += STATUS_EDIT+STATUS_OFFSET; 668 669 /* CPU load pourcentage title string */ 670 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1, v+STATUS_LABEL-1); 671 string = new BStringView(r, "", "CPU load"); 672 string->SetAlignment(B_ALIGN_CENTER); 673 string->SetFont(&font); 674 fStatusBox->AddChild(string); 675 676 v += STATUS_LABEL+STATUS_OFFSET; 677 678 /* CPU load pourcentage display string */ 679 r.Set(h-1, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET, v+STATUS_EDIT-1); 680 cpu_load = new BStringView(r, "", "0.0"); 681 cpu_load->SetAlignment(B_ALIGN_RIGHT); 682 cpu_load->SetFont(be_bold_font); 683 cpu_load->SetFontSize(24.0); 684 cpu_load->SetViewColor(B_TRANSPARENT_32_BIT); 685 fStatusBox->AddChild(cpu_load); 686 687 v2 += STATUS_BOX+LEFT_OFFSET*2; 688 h = h2; 689 v = v2; 690 691 /* Fullscreen mode check box */ 692 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-1, v+FULL_SCREEN-1); 693 full_screen = new BCheckBox(r, "", "Full Screen", new BMessage(FULL_SCREEN_MSG)); 694 full_screen->SetTarget(this); 695 full_screen->SetFont(&font); 696 full_screen->ResizeToPreferred(); 697 698 float width, height; 699 full_screen->GetPreferredSize(&width, &height); 700 boxWidth = max_c(width + r.left, boxWidth); 701 fLeftView->AddChild(full_screen); 702 703 v2 += FULL_SCREEN+LEFT_OFFSET*2; 704 h = h2; 705 v = v2; 706 707 /* Automatic demonstration activation button */ 708 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-1, v+AUTO_DEMO-1); 709 button = new BButton(r, "", "Auto demo", new BMessage(AUTO_DEMO_MSG)); 710 button->SetTarget(this); 711 button->ResizeToPreferred(); 712 button->GetPreferredSize(&width, &height); 713 boxWidth = max_c(width + r.left, boxWidth); 714 fLeftView->AddChild(button); 715 716 v2 += AUTO_DEMO+LEFT_OFFSET*2; 717 h = h2; 718 v = v2; 719 720 /* Enabling second thread check box */ 721 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-1, v+SECOND_THREAD-1); 722 check_box = new BCheckBox(r, "", "2 Threads", new BMessage(SECOND_THREAD_MSG)); 723 check_box->SetTarget(this); 724 check_box->SetFont(&font); 725 check_box->ResizeToPreferred(); 726 fLeftView->AddChild(check_box); 727 728 v2 += SECOND_THREAD+LEFT_OFFSET*2 + 2; 729 h = h2; 730 v = v2; 731 732 /* Star color selection box */ 733 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2, v+COLORS_BOX-1); 734 fColorsBox = new BBox(r); 735 fColorsBox->SetLabel("Colors"); 736 fColorsBox->SetFont(&boldFont); 737 fLeftView->AddChild(fColorsBox); 738 739 h = BOX_H_OFFSET; 740 v = BOX_V_OFFSET; 741 742 /* star color red check box */ 743 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1, v+COLORS_LABEL-1); 744 check_box = new BCheckBox(r, "", "Red", new BMessage(COLORS_RED_MSG)); 745 check_box->SetFont(&font); 746 check_box->ResizeToPreferred(); 747 fColorsBox->AddChild(check_box); 748 749 v += COLORS_LABEL+COLORS_OFFSET; 750 751 /* star color green check box */ 752 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1, v+COLORS_LABEL-1); 753 check_box = new BCheckBox(r, "", "Green", new BMessage(COLORS_GREEN_MSG)); 754 check_box->SetValue(1); 755 check_box->SetFont(&font); 756 check_box->ResizeToPreferred(); 757 fColorsBox->AddChild(check_box); 758 759 v += COLORS_LABEL+COLORS_OFFSET; 760 761 /* star color blue check box */ 762 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1, v+COLORS_LABEL-1); 763 check_box = new BCheckBox(r, "", "Blue", new BMessage(COLORS_BLUE_MSG)); 764 check_box->SetValue(1); 765 check_box->SetFont(&font); 766 check_box->ResizeToPreferred(); 767 fColorsBox->AddChild(check_box); 768 769 v += COLORS_LABEL+COLORS_OFFSET; 770 771 /* star color yellow check box */ 772 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1, v+COLORS_LABEL-1); 773 check_box = new BCheckBox(r, "", "Yellow", new BMessage(COLORS_YELLOW_MSG)); 774 check_box->SetValue(1); 775 check_box->SetFont(&font); 776 check_box->ResizeToPreferred(); 777 fColorsBox->AddChild(check_box); 778 779 v += COLORS_LABEL+COLORS_OFFSET; 780 781 /* star color orange check box */ 782 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1, v+COLORS_LABEL-1); 783 check_box = new BCheckBox(r, "", "Orange", new BMessage(COLORS_ORANGE_MSG)); 784 check_box->SetFont(&font); 785 check_box->ResizeToPreferred(); 786 fColorsBox->AddChild(check_box); 787 788 v += COLORS_LABEL+COLORS_OFFSET; 789 790 /* star color pink check box */ 791 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1, v+COLORS_LABEL-1); 792 check_box = new BCheckBox(r, "", "Pink", new BMessage(COLORS_PINK_MSG)); 793 check_box->SetFont(&font); 794 check_box->ResizeToPreferred(); 795 fColorsBox->AddChild(check_box); 796 797 v += COLORS_LABEL+COLORS_OFFSET; 798 799 /* star color white check box */ 800 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1, v+COLORS_LABEL-1); 801 check_box = new BCheckBox(r, "", "White", new BMessage(COLORS_WHITE_MSG)); 802 check_box->SetFont(&font); 803 check_box->ResizeToPreferred(); 804 fColorsBox->AddChild(check_box); 805 806 v2 += COLORS_BOX+LEFT_OFFSET*2; 807 h = h2; 808 v = v2; 809 810 /* Special type selection box */ 811 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2, v+SPECIAL_BOX-1); 812 fSpecialBox = new BBox(r); 813 fSpecialBox->SetFont(&boldFont); 814 fSpecialBox->SetLabel("Special"); 815 fLeftView->AddChild(fSpecialBox); 816 817 h = BOX_H_OFFSET; 818 v = BOX_V_OFFSET; 819 820 /* no special radio button */ 821 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1, v+COLORS_LABEL-1); 822 radio = new BRadioButton(r, "", "None", new BMessage(SPECIAL_NONE_MSG)); 823 radio->SetValue(1); 824 radio->SetFont(&font); 825 radio->ResizeToPreferred(); 826 fSpecialBox->AddChild(radio); 827 828 v += COLORS_LABEL+COLORS_OFFSET; 829 830 /* comet special animation radio button */ 831 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1, v+COLORS_LABEL-1); 832 radio = new BRadioButton(r, "", "Comet", new BMessage(SPECIAL_COMET_MSG)); 833 radio->SetFont(&font); 834 radio->ResizeToPreferred(); 835 fSpecialBox->AddChild(radio); 836 837 v += COLORS_LABEL+COLORS_OFFSET; 838 839 /* novas special animation radio button */ 840 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1, v+COLORS_LABEL-1); 841 radio = new BRadioButton(r, "", "Novas", new BMessage(SPECIAL_NOVAS_MSG)); 842 radio->SetFont(&font); 843 radio->ResizeToPreferred(); 844 fSpecialBox->AddChild(radio); 845 846 v += COLORS_LABEL+COLORS_OFFSET; 847 848 /* space batle special animation radio button (not implemented) */ 849 r.Set(h, v, h+LEFT_WIDTH-2*LEFT_OFFSET-2*BOX_H_OFFSET-1, v+COLORS_LABEL-1); 850 radio = new BRadioButton(r, "", "Battle", new BMessage(SPECIAL_BATTLE_MSG)); 851 radio->SetEnabled(false); 852 radio->SetFont(&font); 853 radio->ResizeToPreferred(); 854 fSpecialBox->AddChild(radio); 855 856 // Note: direct window mode uses LEFT_WIDTH 857 //fLeftView->ResizeTo(max_c(boxWidth + 2, fLeftView->Bounds().Width()), fLeftView->Bounds().Height()); 858 859 /* animation area */ 860 r.Set(fLeftView->Frame().right, TOP_LEFT_LIMIT, frame.right, frame.bottom); 861 fChartView = new ChartView(r); 862 fChartView->SetViewColor(0, 0, 0); 863 AddChild(fChartView); 864 865 /* allocate the semaphores */ 866 fDrawingLock = create_sem(1, "chart locker"); 867 second_thread_lock = create_sem(0, "chart second locker"); 868 second_thread_release = create_sem(0, "chart second release"); 869 870 /* spawn the asynchronous animation threads */ 871 fKillThread = false; 872 fAnimationThread = spawn_thread(ChartWindow::Animation, "chart animation", 873 B_NORMAL_PRIORITY, 874 (void*)this); 875 876 fSecondAnimationThread = spawn_thread(ChartWindow::Animation2, "chart animation2", 877 B_NORMAL_PRIORITY, 878 (void*)this); 879 resume_thread(fSecondAnimationThread); 880 resume_thread(fAnimationThread); 881 } 882 883 884 ChartWindow::~ChartWindow() 885 { 886 int32 result; 887 888 /* setting this flag force both animation threads to quit */ 889 fKillThread = true; 890 /* wait for the two animation threads to quit */ 891 wait_for_thread(fAnimationThread, &result); 892 wait_for_thread(fSecondAnimationThread, &result); 893 894 /* free the offscreen bitmap if any */ 895 if (fOffscreen != NULL) 896 delete fOffscreen; 897 898 /* release the semaphores used for synchronisation */ 899 delete_sem(fDrawingLock); 900 delete_sem(second_thread_lock); 901 delete_sem(second_thread_release); 902 903 /* free the buffers used to store the starlists */ 904 free(stars.list); 905 free(specials.list); 906 free(special_list); 907 } 908 909 910 // #pragma mark Standard window members 911 912 913 bool 914 ChartWindow::QuitRequested() 915 { 916 be_app->PostMessage(B_QUIT_REQUESTED); 917 return(TRUE); 918 } 919 920 921 void 922 ChartWindow::MessageReceived(BMessage *message) 923 { 924 int32 index, color; 925 BHandler *handler; 926 BCheckBox *check_box; 927 //BTextControl *text_ctrl; 928 //BColorControl *color_ctrl; 929 BSlider *slider; 930 931 message->FindPointer("source", (void**)&handler); 932 switch(message->what) { 933 /* This is a key part of the architecture. MessageReceived is 934 called whenever the user interact with a UI element to change 935 a setting. The window is locked at this point, so changing 936 the setting of the engine at that point would be dangerous. 937 We could easily goofed and create a bad dependencies between 938 the Window locking mechanism and DirectConnected, that 939 would generate a deadlock and force the app_server to kill 940 the application. Bad business. To avoid that, we keep two 941 different engine setting. One that is currently used by the 942 animation engine, the other one that will retain all the 943 changes generated by the user until the engine is ready to 944 use them. So message received will write into that setting 945 state and the engine will read it from time to time. Both 946 access can be done asynchronously as all intermediate state 947 generated by the MessageReceived write are valid (we don't 948 need to make those transactions atomic). */ 949 case ANIM_OFF_MSG : 950 case ANIM_SLOW_ROT_MSG : 951 case ANIM_SLOW_MOVE_MSG : 952 case ANIM_FAST_MOVE_MSG : 953 case ANIM_FREE_MOVE_MSG : 954 next_set.animation = ANIMATION_OFF + (message->what - ANIM_OFF_MSG); 955 break; 956 case DISP_OFF_MSG : 957 case DISP_BITMAP_MSG : 958 case DISP_DIRECT_MSG : 959 next_set.display = DISPLAY_OFF + (message->what - DISP_OFF_MSG); 960 break; 961 case SPACE_CHAOS_MSG : 962 case SPACE_AMAS_MSG : 963 case SPACE_SPIRAL_MSG : 964 next_set.space_model = SPACE_CHAOS + (message->what - SPACE_CHAOS_MSG); 965 break; 966 case FULL_SCREEN_MSG : 967 check_box = dynamic_cast<BCheckBox*>(handler); 968 if (check_box->Value()) 969 next_set.fullscreen_mode = FULLSCREEN_MODE; 970 else 971 next_set.fullscreen_mode = WINDOW_MODE; 972 break; 973 case AUTO_DEMO_MSG : 974 next_set.fullscreen_mode = FULLDEMO_MODE; 975 next_set.animation = ANIMATION_FREE_MOVE; 976 next_set.special = SPECIAL_COMET; 977 LaunchSound(); 978 break; 979 case BACK_DEMO_MSG : 980 next_set.fullscreen_mode = previous_fullscreen_mode; 981 break; 982 case SECOND_THREAD_MSG : 983 check_box = dynamic_cast<BCheckBox*>(handler); 984 next_set.second_thread = check_box->Value(); 985 break; 986 case COLORS_RED_MSG : 987 case COLORS_GREEN_MSG : 988 case COLORS_BLUE_MSG : 989 case COLORS_YELLOW_MSG : 990 case COLORS_ORANGE_MSG : 991 case COLORS_PINK_MSG : 992 case COLORS_WHITE_MSG : 993 index = message->what - COLORS_RED_MSG; 994 check_box = dynamic_cast<BCheckBox*>(handler); 995 next_set.colors[index] = (check_box->Value()?true:false); 996 break; 997 case SPECIAL_NONE_MSG : 998 case SPECIAL_COMET_MSG : 999 case SPECIAL_NOVAS_MSG : 1000 case SPECIAL_BATTLE_MSG : 1001 next_set.special = SPECIAL_NONE + (message->what - SPECIAL_NONE_MSG); 1002 break; 1003 case COLOR_PALETTE_MSG : 1004 message->FindInt32("be:value", &color); 1005 next_set.back_color.red = (color >> 24); 1006 next_set.back_color.green = (color >> 16); 1007 next_set.back_color.blue = (color >> 8); 1008 next_set.back_color.alpha = color; 1009 break; 1010 case STAR_DENSITY_MSG : 1011 slider = dynamic_cast<BSlider*>(handler); 1012 next_set.star_density = slider->Value(); 1013 break; 1014 case REFRESH_RATE_MSG : 1015 slider = dynamic_cast<BSlider*>(handler); 1016 next_set.refresh_rate = exp(slider->Value()*0.001*(log(REFRESH_RATE_MAX/REFRESH_RATE_MIN)))* 1017 REFRESH_RATE_MIN; 1018 break; 1019 /* open the three floating window used to do live setting of 1020 some advanced parameters. Those windows will return live 1021 feedback that will be executed by some of the previous 1022 messages. */ 1023 case OPEN_COLOR_MSG : 1024 OpenColorPalette(BPoint(200.0, 200.0)); 1025 break; 1026 case OPEN_DENSITY_MSG : 1027 OpenStarDensity(BPoint(280.0, 280.0)); 1028 break; 1029 case OPEN_REFRESH_MSG : 1030 OpenRefresh(BPoint(240.0, 340.0)); 1031 break; 1032 /* let other messages pass through... */ 1033 default : 1034 BDirectWindow::MessageReceived(message); 1035 break; 1036 } 1037 } 1038 1039 1040 void 1041 ChartWindow::ScreenChanged(BRect screen_size, color_space depth) 1042 { 1043 BScreen my_screen(this); 1044 1045 /* this is the same principle than the one described for 1046 MessageReceived, to inform the engine that the depth of 1047 the screen changed (needed only for offscreen bitmap. 1048 In DirectWindow, you get a direct notification). */ 1049 next_set.depth = my_screen.ColorSpace(); 1050 } 1051 1052 1053 void 1054 ChartWindow::FrameResized(float new_width, float new_height) 1055 { 1056 /* this is the same principle than the one described for 1057 MessageReceived, to inform the engine that the window 1058 size changed (needed only for offscreen bitmap. In 1059 DirectWindow, you get a direct notification). */ 1060 next_set.width = (int32)Frame().Width()+1-LEFT_WIDTH; 1061 next_set.height = (int32)Frame().Height()+1-TOP_LEFT_LIMIT; 1062 } 1063 1064 1065 // #pragma mark User Interface related stuff... 1066 1067 1068 /* loop through the window list of the application, looking for 1069 a window with a specified name. */ 1070 BWindow * 1071 ChartWindow::GetAppWindow(char *name) 1072 { 1073 int32 index; 1074 BWindow *window; 1075 1076 for (index = 0;; index++) { 1077 window = be_app->WindowAt(index); 1078 if (window == NULL) 1079 break; 1080 if (window->LockWithTimeout(200000) == B_OK) { 1081 if (strcmp(window->Name(), name) == 0) { 1082 window->Unlock(); 1083 break; 1084 } 1085 window->Unlock(); 1086 } 1087 } 1088 return window; 1089 } 1090 1091 /* this function return a picture (in active or inactive state) of 1092 a standard BButton with some specific content draw in the middle. 1093 button_type indicate what special content should be used. */ 1094 BPicture * 1095 ChartWindow::ButtonPicture(bool active, int32 button_type) 1096 { 1097 char word[6]; 1098 int32 value; 1099 BRect r; 1100 BPoint delta; 1101 BPicture *pict; 1102 1103 1104 /* create and open the picture */ 1105 pict = new BPicture(); 1106 r = offwindow_button->Bounds(); 1107 offwindow_button->SetValue(active); 1108 offwindow_button->BeginPicture(pict); 1109 /* draw the standard BButton in whatever state is required. */ 1110 offwindow_button->Draw(r); 1111 if (button_type == COLOR_BUTTON_PICT) { 1112 /* this button just contains a rectangle of the current background 1113 color, with a one pixel black border. */ 1114 r.InsetBy(6.0, 4.0); 1115 offwindow_button->SetHighColor(0, 0, 0); 1116 offwindow_button->StrokeRect(r); 1117 r.InsetBy(1.0, 1.0); 1118 offwindow_button->SetHighColor(fCurrentSettings.back_color); 1119 offwindow_button->FillRect(r); 1120 } 1121 else if (button_type == DENSITY_BUTTON_PICT) { 1122 /* this button just contains a big string (using a bigger font size 1123 than what a standard BButton would allow) with the current value 1124 of the star density pourcentage. */ 1125 value = (fCurrentSettings.star_density*100 + STAR_DENSITY_MAX/2) / STAR_DENSITY_MAX; 1126 sprintf(word, "%3ld", value); 1127 draw_string: 1128 offwindow_button->SetFont(be_bold_font); 1129 offwindow_button->SetFontSize(14.0); 1130 delta.x = BUTTON_WIDTH/2-(offwindow_button->StringWidth(word) * 0.5); 1131 delta.y = (TOP_LEFT_LIMIT-2*V_BORDER)/2 + 6.0; 1132 offwindow_button->DrawString(word, delta); 1133 } 1134 else { 1135 /* this button just contains a big string (using a bigger font size 1136 than what a standard BButton would allow) with the current value 1137 of the target refresh rate in frames per second. */ 1138 sprintf(word, "%3.1f", fCurrentSettings.refresh_rate + 0.05); 1139 goto draw_string; 1140 } 1141 /* close and return the picture */ 1142 return offwindow_button->EndPicture(); 1143 } 1144 1145 /* Create a floating window including a slightly modified version of 1146 BColorControl, ChartColorControl, that will return live feedback 1147 as the same time the user will change the color setting of the 1148 background. */ 1149 void 1150 ChartWindow::OpenColorPalette(BPoint here) 1151 { 1152 BRect frame; 1153 BPoint point; 1154 1155 BWindow *window = GetAppWindow("Space color"); 1156 if (window == NULL) { 1157 frame.Set(here.x, here.y, here.x + 199.0, here.y + 99.0); 1158 window = new BWindow(frame, "Space color", 1159 B_FLOATING_WINDOW_LOOK, 1160 B_FLOATING_APP_WINDOW_FEEL, 1161 B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK | B_NOT_RESIZABLE); 1162 point.Set(0, 0); 1163 BColorControl *colorControl = new ChartColorControl(point, 1164 new BMessage(COLOR_PALETTE_MSG)); 1165 colorControl->SetViewColor(background_color); 1166 colorControl->SetTarget(NULL, this); 1167 colorControl->SetValue(fCurrentSettings.back_color); 1168 colorControl->ResizeToPreferred(); 1169 window->ResizeTo(colorControl->Bounds().Width(), colorControl->Bounds().Height()); 1170 window->AddChild(colorControl); 1171 window->Show(); 1172 } 1173 window->Activate(); 1174 } 1175 1176 /* Create a floating window including a BSlider, that will return 1177 live feedback when the user will change the star density of the 1178 starfield */ 1179 void 1180 ChartWindow::OpenStarDensity(BPoint here) 1181 { 1182 BRect frame; 1183 BSlider *slider; 1184 BWindow *window; 1185 1186 window = GetAppWindow("Star density"); 1187 if (window == NULL) { 1188 frame.Set(here.x, here.y, here.x + STAR_DENSITY_H-1, here.y + STAR_DENSITY_V-1); 1189 window = new BWindow(frame, "Star density", 1190 B_FLOATING_WINDOW_LOOK, 1191 B_FLOATING_APP_WINDOW_FEEL, 1192 B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK); 1193 frame.OffsetTo(0.0, 0.0); 1194 slider = new BSlider(frame, "", NULL, new BMessage(STAR_DENSITY_MSG), 1195 STAR_DENSITY_MIN, STAR_DENSITY_MAX); 1196 slider->SetViewColor(background_color); 1197 slider->SetTarget(NULL, this); 1198 slider->SetValue(fCurrentSettings.star_density); 1199 slider->SetModificationMessage(new BMessage(STAR_DENSITY_MSG)); 1200 slider->SetLimitLabels(" 5% (low)", "(high) 100% "); 1201 slider->ResizeToPreferred(); 1202 window->ResizeTo(slider->Bounds().Width(), slider->Bounds().Height()); 1203 window->AddChild(slider); 1204 window->Show(); 1205 } 1206 window->Activate(); 1207 } 1208 1209 /* Create a floating window including a BSlider, that will return 1210 live feedback when the user will change the target refresh rate 1211 of the animation */ 1212 void 1213 ChartWindow::OpenRefresh(BPoint here) 1214 { 1215 BRect frame; 1216 BSlider *slider; 1217 BWindow *window; 1218 1219 window = GetAppWindow("Refresh rate"); 1220 if (window == NULL) { 1221 frame.Set(here.x, here.y, here.x + REFRESH_RATE_H-1, here.y + REFRESH_RATE_V-1); 1222 window = new BWindow(frame, "Refresh rate", 1223 B_FLOATING_WINDOW_LOOK, 1224 B_FLOATING_APP_WINDOW_FEEL, 1225 B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK); 1226 frame.OffsetTo(0.0, 0.0); 1227 slider = new BSlider(frame, "", NULL, new BMessage(REFRESH_RATE_MSG), 0.0, 1000.0); 1228 slider->SetViewColor(background_color); 1229 slider->SetTarget(NULL, this); 1230 slider->SetValue(1000.0*log(fCurrentSettings.refresh_rate/REFRESH_RATE_MIN)/log(REFRESH_RATE_MAX/REFRESH_RATE_MIN)); 1231 slider->SetModificationMessage(new BMessage(REFRESH_RATE_MSG)); 1232 slider->SetLimitLabels(" 0.6 f/s (logarythmic scale)", "600.0 f/s"); 1233 slider->ResizeToPreferred(); 1234 window->ResizeTo(slider->Bounds().Width(), slider->Bounds().Height()); 1235 window->AddChild(slider); 1236 window->Show(); 1237 } 1238 window->Activate(); 1239 } 1240 1241 /* This update the state of the frames per second vue-meter in a lazy way. */ 1242 void 1243 ChartWindow::DrawInstantLoad(float frame_per_second) 1244 { 1245 int32 level, i; 1246 bigtime_t timeout; 1247 1248 level = (int32)((frame_per_second + 6.0) * (1.0/12.0)); 1249 if (level > 50) level = 50; 1250 1251 /* if the load level is inchanged, nothing more to do... */ 1252 if (level == fInstantLoadLevel) 1253 return; 1254 1255 /* We need to lock the window to be able to draw that. But as some 1256 BControl are still synchronous, if the user is still tracking them, 1257 the window can stay block for a long time. It's not such a big deal 1258 when using the offscreen buffer as we won't be able to draw it in 1259 any case. But in DirectWindow mode, we're not limited by that so 1260 it would be stupid to block the engine loop here. That's why in 1261 that case, we will try to lock the window with a timeout of 0us. */ 1262 if (fCurrentSettings.display == DISPLAY_BITMAP) 1263 timeout = 100000; 1264 else 1265 timeout = 0; 1266 if (LockWithTimeout(timeout) != B_OK) 1267 return; 1268 1269 /* the new level is higher than the previous. We need to draw more 1270 colored bars. */ 1271 if (level > fInstantLoadLevel) { 1272 for (i=fInstantLoadLevel; i<level; i++) { 1273 if (i<fInstantLoad->step) fInstantLoad->SetHighColor(255.0, 90.0, 90.0); 1274 else if ((i/fInstantLoad->step) & 1) fInstantLoad->SetHighColor(90.0, 255.0, 90.0); 1275 else fInstantLoad->SetHighColor(40.0, 200.0, 40.0); 1276 fInstantLoad->FillRect(BRect(3+i*4, 2, 5+i*4, 19)); 1277 } 1278 } 1279 /* the level is lower than before, we need to erase some bars. */ 1280 else { 1281 fInstantLoad->SetHighColor(0.0, 0.0, 0.0); 1282 for (i=level; i< fInstantLoadLevel; i++) 1283 fInstantLoad->FillRect(BRect(3+i*4, 2, 5+i*4, 19)); 1284 } 1285 /* we want that drawing to be completed as soon as possible */ 1286 Flush(); 1287 1288 fInstantLoadLevel = level; 1289 Unlock(); 1290 } 1291 1292 1293 void 1294 ChartWindow::PrintStatNumbers(float fps) 1295 { 1296 char text_frames[6]; 1297 char text_cpu_load[6]; 1298 float frame_rate, load; 1299 bigtime_t timeout; 1300 1301 /* rules use to determine the stat numbers : if the target framerate 1302 is greater than the simulate one, then we consider that 100.0 cpu 1303 was used, and we only got the simulate framerate. */ 1304 if (fps <= fCurrentSettings.refresh_rate) { 1305 load = 100.0; 1306 frame_rate = fps + 0.05; 1307 } 1308 /* if the target framerate is less than the simulate one, then we 1309 calculate what fraction of the cpu would have been required to 1310 deliver the target framerate, and we said that the target framerate 1311 was delivered. */ 1312 else { 1313 load = (100.0*fCurrentSettings.refresh_rate)/fps + 0.05; 1314 frame_rate = fCurrentSettings.refresh_rate + 0.05; 1315 } 1316 1317 /* convert numbers in strings */ 1318 sprintf(text_cpu_load, "%3.1f", load); 1319 sprintf(text_frames, "%3.1f", frame_rate); 1320 1321 /* same remark than for DrawInstantLoad. We want to avoid to 1322 block if using DirectWindow mode. */ 1323 if (fCurrentSettings.display == DISPLAY_BITMAP) 1324 timeout = 100000; 1325 else 1326 timeout = 0; 1327 1328 if (LockWithTimeout(timeout) == B_OK) { 1329 frames->SetText(text_frames); 1330 cpu_load->SetText(text_cpu_load); 1331 Unlock(); 1332 } 1333 } 1334 1335 1336 // #pragma mark Engine setting related functions. 1337 1338 1339 void 1340 ChartWindow::InitGeometry() 1341 { 1342 float dz; 1343 1344 /* calculate some parameters required for the 3d processing */ 1345 dz = sqrt(1.0 - (DH_REF*DH_REF + DV_REF*DV_REF) * (0.5 + 0.5/Z_CUT_RATIO) * (0.5 + 0.5/Z_CUT_RATIO)); 1346 depth_ref = dz / (1.0 - 1.0/Z_CUT_RATIO); 1347 1348 /* set the position of the pyramid of vision, so that it was always 1349 possible to include it into a 1x1x1 cube parallel to the 3 main 1350 axis. */ 1351 geo.z_max = depth_ref; 1352 geo.z_min = depth_ref/Z_CUT_RATIO; 1353 1354 /* used for lighting processing */ 1355 geo.z_max_square = geo.z_max * geo.z_max; 1356 1357 /* preprocess that for the fast clipping based on the pyramid of vision */ 1358 geo.xz_max = (0.5*DH_REF)/geo.z_max; 1359 geo.xz_min = -geo.xz_max; 1360 geo.yz_max = (0.5*DV_REF)/geo.z_max; 1361 geo.yz_min = -geo.yz_max; 1362 } 1363 1364 /* second part of the asynchronous setting mechanism. This will be 1365 called once during every loop of the animation engine, at a time 1366 when the engine is not using the setting for realtime processing. 1367 Each setting will be checked for potential change, and action 1368 will be taken if needed. The window can be locked at that time 1369 because the structure of the animation engine loop guarantees 1370 that DirectConnected can not stay blocked at the same time that 1371 this method is executed. */ 1372 void 1373 ChartWindow::ChangeSetting(setting new_set) 1374 { 1375 //star *s; 1376 int32 i, color_count, old_step; 1377 int32 color_index[7]; 1378 1379 /* check for change of window/fullscreen/fullscreen demo mode */ 1380 if (fCurrentSettings.fullscreen_mode != new_set.fullscreen_mode) { 1381 switch (new_set.fullscreen_mode) { 1382 case WINDOW_MODE : 1383 previous_fullscreen_mode = WINDOW_MODE; 1384 ResizeTo(PreviousFrame.Width(), PreviousFrame.Height()); 1385 MoveTo(PreviousFrame.left, PreviousFrame.top); 1386 break; 1387 case FULLSCREEN_MODE : 1388 { 1389 previous_fullscreen_mode = FULLSCREEN_MODE; 1390 if (fCurrentSettings.fullscreen_mode == WINDOW_MODE) 1391 PreviousFrame = Frame(); 1392 BScreen a_screen(this); 1393 MoveTo(a_screen.Frame().left, a_screen.Frame().top); 1394 ResizeTo(a_screen.Frame().Width(), a_screen.Frame().Height()); 1395 } 1396 break; 1397 case FULLDEMO_MODE : 1398 { 1399 previous_fullscreen_mode = fCurrentSettings.fullscreen_mode; 1400 if (fCurrentSettings.fullscreen_mode == WINDOW_MODE) 1401 PreviousFrame = Frame(); 1402 BScreen b_screen(this); 1403 ResizeTo(b_screen.Frame().Width() + LEFT_WIDTH, b_screen.Frame().Height() + TOP_LEFT_LIMIT); 1404 MoveTo(b_screen.Frame().left - LEFT_WIDTH, b_screen.Frame().top - TOP_LEFT_LIMIT); 1405 } 1406 break; 1407 } 1408 } 1409 1410 /* check for change in the target refresh rate */ 1411 if (fCurrentSettings.refresh_rate != new_set.refresh_rate) { 1412 fCurrentSettings.refresh_rate = new_set.refresh_rate; 1413 old_step = fInstantLoad->step; 1414 fInstantLoad->step = (int32)((fCurrentSettings.refresh_rate+6.0)/12.0); 1415 if (fInstantLoad->step < 1) 1416 fInstantLoad->step = 1; 1417 if (LockWithTimeout(200000) == B_OK) { 1418 if (old_step != fInstantLoad->step) 1419 fInstantLoad->Invalidate(); 1420 refresh_button->SetEnabledOff(ButtonPicture(false, REFRESH_BUTTON_PICT)); 1421 refresh_button->SetEnabledOn(ButtonPicture(true, REFRESH_BUTTON_PICT)); 1422 refresh_button->Invalidate(); 1423 Unlock(); 1424 } 1425 if (fCurrentSettings.animation != ANIMATION_OFF) 1426 frame_delay = (bigtime_t)(1000000.0/new_set.refresh_rate); 1427 } 1428 1429 /* check for change in the star colors list */ 1430 for (i=0; i<7; i++) 1431 if (fCurrentSettings.colors[i] != new_set.colors[i]) { 1432 /* if any, get the list of usable color index... */ 1433 color_count = 0; 1434 for (i=0; i<7; i++) 1435 if (new_set.colors[i]) 1436 color_index[color_count++] = i; 1437 /* check that at least one color is enabled */ 1438 if (color_count == 0) 1439 color_index[color_count++] = 6; 1440 /* set a new color distribution in the starfield */ 1441 SetStarColors(color_index, color_count); 1442 break; 1443 } 1444 1445 /* check for change of the special effect setting */ 1446 if (new_set.special != fCurrentSettings.special) 1447 InitSpecials(new_set.special); 1448 1449 /* check for change of the display method */ 1450 if (new_set.display != fCurrentSettings.display) { 1451 if (new_set.display == DISPLAY_BITMAP) { 1452 /* check the settings of the offscreen bitmap */ 1453 CheckBitmap(new_set.depth, new_set.width, new_set.height); 1454 /* synchronise the camera geometry and the offscreen buffer geometry */ 1455 SetGeometry(bitmap_buffer.buffer_width, bitmap_buffer.buffer_height); 1456 /* reset the offscreen background and cancel the erasing */ 1457 SetBitmapBackGround(); 1458 stars.erase_count = 0; 1459 specials.erase_count = 0; 1460 } 1461 if (new_set.display == DISPLAY_DIRECT) { 1462 /* this need to be atomic in regard of DirectConnected */ 1463 while (acquire_sem(fDrawingLock) == B_INTERRUPTED) 1464 ; 1465 /* synchronise the camera geometry and the direct buffer geometry */ 1466 SetGeometry(direct_buffer.buffer_width, direct_buffer.buffer_height); 1467 /* cancel erasing of stars not in visible part of the direct window */ 1468 RefreshClipping(&direct_buffer, &stars); 1469 RefreshClipping(&direct_buffer, &specials); 1470 release_sem(fDrawingLock); 1471 } 1472 } 1473 1474 /* check for change of the animation mode. */ 1475 if (new_set.animation != fCurrentSettings.animation) { 1476 /* when there is no camera animation, we loop only 1477 10 times per second. */ 1478 if (new_set.animation == ANIMATION_OFF) 1479 frame_delay = 100000; 1480 else 1481 frame_delay = (bigtime_t)(1000000.0/new_set.refresh_rate); 1482 /* reset the free camera animation context for a fresh start */ 1483 if (new_set.animation == ANIMATION_FREE_MOVE) { 1484 d_alpha = 0.0; 1485 d_theta = 0.0; 1486 d_phi = 0.0; 1487 cnt_alpha = 0; 1488 cnt_theta = 0; 1489 cnt_phi = 0; 1490 } 1491 } 1492 1493 /* check for change of starfield model */ 1494 if (new_set.space_model != fCurrentSettings.space_model) { 1495 /* Generate a new starfield. Also reset the special animation */ 1496 InitStars(new_set.space_model); 1497 InitSpecials(new_set.special); 1498 } 1499 1500 /* check for change of the background color */ 1501 if ((new_set.back_color.red != fCurrentSettings.back_color.red) || 1502 (new_set.back_color.green != fCurrentSettings.back_color.green) || 1503 (new_set.back_color.blue != fCurrentSettings.back_color.blue)) { 1504 if (LockWithTimeout(200000) == B_OK) { 1505 BScreen screen(this); 1506 /* set the background color and it's 8 bits index equivalent */ 1507 fCurrentSettings.back_color = new_set.back_color; 1508 back_color_index = screen.IndexForColor(new_set.back_color); 1509 /* set the nackground color of the view (directwindow mode) */ 1510 fChartView->SetViewColor(new_set.back_color); 1511 /* change the color of the picture button used in the UI */ 1512 color_button->SetEnabledOff(ButtonPicture(false, COLOR_BUTTON_PICT)); 1513 color_button->SetEnabledOn(ButtonPicture(true, COLOR_BUTTON_PICT)); 1514 color_button->Invalidate(); 1515 /* update all dependencies in the offscreen buffer descriptor */ 1516 SetColorSpace(&bitmap_buffer, bitmap_buffer.depth); 1517 /* update all dependencies in the directwindow buffer descriptor */ 1518 while (acquire_sem(fDrawingLock) == B_INTERRUPTED) 1519 ; 1520 SetColorSpace(&direct_buffer, direct_buffer.depth); 1521 release_sem(fDrawingLock); 1522 /* in offscreen mode, erase the background and cancel star erasing */ 1523 if (new_set.display == DISPLAY_BITMAP) { 1524 SetBitmapBackGround(); 1525 stars.erase_count = 0; 1526 specials.erase_count = 0; 1527 } 1528 /* in directwindow mode, just force an update */ 1529 else 1530 fChartView->Invalidate(); 1531 Unlock(); 1532 } 1533 } 1534 1535 /* check for change of the star animation density */ 1536 if (new_set.star_density != fCurrentSettings.star_density) { 1537 if (LockWithTimeout(200000) == B_OK) { 1538 fCurrentSettings.star_density = new_set.star_density; 1539 /* change the picture button used in the UI */ 1540 density_button->SetEnabledOff(ButtonPicture(false, DENSITY_BUTTON_PICT)); 1541 density_button->SetEnabledOn(ButtonPicture(true, DENSITY_BUTTON_PICT)); 1542 density_button->Invalidate(); 1543 Unlock(); 1544 } 1545 stars.count = new_set.star_density; 1546 } 1547 1548 /* check for change in the buffer format for the offscreen bitmap. 1549 DirectWindow depth change are always handle in realtime */ 1550 if (new_set.depth != fCurrentSettings.depth) { 1551 CheckBitmap(new_set.depth, new_set.width, new_set.height); 1552 /* need to reset the buffer if it's currently used for display */ 1553 if (new_set.display == DISPLAY_BITMAP) { 1554 SetBitmapBackGround(); 1555 stars.erase_count = 0; 1556 specials.erase_count = 0; 1557 } 1558 } 1559 1560 /* check for change in the drawing area of the offscreen bitmap */ 1561 if ((new_set.width != fCurrentSettings.width) || (new_set.height != fCurrentSettings.height)) { 1562 CheckBitmap(new_set.depth, new_set.width, new_set.height); 1563 bitmap_buffer.buffer_width = new_set.width; 1564 bitmap_buffer.buffer_height = new_set.height; 1565 if (new_set.display == DISPLAY_BITMAP) 1566 SetGeometry(bitmap_buffer.buffer_width, bitmap_buffer.buffer_height); 1567 SetBitmapClipping(new_set.width, new_set.height); 1568 } 1569 1570 /* copy the new state as the new current state */ 1571 fCurrentSettings.Set(&new_set); 1572 } 1573 1574 /* Initialise the starfield in the different modes */ 1575 void 1576 ChartWindow::InitStars(int32 space_model) 1577 { 1578 star *s; 1579 int32 step; 1580 int32 amas_select[32]; 1581 float dx, dy, dz, dist, fact, alpha, r; 1582 float factor[8]; 1583 uint32 i, index, i_step; 1584 TPoint amas[8]; 1585 1586 switch (space_model) { 1587 /* Create a random starfield */ 1588 case SPACE_CHAOS : 1589 FillStarList(stars.list, STAR_DENSITY_MAX); 1590 key_point_count = 0; 1591 break; 1592 1593 /* Create a starfield with big concentration of stars (amas) */ 1594 case SPACE_AMAS : 1595 case SPACE_SPIRAL : 1596 /* pick 8 random position for the amas */ 1597 FillStarList(stars.list, 8); 1598 for (i=0; i<8; i++) { 1599 amas[i].x = stars.list[i].x; 1600 amas[i].y = stars.list[i].y; 1601 amas[i].z = stars.list[i].z; 1602 amas_select[i] = i; 1603 factor[i] = ((float)(crc_alea&2047) + 0.5)*(1.0/128.0) + 16.0/3.0; 1604 CrcStep(); 1605 CrcStep(); 1606 } 1607 /* make each amas ramdomly smaller or bigger */ 1608 for (i=8; i<32; i++) { 1609 amas_select[i] = (crc_alea & 7); 1610 CrcStep(); 1611 } 1612 1613 /* create a random starfield */ 1614 FillStarList(stars.list, STAR_DENSITY_MAX); 1615 1616 /* In spiral mode, only half the star will be put into the amas. 1617 the other half will be put into the spiral galaxy. */ 1618 if (space_model == SPACE_AMAS) 1619 i_step = 1; 1620 else 1621 i_step = 2; 1622 s = stars.list; 1623 1624 for (i=0; i<STAR_DENSITY_MAX; i+=i_step) { 1625 /* for every star, calculate its position relative to the 1626 center of the corresponding amas. */ 1627 index = amas_select[i&31]; 1628 dx = s->x-amas[index].x; 1629 if (dx < -0.5) dx += 1.0; 1630 if (dx > 0.5) dx -= 1.0; 1631 dy = s->y-amas[index].y; 1632 if (dy < -0.5) dy += 1.0; 1633 if (dy > 0.5) dy -= 1.0; 1634 dz = s->z-amas[index].z; 1635 if (dz < -0.5) dz += 1.0; 1636 if (dz > 0.5) dz -= 1.0; 1637 1638 /* make the star randomly closer from its center, but keep 1639 it on the same orientation. */ 1640 step = 0; 1641 dist = (abs(dx) + abs(dy) + abs(dz))*factor[index]; 1642 while (dist > 1.0) { 1643 dist *= 0.5; 1644 step++; 1645 } 1646 1647 step -= (crc_alea&3); 1648 CrcStep(); 1649 fact = 1.0; 1650 for (;step>=0; step--) 1651 fact *= 0.55; 1652 dx *= fact; 1653 dy *= fact; 1654 dz *= fact; 1655 1656 /* put the star back in the [0-1]x[0-1]x[0-1] iteration of 1657 the cubic torus. */ 1658 s->x = amas[index].x + dx; 1659 if (s->x >= 1.0) s->x -= 1.0; 1660 if (s->x <= 0.0) s->x += 1.0; 1661 s->y = amas[index].y + dy; 1662 if (s->y >= 1.0) s->y -= 1.0; 1663 if (s->y <= 0.0) s->y += 1.0; 1664 s->z = amas[index].z + dz; 1665 if (s->z >= 1.0) s->z -= 1.0; 1666 if (s->z <= 0.0) s->z += 1.0; 1667 1668 s += i_step; 1669 } 1670 1671 /* record the center of the amas as key points for the free 1672 camera animation mode. */ 1673 for (i=0; i<8; i++) 1674 key_points[i] = amas[i]; 1675 key_point_count = 8; 1676 1677 /* no further processing needed in amas only mode. */ 1678 if (space_model == SPACE_AMAS) 1679 break; 1680 1681 /* in spiral mode, the second half of the star will be distributed 1682 on random spiral like galaxy. */ 1683 s = stars.list+1; 1684 for (i=1; i<STAR_DENSITY_MAX; i+=2) { 1685 /* some random point (probability 50 %) will be move into a 1686 big amas at the center of the spiral galaxy. */ 1687 if (crc_alea & 2048) { 1688 /* for every star, calculate its position relative to the 1689 center of the galaxy. */ 1690 dx = s->x - 0.5; 1691 dy = s->y - 0.5; 1692 dz = s->z - 0.5; 1693 1694 /* make the star randomly closer from its center, but keep 1695 it on the same orientation. */ 1696 step = 0; 1697 dist = (dx*dx + dy*dy + dz*dz) * (32.0/0.75); 1698 while (dist > 1.0) { 1699 dist *= 0.5; 1700 step++; 1701 } 1702 1703 step -= (crc_alea&3); 1704 CrcStep(); 1705 fact = 0.5; 1706 for (;step>=0; step--) 1707 fact *= 0.55; 1708 dx *= fact; 1709 dy *= fact; 1710 dz *= fact; 1711 } 1712 else { 1713 /* other star are put at a random place somewhere on one of 1714 teh two spiral arms... */ 1715 alpha = 3.4 * s->x * (s->x*0.5 + 1.0); 1716 if (crc_alea & 64) 1717 alpha += 3.14159; 1718 r = s->x * 0.34 + 0.08; 1719 r += (s->y-0.725 + 0.03 * (float)(crc_alea & 15))*0.04*(1.2+r); 1720 r *= 0.5; 1721 dx = (s->z-0.8 + 0.04 * (float)(crc_alea & 15)) * (2.0 - abs(s->y - 0.5)) * (0.025*0.5); 1722 dy = cos(alpha) * r; 1723 dz = sin(alpha) * r; 1724 } 1725 CrcStep(); 1726 1727 /* put the star back in the [0-1]x[0-1]x[0-1] iteration of 1728 the cubic torus. */ 1729 s->x = 0.5 + dx; 1730 s->y = 0.5 + dy; 1731 s->z = 0.5 + dz; 1732 s += 2; 1733 } 1734 1735 /* add the center of the galaxy to the key point list for free camera 1736 animation mode */ 1737 key_points[8].x = 0.5; 1738 key_points[8].y = 0.5; 1739 key_points[8].z = 0.5; 1740 /* add seven other galaxy star to the key point list */ 1741 for (i=9; i<16; i++) { 1742 key_points[i].x = stars.list[i*(STAR_DENSITY_MAX/18)].x; 1743 key_points[i].y = stars.list[i*(STAR_DENSITY_MAX/18)].y; 1744 key_points[i].z = stars.list[i*(STAR_DENSITY_MAX/18)].z; 1745 } 1746 key_point_count = 16; 1747 break; 1748 } 1749 1750 /* In all starfield modes, for all stars, peek a random brightness level */ 1751 for (i=0; i<STAR_DENSITY_MAX; i++) { 1752 stars.list[i].size = (float)((crc_alea&15)+17)*(1.0/56.0); 1753 if ((crc_alea & 0xc0) == 0) 1754 stars.list[i].size *= 2.0; 1755 if ((crc_alea & 0x3f00) == 0) 1756 stars.list[i].size *= 3.0; 1757 CrcStep(); 1758 } 1759 } 1760 1761 /* Fill a list of star with random position in the [0-1]x[0-1]x[0-1] cube */ 1762 void 1763 ChartWindow::FillStarList(star *list, int32 count) 1764 { 1765 int32 i; 1766 1767 for (i=0; i<count; i++) { 1768 list[i].x = ((float)(crc_alea&2047) + 0.5)*(1.0/2048.0); 1769 CrcStep(); 1770 } 1771 for (i=0; i<count; i++) { 1772 list[i].y = ((float)(crc_alea&2047) + 0.5)*(1.0/2048.0); 1773 CrcStep(); 1774 } 1775 for (i=0; i<count; i++) { 1776 list[i].z = ((float)(crc_alea&2047) + 0.5)*(1.0/2048.0); 1777 CrcStep(); 1778 } 1779 } 1780 1781 /* initialise anything needed to enable a specific special animation */ 1782 void 1783 ChartWindow::InitSpecials(int32 code) 1784 { 1785 int i, j; 1786 float alpha, ksin, kcos, coeff; 1787 TPoint dx, dy; 1788 TMatrix matrix; 1789 1790 switch (code) { 1791 /* turn special animation off */ 1792 case SPECIAL_NONE : 1793 specials.count = 0; 1794 break; 1795 1796 /* Initialise the pixel-comet animation */ 1797 case SPECIAL_COMET : 1798 /* Get a bunchof random values by getting some radom stars */ 1799 specials.count = 512; 1800 FillStarList(specials.list, 4); 1801 /* for both comets... */ 1802 for (j=0; j<2; j++) { 1803 /* select the initial position of the comet head */ 1804 comet[j].x = specials.list[j].x; 1805 comet[j].y = specials.list[j].y; 1806 comet[j].z = specials.list[j].z; 1807 specials.list[0].size = 1.4; 1808 1809 /* select the speed vector of the comet */ 1810 matrix.Set(specials.list[j+2].x * 6.28319, specials.list[j+2].y * 3.14159 - 1.5708, 0.0); 1811 delta_comet[j] = matrix.Axis(0) * 0.0015; 1812 dx = matrix.Axis(1); 1813 dy = matrix.Axis(2); 1814 1815 for (i=j+2; i<specials.count; i+=2) { 1816 /* make the pixel invisible at first */ 1817 specials.list[i].x = -10.0; 1818 specials.list[i].y = 0.0; 1819 specials.list[i].z = 0.0; 1820 /* spread the initial count on a linear scale (to make pixel 1821 appear progressively */ 1822 special_list[i].comet.count = i/2; 1823 /* spread the pixel trace count randomly on a [93-124] range */ 1824 special_list[i].comet.count0 = (crc_alea & 31) + 93; 1825 CrcStep(); 1826 /* pick a random ejection angle */ 1827 alpha = ((crc_alea>>8) & 1023) * (6.283159/1024.0); 1828 CrcStep(); 1829 1830 /* pick a random ejection speed */ 1831 coeff = 0.000114 + 0.0000016 * (float)((crc_alea>>17) & 31); 1832 if ((crc_alea & 7) > 4) coeff *= 0.75; 1833 if ((crc_alea & 7) == 7) coeff *= 0.65; 1834 CrcStep(); 1835 1836 /* calculate the ejection speed vector */ 1837 ksin = sin(alpha) * coeff; 1838 kcos = cos(alpha) * coeff; 1839 special_list[i].comet.dx = dx.x * kcos + dy.x * ksin; 1840 special_list[i].comet.dy = dx.y * kcos + dy.y * ksin; 1841 special_list[i].comet.dz = dx.z * kcos + dy.z * ksin; 1842 } 1843 } 1844 break; 1845 1846 /* Add a list of random star (used for nova effect by modifying their 1847 brightness level in real time) close from the first stars of the 1848 starfield. */ 1849 case SPECIAL_NOVAS : 1850 specials.count = 96; 1851 for (i=0; i<specials.count; i++) { 1852 special_list[i].nova.count = i + 40; 1853 special_list[i].nova.count0 = (crc_alea & 63) + 28; 1854 CrcStep(); 1855 specials.list[i].x = stars.list[i].x + (crc_alea & 1)*0.02 - 0.01; 1856 CrcStep(); 1857 specials.list[i].y = stars.list[i].y + (crc_alea & 1)*0.02 - 0.01; 1858 CrcStep(); 1859 specials.list[i].z = stars.list[i].z + (crc_alea & 1)*0.02 - 0.01; 1860 CrcStep(); 1861 specials.list[i].size = 0.0; 1862 } 1863 break; 1864 1865 /* not implemented */ 1866 case SPECIAL_BATTLE : 1867 specials.count = 0; 1868 break; 1869 } 1870 } 1871 1872 /* select a color for each star (and special animation point) by 1873 looping through the color index list. */ 1874 void 1875 ChartWindow::SetStarColors(int32 *color_list, int32 color_count) 1876 { 1877 int32 i, index; 1878 1879 index = 0; 1880 for (i=0; i<STAR_DENSITY_MAX; i++) { 1881 stars.list[i].color_type = color_list[index]; 1882 index++; 1883 if (index >= color_count) 1884 index = 0; 1885 } 1886 for (i=0; i<SPECIAL_COUNT_MAX; i++) { 1887 specials.list[i].color_type = color_list[index]; 1888 index++; 1889 if (index >= color_count) 1890 index = 0; 1891 } 1892 } 1893 1894 1895 void 1896 ChartWindow::SetGeometry(int32 dh, int32 dv) 1897 { 1898 float zoom; 1899 1900 /* calculate the zoom factor for the 3d projection */ 1901 geo.zoom_factor = (float)dh*(depth_ref/DH_REF); 1902 zoom = (float)dv*(depth_ref/DV_REF); 1903 if (zoom > geo.zoom_factor) 1904 geo.zoom_factor = zoom; 1905 1906 /* offset of the origin in the view area */ 1907 geo.offset_h = (float)dh * 0.5; 1908 geo.offset_v = (float)dv * 0.5; 1909 1910 /* sub-pixel precision double-sampling */ 1911 geo.zoom_factor *= 2.0; 1912 geo.offset_h = geo.offset_h * 2.0 - 1.0; 1913 geo.offset_v = geo.offset_v * 2.0 - 1.0; 1914 } 1915 1916 1917 void 1918 ChartWindow::SetColorSpace(buffer *buf, color_space depth) 1919 { 1920 bool swap_needed; 1921 int32 red_shift = 0, green_shift = 0; 1922 int32 blue_shift = 0, alpha_shift = 0; 1923 int32 step_doubling = 0; 1924 1925 int32 red_divide_shift = 0, green_divide_shift = 0; 1926 int32 blue_divide_shift = 0, alpha_divide_shift = 0; 1927 int32 i; 1928 uint32 color; 1929 uint32 *col; 1930 BScreen screen(this); 1931 rgb_color ref_color; 1932 1933 /* depending the colorspace of the target buffer, set parameters used 1934 to encode the RGBA information for various color information in 1935 the right format. */ 1936 buf->depth = depth; 1937 switch (depth) { 1938 case B_RGBA32_BIG : 1939 case B_RGB32_BIG : 1940 case B_RGBA32 : 1941 case B_RGB32 : 1942 buf->depth_mode = PIXEL_4_BYTES; 1943 buf->bytes_per_pixel = 4; 1944 red_shift = 16; 1945 green_shift = 8; 1946 blue_shift = 0; 1947 alpha_shift = 24; 1948 red_divide_shift = 0; 1949 green_divide_shift = 0; 1950 blue_divide_shift = 0; 1951 alpha_divide_shift = 0; 1952 step_doubling = 32; 1953 break; 1954 case B_RGB16_BIG : 1955 case B_RGB16 : 1956 buf->depth_mode = PIXEL_2_BYTES; 1957 buf->bytes_per_pixel = 2; 1958 red_shift = 11; 1959 red_divide_shift = 3; 1960 green_shift = 5; 1961 green_divide_shift = 2; 1962 blue_shift = 0; 1963 blue_divide_shift = 3; 1964 alpha_shift = 32; 1965 alpha_divide_shift = 8; 1966 step_doubling = 16; 1967 break; 1968 case B_RGB15 : 1969 case B_RGBA15 : 1970 case B_RGB15_BIG : 1971 case B_RGBA15_BIG : 1972 buf->depth_mode = PIXEL_2_BYTES; 1973 buf->bytes_per_pixel = 2; 1974 red_shift = 10; 1975 red_divide_shift = 3; 1976 green_shift = 5; 1977 green_divide_shift = 3; 1978 blue_shift = 0; 1979 blue_divide_shift = 3; 1980 alpha_shift = 15; 1981 alpha_divide_shift = 7; 1982 step_doubling = 16; 1983 break; 1984 case B_CMAP8 : 1985 default: 1986 buf->depth_mode = PIXEL_1_BYTE; 1987 buf->bytes_per_pixel = 1; 1988 break; 1989 } 1990 1991 /* Check if the endianess of the buffer is different from the 1992 endianess use by the processor to encode the color information */ 1993 switch (depth) { 1994 case B_RGBA32_BIG : 1995 case B_RGB32_BIG : 1996 case B_RGB16_BIG : 1997 case B_RGB15_BIG : 1998 case B_RGBA15_BIG : 1999 swap_needed = true; 2000 break; 2001 case B_RGBA32 : 2002 case B_RGB32 : 2003 case B_RGB16 : 2004 case B_RGB15 : 2005 case B_RGBA15 : 2006 case B_CMAP8 : 2007 default: 2008 swap_needed = false; 2009 break; 2010 } 2011 2012 #if B_HOST_IS_BENDIAN 2013 swap_needed = ~swap_needed; 2014 #endif 2015 /* fill the color tables (8 light level for 7 colors, and also encode 2016 the background color */ 2017 col = buf->colors[0]; 2018 switch (buf->depth_mode) { 2019 case PIXEL_1_BYTE : 2020 /* 8 bits, indexed mode */ 2021 for (i=0; i<7*8; i++) { 2022 ref_color = color_list[i>>3]; 2023 ref_color.red = (ref_color.red*light_gradient[i&7])>>16; 2024 ref_color.green = (ref_color.green*light_gradient[i&7])>>16; 2025 ref_color.blue = (ref_color.blue*light_gradient[i&7])>>16; 2026 color = screen.IndexForColor(ref_color); 2027 col[i] = (color<<24) | (color<<16) | (color<<8) | color; 2028 } 2029 color = screen.IndexForColor(fCurrentSettings.back_color); 2030 buf->back_color = (color<<24) | (color<<16) | (color<<8) | color; 2031 break; 2032 case PIXEL_2_BYTES : 2033 case PIXEL_4_BYTES : 2034 /* 15, 16 or 32 bytes, RGB modes. Those modes just directly encode 2035 part of the bits of the initial rgba_color, at the right bit 2036 position */ 2037 for (i=0; i<7*8; i++) { 2038 ref_color = color_list[i>>3]; 2039 ref_color.red = (ref_color.red*light_gradient[i&7])>>16; 2040 ref_color.green = (ref_color.green*light_gradient[i&7])>>16; 2041 ref_color.blue = (ref_color.blue*light_gradient[i&7])>>16; 2042 color = ((uint8)ref_color.red >> red_divide_shift) << red_shift; 2043 color |= ((uint8)ref_color.green >> green_divide_shift) << green_shift; 2044 color |= ((uint8)ref_color.blue >> blue_divide_shift) << blue_shift; 2045 color |= ((uint8)ref_color.alpha >> alpha_divide_shift) << alpha_shift; 2046 col[i] = (color<<step_doubling) | color; 2047 } 2048 color = ((uint8)fCurrentSettings.back_color.red >> red_divide_shift) << red_shift; 2049 color |= ((uint8)fCurrentSettings.back_color.green >> green_divide_shift) << green_shift; 2050 color |= ((uint8)fCurrentSettings.back_color.blue >> blue_divide_shift) << blue_shift; 2051 color |= ((uint8)fCurrentSettings.back_color.alpha >> alpha_divide_shift) << alpha_shift; 2052 buf->back_color = (color<<step_doubling) | color; 2053 break; 2054 } 2055 2056 /* do the endianess swap if needed */ 2057 if (swap_needed) { 2058 col = buf->colors[0]; 2059 for (i = 0; i < 7*8; i++) { 2060 B_SWAP_INT32(col[i]); 2061 } 2062 B_SWAP_INT32(buf->back_color); 2063 } 2064 } 2065 2066 2067 /*! 2068 For each different offset used to access a pixel of the star matrix, 2069 create a buffer pointer based on the main buffer pointer offset by 2070 the pixel matrix offset. That way, any pixel of the matrix can be 2071 address later by just picking the right pointer and indexing it by 2072 the global star offset 2073 */ 2074 void 2075 ChartWindow::SetPatternBits(buffer *buf) 2076 { 2077 for (int32 i=0; i<32; i++) { 2078 buf->pattern_bits[i] = (void*)((char*)buf->bits + 2079 buf->bytes_per_row * pattern_dv[i] + 2080 buf->bytes_per_pixel * pattern_dh[i]); 2081 } 2082 } 2083 2084 2085 // #pragma mark Engine processing related functions. 2086 2087 2088 /*! 2089 That's the main thread controling the animation and synchronising 2090 the engine state with the changes coming from the UI. 2091 */ 2092 long 2093 ChartWindow::Animation(void *data) 2094 { 2095 int32 i, cur_4_frames_index, cur_last_fps, count_fps; 2096 float time_factor = 0, total_fps; 2097 float last_fps[4]; 2098 bigtime_t next_stat; 2099 bigtime_t timer, time_left, current; 2100 bigtime_t before_frame, after_frame, fps; 2101 bigtime_t last_4_frames[4]; 2102 ChartWindow *w; 2103 2104 w = (ChartWindow*)data; 2105 2106 /* init refresh rate control */ 2107 timer = system_time(); 2108 w->frame_delay = 100000; 2109 2110 /* init performance timing control variables */ 2111 next_stat = timer + STAT_DELAY; 2112 cur_4_frames_index = 0; 2113 cur_last_fps = 0; 2114 for (i=0; i<4; i++) 2115 last_fps[i] = 0.0; 2116 total_fps = 0.0; 2117 count_fps = 0; 2118 2119 /* here start the loop doing all the good stuff */ 2120 while (!w->fKillThread) { 2121 2122 /* start the performance mesurement here */ 2123 before_frame = system_time(); 2124 2125 /* credit the timer by the current delay between frame */ 2126 timer += w->frame_delay; 2127 2128 /* change the settings, if needed */ 2129 w->ChangeSetting(w->next_set); 2130 2131 /* draw the next frame */ 2132 if (w->fCurrentSettings.display == DISPLAY_BITMAP) { 2133 w->RefreshStars(&w->bitmap_buffer, time_factor * 2.4); 2134 if (w->LockWithTimeout(200000) == B_OK) { 2135 w->fChartView->DrawBitmap(w->fOffscreen); 2136 w->Unlock(); 2137 } 2138 } 2139 else if (w->fCurrentSettings.display == DISPLAY_DIRECT) { 2140 /* This part get the drawing-lock to guarantee that the 2141 directbuffer context won't change during the drawing 2142 operations. During that period, no Window should be 2143 done to avoid any potential deadlock. */ 2144 while (acquire_sem(w->fDrawingLock) == B_INTERRUPTED) 2145 ; 2146 if (w->fDirectConnected) 2147 w->RefreshStars(&w->direct_buffer, time_factor * 2.4); 2148 release_sem(w->fDrawingLock); 2149 } 2150 2151 /* do the camera animation */ 2152 w->CameraAnimation(time_factor); 2153 2154 /* end the performance mesurement here */ 2155 after_frame = system_time(); 2156 2157 /* performance timing calculation here (if display enabled). */ 2158 if (w->fCurrentSettings.display != DISPLAY_OFF) { 2159 /* record frame duration into a 2 levels 4 entries ring buffer */ 2160 last_4_frames[cur_4_frames_index] = after_frame - before_frame; 2161 cur_4_frames_index++; 2162 if (cur_4_frames_index == 4) { 2163 cur_4_frames_index = 0; 2164 last_fps[cur_last_fps++ & 3] = 2165 last_4_frames[0]+last_4_frames[1]+last_4_frames[2]+last_4_frames[3]; 2166 /* the instant load is calculated based on the average duration 2167 of the last 16 frames. */ 2168 fps = (bigtime_t) (16e6 / (last_fps[0]+last_fps[1]+last_fps[2]+last_fps[3])); 2169 w->DrawInstantLoad(fps); 2170 2171 total_fps += fps; 2172 count_fps += 1; 2173 2174 /* The statistic numbers are based on the ratio between the real 2175 duration and the frame count during a period of approximately 2176 STAT_DELAY microseconds. */ 2177 if (after_frame > next_stat) { 2178 w->PrintStatNumbers(total_fps/(float)count_fps); 2179 next_stat = after_frame+STAT_DELAY; 2180 total_fps = 0.0; 2181 count_fps = 0; 2182 } 2183 } 2184 } 2185 2186 /* do a pause if necessary */ 2187 current = system_time(); 2188 time_left = timer-current; 2189 if (time_left > 2000) { 2190 snooze(time_left); 2191 time_left = 0; 2192 } 2193 else if (time_left < -5000) 2194 timer = current; 2195 2196 /* this factor controls the dynamic timing configuration, that 2197 slow down or speed up the whole animation step to compensate 2198 for varaiation of the framerate. */ 2199 time_factor = (float)(system_time() - before_frame) * (1.0/4e4); 2200 } 2201 return 0; 2202 } 2203 2204 /* This is the second thread doing star animation. It's just a poor 2205 slave of the Animation thread. It's directly synchronised with its 2206 master, and will only do some star animation processing whenever 2207 its master allows him to do so. */ 2208 long 2209 ChartWindow::Animation2(void *data) 2210 { 2211 bigtime_t before, after; 2212 ChartWindow *w; 2213 2214 w = (ChartWindow*)data; 2215 while (!w->fKillThread) { 2216 /* This thread need to both wait for its master to unblock 2217 him to do some real work, or for the main control to 2218 set the fKillThread flag, asking it to quit. */ 2219 status_t status; 2220 do { 2221 status = acquire_sem_etc(w->second_thread_lock, 1, B_TIMEOUT, 500000); 2222 if (w->fKillThread) 2223 return 0; 2224 } while (status == B_TIMED_OUT || status == B_INTERRUPTED); 2225 2226 /* the duration of the processing is needed to control the 2227 dynamic load split (see RefreshStar) */ 2228 before = system_time(); 2229 RefreshStarPacket(w->second_thread_buffer, &w->stars2, &w->geo); 2230 RefreshStarPacket(w->second_thread_buffer, &w->specials2, &w->geo); 2231 after = system_time(); 2232 2233 w->second_thread_delay = after-before; 2234 2235 release_sem(w->second_thread_release); 2236 } 2237 return 0; 2238 } 2239 2240 2241 void 2242 ChartWindow::SetCubeOffset() 2243 { 2244 int32 i; 2245 TPoint min, max, dx, dy, dz, p1; 2246 2247 /* calculate the shortest aligned cube encapsulating the pyramid 2248 of vision, by calculating the min and max on the 3 main axis 2249 of the coordinates of the 8 extremities of the pyramid of 2250 vision (as limited by its 4 sides and the rear and front 2251 cut plan) */ 2252 min.x = min.y = min.z = 10.0; 2253 max.x = max.y = max.z = -10.0; 2254 2255 dx = camera.Axis(0)*(DH_REF*0.5); 2256 dy = camera.Axis(1)*(DV_REF*0.5); 2257 dz = camera.Axis(2)*depth_ref; 2258 2259 for (i=0; i<8; i++) { 2260 /* left side / right side */ 2261 if (i&1) p1 = dz + dx; 2262 else p1 = dz - dx; 2263 /* top side / bottom side */ 2264 if (i&2) p1 = p1 + dy; 2265 else p1 = p1 - dy; 2266 /* rear cut plan / front cut plan */ 2267 if (i&4) p1 = p1 * (1.0 / Z_CUT_RATIO); 2268 /* relative to the position of the camera */ 2269 p1 = p1 + origin; 2270 2271 if (min.x > p1.x) min.x = p1.x; 2272 if (min.y > p1.y) min.y = p1.y; 2273 if (min.z > p1.z) min.z = p1.z; 2274 if (max.x < p1.x) max.x = p1.x; 2275 if (max.y < p1.y) max.y = p1.y; 2276 if (max.z < p1.z) max.z = p1.z; 2277 } 2278 2279 /* offset the camera origin by +1 or -1 on any axis (which 2280 doesn't change its relative position in the cubic torus 2281 as the cubic torus repeat itself identicaly for any move 2282 of +1 or -1 on any axis), to get the bounding cube into 2283 [0-2[ x [0-2[ x [0-2[. As the pyramid of vision is just 2284 small enough to gurantee that its bounding box will never 2285 be larger than 1 on any axis, it's always possible. */ 2286 while (min.x < 0.0) { 2287 min.x += 1.0; 2288 max.x += 1.0; 2289 origin.x += 1.0; 2290 } 2291 while (min.y < 0.0) { 2292 min.y += 1.0; 2293 max.y += 1.0; 2294 origin.y += 1.0; 2295 } 2296 while (min.z < 0.0) { 2297 min.z += 1.0; 2298 max.z += 1.0; 2299 origin.z += 1.0; 2300 } 2301 while (max.x >= 2.0) { 2302 min.x -= 1.0; 2303 max.x -= 1.0; 2304 origin.x -= 1.0; 2305 } 2306 while (max.y >= 2.0) { 2307 min.y -= 1.0; 2308 max.y -= 1.0; 2309 origin.y -= 1.0; 2310 } 2311 while (max.z >= 2.0) { 2312 min.z -= 1.0; 2313 max.z -= 1.0; 2314 origin.z -= 1.0; 2315 } 2316 2317 /* set the cutting plans. For example, if the bouding box of 2318 the pyramid of vision of the camera imcludes only X in 2319 [0.43 ; 1.37], we know that points with X in [0 ; 0.4] are 2320 not visible from the camera. So we will offset them by +1 2321 in [1.0 ; 1.4] where they will be visible. Same process 2322 on other axis. That way, we have to test every star of the 2323 starfield in one position and only one. */ 2324 cut.x = (min.x + max.x - 1.0) * 0.5; 2325 cut.y = (min.y + max.y - 1.0) * 0.5; 2326 cut.z = (min.z + max.z - 1.0) * 0.5; 2327 2328 /* Make sure those new settings are copied into the struct 2329 used by the embedded C-engine. */ 2330 SyncGeo(); 2331 } 2332 2333 /* move the camera around, as defined by the animation popup. 2334 This is adjusted by a time factor to compensate for change 2335 in the framerate. */ 2336 void 2337 ChartWindow::CameraAnimation(float time_factor) 2338 { 2339 TPoint move; 2340 2341 switch (fCurrentSettings.animation) { 2342 /* Slow rotation around the "center" of the visible area. */ 2343 case ANIMATION_ROTATE : 2344 /* turn around a point at 0.45 in front of the camera */ 2345 move = camera.Axis(2); 2346 move = move * 0.45; 2347 origin = origin + move; 2348 2349 /* turn around the alpha angle of the spheric rotation 2350 matrix */ 2351 camera_alpha += 0.011*time_factor; 2352 if (camera_alpha > 2*3.14159) 2353 camera_alpha -= 2*3.14159; 2354 2355 /* set the other two angles close from hardcoded values */ 2356 if (camera_theta < 0.18) 2357 camera_theta += 0.003*time_factor; 2358 if (camera_theta > 0.22) 2359 camera_theta -= 0.003*time_factor; 2360 2361 if (camera_phi < -0.02) 2362 camera_phi += 0.003*time_factor; 2363 if (camera_phi > 0.02) 2364 camera_phi -= 0.003*time_factor; 2365 2366 camera.Set(camera_alpha, camera_theta, camera_phi); 2367 camera_invert = camera.Transpose(); 2368 move = camera.Axis(2); 2369 move = move * -0.45; 2370 origin = origin + move; 2371 /* As we moved or rotated the camera, we need to process 2372 again the parameters specific to the pyramid of vision. */ 2373 SetCubeOffset(); 2374 break; 2375 2376 case ANIMATION_SLOW_MOVE : 2377 /* Just move forward, at slow speed */ 2378 move = camera.Axis(2); 2379 move = move * 0.006*time_factor; 2380 origin = origin + move; 2381 SetCubeOffset(); 2382 break; 2383 2384 case ANIMATION_FAST_MOVE : 2385 /* Just move forward, at fast speed */ 2386 move = camera.Axis(2); 2387 move = move * 0.018*time_factor; 2388 origin = origin + move; 2389 SetCubeOffset(); 2390 break; 2391 2392 case ANIMATION_FREE_MOVE : 2393 /* go into advanced selection process no more than once every 2394 0.5 time unit (average time). */ 2395 last_dynamic_delay += time_factor; 2396 if (last_dynamic_delay > 0.5) { 2397 last_dynamic_delay -= 0.5; 2398 if (last_dynamic_delay > 0.2) 2399 last_dynamic_delay = 0.2; 2400 2401 /* if we're not following any target, then just turn 2402 randomly (modifying only the direction of the 2403 acceleration) */ 2404 if (tracking_target < 0) { 2405 if ((crc_alea & 0x4200) == 0) { 2406 if (crc_alea & 0x8000) 2407 cnt_alpha += 1 - (cnt_alpha/4); 2408 else 2409 cnt_alpha += -1 - (cnt_alpha/4); 2410 CrcStep(); 2411 if (crc_alea & 0x8000) 2412 cnt_theta += 1 - (cnt_theta/4); 2413 else 2414 cnt_theta += -1 - (cnt_theta/4); 2415 CrcStep(); 2416 if (crc_alea & 0x8000) 2417 cnt_phi += 1 - (cnt_phi/4); 2418 else 2419 cnt_phi += -1 - (cnt_phi/4); 2420 CrcStep(); 2421 } 2422 CrcStep(); 2423 } 2424 /* if following a target, try to turn in its direction */ 2425 else 2426 FollowTarget(); 2427 2428 /* Change target everyonce in a while... */ 2429 if ((crc_alea & 0xf80) == 0) 2430 SelectNewTarget(); 2431 2432 /* depending the direction of acceleration, increase or 2433 reduce the angular speed of the 3 spherical angles. */ 2434 if (cnt_alpha < 0) 2435 d_alpha += -0.0005 - d_alpha * 0.025; 2436 else if (cnt_alpha > 0) 2437 d_alpha += 0.0005 - d_alpha * 0.025; 2438 2439 if (cnt_theta < 0) 2440 d_theta += -0.0002 - d_theta * 0.025; 2441 else if (cnt_theta > 0) 2442 d_theta += 0.0002 - d_theta * 0.025; 2443 2444 if (cnt_phi < 0) 2445 d_phi += -0.00025 - d_phi * 0.025; 2446 else if (cnt_phi >0) 2447 d_phi += 0.00025 - d_phi * 0.025; 2448 } 2449 2450 /* turn the camera following the specified angular speed */ 2451 camera_alpha += d_alpha*time_factor; 2452 if (camera_alpha < 0.0) 2453 camera_alpha += 2*3.14159; 2454 else if (camera_alpha > 2*3.14159) 2455 camera_alpha -= 2*3.14159; 2456 2457 camera_theta += d_theta*time_factor; 2458 if (camera_theta < 0.0) 2459 camera_theta += 2*3.14159; 2460 else if (camera_theta > 2*3.14159) 2461 camera_theta -= 2*3.14159; 2462 2463 camera_phi += d_phi*time_factor; 2464 if (camera_phi < 0.0) 2465 camera_phi += 2*3.14159; 2466 else if (camera_phi > 2*3.14159) 2467 camera_phi -= 2*3.14159; 2468 2469 /* Set the new rotation matrix of the camera */ 2470 camera.Set(camera_alpha, camera_theta, camera_phi); 2471 camera_invert = camera.Transpose(); 2472 2473 /* move the camera forward at medium speed */ 2474 move = camera.Axis(2); 2475 move = move * 0.0115*time_factor; 2476 origin = origin + move; 2477 SetCubeOffset(); 2478 break; 2479 } 2480 } 2481 2482 2483 void 2484 ChartWindow::SelectNewTarget() 2485 { 2486 float ratio, ratio_min; 2487 float dist, lateral, axial, ftmp; 2488 int32 i, index_min; 2489 TPoint axis, pt, vect; 2490 2491 axis = camera.Axis(2); 2492 ratio_min = 1e6; 2493 index_min = -3; 2494 2495 for (i=-2; i<key_point_count; i++) { 2496 /* if they're used, the comets are two good potential 2497 targets. */ 2498 if (i < 0) { 2499 if (fCurrentSettings.special == SPECIAL_COMET) 2500 pt = comet[i+2]; 2501 else 2502 continue; 2503 } 2504 /* other potential targets are the key_points defined 2505 in the star field. */ 2506 else 2507 pt = key_points[i]; 2508 2509 /* Qualify the interest of the potential target in 2510 relationship with its distance and its proximity to 2511 the axis of the camera. */ 2512 if (pt.x < cut.x) 2513 pt.x += 1.0; 2514 if (pt.y < cut.y) 2515 pt.y += 1.0; 2516 if (pt.z < cut.z) 2517 pt.z += 1.0; 2518 pt = pt - origin; 2519 dist = pt.Length(); 2520 ftmp = 1.0/dist; 2521 pt.x *= ftmp; 2522 pt.y *= ftmp; 2523 pt.z *= ftmp; 2524 vect = pt ^ axis; 2525 lateral = axis.Length(); 2526 axial = pt.x*axis.x + pt.y*axis.y + pt.z*axis.z; 2527 ratio = (lateral/axial) * sqrt(dist); 2528 2529 /* keep track of the best possible choice */ 2530 if ((dist > 0.05) && (ratio < ratio_min)) { 2531 ratio_min = ratio; 2532 index_min = i; 2533 } 2534 } 2535 2536 /* record what target has been chosen */ 2537 tracking_target = index_min+2; 2538 } 2539 2540 /* Try to change the angular acceleration to aim in direction 2541 of the current target. */ 2542 void 2543 ChartWindow::FollowTarget() 2544 { 2545 float x0, y0, x, y, z, cphi, sphi; 2546 TPoint pt; 2547 2548 /* get the target point */ 2549 if (tracking_target < 2) 2550 pt = comet[tracking_target]; 2551 else 2552 pt = key_points[tracking_target-2]; 2553 /* move it in the right iteration of the cubic torus (the 2554 one iteration that is the most likely to be close from 2555 the pyramid of vision. */ 2556 if (pt.x < cut.x) 2557 pt.x += 1.0; 2558 if (pt.y < cut.y) 2559 pt.y += 1.0; 2560 if (pt.z < cut.z) 2561 pt.z += 1.0; 2562 /* convert the target coordinates in the camera referential */ 2563 pt = pt - origin; 2564 x = camera_invert.m[0][0]*pt.x + camera_invert.m[1][0]*pt.y + camera_invert.m[2][0]*pt.z; 2565 y = camera_invert.m[0][1]*pt.x + camera_invert.m[1][1]*pt.y + camera_invert.m[2][1]*pt.z; 2566 z = camera_invert.m[0][2]*pt.x + camera_invert.m[1][2]*pt.y + camera_invert.m[2][2]*pt.z; 2567 if (z <= 0.001) { 2568 /* need to do a U-turn (better to do it using theta). */ 2569 cnt_alpha = 0; 2570 cnt_theta = -1; 2571 cnt_phi = 0; 2572 } 2573 else { 2574 /* need to do a direction adjustement (play with 2575 alpha and theta) */ 2576 cphi = cos(camera_phi); 2577 sphi = sin(camera_phi); 2578 x0 = x*cphi - y*sphi; 2579 y0 = x*sphi + y*cphi; 2580 2581 /* need to move first on the left/right axis */ 2582 if (abs(x0) > abs(y0)) { 2583 if (x0 > 0) 2584 cnt_alpha = -1; 2585 else 2586 cnt_alpha = 1; 2587 cnt_theta = 0; 2588 cnt_phi = 0; 2589 } 2590 /* need to move first on the top/bottom axis */ 2591 else { 2592 if (y0 > 0) 2593 cnt_theta = -1; 2594 else 2595 cnt_theta = 1; 2596 cnt_alpha = 0; 2597 cnt_phi = 0; 2598 } 2599 } 2600 } 2601 2602 /* Do whatever special processing is required to do special 2603 animation. This used a time_step (or time_factor) to 2604 compensate for change in the framerate of the animation. */ 2605 void 2606 ChartWindow::AnimSpecials(float time_step) 2607 { 2608 int i, j; 2609 star *s; 2610 float delta; 2611 special *sp; 2612 2613 switch (fCurrentSettings.special) { 2614 case SPECIAL_COMET : 2615 /* for both comets... */ 2616 for (j=0; j<2; j++) { 2617 /* move the comet forward, at its specific speed */ 2618 comet[j] = comet[j] + delta_comet[j] * time_step; 2619 /* Insure that the comet stays in the [0-1]x[0-1]x[0-1] 2620 iteration of the cubic torus. */ 2621 if (comet[j].x < 0.0) comet[j].x += 1.0; 2622 else if (comet[j].x > 1.0) comet[j].x -= 1.0; 2623 if (comet[j].y < 0.0) comet[j].y += 1.0; 2624 else if (comet[j].y > 1.0) comet[j].y -= 1.0; 2625 if (comet[j].z < 0.0) comet[j].z += 1.0; 2626 else if (comet[j].z > 1.0) comet[j].z -= 1.0; 2627 /* set the position of the star used to represent the 2628 head of the comet. */ 2629 specials.list[j].x = comet[j].x; 2630 specials.list[j].y = comet[j].y; 2631 specials.list[j].z = comet[j].z; 2632 2633 /* for other point, the ones that are ejected from the 2634 comet, depending for allow long they have been ejected... */ 2635 s = specials.list+j+2; 2636 sp = special_list+j+2; 2637 for (i=j+2; i<specials.count; i+=2) { 2638 sp->comet.count -= (int32)time_step; 2639 /* they are reset and reejected again, just a little in 2640 the back of the head of the comet */ 2641 if (sp->comet.count <= 0.0) { 2642 delta = (0.6 + (float)(crc_alea & 31) * (1.0/32.0)) * time_step; 2643 s->x = comet[j].x + 6.0 * sp->comet.dx - delta_comet[j].x * delta; 2644 s->y = comet[j].y + 6.0 * sp->comet.dy - delta_comet[j].y * delta; 2645 s->z = comet[j].z + 6.0 * sp->comet.dz - delta_comet[j].z * delta; 2646 s->size = 0.6; 2647 sp->comet.count = (int32)(sp->comet.count0 + (crc_alea & 63)); 2648 CrcStep(); 2649 } 2650 /* or they just move at their own (ejection) speed */ 2651 else { 2652 s->x += sp->comet.dx * time_step; 2653 s->y += sp->comet.dy * time_step; 2654 s->z += sp->comet.dz * time_step; 2655 s->size *= (1.0 - 0.031 * time_step + 0.001 * time_step * time_step); 2656 } 2657 sp+=2; 2658 s+=2; 2659 } 2660 } 2661 break; 2662 2663 case SPECIAL_NOVAS : 2664 /* Novas are just stars (usualy invisible) that periodically 2665 become much brighter during a suddent flash, then disappear 2666 again until their next cycle */ 2667 sp = special_list; 2668 for (i=0; i<specials.count; i++) { 2669 sp->nova.count -= time_step; 2670 if (sp->nova.count <= 0.0) { 2671 specials.list[i].x -= 10.0; 2672 sp->nova.count = sp->nova.count0 + (crc_alea & 31); 2673 CrcStep(); 2674 } 2675 else if (sp->nova.count < 16.0) { 2676 if (specials.list[i].x < 0.0) 2677 specials.list[i].x += 10.0; 2678 specials.list[i].size = sp->nova.count; 2679 } 2680 sp++; 2681 } 2682 break; 2683 2684 case SPECIAL_BATTLE : 2685 /* not implemented */ 2686 break; 2687 } 2688 } 2689 2690 2691 /* Sync the embedded camera state with the window class camera 2692 state (before calling the embedded C-engine in ChartRender.c */ 2693 void 2694 ChartWindow::SyncGeo() 2695 { 2696 geo.x = origin.x; 2697 geo.y = origin.y; 2698 geo.z = origin.z; 2699 geo.cutx = cut.x; 2700 geo.cuty = cut.y; 2701 geo.cutz = cut.z; 2702 memcpy(geo.m, camera_invert.m, sizeof(float)*9); 2703 } 2704 2705 2706 void 2707 ChartWindow::RefreshStars(buffer *buf, float time_step) 2708 { 2709 float ratio; 2710 int32 star_threshold, special_threshold; 2711 bigtime_t before, after; 2712 star_packet stars1, specials1; 2713 2714 /* do the specials animation (single-threaded) */ 2715 AnimSpecials(time_step); 2716 2717 /* do the projection, clipping, erase and redraw 2718 of all stars. This operation is done by the 2719 embedded C-engine. This code only control the 2720 dynamic load split between the two threads, when 2721 needed. */ 2722 if (fCurrentSettings.second_thread) { 2723 star_threshold = (int32)((float)stars.count * second_thread_threshold + 0.5); 2724 special_threshold = (int32)((float)specials.count * second_thread_threshold + 0.5); 2725 2726 /* split the work load (star and special animation) 2727 between the two threads, proportionnaly to the 2728 last split factor determined during the last 2729 cycle. */ 2730 stars1.list = stars.list; 2731 stars1.count = star_threshold; 2732 stars1.erase_count = star_threshold; 2733 if (stars1.erase_count > stars.erase_count) 2734 stars1.erase_count = stars.erase_count; 2735 2736 stars2.list = stars.list + star_threshold; 2737 stars2.count = stars.count - star_threshold; 2738 stars2.erase_count = stars.erase_count - star_threshold; 2739 if (stars2.erase_count < 0) 2740 stars2.erase_count = 0; 2741 2742 specials1.list = specials.list; 2743 specials1.count = special_threshold; 2744 specials1.erase_count = special_threshold; 2745 if (specials1.erase_count > specials.erase_count) 2746 specials1.erase_count = specials.erase_count; 2747 2748 specials2.list = specials.list + special_threshold; 2749 specials2.count = specials.count - special_threshold; 2750 specials2.erase_count = specials.erase_count - special_threshold; 2751 if (specials2.erase_count < 0) 2752 specials2.erase_count = 0; 2753 2754 second_thread_buffer = buf; 2755 2756 /* release the slave thread */ 2757 release_sem(second_thread_lock); 2758 2759 /* do its own part (time it) */ 2760 before = system_time(); 2761 RefreshStarPacket(buf, &stars1, &geo); 2762 RefreshStarPacket(buf, &specials1, &geo); 2763 after = system_time(); 2764 2765 /* wait for completion of the second thread */ 2766 while (acquire_sem(second_thread_release) == B_INTERRUPTED) 2767 ; 2768 2769 /* calculate the new optimal split ratio depending 2770 of the previous one and the time used by both 2771 threads to do their work. */ 2772 ratio = ((float)second_thread_delay/(float)(after-before)) * 2773 (second_thread_threshold/(1.0-second_thread_threshold)); 2774 second_thread_threshold = ratio / (1.0+ratio); 2775 2776 2777 } 2778 /* In single-threaded mode, nothing fancy to be done. */ 2779 else { 2780 RefreshStarPacket(buf, &stars, &geo); 2781 RefreshStarPacket(buf, &specials, &geo); 2782 } 2783 2784 /* All the stars that were drawn will have to be erased during 2785 the next frame. */ 2786 stars.erase_count = stars.count; 2787 specials.erase_count = specials.count; 2788 } 2789 2790 2791 // #pragma mark Offscreen bitmap configuration related functions. 2792 2793 2794 void 2795 ChartWindow::CheckBitmap(color_space depth, int32 width, int32 height) 2796 { 2797 color_space cur_depth; 2798 2799 if (LockWithTimeout(200000) != B_OK) 2800 return; 2801 /* If there was no offscreen before, or if it was too small 2802 or in the wrong depth, then... */ 2803 if (fOffscreen == NULL) 2804 cur_depth = B_NO_COLOR_SPACE; 2805 else 2806 cur_depth = bitmap_buffer.depth; 2807 if ((cur_depth != depth) || (width > max_width) || (height > max_height)) { 2808 /* We free the old one if needed... */ 2809 if (fOffscreen) 2810 delete fOffscreen; 2811 /* We chose a new size (resizing are done by big step to 2812 avoid resizing to often)... */ 2813 while ((width > max_width) || (height > max_height)) { 2814 max_width += WINDOW_H_STEP; 2815 max_height += WINDOW_V_STEP; 2816 } 2817 /* And we try to allocate a new BBitmap at the new size. */ 2818 fOffscreen = new BBitmap(BRect(0, 0, max_width-1, max_height-1), depth); 2819 if (!fOffscreen->IsValid()) { 2820 /* If we failed, the offscreen is released and the buffer 2821 clipping is set as empty. */ 2822 delete fOffscreen; 2823 fOffscreen = NULL; 2824 bitmap_buffer.depth = B_NO_COLOR_SPACE; 2825 bitmap_buffer.clip_bounds.top = 0; 2826 bitmap_buffer.clip_bounds.left = 0; 2827 bitmap_buffer.clip_bounds.right = -1; 2828 bitmap_buffer.clip_bounds.bottom = -1; 2829 } 2830 else { 2831 /* If we succeed, then initialise the generic buffer 2832 descriptor, we set the clipping to the required size, 2833 and we set the buffer background color. */ 2834 bitmap_buffer.bits = fOffscreen->Bits(); 2835 bitmap_buffer.bytes_per_row = fOffscreen->BytesPerRow(); 2836 bitmap_buffer.buffer_width = fCurrentSettings.width; 2837 bitmap_buffer.buffer_height = fCurrentSettings.height; 2838 SetColorSpace(&bitmap_buffer, fOffscreen->ColorSpace()); 2839 SetPatternBits(&bitmap_buffer); 2840 SetBitmapClipping(fCurrentSettings.width, fCurrentSettings.height); 2841 SetBitmapBackGround(); 2842 } 2843 } 2844 Unlock(); 2845 } 2846 2847 2848 void 2849 ChartWindow::SetBitmapClipping(int32 width, int32 height) 2850 { 2851 /* Set the bitmap buffer clipping to the required size of 2852 the buffer (even if the allocated buffer is larger) */ 2853 bitmap_buffer.clip_list_count = 1; 2854 bitmap_buffer.clip_bounds.top = 0; 2855 bitmap_buffer.clip_bounds.left = 0; 2856 bitmap_buffer.clip_bounds.right = width-1; 2857 bitmap_buffer.clip_bounds.bottom = height-1; 2858 bitmap_buffer.clip_list[0].top = bitmap_buffer.clip_bounds.top; 2859 bitmap_buffer.clip_list[0].left = bitmap_buffer.clip_bounds.left; 2860 bitmap_buffer.clip_list[0].right = bitmap_buffer.clip_bounds.right; 2861 bitmap_buffer.clip_list[0].bottom = bitmap_buffer.clip_bounds.bottom; 2862 } 2863 2864 2865 void 2866 ChartWindow::SetBitmapBackGround() 2867 { 2868 int32 i, count; 2869 uint32 *bits; 2870 uint32 color; 2871 2872 /* set the bitmap buffer to the right background color */ 2873 bits = (uint32*)fOffscreen->Bits(); 2874 count = fOffscreen->BitsLength()/4; 2875 color = bitmap_buffer.back_color; 2876 2877 for (i=0; i<count; i++) 2878 bits[i] = color; 2879 } 2880 2881 2882 // #pragma mark DirectWindow related functions. 2883 2884 2885 void 2886 ChartWindow::DirectConnected(direct_buffer_info *info) 2887 { 2888 /* block the animation thread. */ 2889 while (acquire_sem(fDrawingLock) == B_INTERRUPTED) 2890 ; 2891 /* update the direct screen infos. */ 2892 SwitchContext(info); 2893 /* unblock the animation thread. */ 2894 release_sem(fDrawingLock); 2895 } 2896 2897 /* This function update the internal graphic context of the ChartWindow 2898 object to reflect the infos send through the DirectConnected API. 2899 It also update the state of stars (and erase some of them) to 2900 insure a clean transition during resize. As this function is called 2901 in DirectConnected, it's a bad idea to do any heavy drawing (long) 2902 operation. But as we only update the stars (the background will be 2903 update a little later by the view system), it's not a big deal. */ 2904 void 2905 ChartWindow::SwitchContext(direct_buffer_info *info) 2906 { 2907 //star *s; 2908 uint32 i, j; 2909 2910 /* you need to use that mask to read the buffer state. */ 2911 switch (info->buffer_state & B_DIRECT_MODE_MASK) { 2912 /* start a direct screen connection. */ 2913 case B_DIRECT_START : 2914 /* set the status as connected, and continue as a modify */ 2915 fDirectConnected = true; 2916 2917 /* change the state of a direct screen connection. */ 2918 case B_DIRECT_MODIFY : 2919 /* update the description of the abstract buffer representing 2920 the direct window connection. DirectConnected returns the 2921 description of the full content area. As we want to use 2922 only the animation view part of the window, we will need 2923 to compensate for that when update the descriptor. */ 2924 2925 /* This calculate the base address of the animation view, taking into 2926 account the base address of the screen buffer, the position of the 2927 window and the position of the view in the window */ 2928 direct_buffer.bits = (void*)((char*)info->bits + 2929 (info->window_bounds.top + TOP_LEFT_LIMIT) * info->bytes_per_row + 2930 (info->window_bounds.left + LEFT_WIDTH) * (info->bits_per_pixel>>3)); 2931 /* Bytes per row and pixel-format are the same than the window values */ 2932 direct_buffer.bytes_per_row = info->bytes_per_row; 2933 SetColorSpace(&direct_buffer, info->pixel_format); 2934 SetPatternBits(&direct_buffer); 2935 2936 /* the width and height of the animation view are linked to the width 2937 and height of the window itself, reduced by the size of the borders 2938 reserved for the UI. */ 2939 direct_buffer.buffer_width = 2940 info->window_bounds.right-info->window_bounds.left+1 - LEFT_WIDTH; 2941 direct_buffer.buffer_height = 2942 info->window_bounds.bottom-info->window_bounds.top+1 - TOP_LEFT_LIMIT; 2943 2944 /* Now, we go through the clipping list and "clip" the clipping 2945 rectangle to the animation view boundary. */ 2946 j = 0; 2947 for (i=0; i<info->clip_list_count; i++) { 2948 direct_buffer.clip_list[j].top = info->clip_list[i].top - info->window_bounds.top; 2949 if (direct_buffer.clip_list[j].top < TOP_LEFT_LIMIT) 2950 direct_buffer.clip_list[j].top = TOP_LEFT_LIMIT; 2951 direct_buffer.clip_list[j].left = info->clip_list[i].left - info->window_bounds.left; 2952 if (direct_buffer.clip_list[j].left < LEFT_WIDTH) 2953 direct_buffer.clip_list[j].left = LEFT_WIDTH; 2954 direct_buffer.clip_list[j].right = info->clip_list[i].right - info->window_bounds.left; 2955 direct_buffer.clip_list[j].bottom = info->clip_list[i].bottom - info->window_bounds.top; 2956 2957 /* All clipped rectangle that are not empty are recorded in 2958 the buffer clipping list. We keep only the 64 first (as 2959 a reasonnable approximation of most cases), but the rectangle 2960 list could easily be made dynamic if needed. Those clipping 2961 rectangle are offset to animation view coordinates */ 2962 if ((direct_buffer.clip_list[j].top <= direct_buffer.clip_list[j].bottom) && 2963 (direct_buffer.clip_list[j].left <= direct_buffer.clip_list[j].right)) { 2964 direct_buffer.clip_list[j].top -= TOP_LEFT_LIMIT; 2965 direct_buffer.clip_list[j].left -= LEFT_WIDTH; 2966 direct_buffer.clip_list[j].right -= LEFT_WIDTH; 2967 direct_buffer.clip_list[j].bottom -= TOP_LEFT_LIMIT; 2968 j++; 2969 if (j == 64) 2970 break; 2971 } 2972 } 2973 /* record the count of clipping rect in the new clipping list (less 2974 or equal to the window clipping list count, as some rectangle can 2975 be made invisible by the extra animation view clipping */ 2976 direct_buffer.clip_list_count = j; 2977 2978 /* the bounding box of the clipping list need to be calculate again 2979 from scratsh. Clipping the bounding box of the window clipping 2980 region to the animation view can give us an incorrect (larger) 2981 bounding box. Remember that the bounding box of a region is 2982 required to be minimal */ 2983 direct_buffer.clip_bounds.top = 20000; 2984 direct_buffer.clip_bounds.left = 20000; 2985 direct_buffer.clip_bounds.right = -20000; 2986 direct_buffer.clip_bounds.bottom = -20000; 2987 2988 for (i=0; i<direct_buffer.clip_list_count; i++) { 2989 if (direct_buffer.clip_bounds.top > direct_buffer.clip_list[i].top) 2990 direct_buffer.clip_bounds.top = direct_buffer.clip_list[i].top; 2991 if (direct_buffer.clip_bounds.left > direct_buffer.clip_list[i].left) 2992 direct_buffer.clip_bounds.left = direct_buffer.clip_list[i].left; 2993 if (direct_buffer.clip_bounds.right < direct_buffer.clip_list[i].right) 2994 direct_buffer.clip_bounds.right = direct_buffer.clip_list[i].right; 2995 if (direct_buffer.clip_bounds.bottom < direct_buffer.clip_list[i].bottom) 2996 direct_buffer.clip_bounds.bottom = direct_buffer.clip_list[i].bottom; 2997 } 2998 2999 /* If the bounding box is empty, nothing is visible and all erasing 3000 should be canceled */ 3001 if ((direct_buffer.clip_bounds.top > direct_buffer.clip_bounds.bottom) || 3002 (direct_buffer.clip_bounds.left > direct_buffer.clip_bounds.right)) { 3003 stars.erase_count = 0; 3004 goto nothing_visible; 3005 } 3006 3007 if (fCurrentSettings.display == DISPLAY_DIRECT) { 3008 /* When the direct display mode is used, the geometry changes 3009 need to be immediatly applied to the engine. */ 3010 SetGeometry(direct_buffer.buffer_width, direct_buffer.buffer_height); 3011 /* if the buffer was reset (that includes testing the work-around 3012 for the known bug in the 1.3.0 version of the app_server), then 3013 we cancel the erasing of the stars for the next frame. */ 3014 if ((info->buffer_state & B_BUFFER_RESET) || 3015 (need_r3_buffer_reset_work_around && 3016 ((info->buffer_state & (B_DIRECT_MODE_MASK|B_BUFFER_MOVED)) == B_DIRECT_START))) { 3017 stars.erase_count = 0; 3018 } 3019 /* In the other case, we need to cancel the erasing of star that 3020 were drawn at the previous frame, but are no longer visible */ 3021 else if (info->buffer_state & B_CLIPPING_MODIFIED) { 3022 RefreshClipping(&direct_buffer, &stars); 3023 RefreshClipping(&direct_buffer, &specials); 3024 } 3025 } 3026 break; 3027 3028 /* stop a direct screen connection */ 3029 case B_DIRECT_STOP : 3030 /* set the status as not connected */ 3031 fDirectConnected = false; 3032 nothing_visible: 3033 /* set an empty clipping */ 3034 direct_buffer.clip_list_count = 1; 3035 direct_buffer.clip_bounds.top = 0; 3036 direct_buffer.clip_bounds.left = 0; 3037 direct_buffer.clip_bounds.right = -1; 3038 direct_buffer.clip_bounds.bottom = -1; 3039 direct_buffer.clip_list[0].top = 0; 3040 direct_buffer.clip_list[0].left = 0; 3041 direct_buffer.clip_list[0].right = -1; 3042 direct_buffer.clip_list[0].bottom = -1; 3043 break; 3044 } 3045 } 3046 3047 3048 /*! copy a setting into another */ 3049 void 3050 ChartWindow::setting::Set(setting *master) 3051 { 3052 memcpy(this, master, sizeof(setting)); 3053 } 3054 3055 3056 /*! Pseudo-random generator increment function. */ 3057 void 3058 ChartWindow::CrcStep() 3059 { 3060 crc_alea <<= 1; 3061 if (crc_alea < 0) 3062 crc_alea ^= CRC_KEY; 3063 } 3064