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 fNextSettings.Set(&fCurrentSettings); 426 427 /* initialise various global parameters */ 428 fInstantLoadLevel = 0; 429 fSecondThreadThreshold = 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 fSecondThreadLock = create_sem(0, "chart second locker"); 868 fSecondThreadRelease = 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 status_t result; 887 888 /* setting this flag force both animation threads to quit */ 889 fKillThread = true; 890 891 /* wait for the two animation threads to quit */ 892 wait_for_thread(fAnimationThread, &result); 893 wait_for_thread(fSecondAnimationThread, &result); 894 895 /* free the offscreen bitmap if any */ 896 delete fOffscreen; 897 898 /* release the semaphores used for synchronisation */ 899 delete_sem(fDrawingLock); 900 delete_sem(fSecondThreadLock); 901 delete_sem(fSecondThreadRelease); 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 BWindow::QuitRequested(); 918 } 919 920 921 void 922 ChartWindow::MessageReceived(BMessage *message) 923 { 924 int32 index, color; 925 BHandler *handler; 926 BCheckBox *check_box; 927 BSlider *slider; 928 929 message->FindPointer("source", (void**)&handler); 930 switch(message->what) { 931 /* This is a key part of the architecture. MessageReceived is 932 called whenever the user interact with a UI element to change 933 a setting. The window is locked at this point, so changing 934 the setting of the engine at that point would be dangerous. 935 We could easily goofed and create a bad dependencies between 936 the Window locking mechanism and DirectConnected, that 937 would generate a deadlock and force the app_server to kill 938 the application. Bad business. To avoid that, we keep two 939 different engine setting. One that is currently used by the 940 animation engine, the other one that will retain all the 941 changes generated by the user until the engine is ready to 942 use them. So message received will write into that setting 943 state and the engine will read it from time to time. Both 944 access can be done asynchronously as all intermediate state 945 generated by the MessageReceived write are valid (we don't 946 need to make those transactions atomic). */ 947 case ANIM_OFF_MSG : 948 case ANIM_SLOW_ROT_MSG : 949 case ANIM_SLOW_MOVE_MSG : 950 case ANIM_FAST_MOVE_MSG : 951 case ANIM_FREE_MOVE_MSG : 952 fNextSettings.animation = ANIMATION_OFF + (message->what - ANIM_OFF_MSG); 953 break; 954 case DISP_OFF_MSG : 955 case DISP_BITMAP_MSG : 956 case DISP_DIRECT_MSG : 957 fNextSettings.display = DISPLAY_OFF + (message->what - DISP_OFF_MSG); 958 break; 959 case SPACE_CHAOS_MSG : 960 case SPACE_AMAS_MSG : 961 case SPACE_SPIRAL_MSG : 962 fNextSettings.space_model = SPACE_CHAOS + (message->what - SPACE_CHAOS_MSG); 963 break; 964 case FULL_SCREEN_MSG : 965 check_box = dynamic_cast<BCheckBox*>(handler); 966 if (check_box->Value()) 967 fNextSettings.fullscreen_mode = FULLSCREEN_MODE; 968 else 969 fNextSettings.fullscreen_mode = WINDOW_MODE; 970 break; 971 case AUTO_DEMO_MSG : 972 fNextSettings.fullscreen_mode = FULLDEMO_MODE; 973 fNextSettings.animation = ANIMATION_FREE_MOVE; 974 fNextSettings.special = SPECIAL_COMET; 975 LaunchSound(); 976 break; 977 case BACK_DEMO_MSG : 978 fNextSettings.fullscreen_mode = previous_fullscreen_mode; 979 break; 980 case SECOND_THREAD_MSG : 981 check_box = dynamic_cast<BCheckBox*>(handler); 982 fNextSettings.second_thread = (check_box->Value()?true:false); 983 break; 984 case COLORS_RED_MSG : 985 case COLORS_GREEN_MSG : 986 case COLORS_BLUE_MSG : 987 case COLORS_YELLOW_MSG : 988 case COLORS_ORANGE_MSG : 989 case COLORS_PINK_MSG : 990 case COLORS_WHITE_MSG : 991 index = message->what - COLORS_RED_MSG; 992 check_box = dynamic_cast<BCheckBox*>(handler); 993 fNextSettings.colors[index] = (check_box->Value()?true:false); 994 break; 995 case SPECIAL_NONE_MSG : 996 case SPECIAL_COMET_MSG : 997 case SPECIAL_NOVAS_MSG : 998 case SPECIAL_BATTLE_MSG : 999 fNextSettings.special = SPECIAL_NONE + (message->what - SPECIAL_NONE_MSG); 1000 break; 1001 case COLOR_PALETTE_MSG : 1002 message->FindInt32("be:value", &color); 1003 fNextSettings.back_color.red = (color >> 24); 1004 fNextSettings.back_color.green = (color >> 16); 1005 fNextSettings.back_color.blue = (color >> 8); 1006 fNextSettings.back_color.alpha = color; 1007 break; 1008 case STAR_DENSITY_MSG : 1009 slider = dynamic_cast<BSlider*>(handler); 1010 fNextSettings.star_density = slider->Value(); 1011 break; 1012 case REFRESH_RATE_MSG : 1013 slider = dynamic_cast<BSlider*>(handler); 1014 fNextSettings.refresh_rate = exp(slider->Value()*0.001*(log(REFRESH_RATE_MAX/REFRESH_RATE_MIN)))* 1015 REFRESH_RATE_MIN; 1016 break; 1017 /* open the three floating window used to do live setting of 1018 some advanced parameters. Those windows will return live 1019 feedback that will be executed by some of the previous 1020 messages. */ 1021 case OPEN_COLOR_MSG : 1022 OpenColorPalette(BPoint(200.0, 200.0)); 1023 break; 1024 case OPEN_DENSITY_MSG : 1025 OpenStarDensity(BPoint(280.0, 280.0)); 1026 break; 1027 case OPEN_REFRESH_MSG : 1028 OpenRefresh(BPoint(240.0, 340.0)); 1029 break; 1030 /* let other messages pass through... */ 1031 default : 1032 BDirectWindow::MessageReceived(message); 1033 break; 1034 } 1035 } 1036 1037 1038 void 1039 ChartWindow::ScreenChanged(BRect screen_size, color_space depth) 1040 { 1041 /* this is the same principle than the one described for 1042 MessageReceived, to inform the engine that the depth of 1043 the screen changed (needed only for offscreen bitmap. 1044 In DirectWindow, you get a direct notification). */ 1045 fNextSettings.depth = BScreen(this).ColorSpace(); 1046 } 1047 1048 1049 void 1050 ChartWindow::FrameResized(float new_width, float new_height) 1051 { 1052 /* this is the same principle than the one described for 1053 MessageReceived, to inform the engine that the window 1054 size changed (needed only for offscreen bitmap. In 1055 DirectWindow, you get a direct notification). */ 1056 fNextSettings.width = (int32)Frame().Width()+1-LEFT_WIDTH; 1057 fNextSettings.height = (int32)Frame().Height()+1-TOP_LEFT_LIMIT; 1058 } 1059 1060 1061 // #pragma mark User Interface related stuff... 1062 1063 1064 /* loop through the window list of the application, looking for 1065 a window with a specified name. */ 1066 BWindow * 1067 ChartWindow::GetAppWindow(char *name) 1068 { 1069 int32 index; 1070 BWindow *window; 1071 1072 for (index = 0;; index++) { 1073 window = be_app->WindowAt(index); 1074 if (window == NULL) 1075 break; 1076 if (window->LockWithTimeout(200000) == B_OK) { 1077 if (strcmp(window->Name(), name) == 0) { 1078 window->Unlock(); 1079 break; 1080 } 1081 window->Unlock(); 1082 } 1083 } 1084 return window; 1085 } 1086 1087 /* this function return a picture (in active or inactive state) of 1088 a standard BButton with some specific content draw in the middle. 1089 button_type indicate what special content should be used. */ 1090 BPicture * 1091 ChartWindow::ButtonPicture(bool active, int32 button_type) 1092 { 1093 char word[6]; 1094 int32 value; 1095 BRect r; 1096 BPoint delta; 1097 BPicture *pict; 1098 1099 1100 /* create and open the picture */ 1101 pict = new BPicture(); 1102 r = offwindow_button->Bounds(); 1103 offwindow_button->SetValue(active); 1104 offwindow_button->BeginPicture(pict); 1105 /* draw the standard BButton in whatever state is required. */ 1106 offwindow_button->Draw(r); 1107 if (button_type == COLOR_BUTTON_PICT) { 1108 /* this button just contains a rectangle of the current background 1109 color, with a one pixel black border. */ 1110 r.InsetBy(6.0, 4.0); 1111 offwindow_button->SetHighColor(0, 0, 0); 1112 offwindow_button->StrokeRect(r); 1113 r.InsetBy(1.0, 1.0); 1114 offwindow_button->SetHighColor(fCurrentSettings.back_color); 1115 offwindow_button->FillRect(r); 1116 } 1117 else if (button_type == DENSITY_BUTTON_PICT) { 1118 /* this button just contains a big string (using a bigger font size 1119 than what a standard BButton would allow) with the current value 1120 of the star density pourcentage. */ 1121 value = (fCurrentSettings.star_density*100 + STAR_DENSITY_MAX/2) / STAR_DENSITY_MAX; 1122 sprintf(word, "%3ld", value); 1123 draw_string: 1124 offwindow_button->SetFont(be_bold_font); 1125 offwindow_button->SetFontSize(14.0); 1126 delta.x = BUTTON_WIDTH/2-(offwindow_button->StringWidth(word) * 0.5); 1127 delta.y = (TOP_LEFT_LIMIT-2*V_BORDER)/2 + 6.0; 1128 offwindow_button->DrawString(word, delta); 1129 } 1130 else { 1131 /* this button just contains a big string (using a bigger font size 1132 than what a standard BButton would allow) with the current value 1133 of the target refresh rate in frames per second. */ 1134 sprintf(word, "%3.1f", fCurrentSettings.refresh_rate + 0.05); 1135 goto draw_string; 1136 } 1137 /* close and return the picture */ 1138 return offwindow_button->EndPicture(); 1139 } 1140 1141 /* Create a floating window including a slightly modified version of 1142 BColorControl, ChartColorControl, that will return live feedback 1143 as the same time the user will change the color setting of the 1144 background. */ 1145 void 1146 ChartWindow::OpenColorPalette(BPoint here) 1147 { 1148 BRect frame; 1149 BPoint point; 1150 1151 BWindow *window = GetAppWindow("Space color"); 1152 if (window == NULL) { 1153 frame.Set(here.x, here.y, here.x + 199.0, here.y + 99.0); 1154 window = new BWindow(frame, "Space color", 1155 B_FLOATING_WINDOW_LOOK, 1156 B_FLOATING_APP_WINDOW_FEEL, 1157 B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK | B_NOT_RESIZABLE); 1158 point.Set(0, 0); 1159 BColorControl *colorControl = new ChartColorControl(point, 1160 new BMessage(COLOR_PALETTE_MSG)); 1161 colorControl->SetViewColor(background_color); 1162 colorControl->SetTarget(NULL, this); 1163 colorControl->SetValue(fCurrentSettings.back_color); 1164 colorControl->ResizeToPreferred(); 1165 window->ResizeTo(colorControl->Bounds().Width(), colorControl->Bounds().Height()); 1166 window->AddChild(colorControl); 1167 window->Show(); 1168 } 1169 window->Activate(); 1170 } 1171 1172 /* Create a floating window including a BSlider, that will return 1173 live feedback when the user will change the star density of the 1174 starfield */ 1175 void 1176 ChartWindow::OpenStarDensity(BPoint here) 1177 { 1178 BRect frame; 1179 BSlider *slider; 1180 BWindow *window; 1181 1182 window = GetAppWindow("Star density"); 1183 if (window == NULL) { 1184 frame.Set(here.x, here.y, here.x + STAR_DENSITY_H-1, here.y + STAR_DENSITY_V-1); 1185 window = new BWindow(frame, "Star density", 1186 B_FLOATING_WINDOW_LOOK, 1187 B_FLOATING_APP_WINDOW_FEEL, 1188 B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK); 1189 frame.OffsetTo(0.0, 0.0); 1190 slider = new BSlider(frame, "", NULL, new BMessage(STAR_DENSITY_MSG), 1191 STAR_DENSITY_MIN, STAR_DENSITY_MAX); 1192 slider->SetViewColor(background_color); 1193 slider->SetTarget(NULL, this); 1194 slider->SetValue(fCurrentSettings.star_density); 1195 slider->SetModificationMessage(new BMessage(STAR_DENSITY_MSG)); 1196 slider->SetLimitLabels(" 5% (low)", "(high) 100% "); 1197 slider->ResizeToPreferred(); 1198 window->ResizeTo(slider->Bounds().Width(), slider->Bounds().Height()); 1199 window->AddChild(slider); 1200 window->Show(); 1201 } 1202 window->Activate(); 1203 } 1204 1205 /* Create a floating window including a BSlider, that will return 1206 live feedback when the user will change the target refresh rate 1207 of the animation */ 1208 void 1209 ChartWindow::OpenRefresh(BPoint here) 1210 { 1211 BRect frame; 1212 BSlider *slider; 1213 BWindow *window; 1214 1215 window = GetAppWindow("Refresh rate"); 1216 if (window == NULL) { 1217 frame.Set(here.x, here.y, here.x + REFRESH_RATE_H-1, here.y + REFRESH_RATE_V-1); 1218 window = new BWindow(frame, "Refresh rate", 1219 B_FLOATING_WINDOW_LOOK, 1220 B_FLOATING_APP_WINDOW_FEEL, 1221 B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK); 1222 frame.OffsetTo(0.0, 0.0); 1223 slider = new BSlider(frame, "", NULL, new BMessage(REFRESH_RATE_MSG), 0.0, 1000.0); 1224 slider->SetViewColor(background_color); 1225 slider->SetTarget(NULL, this); 1226 slider->SetValue(1000.0*log(fCurrentSettings.refresh_rate/REFRESH_RATE_MIN)/log(REFRESH_RATE_MAX/REFRESH_RATE_MIN)); 1227 slider->SetModificationMessage(new BMessage(REFRESH_RATE_MSG)); 1228 slider->SetLimitLabels(" 0.6 f/s (logarythmic scale)", "600.0 f/s"); 1229 slider->ResizeToPreferred(); 1230 window->ResizeTo(slider->Bounds().Width(), slider->Bounds().Height()); 1231 window->AddChild(slider); 1232 window->Show(); 1233 } 1234 window->Activate(); 1235 } 1236 1237 /* This update the state of the frames per second vue-meter in a lazy way. */ 1238 void 1239 ChartWindow::DrawInstantLoad(float frame_per_second) 1240 { 1241 int32 i; 1242 bigtime_t timeout; 1243 1244 int32 level = (int32)((frame_per_second + 6.0) * (1.0/12.0)); 1245 if (level > 50) 1246 level = 50; 1247 1248 /* if the load level is inchanged, nothing more to do... */ 1249 if (level == fInstantLoadLevel) 1250 return; 1251 1252 /* We need to lock the window to be able to draw that. But as some 1253 BControl are still synchronous, if the user is still tracking them, 1254 the window can stay block for a long time. It's not such a big deal 1255 when using the offscreen buffer as we won't be able to draw it in 1256 any case. But in DirectWindow mode, we're not limited by that so 1257 it would be stupid to block the engine loop here. That's why in 1258 that case, we will try to lock the window with a timeout of 0us. */ 1259 if (fCurrentSettings.display == DISPLAY_BITMAP) 1260 timeout = 100000; 1261 else 1262 timeout = 0; 1263 if (LockWithTimeout(timeout) != B_OK) 1264 return; 1265 1266 /* the new level is higher than the previous. We need to draw more 1267 colored bars. */ 1268 if (level > fInstantLoadLevel) { 1269 for (i=fInstantLoadLevel; i<level; i++) { 1270 if (i<fInstantLoad->step) fInstantLoad->SetHighColor(255.0, 90.0, 90.0); 1271 else if ((i/fInstantLoad->step) & 1) fInstantLoad->SetHighColor(90.0, 255.0, 90.0); 1272 else fInstantLoad->SetHighColor(40.0, 200.0, 40.0); 1273 fInstantLoad->FillRect(BRect(3+i*4, 2, 5+i*4, 19)); 1274 } 1275 } 1276 /* the level is lower than before, we need to erase some bars. */ 1277 else { 1278 fInstantLoad->SetHighColor(0.0, 0.0, 0.0); 1279 for (i=level; i< fInstantLoadLevel; i++) 1280 fInstantLoad->FillRect(BRect(3+i*4, 2, 5+i*4, 19)); 1281 } 1282 /* we want that drawing to be completed as soon as possible */ 1283 Flush(); 1284 1285 fInstantLoadLevel = level; 1286 Unlock(); 1287 } 1288 1289 1290 void 1291 ChartWindow::PrintStatNumbers(float fps) 1292 { 1293 char text_frames[6]; 1294 char text_cpu_load[6]; 1295 float frame_rate, load; 1296 bigtime_t timeout; 1297 1298 /* rules use to determine the stat numbers : if the target framerate 1299 is greater than the simulate one, then we consider that 100.0 cpu 1300 was used, and we only got the simulate framerate. */ 1301 if (fps <= fCurrentSettings.refresh_rate) { 1302 load = 100.0; 1303 frame_rate = fps + 0.05; 1304 } 1305 /* if the target framerate is less than the simulate one, then we 1306 calculate what fraction of the cpu would have been required to 1307 deliver the target framerate, and we said that the target framerate 1308 was delivered. */ 1309 else { 1310 load = (100.0*fCurrentSettings.refresh_rate)/fps + 0.05; 1311 frame_rate = fCurrentSettings.refresh_rate + 0.05; 1312 } 1313 1314 /* convert numbers in strings */ 1315 sprintf(text_cpu_load, "%3.1f", load); 1316 sprintf(text_frames, "%3.1f", frame_rate); 1317 1318 /* same remark than for DrawInstantLoad. We want to avoid to 1319 block if using DirectWindow mode. */ 1320 if (fCurrentSettings.display == DISPLAY_BITMAP) 1321 timeout = 100000; 1322 else 1323 timeout = 0; 1324 1325 if (LockWithTimeout(timeout) == B_OK) { 1326 frames->SetText(text_frames); 1327 cpu_load->SetText(text_cpu_load); 1328 Unlock(); 1329 } 1330 } 1331 1332 1333 // #pragma mark Engine setting related functions. 1334 1335 1336 void 1337 ChartWindow::InitGeometry() 1338 { 1339 /* calculate some parameters required for the 3d processing */ 1340 float 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)); 1341 depth_ref = dz / (1.0 - 1.0/Z_CUT_RATIO); 1342 1343 /* set the position of the pyramid of vision, so that it was always 1344 possible to include it into a 1x1x1 cube parallel to the 3 main 1345 axis. */ 1346 geo.z_max = depth_ref; 1347 geo.z_min = depth_ref/Z_CUT_RATIO; 1348 1349 /* used for lighting processing */ 1350 geo.z_max_square = geo.z_max * geo.z_max; 1351 1352 /* preprocess that for the fast clipping based on the pyramid of vision */ 1353 geo.xz_max = (0.5*DH_REF)/geo.z_max; 1354 geo.xz_min = -geo.xz_max; 1355 geo.yz_max = (0.5*DV_REF)/geo.z_max; 1356 geo.yz_min = -geo.yz_max; 1357 } 1358 1359 /* second part of the asynchronous setting mechanism. This will be 1360 called once during every loop of the animation engine, at a time 1361 when the engine is not using the setting for realtime processing. 1362 Each setting will be checked for potential change, and action 1363 will be taken if needed. The window can be locked at that time 1364 because the structure of the animation engine loop guarantees 1365 that DirectConnected can not stay blocked at the same time that 1366 this method is executed. */ 1367 void 1368 ChartWindow::ChangeSetting(setting new_set) 1369 { 1370 int32 i, color_count, old_step; 1371 int32 color_index[7]; 1372 1373 /* check for change of window/fullscreen/fullscreen demo mode */ 1374 if (fCurrentSettings.fullscreen_mode != new_set.fullscreen_mode) { 1375 switch (new_set.fullscreen_mode) { 1376 case WINDOW_MODE : 1377 previous_fullscreen_mode = WINDOW_MODE; 1378 ResizeTo(PreviousFrame.Width(), PreviousFrame.Height()); 1379 MoveTo(PreviousFrame.left, PreviousFrame.top); 1380 break; 1381 case FULLSCREEN_MODE : 1382 { 1383 previous_fullscreen_mode = FULLSCREEN_MODE; 1384 if (fCurrentSettings.fullscreen_mode == WINDOW_MODE) 1385 PreviousFrame = Frame(); 1386 BScreen a_screen(this); 1387 MoveTo(a_screen.Frame().left, a_screen.Frame().top); 1388 ResizeTo(a_screen.Frame().Width(), a_screen.Frame().Height()); 1389 } 1390 break; 1391 case FULLDEMO_MODE : 1392 { 1393 previous_fullscreen_mode = fCurrentSettings.fullscreen_mode; 1394 if (fCurrentSettings.fullscreen_mode == WINDOW_MODE) 1395 PreviousFrame = Frame(); 1396 BScreen b_screen(this); 1397 ResizeTo(b_screen.Frame().Width() + LEFT_WIDTH, b_screen.Frame().Height() + TOP_LEFT_LIMIT); 1398 MoveTo(b_screen.Frame().left - LEFT_WIDTH, b_screen.Frame().top - TOP_LEFT_LIMIT); 1399 } 1400 break; 1401 } 1402 } 1403 1404 /* check for change in the target refresh rate */ 1405 if (fCurrentSettings.refresh_rate != new_set.refresh_rate) { 1406 fCurrentSettings.refresh_rate = new_set.refresh_rate; 1407 old_step = fInstantLoad->step; 1408 fInstantLoad->step = (int32)((fCurrentSettings.refresh_rate+6.0)/12.0); 1409 if (fInstantLoad->step < 1) 1410 fInstantLoad->step = 1; 1411 if (LockWithTimeout(200000) == B_OK) { 1412 if (old_step != fInstantLoad->step) 1413 fInstantLoad->Invalidate(); 1414 refresh_button->SetEnabledOff(ButtonPicture(false, REFRESH_BUTTON_PICT)); 1415 refresh_button->SetEnabledOn(ButtonPicture(true, REFRESH_BUTTON_PICT)); 1416 refresh_button->Invalidate(); 1417 Unlock(); 1418 } 1419 if (fCurrentSettings.animation != ANIMATION_OFF) 1420 frame_delay = (bigtime_t)(1000000.0/new_set.refresh_rate); 1421 } 1422 1423 /* check for change in the star colors list */ 1424 for (i=0; i<7; i++) 1425 if (fCurrentSettings.colors[i] != new_set.colors[i]) { 1426 /* if any, get the list of usable color index... */ 1427 color_count = 0; 1428 for (i=0; i<7; i++) 1429 if (new_set.colors[i]) 1430 color_index[color_count++] = i; 1431 /* check that at least one color is enabled */ 1432 if (color_count == 0) 1433 color_index[color_count++] = 6; 1434 /* set a new color distribution in the starfield */ 1435 SetStarColors(color_index, color_count); 1436 break; 1437 } 1438 1439 /* check for change of the special effect setting */ 1440 if (new_set.special != fCurrentSettings.special) 1441 InitSpecials(new_set.special); 1442 1443 /* check for change of the display method */ 1444 if (new_set.display != fCurrentSettings.display) { 1445 if (new_set.display == DISPLAY_BITMAP) { 1446 /* check the settings of the offscreen bitmap */ 1447 CheckBitmap(new_set.depth, new_set.width, new_set.height); 1448 /* synchronise the camera geometry and the offscreen buffer geometry */ 1449 SetGeometry(bitmap_buffer.buffer_width, bitmap_buffer.buffer_height); 1450 /* reset the offscreen background and cancel the erasing */ 1451 SetBitmapBackGround(); 1452 stars.erase_count = 0; 1453 specials.erase_count = 0; 1454 } 1455 if (new_set.display == DISPLAY_DIRECT) { 1456 /* this need to be atomic in regard of DirectConnected */ 1457 while (acquire_sem(fDrawingLock) == B_INTERRUPTED) 1458 ; 1459 /* synchronise the camera geometry and the direct buffer geometry */ 1460 SetGeometry(direct_buffer.buffer_width, direct_buffer.buffer_height); 1461 /* cancel erasing of stars not in visible part of the direct window */ 1462 RefreshClipping(&direct_buffer, &stars); 1463 RefreshClipping(&direct_buffer, &specials); 1464 release_sem(fDrawingLock); 1465 } 1466 } 1467 1468 /* check for change of the animation mode. */ 1469 if (new_set.animation != fCurrentSettings.animation) { 1470 /* when there is no camera animation, we loop only 1471 10 times per second. */ 1472 if (new_set.animation == ANIMATION_OFF) 1473 frame_delay = 100000; 1474 else 1475 frame_delay = (bigtime_t)(1000000.0/new_set.refresh_rate); 1476 /* reset the free camera animation context for a fresh start */ 1477 if (new_set.animation == ANIMATION_FREE_MOVE) { 1478 d_alpha = 0.0; 1479 d_theta = 0.0; 1480 d_phi = 0.0; 1481 cnt_alpha = 0; 1482 cnt_theta = 0; 1483 cnt_phi = 0; 1484 } 1485 } 1486 1487 /* check for change of starfield model */ 1488 if (new_set.space_model != fCurrentSettings.space_model) { 1489 /* Generate a new starfield. Also reset the special animation */ 1490 InitStars(new_set.space_model); 1491 InitSpecials(new_set.special); 1492 } 1493 1494 /* check for change of the background color */ 1495 if ((new_set.back_color.red != fCurrentSettings.back_color.red) || 1496 (new_set.back_color.green != fCurrentSettings.back_color.green) || 1497 (new_set.back_color.blue != fCurrentSettings.back_color.blue)) { 1498 if (LockWithTimeout(200000) == B_OK) { 1499 BScreen screen(this); 1500 /* set the background color and it's 8 bits index equivalent */ 1501 fCurrentSettings.back_color = new_set.back_color; 1502 back_color_index = screen.IndexForColor(new_set.back_color); 1503 /* set the nackground color of the view (directwindow mode) */ 1504 fChartView->SetViewColor(new_set.back_color); 1505 /* change the color of the picture button used in the UI */ 1506 color_button->SetEnabledOff(ButtonPicture(false, COLOR_BUTTON_PICT)); 1507 color_button->SetEnabledOn(ButtonPicture(true, COLOR_BUTTON_PICT)); 1508 color_button->Invalidate(); 1509 /* update all dependencies in the offscreen buffer descriptor */ 1510 SetColorSpace(&bitmap_buffer, bitmap_buffer.depth); 1511 /* update all dependencies in the directwindow buffer descriptor */ 1512 while (acquire_sem(fDrawingLock) == B_INTERRUPTED) 1513 ; 1514 SetColorSpace(&direct_buffer, direct_buffer.depth); 1515 release_sem(fDrawingLock); 1516 /* in offscreen mode, erase the background and cancel star erasing */ 1517 if (new_set.display == DISPLAY_BITMAP) { 1518 SetBitmapBackGround(); 1519 stars.erase_count = 0; 1520 specials.erase_count = 0; 1521 } 1522 /* in directwindow mode, just force an update */ 1523 else 1524 fChartView->Invalidate(); 1525 Unlock(); 1526 } 1527 } 1528 1529 /* check for change of the star animation density */ 1530 if (new_set.star_density != fCurrentSettings.star_density) { 1531 if (LockWithTimeout(200000) == B_OK) { 1532 fCurrentSettings.star_density = new_set.star_density; 1533 /* change the picture button used in the UI */ 1534 density_button->SetEnabledOff(ButtonPicture(false, DENSITY_BUTTON_PICT)); 1535 density_button->SetEnabledOn(ButtonPicture(true, DENSITY_BUTTON_PICT)); 1536 density_button->Invalidate(); 1537 Unlock(); 1538 } 1539 stars.count = new_set.star_density; 1540 } 1541 1542 /* check for change in the buffer format for the offscreen bitmap. 1543 DirectWindow depth change are always handle in realtime */ 1544 if (new_set.depth != fCurrentSettings.depth) { 1545 CheckBitmap(new_set.depth, new_set.width, new_set.height); 1546 /* need to reset the buffer if it's currently used for display */ 1547 if (new_set.display == DISPLAY_BITMAP) { 1548 SetBitmapBackGround(); 1549 stars.erase_count = 0; 1550 specials.erase_count = 0; 1551 } 1552 } 1553 1554 /* check for change in the drawing area of the offscreen bitmap */ 1555 if ((new_set.width != fCurrentSettings.width) || (new_set.height != fCurrentSettings.height)) { 1556 CheckBitmap(new_set.depth, new_set.width, new_set.height); 1557 bitmap_buffer.buffer_width = new_set.width; 1558 bitmap_buffer.buffer_height = new_set.height; 1559 if (new_set.display == DISPLAY_BITMAP) 1560 SetGeometry(bitmap_buffer.buffer_width, bitmap_buffer.buffer_height); 1561 SetBitmapClipping(new_set.width, new_set.height); 1562 } 1563 1564 /* copy the new state as the new current state */ 1565 fCurrentSettings.Set(&new_set); 1566 } 1567 1568 /* Initialise the starfield in the different modes */ 1569 void 1570 ChartWindow::InitStars(int32 space_model) 1571 { 1572 star *s; 1573 int32 step; 1574 int32 amas_select[32]; 1575 float dx, dy, dz, dist, fact, alpha, r; 1576 float factor[8]; 1577 uint32 i, index, i_step; 1578 TPoint amas[8]; 1579 1580 switch (space_model) { 1581 /* Create a random starfield */ 1582 case SPACE_CHAOS : 1583 FillStarList(stars.list, STAR_DENSITY_MAX); 1584 key_point_count = 0; 1585 break; 1586 1587 /* Create a starfield with big concentration of stars (amas) */ 1588 case SPACE_AMAS : 1589 case SPACE_SPIRAL : 1590 /* pick 8 random position for the amas */ 1591 FillStarList(stars.list, 8); 1592 for (i=0; i<8; i++) { 1593 amas[i].x = stars.list[i].x; 1594 amas[i].y = stars.list[i].y; 1595 amas[i].z = stars.list[i].z; 1596 amas_select[i] = i; 1597 factor[i] = ((float)(crc_alea&2047) + 0.5)*(1.0/128.0) + 16.0/3.0; 1598 CrcStep(); 1599 CrcStep(); 1600 } 1601 /* make each amas ramdomly smaller or bigger */ 1602 for (i=8; i<32; i++) { 1603 amas_select[i] = (crc_alea & 7); 1604 CrcStep(); 1605 } 1606 1607 /* create a random starfield */ 1608 FillStarList(stars.list, STAR_DENSITY_MAX); 1609 1610 /* In spiral mode, only half the star will be put into the amas. 1611 the other half will be put into the spiral galaxy. */ 1612 if (space_model == SPACE_AMAS) 1613 i_step = 1; 1614 else 1615 i_step = 2; 1616 s = stars.list; 1617 1618 for (i=0; i<STAR_DENSITY_MAX; i+=i_step) { 1619 /* for every star, calculate its position relative to the 1620 center of the corresponding amas. */ 1621 index = amas_select[i&31]; 1622 dx = s->x-amas[index].x; 1623 if (dx < -0.5) dx += 1.0; 1624 if (dx > 0.5) dx -= 1.0; 1625 dy = s->y-amas[index].y; 1626 if (dy < -0.5) dy += 1.0; 1627 if (dy > 0.5) dy -= 1.0; 1628 dz = s->z-amas[index].z; 1629 if (dz < -0.5) dz += 1.0; 1630 if (dz > 0.5) dz -= 1.0; 1631 1632 /* make the star randomly closer from its center, but keep 1633 it on the same orientation. */ 1634 step = 0; 1635 dist = (abs(dx) + abs(dy) + abs(dz))*factor[index]; 1636 while (dist > 1.0) { 1637 dist *= 0.5; 1638 step++; 1639 } 1640 1641 step -= (crc_alea&3); 1642 CrcStep(); 1643 fact = 1.0; 1644 for (;step>=0; step--) 1645 fact *= 0.55; 1646 dx *= fact; 1647 dy *= fact; 1648 dz *= fact; 1649 1650 /* put the star back in the [0-1]x[0-1]x[0-1] iteration of 1651 the cubic torus. */ 1652 s->x = amas[index].x + dx; 1653 if (s->x >= 1.0) s->x -= 1.0; 1654 if (s->x <= 0.0) s->x += 1.0; 1655 s->y = amas[index].y + dy; 1656 if (s->y >= 1.0) s->y -= 1.0; 1657 if (s->y <= 0.0) s->y += 1.0; 1658 s->z = amas[index].z + dz; 1659 if (s->z >= 1.0) s->z -= 1.0; 1660 if (s->z <= 0.0) s->z += 1.0; 1661 1662 s += i_step; 1663 } 1664 1665 /* record the center of the amas as key points for the free 1666 camera animation mode. */ 1667 for (i=0; i<8; i++) 1668 key_points[i] = amas[i]; 1669 key_point_count = 8; 1670 1671 /* no further processing needed in amas only mode. */ 1672 if (space_model == SPACE_AMAS) 1673 break; 1674 1675 /* in spiral mode, the second half of the star will be distributed 1676 on random spiral like galaxy. */ 1677 s = stars.list+1; 1678 for (i=1; i<STAR_DENSITY_MAX; i+=2) { 1679 /* some random point (probability 50 %) will be move into a 1680 big amas at the center of the spiral galaxy. */ 1681 if (crc_alea & 2048) { 1682 /* for every star, calculate its position relative to the 1683 center of the galaxy. */ 1684 dx = s->x - 0.5; 1685 dy = s->y - 0.5; 1686 dz = s->z - 0.5; 1687 1688 /* make the star randomly closer from its center, but keep 1689 it on the same orientation. */ 1690 step = 0; 1691 dist = (dx*dx + dy*dy + dz*dz) * (32.0/0.75); 1692 while (dist > 1.0) { 1693 dist *= 0.5; 1694 step++; 1695 } 1696 1697 step -= (crc_alea&3); 1698 CrcStep(); 1699 fact = 0.5; 1700 for (;step>=0; step--) 1701 fact *= 0.55; 1702 dx *= fact; 1703 dy *= fact; 1704 dz *= fact; 1705 } 1706 else { 1707 /* other star are put at a random place somewhere on one of 1708 teh two spiral arms... */ 1709 alpha = 3.4 * s->x * (s->x*0.5 + 1.0); 1710 if (crc_alea & 64) 1711 alpha += 3.14159; 1712 r = s->x * 0.34 + 0.08; 1713 r += (s->y-0.725 + 0.03 * (float)(crc_alea & 15))*0.04*(1.2+r); 1714 r *= 0.5; 1715 dx = (s->z-0.8 + 0.04 * (float)(crc_alea & 15)) * (2.0 - abs(s->y - 0.5)) * (0.025*0.5); 1716 dy = cos(alpha) * r; 1717 dz = sin(alpha) * r; 1718 } 1719 CrcStep(); 1720 1721 /* put the star back in the [0-1]x[0-1]x[0-1] iteration of 1722 the cubic torus. */ 1723 s->x = 0.5 + dx; 1724 s->y = 0.5 + dy; 1725 s->z = 0.5 + dz; 1726 s += 2; 1727 } 1728 1729 /* add the center of the galaxy to the key point list for free camera 1730 animation mode */ 1731 key_points[8].x = 0.5; 1732 key_points[8].y = 0.5; 1733 key_points[8].z = 0.5; 1734 /* add seven other galaxy star to the key point list */ 1735 for (i=9; i<16; i++) { 1736 key_points[i].x = stars.list[i*(STAR_DENSITY_MAX/18)].x; 1737 key_points[i].y = stars.list[i*(STAR_DENSITY_MAX/18)].y; 1738 key_points[i].z = stars.list[i*(STAR_DENSITY_MAX/18)].z; 1739 } 1740 key_point_count = 16; 1741 break; 1742 } 1743 1744 /* In all starfield modes, for all stars, peek a random brightness level */ 1745 for (i=0; i<STAR_DENSITY_MAX; i++) { 1746 stars.list[i].size = (float)((crc_alea&15)+17)*(1.0/56.0); 1747 if ((crc_alea & 0xc0) == 0) 1748 stars.list[i].size *= 2.0; 1749 if ((crc_alea & 0x3f00) == 0) 1750 stars.list[i].size *= 3.0; 1751 CrcStep(); 1752 } 1753 } 1754 1755 /* Fill a list of star with random position in the [0-1]x[0-1]x[0-1] cube */ 1756 void 1757 ChartWindow::FillStarList(star *list, int32 count) 1758 { 1759 int32 i; 1760 1761 for (i=0; i<count; i++) { 1762 list[i].x = ((float)(crc_alea&2047) + 0.5)*(1.0/2048.0); 1763 CrcStep(); 1764 } 1765 for (i=0; i<count; i++) { 1766 list[i].y = ((float)(crc_alea&2047) + 0.5)*(1.0/2048.0); 1767 CrcStep(); 1768 } 1769 for (i=0; i<count; i++) { 1770 list[i].z = ((float)(crc_alea&2047) + 0.5)*(1.0/2048.0); 1771 CrcStep(); 1772 } 1773 } 1774 1775 /* initialise anything needed to enable a specific special animation */ 1776 void 1777 ChartWindow::InitSpecials(int32 code) 1778 { 1779 int i, j; 1780 float alpha, ksin, kcos, coeff; 1781 TPoint dx, dy; 1782 TMatrix matrix; 1783 1784 switch (code) { 1785 /* turn special animation off */ 1786 case SPECIAL_NONE : 1787 specials.count = 0; 1788 break; 1789 1790 /* Initialise the pixel-comet animation */ 1791 case SPECIAL_COMET : 1792 /* Get a bunchof random values by getting some radom stars */ 1793 specials.count = 512; 1794 FillStarList(specials.list, 4); 1795 /* for both comets... */ 1796 for (j=0; j<2; j++) { 1797 /* select the initial position of the comet head */ 1798 comet[j].x = specials.list[j].x; 1799 comet[j].y = specials.list[j].y; 1800 comet[j].z = specials.list[j].z; 1801 specials.list[0].size = 1.4; 1802 1803 /* select the speed vector of the comet */ 1804 matrix.Set(specials.list[j+2].x * 6.28319, specials.list[j+2].y * 3.14159 - 1.5708, 0.0); 1805 delta_comet[j] = matrix.Axis(0) * 0.0015; 1806 dx = matrix.Axis(1); 1807 dy = matrix.Axis(2); 1808 1809 for (i=j+2; i<specials.count; i+=2) { 1810 /* make the pixel invisible at first */ 1811 specials.list[i].x = -10.0; 1812 specials.list[i].y = 0.0; 1813 specials.list[i].z = 0.0; 1814 /* spread the initial count on a linear scale (to make pixel 1815 appear progressively */ 1816 special_list[i].comet.count = i/2; 1817 /* spread the pixel trace count randomly on a [93-124] range */ 1818 special_list[i].comet.count0 = (crc_alea & 31) + 93; 1819 CrcStep(); 1820 /* pick a random ejection angle */ 1821 alpha = ((crc_alea>>8) & 1023) * (6.283159/1024.0); 1822 CrcStep(); 1823 1824 /* pick a random ejection speed */ 1825 coeff = 0.000114 + 0.0000016 * (float)((crc_alea>>17) & 31); 1826 if ((crc_alea & 7) > 4) coeff *= 0.75; 1827 if ((crc_alea & 7) == 7) coeff *= 0.65; 1828 CrcStep(); 1829 1830 /* calculate the ejection speed vector */ 1831 ksin = sin(alpha) * coeff; 1832 kcos = cos(alpha) * coeff; 1833 special_list[i].comet.dx = dx.x * kcos + dy.x * ksin; 1834 special_list[i].comet.dy = dx.y * kcos + dy.y * ksin; 1835 special_list[i].comet.dz = dx.z * kcos + dy.z * ksin; 1836 } 1837 } 1838 break; 1839 1840 /* Add a list of random star (used for nova effect by modifying their 1841 brightness level in real time) close from the first stars of the 1842 starfield. */ 1843 case SPECIAL_NOVAS : 1844 specials.count = 96; 1845 for (i=0; i<specials.count; i++) { 1846 special_list[i].nova.count = i + 40; 1847 special_list[i].nova.count0 = (crc_alea & 63) + 28; 1848 CrcStep(); 1849 specials.list[i].x = stars.list[i].x + (crc_alea & 1)*0.02 - 0.01; 1850 CrcStep(); 1851 specials.list[i].y = stars.list[i].y + (crc_alea & 1)*0.02 - 0.01; 1852 CrcStep(); 1853 specials.list[i].z = stars.list[i].z + (crc_alea & 1)*0.02 - 0.01; 1854 CrcStep(); 1855 specials.list[i].size = 0.0; 1856 } 1857 break; 1858 1859 /* not implemented */ 1860 case SPECIAL_BATTLE : 1861 specials.count = 0; 1862 break; 1863 } 1864 } 1865 1866 /* select a color for each star (and special animation point) by 1867 looping through the color index list. */ 1868 void 1869 ChartWindow::SetStarColors(int32 *color_list, int32 color_count) 1870 { 1871 int32 i, index; 1872 1873 index = 0; 1874 for (i=0; i<STAR_DENSITY_MAX; i++) { 1875 stars.list[i].color_type = color_list[index]; 1876 index++; 1877 if (index >= color_count) 1878 index = 0; 1879 } 1880 for (i=0; i<SPECIAL_COUNT_MAX; i++) { 1881 specials.list[i].color_type = color_list[index]; 1882 index++; 1883 if (index >= color_count) 1884 index = 0; 1885 } 1886 } 1887 1888 1889 void 1890 ChartWindow::SetGeometry(int32 dh, int32 dv) 1891 { 1892 float zoom; 1893 1894 /* calculate the zoom factor for the 3d projection */ 1895 geo.zoom_factor = (float)dh*(depth_ref/DH_REF); 1896 zoom = (float)dv*(depth_ref/DV_REF); 1897 if (zoom > geo.zoom_factor) 1898 geo.zoom_factor = zoom; 1899 1900 /* offset of the origin in the view area */ 1901 geo.offset_h = (float)dh * 0.5; 1902 geo.offset_v = (float)dv * 0.5; 1903 1904 /* sub-pixel precision double-sampling */ 1905 geo.zoom_factor *= 2.0; 1906 geo.offset_h = geo.offset_h * 2.0 - 1.0; 1907 geo.offset_v = geo.offset_v * 2.0 - 1.0; 1908 } 1909 1910 1911 void 1912 ChartWindow::SetColorSpace(buffer *buf, color_space depth) 1913 { 1914 bool swap_needed; 1915 int32 red_shift = 0, green_shift = 0; 1916 int32 blue_shift = 0, alpha_shift = 0; 1917 int32 step_doubling = 0; 1918 1919 int32 red_divide_shift = 0, green_divide_shift = 0; 1920 int32 blue_divide_shift = 0, alpha_divide_shift = 0; 1921 int32 i; 1922 uint32 color; 1923 uint32 *col; 1924 BScreen screen(this); 1925 rgb_color ref_color; 1926 1927 /* depending the colorspace of the target buffer, set parameters used 1928 to encode the RGBA information for various color information in 1929 the right format. */ 1930 buf->depth = depth; 1931 switch (depth) { 1932 case B_RGBA32_BIG : 1933 case B_RGB32_BIG : 1934 case B_RGBA32 : 1935 case B_RGB32 : 1936 buf->depth_mode = PIXEL_4_BYTES; 1937 buf->bytes_per_pixel = 4; 1938 red_shift = 16; 1939 green_shift = 8; 1940 blue_shift = 0; 1941 alpha_shift = 24; 1942 red_divide_shift = 0; 1943 green_divide_shift = 0; 1944 blue_divide_shift = 0; 1945 alpha_divide_shift = 0; 1946 step_doubling = 32; 1947 break; 1948 case B_RGB16_BIG : 1949 case B_RGB16 : 1950 buf->depth_mode = PIXEL_2_BYTES; 1951 buf->bytes_per_pixel = 2; 1952 red_shift = 11; 1953 red_divide_shift = 3; 1954 green_shift = 5; 1955 green_divide_shift = 2; 1956 blue_shift = 0; 1957 blue_divide_shift = 3; 1958 alpha_shift = 32; 1959 alpha_divide_shift = 8; 1960 step_doubling = 16; 1961 break; 1962 case B_RGB15 : 1963 case B_RGBA15 : 1964 case B_RGB15_BIG : 1965 case B_RGBA15_BIG : 1966 buf->depth_mode = PIXEL_2_BYTES; 1967 buf->bytes_per_pixel = 2; 1968 red_shift = 10; 1969 red_divide_shift = 3; 1970 green_shift = 5; 1971 green_divide_shift = 3; 1972 blue_shift = 0; 1973 blue_divide_shift = 3; 1974 alpha_shift = 15; 1975 alpha_divide_shift = 7; 1976 step_doubling = 16; 1977 break; 1978 case B_CMAP8 : 1979 default: 1980 buf->depth_mode = PIXEL_1_BYTE; 1981 buf->bytes_per_pixel = 1; 1982 break; 1983 } 1984 1985 /* Check if the endianess of the buffer is different from the 1986 endianess use by the processor to encode the color information */ 1987 switch (depth) { 1988 case B_RGBA32_BIG : 1989 case B_RGB32_BIG : 1990 case B_RGB16_BIG : 1991 case B_RGB15_BIG : 1992 case B_RGBA15_BIG : 1993 swap_needed = true; 1994 break; 1995 case B_RGBA32 : 1996 case B_RGB32 : 1997 case B_RGB16 : 1998 case B_RGB15 : 1999 case B_RGBA15 : 2000 case B_CMAP8 : 2001 default: 2002 swap_needed = false; 2003 break; 2004 } 2005 2006 #if B_HOST_IS_BENDIAN 2007 swap_needed = ~swap_needed; 2008 #endif 2009 /* fill the color tables (8 light level for 7 colors, and also encode 2010 the background color */ 2011 col = buf->colors[0]; 2012 switch (buf->depth_mode) { 2013 case PIXEL_1_BYTE : 2014 /* 8 bits, indexed mode */ 2015 for (i=0; i<7*8; i++) { 2016 ref_color = color_list[i>>3]; 2017 ref_color.red = (ref_color.red*light_gradient[i&7])>>16; 2018 ref_color.green = (ref_color.green*light_gradient[i&7])>>16; 2019 ref_color.blue = (ref_color.blue*light_gradient[i&7])>>16; 2020 color = screen.IndexForColor(ref_color); 2021 col[i] = (color<<24) | (color<<16) | (color<<8) | color; 2022 } 2023 color = screen.IndexForColor(fCurrentSettings.back_color); 2024 buf->back_color = (color<<24) | (color<<16) | (color<<8) | color; 2025 break; 2026 case PIXEL_2_BYTES : 2027 case PIXEL_4_BYTES : 2028 /* 15, 16 or 32 bytes, RGB modes. Those modes just directly encode 2029 part of the bits of the initial rgba_color, at the right bit 2030 position */ 2031 for (i=0; i<7*8; i++) { 2032 ref_color = color_list[i>>3]; 2033 ref_color.red = (ref_color.red*light_gradient[i&7])>>16; 2034 ref_color.green = (ref_color.green*light_gradient[i&7])>>16; 2035 ref_color.blue = (ref_color.blue*light_gradient[i&7])>>16; 2036 color = ((uint8)ref_color.red >> red_divide_shift) << red_shift; 2037 color |= ((uint8)ref_color.green >> green_divide_shift) << green_shift; 2038 color |= ((uint8)ref_color.blue >> blue_divide_shift) << blue_shift; 2039 color |= ((uint8)ref_color.alpha >> alpha_divide_shift) << alpha_shift; 2040 col[i] = (color<<step_doubling) | color; 2041 } 2042 color = ((uint8)fCurrentSettings.back_color.red >> red_divide_shift) << red_shift; 2043 color |= ((uint8)fCurrentSettings.back_color.green >> green_divide_shift) << green_shift; 2044 color |= ((uint8)fCurrentSettings.back_color.blue >> blue_divide_shift) << blue_shift; 2045 color |= ((uint8)fCurrentSettings.back_color.alpha >> alpha_divide_shift) << alpha_shift; 2046 buf->back_color = (color<<step_doubling) | color; 2047 break; 2048 } 2049 2050 /* do the endianess swap if needed */ 2051 if (swap_needed) { 2052 col = buf->colors[0]; 2053 for (i = 0; i < 7*8; i++) { 2054 B_SWAP_INT32(col[i]); 2055 } 2056 B_SWAP_INT32(buf->back_color); 2057 } 2058 } 2059 2060 2061 /*! 2062 For each different offset used to access a pixel of the star matrix, 2063 create a buffer pointer based on the main buffer pointer offset by 2064 the pixel matrix offset. That way, any pixel of the matrix can be 2065 address later by just picking the right pointer and indexing it by 2066 the global star offset 2067 */ 2068 void 2069 ChartWindow::SetPatternBits(buffer *buf) 2070 { 2071 for (int32 i=0; i<32; i++) { 2072 buf->pattern_bits[i] = (void*)((char*)buf->bits + 2073 buf->bytes_per_row * pattern_dv[i] + 2074 buf->bytes_per_pixel * pattern_dh[i]); 2075 } 2076 } 2077 2078 2079 // #pragma mark Engine processing related functions. 2080 2081 2082 /*! 2083 That's the main thread controling the animation and synchronising 2084 the engine state with the changes coming from the UI. 2085 */ 2086 long 2087 ChartWindow::Animation(void *data) 2088 { 2089 int32 i, cur_4_frames_index, cur_last_fps, count_fps; 2090 float time_factor = 0, total_fps; 2091 float last_fps[4]; 2092 bigtime_t next_stat; 2093 bigtime_t timer, time_left, current; 2094 bigtime_t before_frame, after_frame, fps; 2095 bigtime_t last_4_frames[4]; 2096 ChartWindow *w; 2097 2098 w = (ChartWindow*)data; 2099 2100 /* init refresh rate control */ 2101 timer = system_time(); 2102 w->frame_delay = 100000; 2103 2104 /* init performance timing control variables */ 2105 next_stat = timer + STAT_DELAY; 2106 cur_4_frames_index = 0; 2107 cur_last_fps = 0; 2108 for (i=0; i<4; i++) 2109 last_fps[i] = 0.0; 2110 total_fps = 0.0; 2111 count_fps = 0; 2112 2113 /* here start the loop doing all the good stuff */ 2114 while (!w->fKillThread) { 2115 2116 /* start the performance mesurement here */ 2117 before_frame = system_time(); 2118 2119 /* credit the timer by the current delay between frame */ 2120 timer += w->frame_delay; 2121 2122 /* change the settings, if needed */ 2123 w->ChangeSetting(w->fNextSettings); 2124 2125 /* draw the next frame */ 2126 if (w->fCurrentSettings.display == DISPLAY_BITMAP) { 2127 w->RefreshStars(&w->bitmap_buffer, time_factor * 2.4); 2128 if (w->LockWithTimeout(200000) == B_OK) { 2129 w->fChartView->DrawBitmap(w->fOffscreen); 2130 w->Unlock(); 2131 } 2132 } 2133 else if (w->fCurrentSettings.display == DISPLAY_DIRECT) { 2134 /* This part get the drawing-lock to guarantee that the 2135 directbuffer context won't change during the drawing 2136 operations. During that period, no Window should be 2137 done to avoid any potential deadlock. */ 2138 while (acquire_sem(w->fDrawingLock) == B_INTERRUPTED) 2139 ; 2140 if (w->fDirectConnected) 2141 w->RefreshStars(&w->direct_buffer, time_factor * 2.4); 2142 release_sem(w->fDrawingLock); 2143 } 2144 2145 /* do the camera animation */ 2146 w->CameraAnimation(time_factor); 2147 2148 /* end the performance mesurement here */ 2149 after_frame = system_time(); 2150 2151 /* performance timing calculation here (if display enabled). */ 2152 if (w->fCurrentSettings.display != DISPLAY_OFF) { 2153 /* record frame duration into a 2 levels 4 entries ring buffer */ 2154 last_4_frames[cur_4_frames_index] = after_frame - before_frame; 2155 cur_4_frames_index++; 2156 if (cur_4_frames_index == 4) { 2157 cur_4_frames_index = 0; 2158 last_fps[cur_last_fps++ & 3] = 2159 last_4_frames[0]+last_4_frames[1]+last_4_frames[2]+last_4_frames[3]; 2160 /* the instant load is calculated based on the average duration 2161 of the last 16 frames. */ 2162 fps = (bigtime_t) (16e6 / (last_fps[0]+last_fps[1]+last_fps[2]+last_fps[3])); 2163 w->DrawInstantLoad(fps); 2164 2165 total_fps += fps; 2166 count_fps += 1; 2167 2168 /* The statistic numbers are based on the ratio between the real 2169 duration and the frame count during a period of approximately 2170 STAT_DELAY microseconds. */ 2171 if (after_frame > next_stat) { 2172 w->PrintStatNumbers(total_fps/(float)count_fps); 2173 next_stat = after_frame+STAT_DELAY; 2174 total_fps = 0.0; 2175 count_fps = 0; 2176 } 2177 } 2178 } 2179 2180 /* do a pause if necessary */ 2181 current = system_time(); 2182 time_left = timer-current; 2183 if (time_left > 2000) { 2184 snooze(time_left); 2185 time_left = 0; 2186 } 2187 else if (time_left < -5000) 2188 timer = current; 2189 2190 /* this factor controls the dynamic timing configuration, that 2191 slow down or speed up the whole animation step to compensate 2192 for varaiation of the framerate. */ 2193 time_factor = (float)(system_time() - before_frame) * (1.0/4e4); 2194 } 2195 return 0; 2196 } 2197 2198 /* This is the second thread doing star animation. It's just a poor 2199 slave of the Animation thread. It's directly synchronised with its 2200 master, and will only do some star animation processing whenever 2201 its master allows him to do so. */ 2202 long 2203 ChartWindow::Animation2(void *data) 2204 { 2205 ChartWindow *w = (ChartWindow*)data; 2206 while (!w->fKillThread) { 2207 /* This thread need to both wait for its master to unblock 2208 him to do some real work, or for the main control to 2209 set the fKillThread flag, asking it to quit. */ 2210 status_t status; 2211 do { 2212 status = acquire_sem_etc(w->fSecondThreadLock, 1, B_TIMEOUT, 500000); 2213 if (w->fKillThread) 2214 return 0; 2215 } while (status == B_TIMED_OUT || status == B_INTERRUPTED); 2216 2217 /* the duration of the processing is needed to control the 2218 dynamic load split (see RefreshStar) */ 2219 bigtime_t before = system_time(); 2220 RefreshStarPacket(w->fSecondThreadBuffer, &w->fStars2, &w->geo); 2221 RefreshStarPacket(w->fSecondThreadBuffer, &w->fSpecials2, &w->geo); 2222 bigtime_t after = system_time(); 2223 2224 w->fSecondThreadDelay = after-before; 2225 2226 release_sem(w->fSecondThreadRelease); 2227 } 2228 return 0; 2229 } 2230 2231 2232 void 2233 ChartWindow::SetCubeOffset() 2234 { 2235 int32 i; 2236 TPoint min, max, dx, dy, dz, p1; 2237 2238 /* calculate the shortest aligned cube encapsulating the pyramid 2239 of vision, by calculating the min and max on the 3 main axis 2240 of the coordinates of the 8 extremities of the pyramid of 2241 vision (as limited by its 4 sides and the rear and front 2242 cut plan) */ 2243 min.x = min.y = min.z = 10.0; 2244 max.x = max.y = max.z = -10.0; 2245 2246 dx = camera.Axis(0)*(DH_REF*0.5); 2247 dy = camera.Axis(1)*(DV_REF*0.5); 2248 dz = camera.Axis(2)*depth_ref; 2249 2250 for (i=0; i<8; i++) { 2251 /* left side / right side */ 2252 if (i&1) p1 = dz + dx; 2253 else p1 = dz - dx; 2254 /* top side / bottom side */ 2255 if (i&2) p1 = p1 + dy; 2256 else p1 = p1 - dy; 2257 /* rear cut plan / front cut plan */ 2258 if (i&4) p1 = p1 * (1.0 / Z_CUT_RATIO); 2259 /* relative to the position of the camera */ 2260 p1 = p1 + origin; 2261 2262 if (min.x > p1.x) min.x = p1.x; 2263 if (min.y > p1.y) min.y = p1.y; 2264 if (min.z > p1.z) min.z = p1.z; 2265 if (max.x < p1.x) max.x = p1.x; 2266 if (max.y < p1.y) max.y = p1.y; 2267 if (max.z < p1.z) max.z = p1.z; 2268 } 2269 2270 /* offset the camera origin by +1 or -1 on any axis (which 2271 doesn't change its relative position in the cubic torus 2272 as the cubic torus repeat itself identicaly for any move 2273 of +1 or -1 on any axis), to get the bounding cube into 2274 [0-2[ x [0-2[ x [0-2[. As the pyramid of vision is just 2275 small enough to gurantee that its bounding box will never 2276 be larger than 1 on any axis, it's always possible. */ 2277 while (min.x < 0.0) { 2278 min.x += 1.0; 2279 max.x += 1.0; 2280 origin.x += 1.0; 2281 } 2282 while (min.y < 0.0) { 2283 min.y += 1.0; 2284 max.y += 1.0; 2285 origin.y += 1.0; 2286 } 2287 while (min.z < 0.0) { 2288 min.z += 1.0; 2289 max.z += 1.0; 2290 origin.z += 1.0; 2291 } 2292 while (max.x >= 2.0) { 2293 min.x -= 1.0; 2294 max.x -= 1.0; 2295 origin.x -= 1.0; 2296 } 2297 while (max.y >= 2.0) { 2298 min.y -= 1.0; 2299 max.y -= 1.0; 2300 origin.y -= 1.0; 2301 } 2302 while (max.z >= 2.0) { 2303 min.z -= 1.0; 2304 max.z -= 1.0; 2305 origin.z -= 1.0; 2306 } 2307 2308 /* set the cutting plans. For example, if the bouding box of 2309 the pyramid of vision of the camera imcludes only X in 2310 [0.43 ; 1.37], we know that points with X in [0 ; 0.4] are 2311 not visible from the camera. So we will offset them by +1 2312 in [1.0 ; 1.4] where they will be visible. Same process 2313 on other axis. That way, we have to test every star of the 2314 starfield in one position and only one. */ 2315 cut.x = (min.x + max.x - 1.0) * 0.5; 2316 cut.y = (min.y + max.y - 1.0) * 0.5; 2317 cut.z = (min.z + max.z - 1.0) * 0.5; 2318 2319 /* Make sure those new settings are copied into the struct 2320 used by the embedded C-engine. */ 2321 SyncGeo(); 2322 } 2323 2324 /* move the camera around, as defined by the animation popup. 2325 This is adjusted by a time factor to compensate for change 2326 in the framerate. */ 2327 void 2328 ChartWindow::CameraAnimation(float time_factor) 2329 { 2330 TPoint move; 2331 2332 switch (fCurrentSettings.animation) { 2333 /* Slow rotation around the "center" of the visible area. */ 2334 case ANIMATION_ROTATE : 2335 /* turn around a point at 0.45 in front of the camera */ 2336 move = camera.Axis(2); 2337 move = move * 0.45; 2338 origin = origin + move; 2339 2340 /* turn around the alpha angle of the spheric rotation 2341 matrix */ 2342 camera_alpha += 0.011*time_factor; 2343 if (camera_alpha > 2*3.14159) 2344 camera_alpha -= 2*3.14159; 2345 2346 /* set the other two angles close from hardcoded values */ 2347 if (camera_theta < 0.18) 2348 camera_theta += 0.003*time_factor; 2349 if (camera_theta > 0.22) 2350 camera_theta -= 0.003*time_factor; 2351 2352 if (camera_phi < -0.02) 2353 camera_phi += 0.003*time_factor; 2354 if (camera_phi > 0.02) 2355 camera_phi -= 0.003*time_factor; 2356 2357 camera.Set(camera_alpha, camera_theta, camera_phi); 2358 camera_invert = camera.Transpose(); 2359 move = camera.Axis(2); 2360 move = move * -0.45; 2361 origin = origin + move; 2362 /* As we moved or rotated the camera, we need to process 2363 again the parameters specific to the pyramid of vision. */ 2364 SetCubeOffset(); 2365 break; 2366 2367 case ANIMATION_SLOW_MOVE : 2368 /* Just move forward, at slow speed */ 2369 move = camera.Axis(2); 2370 move = move * 0.006*time_factor; 2371 origin = origin + move; 2372 SetCubeOffset(); 2373 break; 2374 2375 case ANIMATION_FAST_MOVE : 2376 /* Just move forward, at fast speed */ 2377 move = camera.Axis(2); 2378 move = move * 0.018*time_factor; 2379 origin = origin + move; 2380 SetCubeOffset(); 2381 break; 2382 2383 case ANIMATION_FREE_MOVE : 2384 /* go into advanced selection process no more than once every 2385 0.5 time unit (average time). */ 2386 last_dynamic_delay += time_factor; 2387 if (last_dynamic_delay > 0.5) { 2388 last_dynamic_delay -= 0.5; 2389 if (last_dynamic_delay > 0.2) 2390 last_dynamic_delay = 0.2; 2391 2392 /* if we're not following any target, then just turn 2393 randomly (modifying only the direction of the 2394 acceleration) */ 2395 if (tracking_target < 0) { 2396 if ((crc_alea & 0x4200) == 0) { 2397 if (crc_alea & 0x8000) 2398 cnt_alpha += 1 - (cnt_alpha/4); 2399 else 2400 cnt_alpha += -1 - (cnt_alpha/4); 2401 CrcStep(); 2402 if (crc_alea & 0x8000) 2403 cnt_theta += 1 - (cnt_theta/4); 2404 else 2405 cnt_theta += -1 - (cnt_theta/4); 2406 CrcStep(); 2407 if (crc_alea & 0x8000) 2408 cnt_phi += 1 - (cnt_phi/4); 2409 else 2410 cnt_phi += -1 - (cnt_phi/4); 2411 CrcStep(); 2412 } 2413 CrcStep(); 2414 } 2415 /* if following a target, try to turn in its direction */ 2416 else 2417 FollowTarget(); 2418 2419 /* Change target everyonce in a while... */ 2420 if ((crc_alea & 0xf80) == 0) 2421 SelectNewTarget(); 2422 2423 /* depending the direction of acceleration, increase or 2424 reduce the angular speed of the 3 spherical angles. */ 2425 if (cnt_alpha < 0) 2426 d_alpha += -0.0005 - d_alpha * 0.025; 2427 else if (cnt_alpha > 0) 2428 d_alpha += 0.0005 - d_alpha * 0.025; 2429 2430 if (cnt_theta < 0) 2431 d_theta += -0.0002 - d_theta * 0.025; 2432 else if (cnt_theta > 0) 2433 d_theta += 0.0002 - d_theta * 0.025; 2434 2435 if (cnt_phi < 0) 2436 d_phi += -0.00025 - d_phi * 0.025; 2437 else if (cnt_phi >0) 2438 d_phi += 0.00025 - d_phi * 0.025; 2439 } 2440 2441 /* turn the camera following the specified angular speed */ 2442 camera_alpha += d_alpha*time_factor; 2443 if (camera_alpha < 0.0) 2444 camera_alpha += 2*3.14159; 2445 else if (camera_alpha > 2*3.14159) 2446 camera_alpha -= 2*3.14159; 2447 2448 camera_theta += d_theta*time_factor; 2449 if (camera_theta < 0.0) 2450 camera_theta += 2*3.14159; 2451 else if (camera_theta > 2*3.14159) 2452 camera_theta -= 2*3.14159; 2453 2454 camera_phi += d_phi*time_factor; 2455 if (camera_phi < 0.0) 2456 camera_phi += 2*3.14159; 2457 else if (camera_phi > 2*3.14159) 2458 camera_phi -= 2*3.14159; 2459 2460 /* Set the new rotation matrix of the camera */ 2461 camera.Set(camera_alpha, camera_theta, camera_phi); 2462 camera_invert = camera.Transpose(); 2463 2464 /* move the camera forward at medium speed */ 2465 move = camera.Axis(2); 2466 move = move * 0.0115*time_factor; 2467 origin = origin + move; 2468 SetCubeOffset(); 2469 break; 2470 } 2471 } 2472 2473 2474 void 2475 ChartWindow::SelectNewTarget() 2476 { 2477 float ratio, ratio_min; 2478 float dist, lateral, axial, ftmp; 2479 int32 i, index_min; 2480 TPoint axis, pt, vect; 2481 2482 axis = camera.Axis(2); 2483 ratio_min = 1e6; 2484 index_min = -3; 2485 2486 for (i=-2; i<key_point_count; i++) { 2487 /* if they're used, the comets are two good potential 2488 targets. */ 2489 if (i < 0) { 2490 if (fCurrentSettings.special == SPECIAL_COMET) 2491 pt = comet[i+2]; 2492 else 2493 continue; 2494 } 2495 /* other potential targets are the key_points defined 2496 in the star field. */ 2497 else 2498 pt = key_points[i]; 2499 2500 /* Qualify the interest of the potential target in 2501 relationship with its distance and its proximity to 2502 the axis of the camera. */ 2503 if (pt.x < cut.x) 2504 pt.x += 1.0; 2505 if (pt.y < cut.y) 2506 pt.y += 1.0; 2507 if (pt.z < cut.z) 2508 pt.z += 1.0; 2509 pt = pt - origin; 2510 dist = pt.Length(); 2511 ftmp = 1.0/dist; 2512 pt.x *= ftmp; 2513 pt.y *= ftmp; 2514 pt.z *= ftmp; 2515 vect = pt ^ axis; 2516 lateral = axis.Length(); 2517 axial = pt.x*axis.x + pt.y*axis.y + pt.z*axis.z; 2518 ratio = (lateral/axial) * sqrt(dist); 2519 2520 /* keep track of the best possible choice */ 2521 if ((dist > 0.05) && (ratio < ratio_min)) { 2522 ratio_min = ratio; 2523 index_min = i; 2524 } 2525 } 2526 2527 /* record what target has been chosen */ 2528 tracking_target = index_min+2; 2529 } 2530 2531 /* Try to change the angular acceleration to aim in direction 2532 of the current target. */ 2533 void 2534 ChartWindow::FollowTarget() 2535 { 2536 float x0, y0, x, y, z, cphi, sphi; 2537 TPoint pt; 2538 2539 /* get the target point */ 2540 if (tracking_target < 2) 2541 pt = comet[tracking_target]; 2542 else 2543 pt = key_points[tracking_target-2]; 2544 /* move it in the right iteration of the cubic torus (the 2545 one iteration that is the most likely to be close from 2546 the pyramid of vision. */ 2547 if (pt.x < cut.x) 2548 pt.x += 1.0; 2549 if (pt.y < cut.y) 2550 pt.y += 1.0; 2551 if (pt.z < cut.z) 2552 pt.z += 1.0; 2553 /* convert the target coordinates in the camera referential */ 2554 pt = pt - origin; 2555 x = camera_invert.m[0][0]*pt.x + camera_invert.m[1][0]*pt.y + camera_invert.m[2][0]*pt.z; 2556 y = camera_invert.m[0][1]*pt.x + camera_invert.m[1][1]*pt.y + camera_invert.m[2][1]*pt.z; 2557 z = camera_invert.m[0][2]*pt.x + camera_invert.m[1][2]*pt.y + camera_invert.m[2][2]*pt.z; 2558 if (z <= 0.001) { 2559 /* need to do a U-turn (better to do it using theta). */ 2560 cnt_alpha = 0; 2561 cnt_theta = -1; 2562 cnt_phi = 0; 2563 } 2564 else { 2565 /* need to do a direction adjustement (play with 2566 alpha and theta) */ 2567 cphi = cos(camera_phi); 2568 sphi = sin(camera_phi); 2569 x0 = x*cphi - y*sphi; 2570 y0 = x*sphi + y*cphi; 2571 2572 /* need to move first on the left/right axis */ 2573 if (abs(x0) > abs(y0)) { 2574 if (x0 > 0) 2575 cnt_alpha = -1; 2576 else 2577 cnt_alpha = 1; 2578 cnt_theta = 0; 2579 cnt_phi = 0; 2580 } 2581 /* need to move first on the top/bottom axis */ 2582 else { 2583 if (y0 > 0) 2584 cnt_theta = -1; 2585 else 2586 cnt_theta = 1; 2587 cnt_alpha = 0; 2588 cnt_phi = 0; 2589 } 2590 } 2591 } 2592 2593 /* Do whatever special processing is required to do special 2594 animation. This used a time_step (or time_factor) to 2595 compensate for change in the framerate of the animation. */ 2596 void 2597 ChartWindow::AnimSpecials(float time_step) 2598 { 2599 int i, j; 2600 star *s; 2601 float delta; 2602 special *sp; 2603 2604 switch (fCurrentSettings.special) { 2605 case SPECIAL_COMET : 2606 /* for both comets... */ 2607 for (j=0; j<2; j++) { 2608 /* move the comet forward, at its specific speed */ 2609 comet[j] = comet[j] + delta_comet[j] * time_step; 2610 /* Insure that the comet stays in the [0-1]x[0-1]x[0-1] 2611 iteration of the cubic torus. */ 2612 if (comet[j].x < 0.0) comet[j].x += 1.0; 2613 else if (comet[j].x > 1.0) comet[j].x -= 1.0; 2614 if (comet[j].y < 0.0) comet[j].y += 1.0; 2615 else if (comet[j].y > 1.0) comet[j].y -= 1.0; 2616 if (comet[j].z < 0.0) comet[j].z += 1.0; 2617 else if (comet[j].z > 1.0) comet[j].z -= 1.0; 2618 /* set the position of the star used to represent the 2619 head of the comet. */ 2620 specials.list[j].x = comet[j].x; 2621 specials.list[j].y = comet[j].y; 2622 specials.list[j].z = comet[j].z; 2623 2624 /* for other point, the ones that are ejected from the 2625 comet, depending for allow long they have been ejected... */ 2626 s = specials.list+j+2; 2627 sp = special_list+j+2; 2628 for (i=j+2; i<specials.count; i+=2) { 2629 sp->comet.count -= (int32)time_step; 2630 /* they are reset and reejected again, just a little in 2631 the back of the head of the comet */ 2632 if (sp->comet.count <= 0.0) { 2633 delta = (0.6 + (float)(crc_alea & 31) * (1.0/32.0)) * time_step; 2634 s->x = comet[j].x + 6.0 * sp->comet.dx - delta_comet[j].x * delta; 2635 s->y = comet[j].y + 6.0 * sp->comet.dy - delta_comet[j].y * delta; 2636 s->z = comet[j].z + 6.0 * sp->comet.dz - delta_comet[j].z * delta; 2637 s->size = 0.6; 2638 sp->comet.count = (int32)(sp->comet.count0 + (crc_alea & 63)); 2639 CrcStep(); 2640 } 2641 /* or they just move at their own (ejection) speed */ 2642 else { 2643 s->x += sp->comet.dx * time_step; 2644 s->y += sp->comet.dy * time_step; 2645 s->z += sp->comet.dz * time_step; 2646 s->size *= (1.0 - 0.031 * time_step + 0.001 * time_step * time_step); 2647 } 2648 sp+=2; 2649 s+=2; 2650 } 2651 } 2652 break; 2653 2654 case SPECIAL_NOVAS : 2655 /* Novas are just stars (usualy invisible) that periodically 2656 become much brighter during a suddent flash, then disappear 2657 again until their next cycle */ 2658 sp = special_list; 2659 for (i=0; i<specials.count; i++) { 2660 sp->nova.count -= time_step; 2661 if (sp->nova.count <= 0.0) { 2662 specials.list[i].x -= 10.0; 2663 sp->nova.count = sp->nova.count0 + (crc_alea & 31); 2664 CrcStep(); 2665 } 2666 else if (sp->nova.count < 16.0) { 2667 if (specials.list[i].x < 0.0) 2668 specials.list[i].x += 10.0; 2669 specials.list[i].size = sp->nova.count; 2670 } 2671 sp++; 2672 } 2673 break; 2674 2675 case SPECIAL_BATTLE : 2676 /* not implemented */ 2677 break; 2678 } 2679 } 2680 2681 2682 /* Sync the embedded camera state with the window class camera 2683 state (before calling the embedded C-engine in ChartRender.c */ 2684 void 2685 ChartWindow::SyncGeo() 2686 { 2687 geo.x = origin.x; 2688 geo.y = origin.y; 2689 geo.z = origin.z; 2690 geo.cutx = cut.x; 2691 geo.cuty = cut.y; 2692 geo.cutz = cut.z; 2693 memcpy(geo.m, camera_invert.m, sizeof(float)*9); 2694 } 2695 2696 2697 void 2698 ChartWindow::RefreshStars(buffer *buf, float time_step) 2699 { 2700 /* do the specials animation (single-threaded) */ 2701 AnimSpecials(time_step); 2702 2703 /* do the projection, clipping, erase and redraw 2704 of all stars. This operation is done by the 2705 embedded C-engine. This code only control the 2706 dynamic load split between the two threads, when 2707 needed. */ 2708 if (fCurrentSettings.second_thread) { 2709 int32 star_threshold = (int32)((float)stars.count * fSecondThreadThreshold + 0.5); 2710 int32 special_threshold = (int32)((float)specials.count * fSecondThreadThreshold + 0.5); 2711 2712 /* split the work load (star and special animation) 2713 between the two threads, proportionnaly to the 2714 last split factor determined during the last 2715 cycle. */ 2716 star_packet stars1; 2717 stars1.list = stars.list; 2718 stars1.count = star_threshold; 2719 stars1.erase_count = star_threshold; 2720 if (stars1.erase_count > stars.erase_count) 2721 stars1.erase_count = stars.erase_count; 2722 2723 fStars2.list = stars.list + star_threshold; 2724 fStars2.count = stars.count - star_threshold; 2725 fStars2.erase_count = stars.erase_count - star_threshold; 2726 if (fStars2.erase_count < 0) 2727 fStars2.erase_count = 0; 2728 2729 star_packet specials1; 2730 specials1.list = specials.list; 2731 specials1.count = special_threshold; 2732 specials1.erase_count = special_threshold; 2733 if (specials1.erase_count > specials.erase_count) 2734 specials1.erase_count = specials.erase_count; 2735 2736 fSpecials2.list = specials.list + special_threshold; 2737 fSpecials2.count = specials.count - special_threshold; 2738 fSpecials2.erase_count = specials.erase_count - special_threshold; 2739 if (fSpecials2.erase_count < 0) 2740 fSpecials2.erase_count = 0; 2741 2742 fSecondThreadBuffer = buf; 2743 2744 /* release the slave thread */ 2745 release_sem(fSecondThreadLock); 2746 2747 /* do its own part (time it) */ 2748 bigtime_t before = system_time(); 2749 RefreshStarPacket(buf, &stars1, &geo); 2750 RefreshStarPacket(buf, &specials1, &geo); 2751 bigtime_t after = system_time(); 2752 2753 /* wait for completion of the second thread */ 2754 while (acquire_sem(fSecondThreadRelease) == B_INTERRUPTED) 2755 ; 2756 2757 /* calculate the new optimal split ratio depending 2758 of the previous one and the time used by both 2759 threads to do their work. */ 2760 float ratio = ((float)fSecondThreadDelay/(float)(after-before)) * 2761 (fSecondThreadThreshold/(1.0-fSecondThreadThreshold)); 2762 fSecondThreadThreshold = ratio / (1.0+ratio); 2763 2764 } else { 2765 /* In single-threaded mode, nothing fancy to be done. */ 2766 RefreshStarPacket(buf, &stars, &geo); 2767 RefreshStarPacket(buf, &specials, &geo); 2768 } 2769 2770 /* All the stars that were drawn will have to be erased during 2771 the next frame. */ 2772 stars.erase_count = stars.count; 2773 specials.erase_count = specials.count; 2774 } 2775 2776 2777 // #pragma mark Offscreen bitmap configuration related functions. 2778 2779 2780 void 2781 ChartWindow::CheckBitmap(color_space depth, int32 width, int32 height) 2782 { 2783 color_space cur_depth; 2784 2785 if (LockWithTimeout(200000) != B_OK) 2786 return; 2787 /* If there was no offscreen before, or if it was too small 2788 or in the wrong depth, then... */ 2789 if (fOffscreen == NULL) 2790 cur_depth = B_NO_COLOR_SPACE; 2791 else 2792 cur_depth = bitmap_buffer.depth; 2793 if ((cur_depth != depth) || (width > max_width) || (height > max_height)) { 2794 /* We free the old one if needed... */ 2795 if (fOffscreen) 2796 delete fOffscreen; 2797 /* We chose a new size (resizing are done by big step to 2798 avoid resizing to often)... */ 2799 while ((width > max_width) || (height > max_height)) { 2800 max_width += WINDOW_H_STEP; 2801 max_height += WINDOW_V_STEP; 2802 } 2803 /* And we try to allocate a new BBitmap at the new size. */ 2804 fOffscreen = new BBitmap(BRect(0, 0, max_width-1, max_height-1), depth); 2805 if (!fOffscreen->IsValid()) { 2806 /* If we failed, the offscreen is released and the buffer 2807 clipping is set as empty. */ 2808 delete fOffscreen; 2809 fOffscreen = NULL; 2810 bitmap_buffer.depth = B_NO_COLOR_SPACE; 2811 bitmap_buffer.clip_bounds.top = 0; 2812 bitmap_buffer.clip_bounds.left = 0; 2813 bitmap_buffer.clip_bounds.right = -1; 2814 bitmap_buffer.clip_bounds.bottom = -1; 2815 } 2816 else { 2817 /* If we succeed, then initialise the generic buffer 2818 descriptor, we set the clipping to the required size, 2819 and we set the buffer background color. */ 2820 bitmap_buffer.bits = fOffscreen->Bits(); 2821 bitmap_buffer.bytes_per_row = fOffscreen->BytesPerRow(); 2822 bitmap_buffer.buffer_width = fCurrentSettings.width; 2823 bitmap_buffer.buffer_height = fCurrentSettings.height; 2824 SetColorSpace(&bitmap_buffer, fOffscreen->ColorSpace()); 2825 SetPatternBits(&bitmap_buffer); 2826 SetBitmapClipping(fCurrentSettings.width, fCurrentSettings.height); 2827 SetBitmapBackGround(); 2828 } 2829 } 2830 Unlock(); 2831 } 2832 2833 2834 void 2835 ChartWindow::SetBitmapClipping(int32 width, int32 height) 2836 { 2837 /* Set the bitmap buffer clipping to the required size of 2838 the buffer (even if the allocated buffer is larger) */ 2839 bitmap_buffer.clip_list_count = 1; 2840 bitmap_buffer.clip_bounds.top = 0; 2841 bitmap_buffer.clip_bounds.left = 0; 2842 bitmap_buffer.clip_bounds.right = width-1; 2843 bitmap_buffer.clip_bounds.bottom = height-1; 2844 bitmap_buffer.clip_list[0].top = bitmap_buffer.clip_bounds.top; 2845 bitmap_buffer.clip_list[0].left = bitmap_buffer.clip_bounds.left; 2846 bitmap_buffer.clip_list[0].right = bitmap_buffer.clip_bounds.right; 2847 bitmap_buffer.clip_list[0].bottom = bitmap_buffer.clip_bounds.bottom; 2848 } 2849 2850 2851 void 2852 ChartWindow::SetBitmapBackGround() 2853 { 2854 int32 i, count; 2855 uint32 *bits; 2856 uint32 color; 2857 2858 /* set the bitmap buffer to the right background color */ 2859 bits = (uint32*)fOffscreen->Bits(); 2860 count = fOffscreen->BitsLength()/4; 2861 color = bitmap_buffer.back_color; 2862 2863 for (i=0; i<count; i++) 2864 bits[i] = color; 2865 } 2866 2867 2868 // #pragma mark DirectWindow related functions. 2869 2870 2871 void 2872 ChartWindow::DirectConnected(direct_buffer_info *info) 2873 { 2874 /* block the animation thread. */ 2875 while (acquire_sem(fDrawingLock) == B_INTERRUPTED) 2876 ; 2877 /* update the direct screen infos. */ 2878 SwitchContext(info); 2879 /* unblock the animation thread. */ 2880 release_sem(fDrawingLock); 2881 } 2882 2883 /* This function update the internal graphic context of the ChartWindow 2884 object to reflect the infos send through the DirectConnected API. 2885 It also update the state of stars (and erase some of them) to 2886 insure a clean transition during resize. As this function is called 2887 in DirectConnected, it's a bad idea to do any heavy drawing (long) 2888 operation. But as we only update the stars (the background will be 2889 update a little later by the view system), it's not a big deal. */ 2890 void 2891 ChartWindow::SwitchContext(direct_buffer_info *info) 2892 { 2893 //star *s; 2894 uint32 i, j; 2895 2896 /* you need to use that mask to read the buffer state. */ 2897 switch (info->buffer_state & B_DIRECT_MODE_MASK) { 2898 /* start a direct screen connection. */ 2899 case B_DIRECT_START : 2900 /* set the status as connected, and continue as a modify */ 2901 fDirectConnected = true; 2902 2903 /* change the state of a direct screen connection. */ 2904 case B_DIRECT_MODIFY : 2905 /* update the description of the abstract buffer representing 2906 the direct window connection. DirectConnected returns the 2907 description of the full content area. As we want to use 2908 only the animation view part of the window, we will need 2909 to compensate for that when update the descriptor. */ 2910 2911 /* This calculate the base address of the animation view, taking into 2912 account the base address of the screen buffer, the position of the 2913 window and the position of the view in the window */ 2914 direct_buffer.bits = (void*)((char*)info->bits + 2915 (info->window_bounds.top + TOP_LEFT_LIMIT) * info->bytes_per_row + 2916 (info->window_bounds.left + LEFT_WIDTH) * (info->bits_per_pixel>>3)); 2917 /* Bytes per row and pixel-format are the same than the window values */ 2918 direct_buffer.bytes_per_row = info->bytes_per_row; 2919 SetColorSpace(&direct_buffer, info->pixel_format); 2920 SetPatternBits(&direct_buffer); 2921 2922 /* the width and height of the animation view are linked to the width 2923 and height of the window itself, reduced by the size of the borders 2924 reserved for the UI. */ 2925 direct_buffer.buffer_width = 2926 info->window_bounds.right-info->window_bounds.left+1 - LEFT_WIDTH; 2927 direct_buffer.buffer_height = 2928 info->window_bounds.bottom-info->window_bounds.top+1 - TOP_LEFT_LIMIT; 2929 2930 /* Now, we go through the clipping list and "clip" the clipping 2931 rectangle to the animation view boundary. */ 2932 j = 0; 2933 for (i=0; i<info->clip_list_count; i++) { 2934 direct_buffer.clip_list[j].top = info->clip_list[i].top - info->window_bounds.top; 2935 if (direct_buffer.clip_list[j].top < TOP_LEFT_LIMIT) 2936 direct_buffer.clip_list[j].top = TOP_LEFT_LIMIT; 2937 direct_buffer.clip_list[j].left = info->clip_list[i].left - info->window_bounds.left; 2938 if (direct_buffer.clip_list[j].left < LEFT_WIDTH) 2939 direct_buffer.clip_list[j].left = LEFT_WIDTH; 2940 direct_buffer.clip_list[j].right = info->clip_list[i].right - info->window_bounds.left; 2941 direct_buffer.clip_list[j].bottom = info->clip_list[i].bottom - info->window_bounds.top; 2942 2943 /* All clipped rectangle that are not empty are recorded in 2944 the buffer clipping list. We keep only the 64 first (as 2945 a reasonnable approximation of most cases), but the rectangle 2946 list could easily be made dynamic if needed. Those clipping 2947 rectangle are offset to animation view coordinates */ 2948 if ((direct_buffer.clip_list[j].top <= direct_buffer.clip_list[j].bottom) && 2949 (direct_buffer.clip_list[j].left <= direct_buffer.clip_list[j].right)) { 2950 direct_buffer.clip_list[j].top -= TOP_LEFT_LIMIT; 2951 direct_buffer.clip_list[j].left -= LEFT_WIDTH; 2952 direct_buffer.clip_list[j].right -= LEFT_WIDTH; 2953 direct_buffer.clip_list[j].bottom -= TOP_LEFT_LIMIT; 2954 j++; 2955 if (j == 64) 2956 break; 2957 } 2958 } 2959 /* record the count of clipping rect in the new clipping list (less 2960 or equal to the window clipping list count, as some rectangle can 2961 be made invisible by the extra animation view clipping */ 2962 direct_buffer.clip_list_count = j; 2963 2964 /* the bounding box of the clipping list need to be calculate again 2965 from scratsh. Clipping the bounding box of the window clipping 2966 region to the animation view can give us an incorrect (larger) 2967 bounding box. Remember that the bounding box of a region is 2968 required to be minimal */ 2969 direct_buffer.clip_bounds.top = 20000; 2970 direct_buffer.clip_bounds.left = 20000; 2971 direct_buffer.clip_bounds.right = -20000; 2972 direct_buffer.clip_bounds.bottom = -20000; 2973 2974 for (i=0; i<direct_buffer.clip_list_count; i++) { 2975 if (direct_buffer.clip_bounds.top > direct_buffer.clip_list[i].top) 2976 direct_buffer.clip_bounds.top = direct_buffer.clip_list[i].top; 2977 if (direct_buffer.clip_bounds.left > direct_buffer.clip_list[i].left) 2978 direct_buffer.clip_bounds.left = direct_buffer.clip_list[i].left; 2979 if (direct_buffer.clip_bounds.right < direct_buffer.clip_list[i].right) 2980 direct_buffer.clip_bounds.right = direct_buffer.clip_list[i].right; 2981 if (direct_buffer.clip_bounds.bottom < direct_buffer.clip_list[i].bottom) 2982 direct_buffer.clip_bounds.bottom = direct_buffer.clip_list[i].bottom; 2983 } 2984 2985 /* If the bounding box is empty, nothing is visible and all erasing 2986 should be canceled */ 2987 if ((direct_buffer.clip_bounds.top > direct_buffer.clip_bounds.bottom) || 2988 (direct_buffer.clip_bounds.left > direct_buffer.clip_bounds.right)) { 2989 stars.erase_count = 0; 2990 goto nothing_visible; 2991 } 2992 2993 if (fCurrentSettings.display == DISPLAY_DIRECT) { 2994 /* When the direct display mode is used, the geometry changes 2995 need to be immediatly applied to the engine. */ 2996 SetGeometry(direct_buffer.buffer_width, direct_buffer.buffer_height); 2997 /* if the buffer was reset (that includes testing the work-around 2998 for the known bug in the 1.3.0 version of the app_server), then 2999 we cancel the erasing of the stars for the next frame. */ 3000 if ((info->buffer_state & B_BUFFER_RESET) || 3001 (need_r3_buffer_reset_work_around && 3002 ((info->buffer_state & (B_DIRECT_MODE_MASK|B_BUFFER_MOVED)) == B_DIRECT_START))) { 3003 stars.erase_count = 0; 3004 } 3005 /* In the other case, we need to cancel the erasing of star that 3006 were drawn at the previous frame, but are no longer visible */ 3007 else if (info->buffer_state & B_CLIPPING_MODIFIED) { 3008 RefreshClipping(&direct_buffer, &stars); 3009 RefreshClipping(&direct_buffer, &specials); 3010 } 3011 } 3012 break; 3013 3014 /* stop a direct screen connection */ 3015 case B_DIRECT_STOP : 3016 /* set the status as not connected */ 3017 fDirectConnected = false; 3018 nothing_visible: 3019 /* set an empty clipping */ 3020 direct_buffer.clip_list_count = 1; 3021 direct_buffer.clip_bounds.top = 0; 3022 direct_buffer.clip_bounds.left = 0; 3023 direct_buffer.clip_bounds.right = -1; 3024 direct_buffer.clip_bounds.bottom = -1; 3025 direct_buffer.clip_list[0].top = 0; 3026 direct_buffer.clip_list[0].left = 0; 3027 direct_buffer.clip_list[0].right = -1; 3028 direct_buffer.clip_list[0].bottom = -1; 3029 break; 3030 } 3031 } 3032 3033 3034 /*! copy a setting into another */ 3035 void 3036 ChartWindow::setting::Set(setting *master) 3037 { 3038 memcpy(this, master, sizeof(setting)); 3039 } 3040 3041 3042 /*! Pseudo-random generator increment function. */ 3043 void 3044 ChartWindow::CrcStep() 3045 { 3046 crc_alea <<= 1; 3047 if (crc_alea < 0) 3048 crc_alea ^= CRC_KEY; 3049 } 3050