00001
00002 #include "revolt.h"
00003 #include "NewColl.h"
00004 #include "light.h"
00005 #include "dx.h"
00006 #include "geom.h"
00007 #include "particle.h"
00008 #include "aerial.h"
00009 #include "play.h"
00010 #include "Body.h"
00011 #include "car.h"
00012 #include "camera.h"
00013 #include "text.h"
00014 #include "main.h"
00015 #include "draw.h"
00016 #include "input.h"
00017 #include "level.h"
00018 #include "model.h"
00019 #include "registry.h"
00020
00021
00022
00023 static VEC LightDelta;
00024 static char LightAxis = 0, LightAxisType = 0;
00025 static short AffectFixedLightCount, AffectMovingLightCount, AffectObjectLightCount;
00026 static LIGHT *AffectFixedLights[LIGHT_MAX];
00027 static LIGHT *AffectMovingLights[LIGHT_MAX];
00028 static LIGHT *AffectObjectLights[LIGHT_MAX];
00029 static MODEL LightOmniModel, LightSpotModel;
00030 short TotalLightCount;
00031 LIGHT Light[LIGHT_MAX];
00032 LIGHT *CurrentEditLight;
00033
00034
00035
00036 static char *LightNames[] = {
00037 "Omni",
00038 "Omni Normal",
00039 "Spot",
00040 "Spot Normal",
00041 };
00042
00043 static char *LightFlags[] = {
00044 "World Only",
00045 "Objects Only",
00046 "World and Objects",
00047 };
00048
00049 static char *LightAxisNames[] = {
00050 "X Y",
00051 "X Z",
00052 "Z Y",
00053 "X",
00054 "Y",
00055 "Z",
00056 };
00057
00058 static char *LightAxisTypeNames[] = {
00059 "Camera",
00060 "World",
00061 };
00062
00063 static char *LightFlickerNames[] = {
00064 "Off",
00065 "On",
00066 };
00067
00069
00071
00072 void InitLights(void)
00073 {
00074 short i;
00075
00076 for (i = 0 ; i < LIGHT_MAX ; i++) Light[i].Flag = 0;
00077 }
00078
00080
00082
00083 LIGHT *AllocLight(void)
00084 {
00085 short i;
00086
00087
00088
00089 for (i = 0 ; i < LIGHT_MAX ; i++) if (!Light[i].Flag)
00090 {
00091 return &Light[i];
00092 }
00093
00094
00095
00096 return NULL;
00097 }
00098
00100
00102
00103 void FreeLight(LIGHT *light)
00104 {
00105 light->Flag = 0;
00106 }
00107
00109
00111
00112 void ProcessLights(void)
00113 {
00114 short i;
00115 LIGHT *l;
00116 long tf;
00117
00118
00119
00120 if (!RenderSettings.Light)
00121 return;
00122
00123
00124
00125 AffectFixedLightCount = 0;
00126 AffectMovingLightCount = 0;
00127 TotalLightCount = 0;
00128
00129
00130
00131 tf = (long)TimeFactor;
00132 l = Light;
00133
00134 for (i = 0 ; i < LIGHT_MAX ; i++, l++) if (l->Flag)
00135 {
00136
00137
00138
00139 TotalLightCount++;
00140
00141
00142
00143 if (l->Flag & LIGHT_FLICKER)
00144 {
00145 if ((rand() % l->Speed) < tf)
00146 l->Flag ^= LIGHT_OFF;
00147 }
00148
00149
00150
00151 if (l->Flag & LIGHT_OFF)
00152 continue;
00153
00154
00155
00156 if (l->Flag & LIGHT_FIXED)
00157 AffectFixedLights[AffectFixedLightCount++] = l;
00158
00159 if (l->Flag & LIGHT_MOVING)
00160 AffectMovingLights[AffectMovingLightCount++] = l;
00161
00162
00163
00164 ProcessOneLight(l);
00165 }
00166 }
00167
00169
00171
00172 void ProcessOneLight(LIGHT *l)
00173 {
00174
00175
00176
00177 l->SquareReach = l->Reach * l->Reach;
00178
00179
00180
00181 l->Xmin = l->x - l->Reach;
00182 l->Xmax = l->x + l->Reach;
00183 l->Ymin = l->y - l->Reach;
00184 l->Ymax = l->y + l->Reach;
00185 l->Zmin = l->z - l->Reach;
00186 l->Zmax = l->z + l->Reach;
00187
00188
00189
00190 l->ConeMul = 180 / l->Cone;
00191 }
00192
00194
00196
00197 void AddPermLight(LIGHT *light)
00198 {
00199 short i;
00200 float dx, dy, dz;
00201 CUBE_HEADER *cube;
00202
00203
00204
00205 if (!light->Flag) return;
00206
00207
00208
00209 ProcessOneLight(light);
00210
00211
00212
00213 cube = World.Cube;
00214
00215 for (i = 0 ; i < World.CubeNum ; i++, cube++)
00216 {
00217
00218
00219
00220 if (cube->Xmin > light->Xmax || cube->Xmax < light->Xmin || cube->Ymin > light->Ymax || cube->Ymax < light->Ymin || cube->Zmin > light->Zmax || cube->Zmax < light->Zmin) continue;
00221
00222
00223
00224 dx = cube->CentreX - light->x;
00225 dy = cube->CentreY - light->y;
00226 dz = cube->CentreZ - light->z;
00227 if ((float)sqrt(dx * dx + dy * dy + dz * dz) > light->Reach + cube->Radius) continue;
00228
00229
00230
00231 switch (light->Type)
00232 {
00233 case LIGHT_OMNI:
00234 AddCubeLightPermOmni(cube, light);
00235 break;
00236 case LIGHT_OMNINORMAL:
00237 AddCubeLightPermOmniNormal(cube, light);
00238 break;
00239 case LIGHT_SPOT:
00240 AddCubeLightPermSpot(cube, light);
00241 break;
00242 case LIGHT_SPOTNORMAL:
00243 AddCubeLightPermSpotNormal(cube, light);
00244 break;
00245 }
00246 }
00247 }
00248
00250
00252
00253 void AddCubeLightPermOmni(CUBE_HEADER *cube, LIGHT *light)
00254 {
00255 WORLD_POLY *p;
00256 WORLD_VERTEX **v;
00257 float squaredist;
00258 long mul;
00259 short i, j;
00260 long rgb[3];
00261 MODEL_RGB *col;
00262
00263
00264
00265 p = cube->Model.PolyPtr;
00266 for (i = 0 ; i < cube->Model.PolyNum ; i++, p++)
00267 {
00268 v = &p->v0;
00269 col = (MODEL_RGB*)&p->rgb0;
00270
00271 for (j = (p->Type & POLY_QUAD) ? 4 : 3 ; j ; j--, v++, col++)
00272 {
00273 LightDelta.v[X] = light->x - (*v)->x;
00274 LightDelta.v[Y] = light->y - (*v)->y;
00275 LightDelta.v[Z] = light->z - (*v)->z;
00276
00277 squaredist = DotProduct(&LightDelta, &LightDelta);
00278 if (squaredist < light->SquareReach)
00279 {
00280 FTOL((1 - squaredist / light->SquareReach) * 256, mul);
00281
00282 rgb[0] = (light->r * mul) >> 8;
00283 rgb[1] = (light->g * mul) >> 8;
00284 rgb[2] = (light->b * mul) >> 8;
00285
00286 ModelAddGouraud(col, rgb, col);
00287 }
00288 }
00289 }
00290 }
00291
00293
00295
00296 void AddCubeLightPermOmniNormal(CUBE_HEADER *cube, LIGHT *light)
00297 {
00298 WORLD_POLY *p;
00299 WORLD_VERTEX **v;
00300 float squaredist, ang, dist;
00301 long mul;
00302 short i, j;
00303 long rgb[3];
00304 MODEL_RGB *col;
00305
00306
00307
00308 p = cube->Model.PolyPtr;
00309 for (i = 0 ; i < cube->Model.PolyNum ; i++, p++)
00310 {
00311 v = &p->v0;
00312 col = (MODEL_RGB*)&p->rgb0;
00313
00314 for (j = (p->Type & POLY_QUAD) ? 4 : 3 ; j ; j--, v++, col++)
00315 {
00316 LightDelta.v[X] = light->x - (*v)->x;
00317 LightDelta.v[Y] = light->y - (*v)->y;
00318 LightDelta.v[Z] = light->z - (*v)->z;
00319
00320 ang = DotProduct(&LightDelta, (VEC*)&(*v)->nx);
00321 if (ang > 0)
00322 {
00323 squaredist = DotProduct(&LightDelta, &LightDelta);
00324 if (squaredist < light->SquareReach)
00325 {
00326 dist = (float)sqrt(squaredist);
00327 FTOL(((1 - squaredist / light->SquareReach) * (ang / dist)) * 256, mul);
00328
00329 rgb[0] = (light->r * mul) >> 8;
00330 rgb[1] = (light->g * mul) >> 8;
00331 rgb[2] = (light->b * mul) >> 8;
00332
00333 ModelAddGouraud(col, rgb, col);
00334 }
00335 }
00336 }
00337 }
00338 }
00339
00341
00343
00344 void AddCubeLightPermSpot(CUBE_HEADER *cube, LIGHT *light)
00345 {
00346 WORLD_POLY *p;
00347 WORLD_VERTEX **v;
00348 float squaredist, cone, dist;
00349 long mul;
00350 short i, j;
00351 long rgb[3];
00352 MODEL_RGB *col;
00353
00354
00355
00356 p = cube->Model.PolyPtr;
00357 for (i = 0 ; i < cube->Model.PolyNum ; i++, p++)
00358 {
00359 v = &p->v0;
00360 col = (MODEL_RGB*)&p->rgb0;
00361
00362 for (j = (p->Type & POLY_QUAD) ? 4 : 3 ; j ; j--, v++, col++)
00363 {
00364 LightDelta.v[X] = light->x - (*v)->x;
00365 LightDelta.v[Y] = light->y - (*v)->y;
00366 LightDelta.v[Z] = light->z - (*v)->z;
00367
00368 squaredist = DotProduct(&LightDelta, &LightDelta);
00369 if (squaredist < light->SquareReach)
00370 {
00371 dist = (float)sqrt(squaredist);
00372 cone = (-DotProduct(&light->DirMatrix.mv[Z], &LightDelta) / dist - 1) * light->ConeMul + 1;
00373 if (cone > 0)
00374 {
00375 FTOL(((1 - squaredist / light->SquareReach) * cone) * 256, mul);
00376
00377 rgb[0] = (light->r * mul) >> 8;
00378 rgb[1] = (light->g * mul) >> 8;
00379 rgb[2] = (light->b * mul) >> 8;
00380
00381 ModelAddGouraud(col, rgb, col);
00382 }
00383 }
00384 }
00385 }
00386 }
00387
00389
00391
00392 void AddCubeLightPermSpotNormal(CUBE_HEADER *cube, LIGHT *light)
00393 {
00394 WORLD_POLY *p;
00395 WORLD_VERTEX **v;
00396 float squaredist, ang, cone, dist;
00397 long mul;
00398 short i, j;
00399 long rgb[3];
00400 MODEL_RGB *col;
00401
00402
00403
00404 p = cube->Model.PolyPtr;
00405 for (i = 0 ; i < cube->Model.PolyNum ; i++, p++)
00406 {
00407 v = &p->v0;
00408 col = (MODEL_RGB*)&p->rgb0;
00409
00410 for (j = (p->Type & POLY_QUAD) ? 4 : 3 ; j ; j--, v++, col++)
00411 {
00412 LightDelta.v[X] = light->x - (*v)->x;
00413 LightDelta.v[Y] = light->y - (*v)->y;
00414 LightDelta.v[Z] = light->z - (*v)->z;
00415
00416 ang = DotProduct(&LightDelta, (VEC*)&(*v)->nx);
00417 if (ang > 0)
00418 {
00419 squaredist = DotProduct(&LightDelta, &LightDelta);
00420 if (squaredist < light->SquareReach)
00421 {
00422 dist = (float)sqrt(squaredist);
00423 cone = (-DotProduct(&light->DirMatrix.mv[Z], &LightDelta) / dist - 1) * light->ConeMul + 1;
00424 if (cone > 0)
00425 {
00426 FTOL(((1 - squaredist / light->SquareReach) * (ang / dist) * cone) * 256, mul);
00427
00428 rgb[0] = (light->r * mul) >> 8;
00429 rgb[1] = (light->g * mul) >> 8;
00430 rgb[2] = (light->b * mul) >> 8;
00431
00432 ModelAddGouraud(col, rgb, col);
00433 }
00434 }
00435 }
00436 }
00437 }
00438 }
00439
00441
00443
00444 char CheckCubeLight(CUBE_HEADER *cube)
00445 {
00446 char i, flag;
00447 float dist, cone, conemul;
00448 LIGHT *l;
00449
00450
00451
00452 if (!RenderSettings.Light)
00453 return FALSE;
00454
00455
00456
00457 flag = 0;
00458
00459 for (i = 0 ; i < AffectFixedLightCount ; i++)
00460 {
00461 l = AffectFixedLights[i];
00462
00463
00464
00465 if (cube->Xmin > l->Xmax || cube->Xmax < l->Xmin || cube->Ymin > l->Ymax || cube->Ymax < l->Ymin || cube->Zmin > l->Zmax || cube->Zmax < l->Zmin) continue;
00466
00467
00468
00469 LightDelta.v[X] = cube->CentreX - l->x;
00470 LightDelta.v[Y] = cube->CentreY - l->y;
00471 LightDelta.v[Z] = cube->CentreZ - l->z;
00472
00473 dist = Length(&LightDelta);
00474 if (dist > l->Reach + cube->Radius) continue;
00475
00476
00477
00478 if ((l->Type == LIGHT_SPOT || l->Type == LIGHT_SPOTNORMAL) && dist > cube->Radius)
00479 {
00480 conemul = 1 / (cube->Radius / dist + l->Cone / 180);
00481 cone = (DotProduct(&l->DirMatrix.mv[Z], &LightDelta) / dist - 1) * conemul + 1;
00482 if (cone <= 0) continue;
00483 }
00484
00485
00486
00487 switch (l->Type)
00488 {
00489 case LIGHT_OMNI:
00490 if (flag) AddCubeLightOmni(cube, l);
00491 else SetCubeLightOmni(cube, l);
00492 break;
00493 case LIGHT_OMNINORMAL:
00494 if (flag) AddCubeLightOmniNormal(cube, l);
00495 else SetCubeLightOmniNormal(cube, l);
00496 break;
00497 case LIGHT_SPOT:
00498 if (flag) AddCubeLightSpot(cube, l);
00499 else SetCubeLightSpot(cube, l);
00500 break;
00501 case LIGHT_SPOTNORMAL:
00502 if (flag) AddCubeLightSpotNormal(cube, l);
00503 else SetCubeLightSpotNormal(cube, l);
00504 break;
00505 }
00506 flag++;
00507 }
00508
00509
00510
00511 return flag;
00512 }
00513
00515
00517
00518 void SetCubeLightOmni(CUBE_HEADER *cube, LIGHT *light)
00519 {
00520 WORLD_VERTEX *v;
00521 float squaredist;
00522 long mul;
00523 short i;
00524
00525
00526
00527 v = cube->Model.VertPtr;
00528 for (i = cube->Model.VertNum ; i ; i--, v++)
00529 {
00530 LightDelta.v[X] = light->x - v->x;
00531 LightDelta.v[Y] = light->y - v->y;
00532 LightDelta.v[Z] = light->z - v->z;
00533
00534 squaredist = DotProduct(&LightDelta, &LightDelta);
00535 if (squaredist >= light->SquareReach)
00536 {
00537 v->r = v->g = v->b = 0;
00538 continue;
00539 }
00540
00541 FTOL((1 - squaredist / light->SquareReach) * 256, mul);
00542
00543 v->r = (light->r * mul) >> 8;
00544 v->g = (light->g * mul) >> 8;
00545 v->b = (light->b * mul) >> 8;
00546 }
00547 }
00548
00550
00552
00553 void AddCubeLightOmni(CUBE_HEADER *cube, LIGHT *light)
00554 {
00555 WORLD_VERTEX *v;
00556 float squaredist;
00557 long mul;
00558 short i;
00559
00560
00561
00562 v = cube->Model.VertPtr;
00563 for (i = cube->Model.VertNum ; i ; i--, v++)
00564 {
00565 LightDelta.v[X] = light->x - v->x;
00566 LightDelta.v[Y] = light->y - v->y;
00567 LightDelta.v[Z] = light->z - v->z;
00568
00569 squaredist = DotProduct(&LightDelta, &LightDelta);
00570 if (squaredist >= light->SquareReach)
00571 continue;
00572
00573 FTOL((1 - squaredist / light->SquareReach) * 256, mul);
00574
00575 v->r += (light->r * mul) >> 8;
00576 v->g += (light->g * mul) >> 8;
00577 v->b += (light->b * mul) >> 8;
00578 }
00579 }
00580
00582
00584
00585 void SetCubeLightOmniNormal(CUBE_HEADER *cube, LIGHT *light)
00586 {
00587 WORLD_VERTEX *v;
00588 float squaredist, ang, dist;
00589 long mul;
00590 short i;
00591
00592
00593
00594 v = cube->Model.VertPtr;
00595 for (i = cube->Model.VertNum ; i ; i--, v++)
00596 {
00597 LightDelta.v[X] = light->x - v->x;
00598 LightDelta.v[Y] = light->y - v->y;
00599 LightDelta.v[Z] = light->z - v->z;
00600
00601 ang = DotProduct(&LightDelta, (VEC*)&v->nx);
00602 if (ang <= 0)
00603 {
00604 v->r = v->g = v->b = 0;
00605 continue;
00606 }
00607
00608 squaredist = DotProduct(&LightDelta, &LightDelta);
00609 if (squaredist >= light->SquareReach)
00610 {
00611 v->r = v->g = v->b = 0;
00612 continue;
00613 }
00614
00615 dist = (float)sqrt(squaredist);
00616 FTOL(((1 - squaredist / light->SquareReach) * (ang / dist)) * 256, mul);
00617
00618 v->r = (light->r * mul) >> 8;
00619 v->g = (light->g * mul) >> 8;
00620 v->b = (light->b * mul) >> 8;
00621 }
00622 }
00623
00625
00627
00628 void AddCubeLightOmniNormal(CUBE_HEADER *cube, LIGHT *light)
00629 {
00630 WORLD_VERTEX *v;
00631 float squaredist, ang, dist;
00632 long mul;
00633 short i;
00634
00635
00636
00637 v = cube->Model.VertPtr;
00638 for (i = cube->Model.VertNum ; i ; i--, v++)
00639 {
00640 LightDelta.v[X] = light->x - v->x;
00641 LightDelta.v[Y] = light->y - v->y;
00642 LightDelta.v[Z] = light->z - v->z;
00643
00644 ang = DotProduct(&LightDelta, (VEC*)&v->nx);
00645 if (ang <= 0)
00646 continue;
00647
00648 squaredist = DotProduct(&LightDelta, &LightDelta);
00649 if (squaredist >= light->SquareReach)
00650 continue;
00651
00652 dist = (float)sqrt(squaredist);
00653 FTOL(((1 - squaredist / light->SquareReach) * (ang / dist)) * 256, mul);
00654
00655 v->r += (light->r * mul) >> 8;
00656 v->g += (light->g * mul) >> 8;
00657 v->b += (light->b * mul) >> 8;
00658 }
00659 }
00660
00662
00664
00665 void SetCubeLightSpot(CUBE_HEADER *cube, LIGHT *light)
00666 {
00667 WORLD_VERTEX *v;
00668 float squaredist, cone, dist;
00669 long mul;
00670 short i;
00671
00672
00673
00674 v = cube->Model.VertPtr;
00675 for (i = cube->Model.VertNum ; i ; i--, v++)
00676 {
00677 LightDelta.v[X] = light->x - v->x;
00678 LightDelta.v[Y] = light->y - v->y;
00679 LightDelta.v[Z] = light->z - v->z;
00680
00681 squaredist = DotProduct(&LightDelta, &LightDelta);
00682 if (squaredist >= light->SquareReach)
00683 {
00684 v->r = v->g = v->b = 0;
00685 continue;
00686 }
00687
00688 dist = (float)sqrt(squaredist);
00689
00690 cone = (-DotProduct(&light->DirMatrix.mv[Z], &LightDelta) / dist - 1) * light->ConeMul + 1;
00691 if (cone <= 0)
00692 {
00693 v->r = v->g = v->b = 0;
00694 continue;
00695 }
00696
00697 FTOL(((1 - squaredist / light->SquareReach) * cone) * 256, mul);
00698
00699 v->r = (light->r * mul) >> 8;
00700 v->g = (light->g * mul) >> 8;
00701 v->b = (light->b * mul) >> 8;
00702 }
00703 }
00704
00706
00708
00709 void AddCubeLightSpot(CUBE_HEADER *cube, LIGHT *light)
00710 {
00711 WORLD_VERTEX *v;
00712 float squaredist, cone, dist;
00713 long mul;
00714 short i;
00715
00716
00717
00718 v = cube->Model.VertPtr;
00719 for (i = cube->Model.VertNum ; i ; i--, v++)
00720 {
00721 LightDelta.v[X] = light->x - v->x;
00722 LightDelta.v[Y] = light->y - v->y;
00723 LightDelta.v[Z] = light->z - v->z;
00724
00725 squaredist = DotProduct(&LightDelta, &LightDelta);
00726 if (squaredist >= light->SquareReach)
00727 continue;
00728
00729 dist = (float)sqrt(squaredist);
00730
00731 cone = (-DotProduct(&light->DirMatrix.mv[Z], &LightDelta) / dist - 1) * light->ConeMul + 1;
00732 if (cone <= 0)
00733 continue;
00734
00735 FTOL(((1 - squaredist / light->SquareReach) * cone) * 256, mul);
00736
00737 v->r += (light->r * mul) >> 8;
00738 v->g += (light->g * mul) >> 8;
00739 v->b += (light->b * mul) >> 8;
00740 }
00741 }
00742
00744
00746
00747 void SetCubeLightSpotNormal(CUBE_HEADER *cube, LIGHT *light)
00748 {
00749 WORLD_VERTEX *v;
00750 float squaredist, ang, cone, dist;
00751 long mul;
00752 short i;
00753
00754
00755
00756 v = cube->Model.VertPtr;
00757 for (i = cube->Model.VertNum ; i ; i--, v++)
00758 {
00759 LightDelta.v[X] = light->x - v->x;
00760 LightDelta.v[Y] = light->y - v->y;
00761 LightDelta.v[Z] = light->z - v->z;
00762
00763 ang = DotProduct(&LightDelta, (VEC*)&v->nx);
00764 if (ang <= 0)
00765 {
00766 v->r = v->g = v->b = 0;
00767 continue;
00768 }
00769
00770 squaredist = DotProduct(&LightDelta, &LightDelta);
00771 if (squaredist >= light->SquareReach)
00772 {
00773 v->r = v->g = v->b = 0;
00774 continue;
00775 }
00776
00777 dist = (float)sqrt(squaredist);
00778
00779 cone = (-DotProduct(&light->DirMatrix.mv[Z], &LightDelta) / dist - 1) * light->ConeMul + 1;
00780 if (cone <= 0)
00781 {
00782 v->r = v->g = v->b = 0;
00783 continue;
00784 }
00785
00786 FTOL(((1 - squaredist / light->SquareReach) * (ang / dist) * cone) * 256, mul);
00787
00788 v->r = (light->r * mul) >> 8;
00789 v->g = (light->g * mul) >> 8;
00790 v->b = (light->b * mul) >> 8;
00791 }
00792 }
00793
00795
00797
00798 void AddCubeLightSpotNormal(CUBE_HEADER *cube, LIGHT *light)
00799 {
00800 WORLD_VERTEX *v;
00801 float squaredist, ang, cone, dist;
00802 long mul;
00803 short i;
00804
00805
00806
00807 v = cube->Model.VertPtr;
00808 for (i = cube->Model.VertNum ; i ; i--, v++)
00809 {
00810 LightDelta.v[X] = light->x - v->x;
00811 LightDelta.v[Y] = light->y - v->y;
00812 LightDelta.v[Z] = light->z - v->z;
00813
00814 ang = DotProduct(&LightDelta, (VEC*)&v->nx);
00815 if (ang <= 0)
00816 continue;
00817
00818 squaredist = DotProduct(&LightDelta, &LightDelta);
00819 if (squaredist >= light->SquareReach)
00820 continue;
00821
00822 dist = (float)sqrt(squaredist);
00823
00824 cone = (-DotProduct(&light->DirMatrix.mv[Z], &LightDelta) / dist - 1) * light->ConeMul + 1;
00825 if (cone <= 0)
00826 continue;
00827
00828 FTOL(((1 - squaredist / light->SquareReach) * (ang / dist) * cone) * 256, mul);
00829
00830 v->r += (light->r * mul) >> 8;
00831 v->g += (light->g * mul) >> 8;
00832 v->b += (light->b * mul) >> 8;
00833 }
00834 }
00835
00837
00839
00840 short CheckObjectLight(VEC *pos, BOUNDING_BOX *box, float rad)
00841 {
00842 char i;
00843 LIGHT *l;
00844 float dist, cone, conemul;
00845
00846
00847
00848 if (!RenderSettings.Light)
00849 return FALSE;
00850
00851
00852
00853 AffectObjectLightCount = 0;
00854
00855
00856
00857 for (i = 0 ; i < AffectMovingLightCount ; i++)
00858 {
00859 l = AffectMovingLights[i];
00860
00861
00862
00863 if (box->Xmin > l->Xmax || box->Xmax < l->Xmin ||
00864 box->Ymin > l->Ymax || box->Ymax < l->Ymin ||
00865 box->Zmin > l->Zmax || box->Zmax < l->Zmin) continue;
00866
00867
00868
00869 LightDelta.v[X] = pos->v[X] - l->x;
00870 LightDelta.v[Y] = pos->v[Y] - l->y;
00871 LightDelta.v[Z] = pos->v[Z] - l->z;
00872 dist = Length(&LightDelta);
00873
00874 if (dist > l->Reach + rad) continue;
00875
00876
00877
00878 if ((l->Type == LIGHT_SPOT || l->Type == LIGHT_SPOTNORMAL) && dist > rad)
00879 {
00880 conemul = 1 / (rad / dist + l->Cone / 180);
00881 cone = (DotProduct(&l->DirMatrix.mv[Z], &LightDelta) / dist - 1) * conemul + 1;
00882 if (cone <= 0) continue;
00883 }
00884
00885
00886
00887 AffectObjectLights[AffectObjectLightCount++] = l;
00888 }
00889
00890
00891
00892 return AffectObjectLightCount;
00893 }
00894
00896
00898
00899 short CheckInstanceLight(INSTANCE *inst, float rad)
00900 {
00901 char i;
00902 LIGHT *l;
00903 float dist, cone, conemul;
00904 BOUNDING_BOX *box = &inst->Box;
00905 VEC *pos = &inst->WorldPos;
00906
00907
00908
00909 if (!RenderSettings.Light)
00910 return FALSE;
00911
00912
00913
00914 AffectObjectLightCount = 0;
00915
00916
00917
00918 for (i = 0 ; i < AffectFixedLightCount ; i++)
00919 {
00920 l = AffectFixedLights[i];
00921
00922
00923
00924 if (box->Xmin > l->Xmax || box->Xmax < l->Xmin ||
00925 box->Ymin > l->Ymax || box->Ymax < l->Ymin ||
00926 box->Zmin > l->Zmax || box->Zmax < l->Zmin) continue;
00927
00928
00929
00930 LightDelta.v[X] = pos->v[X] - l->x;
00931 LightDelta.v[Y] = pos->v[Y] - l->y;
00932 LightDelta.v[Z] = pos->v[Z] - l->z;
00933 dist = Length(&LightDelta);
00934
00935 if (dist > l->Reach + rad) continue;
00936
00937
00938
00939 if ((l->Type == LIGHT_SPOT || l->Type == LIGHT_SPOTNORMAL) && dist > rad)
00940 {
00941 conemul = 1 / (rad / dist + l->Cone / 180);
00942 cone = (DotProduct(&l->DirMatrix.mv[Z], &LightDelta) / dist - 1) * conemul + 1;
00943 if (cone <= 0) continue;
00944 }
00945
00946
00947
00948 AffectObjectLights[AffectObjectLightCount++] = l;
00949 }
00950
00951
00952
00953 return AffectObjectLightCount;
00954 }
00955
00957
00959
00960 short CheckInstanceLightEdit(INSTANCE *inst, float rad)
00961 {
00962 char i;
00963 LIGHT *l;
00964 float dist, cone, conemul;
00965 BOUNDING_BOX *box = &inst->Box;
00966 VEC *pos = &inst->WorldPos;
00967
00968
00969
00970 if (!RenderSettings.Light)
00971 return FALSE;
00972
00973
00974
00975 AffectObjectLightCount = 0;
00976
00977
00978
00979 l = Light;
00980 for (i = 0 ; i < LIGHT_MAX ; i++, l++) if (l->Flag & ~LIGHT_OFF)
00981 {
00982
00983
00984
00985 if ((inst->Flag & INSTANCE_NO_FILE_LIGHTS) && (l->Flag & LIGHT_FILE))
00986 continue;
00987
00988
00989
00990 if (box->Xmin > l->Xmax || box->Xmax < l->Xmin ||
00991 box->Ymin > l->Ymax || box->Ymax < l->Ymin ||
00992 box->Zmin > l->Zmax || box->Zmax < l->Zmin) continue;
00993
00994
00995
00996 LightDelta.v[X] = pos->v[X] - l->x;
00997 LightDelta.v[Y] = pos->v[Y] - l->y;
00998 LightDelta.v[Z] = pos->v[Z] - l->z;
00999 dist = Length(&LightDelta);
01000
01001 if (dist > l->Reach + rad) continue;
01002
01003
01004
01005 if ((l->Type == LIGHT_SPOT || l->Type == LIGHT_SPOTNORMAL) && dist > rad)
01006 {
01007 conemul = 1 / (rad / dist + l->Cone / 180);
01008 cone = (DotProduct(&l->DirMatrix.mv[Z], &LightDelta) / dist - 1) * conemul + 1;
01009 if (cone <= 0) continue;
01010 }
01011
01012
01013
01014 AffectObjectLights[AffectObjectLightCount++] = l;
01015 }
01016
01017
01018
01019 return AffectObjectLightCount;
01020 }
01021
01023
01025
01026 void AddModelLightPermOmni(MODEL *model, LIGHT *light, VEC *pos)
01027 {
01028 MODEL_POLY *p;
01029 MODEL_VERTEX **v;
01030 float squaredist;
01031 long mul, vcount;
01032 short i, j;
01033 long rgb[3];
01034 POLY_RGB *col;
01035
01036
01037
01038 col = model->PolyRGB;
01039 p = model->PolyPtr;
01040
01041 for (i = 0 ; i < model->PolyNum ; i++, p++, col++)
01042 {
01043 v = &p->v0;
01044
01045 vcount = (p->Type & POLY_QUAD) ? 4 : 3;
01046 for (j = 0 ; j < vcount ; j++, v++)
01047 {
01048 LightDelta.v[X] = pos->v[X] - (*v)->x;
01049 LightDelta.v[Y] = pos->v[Y] - (*v)->y;
01050 LightDelta.v[Z] = pos->v[Z] - (*v)->z;
01051
01052 squaredist = DotProduct(&LightDelta, &LightDelta);
01053 if (squaredist < light->SquareReach)
01054 {
01055 FTOL((1 - squaredist / light->SquareReach) * 256, mul);
01056
01057 rgb[0] = (light->r * mul) >> 8;
01058 rgb[1] = (light->g * mul) >> 8;
01059 rgb[2] = (light->b * mul) >> 8;
01060
01061 ModelAddGouraud(&col->rgb[j], rgb, &col->rgb[j]);
01062 }
01063 }
01064 }
01065 }
01066
01068
01070
01071 void AddModelLightPermOmniNormal(MODEL *model, LIGHT *light, VEC *pos)
01072 {
01073 MODEL_POLY *p;
01074 MODEL_VERTEX **v;
01075 float squaredist, ang, dist;
01076 long mul, vcount;
01077 short i, j;
01078 long rgb[3];
01079 POLY_RGB *col;
01080
01081
01082
01083 col = model->PolyRGB;
01084 p = model->PolyPtr;
01085
01086 for (i = 0 ; i < model->PolyNum ; i++, p++, col++)
01087 {
01088 v = &p->v0;
01089
01090 vcount = (p->Type & POLY_QUAD) ? 4 : 3;
01091 for (j = 0 ; j < vcount ; j++, v++)
01092 {
01093 LightDelta.v[X] = pos->v[X] - (*v)->x;
01094 LightDelta.v[Y] = pos->v[Y] - (*v)->y;
01095 LightDelta.v[Z] = pos->v[Z] - (*v)->z;
01096
01097 ang = DotProduct(&LightDelta, (VEC*)&(*v)->nx);
01098 if (ang > 0)
01099 {
01100 squaredist = DotProduct(&LightDelta, &LightDelta);
01101 if (squaredist < light->SquareReach)
01102 {
01103 dist = (float)sqrt(squaredist);
01104 FTOL(((1 - squaredist / light->SquareReach) * (ang / dist)) * 256, mul);
01105
01106 rgb[0] = (light->r * mul) >> 8;
01107 rgb[1] = (light->g * mul) >> 8;
01108 rgb[2] = (light->b * mul) >> 8;
01109
01110 ModelAddGouraud(&col->rgb[j], rgb, &col->rgb[j]);
01111 }
01112 }
01113 }
01114 }
01115 }
01116
01118
01120
01121 void AddModelLightPermSpot(MODEL *model, LIGHT *light, VEC *pos, VEC *dir)
01122 {
01123 MODEL_POLY *p;
01124 MODEL_VERTEX **v;
01125 float squaredist, dist, cone;
01126 long mul, vcount;
01127 short i, j;
01128 long rgb[3];
01129 POLY_RGB *col;
01130
01131
01132
01133 col = model->PolyRGB;
01134 p = model->PolyPtr;
01135
01136 for (i = 0 ; i < model->PolyNum ; i++, p++, col++)
01137 {
01138 v = &p->v0;
01139
01140 vcount = (p->Type & POLY_QUAD) ? 4 : 3;
01141 for (j = 0 ; j < vcount ; j++, v++)
01142 {
01143 LightDelta.v[X] = pos->v[X] - (*v)->x;
01144 LightDelta.v[Y] = pos->v[Y] - (*v)->y;
01145 LightDelta.v[Z] = pos->v[Z] - (*v)->z;
01146
01147 squaredist = DotProduct(&LightDelta, &LightDelta);
01148 if (squaredist < light->SquareReach)
01149 {
01150 dist = (float)sqrt(squaredist);
01151 cone = (-DotProduct(dir, &LightDelta) / dist - 1) * light->ConeMul + 1;
01152 if (cone > 0)
01153 {
01154 FTOL(((1 - squaredist / light->SquareReach) * cone) * 256, mul);
01155
01156 rgb[0] = (light->r * mul) >> 8;
01157 rgb[1] = (light->g * mul) >> 8;
01158 rgb[2] = (light->b * mul) >> 8;
01159
01160 ModelAddGouraud(&col->rgb[j], rgb, &col->rgb[j]);
01161 }
01162 }
01163 }
01164 }
01165 }
01166
01168
01170
01171 void AddModelLightPermSpotNormal(MODEL *model, LIGHT *light, VEC *pos, VEC *dir)
01172 {
01173 MODEL_POLY *p;
01174 MODEL_VERTEX **v;
01175 float squaredist, ang, dist, cone;
01176 long mul, vcount;
01177 short i, j;
01178 long rgb[3];
01179 POLY_RGB *col;
01180
01181
01182
01183 col = model->PolyRGB;
01184 p = model->PolyPtr;
01185
01186 for (i = 0 ; i < model->PolyNum ; i++, p++, col++)
01187 {
01188 v = &p->v0;
01189
01190 vcount = (p->Type & POLY_QUAD) ? 4 : 3;
01191 for (j = 0 ; j < vcount ; j++, v++)
01192 {
01193 LightDelta.v[X] = pos->v[X] - (*v)->x;
01194 LightDelta.v[Y] = pos->v[Y] - (*v)->y;
01195 LightDelta.v[Z] = pos->v[Z] - (*v)->z;
01196
01197 ang = DotProduct(&LightDelta, (VEC*)&(*v)->nx);
01198 if (ang > 0)
01199 {
01200 squaredist = DotProduct(&LightDelta, &LightDelta);
01201 if (squaredist < light->SquareReach)
01202 {
01203 dist = (float)sqrt(squaredist);
01204 cone = (-DotProduct(dir, &LightDelta) / dist - 1) * light->ConeMul + 1;
01205 if (cone > 0)
01206 {
01207 FTOL(((1 - squaredist / light->SquareReach) * (ang / dist) * cone) * 256, mul);
01208
01209 rgb[0] = (light->r * mul) >> 8;
01210 rgb[1] = (light->g * mul) >> 8;
01211 rgb[2] = (light->b * mul) >> 8;
01212
01213 ModelAddGouraud(&col->rgb[j], rgb, &col->rgb[j]);
01214 }
01215 }
01216 }
01217 }
01218 }
01219 }
01220
01222
01224
01225 void AddModelLight(MODEL *model, VEC *pos, MAT *mat)
01226 {
01227 long i;
01228 LIGHT *l;
01229 VEC vec, out, dir;
01230
01231
01232
01233 l = Light;
01234 for (i = 0 ; i < AffectObjectLightCount ; i++)
01235 {
01236 l = AffectObjectLights[i];
01237
01238
01239
01240 SubVector((VEC*)&l->x, pos, &vec);
01241 TransposeRotVector(mat, &vec, &out);
01242
01243
01244
01245 switch (l->Type)
01246 {
01247 case LIGHT_OMNI:
01248 if (i) AddModelLightOmni(model, l, &out);
01249 else SetModelLightOmni(model, l, &out);
01250 break;
01251 case LIGHT_OMNINORMAL:
01252 if (i) AddModelLightOmniNormal(model, l, &out);
01253 else SetModelLightOmniNormal(model, l, &out);
01254 break;
01255 case LIGHT_SPOT:
01256 TransposeRotVector(mat, &l->DirMatrix.mv[Z], &dir);
01257 if (i) AddModelLightSpot(model, l, &out, &dir);
01258 else SetModelLightSpot(model, l, &out, &dir);
01259 break;
01260 case LIGHT_SPOTNORMAL:
01261 TransposeRotVector(mat, &l->DirMatrix.mv[Z], &dir);
01262 if (i) AddModelLightSpotNormal(model, l, &out, &dir);
01263 else SetModelLightSpotNormal(model, l, &out, &dir);
01264 break;
01265 }
01266 }
01267 }
01268
01270
01272
01273 void SetModelLightOmni(MODEL *model, LIGHT *light, VEC *pos)
01274 {
01275 MODEL_VERTEX *v;
01276 float squaredist;
01277 long mul;
01278 short i;
01279
01280
01281
01282 v = model->VertPtr;
01283 for (i = 0 ; i < model->VertNum ; i++, v++)
01284 {
01285 LightDelta.v[X] = pos->v[X] - v->x;
01286 LightDelta.v[Y] = pos->v[Y] - v->y;
01287 LightDelta.v[Z] = pos->v[Z] - v->z;
01288
01289 squaredist = DotProduct(&LightDelta, &LightDelta);
01290 if (squaredist >= light->SquareReach)
01291 {
01292 v->r = v->g = v->b = 0;
01293 continue;
01294 }
01295
01296 FTOL((1 - squaredist / light->SquareReach) * 256, mul);
01297
01298 v->r = (light->r * mul) >> 8;
01299 v->g = (light->g * mul) >> 8;
01300 v->b = (light->b * mul) >> 8;
01301 }
01302 }
01303
01305
01307
01308 void AddModelLightOmni(MODEL *model, LIGHT *light, VEC *pos)
01309 {
01310 MODEL_VERTEX *v;
01311 float squaredist;
01312 long mul;
01313 short i;
01314
01315
01316
01317 v = model->VertPtr;
01318 for (i = 0 ; i < model->VertNum ; i++, v++)
01319 {
01320 LightDelta.v[X] = pos->v[X] - v->x;
01321 LightDelta.v[Y] = pos->v[Y] - v->y;
01322 LightDelta.v[Z] = pos->v[Z] - v->z;
01323
01324 squaredist = DotProduct(&LightDelta, &LightDelta);
01325 if (squaredist >= light->SquareReach)
01326 continue;
01327
01328 FTOL((1 - squaredist / light->SquareReach) * 256, mul);
01329
01330 v->r += (light->r * mul) >> 8;
01331 v->g += (light->g * mul) >> 8;
01332 v->b += (light->b * mul) >> 8;
01333 }
01334 }
01335
01337
01339
01340 void SetModelLightOmniNormal(MODEL *model, LIGHT *light, VEC *pos)
01341 {
01342 MODEL_VERTEX *v;
01343 float squaredist, dist, ang;
01344 long mul;
01345 short i;
01346
01347
01348
01349 v = model->VertPtr;
01350 for (i = 0 ; i < model->VertNum ; i++, v++)
01351 {
01352 LightDelta.v[X] = pos->v[X] - v->x;
01353 LightDelta.v[Y] = pos->v[Y] - v->y;
01354 LightDelta.v[Z] = pos->v[Z] - v->z;
01355
01356 ang = DotProduct(&LightDelta, (VEC*)&v->nx);
01357 if (ang <= 0)
01358 {
01359 v->r = v->g = v->b = 0;
01360 continue;
01361 }
01362
01363 squaredist = DotProduct(&LightDelta, &LightDelta);
01364 if (squaredist >= light->SquareReach)
01365 {
01366 v->r = v->g = v->b = 0;
01367 continue;
01368 }
01369
01370 dist = (float)sqrt(squaredist);
01371 FTOL(((1 - squaredist / light->SquareReach) * (ang / dist)) * 256, mul);
01372
01373 v->r = (light->r * mul) >> 8;
01374 v->g = (light->g * mul) >> 8;
01375 v->b = (light->b * mul) >> 8;
01376 }
01377 }
01378
01380
01382
01383 void AddModelLightOmniNormal(MODEL *model, LIGHT *light, VEC *pos)
01384 {
01385 MODEL_VERTEX *v;
01386 float squaredist, dist, ang;
01387 long mul;
01388 short i;
01389
01390
01391
01392 v = model->VertPtr;
01393 for (i = 0 ; i < model->VertNum ; i++, v++)
01394 {
01395 LightDelta.v[X] = pos->v[X] - v->x;
01396 LightDelta.v[Y] = pos->v[Y] - v->y;
01397 LightDelta.v[Z] = pos->v[Z] - v->z;
01398
01399 ang = DotProduct(&LightDelta, (VEC*)&v->nx);
01400 if (ang <= 0)
01401 continue;
01402
01403 squaredist = DotProduct(&LightDelta, &LightDelta);
01404 if (squaredist >= light->SquareReach)
01405 continue;
01406
01407 dist = (float)sqrt(squaredist);
01408 FTOL(((1 - squaredist / light->SquareReach) * (ang / dist)) * 256, mul);
01409
01410 v->r += (light->r * mul) >> 8;
01411 v->g += (light->g * mul) >> 8;
01412 v->b += (light->b * mul) >> 8;
01413 }
01414 }
01415
01417
01419
01420 void SetModelLightSpot(MODEL *model, LIGHT *light, VEC *pos, VEC *dir)
01421 {
01422 MODEL_VERTEX *v;
01423 float squaredist, dist, cone;
01424 long mul;
01425 short i;
01426
01427
01428
01429 v = model->VertPtr;
01430 for (i = 0 ; i < model->VertNum ; i++, v++)
01431 {
01432 LightDelta.v[X] = pos->v[X] - v->x;
01433 LightDelta.v[Y] = pos->v[Y] - v->y;
01434 LightDelta.v[Z] = pos->v[Z] - v->z;
01435
01436 squaredist = DotProduct(&LightDelta, &LightDelta);
01437 if (squaredist >= light->SquareReach)
01438 {
01439 v->r = v->g = v->b = 0;
01440 continue;
01441 }
01442
01443 dist = (float)sqrt(squaredist);
01444
01445 cone = (-DotProduct(dir, &LightDelta) / dist - 1) * light->ConeMul + 1;
01446 if (cone <= 0)
01447 {
01448 v->r = v->g = v->b = 0;
01449 continue;
01450 }
01451
01452 FTOL(((1 - squaredist / light->SquareReach) * cone) * 256, mul);
01453
01454 v->r = (light->r * mul) >> 8;
01455 v->g = (light->g * mul) >> 8;
01456 v->b = (light->b * mul) >> 8;
01457 }
01458 }
01459
01461
01463
01464 void AddModelLightSpot(MODEL *model, LIGHT *light, VEC *pos, VEC *dir)
01465 {
01466 MODEL_VERTEX *v;
01467 float squaredist, dist, cone;
01468 long mul;
01469 short i;
01470
01471
01472
01473 v = model->VertPtr;
01474 for (i = 0 ; i < model->VertNum ; i++, v++)
01475 {
01476 LightDelta.v[X] = pos->v[X] - v->x;
01477 LightDelta.v[Y] = pos->v[Y] - v->y;
01478 LightDelta.v[Z] = pos->v[Z] - v->z;
01479
01480 squaredist = DotProduct(&LightDelta, &LightDelta);
01481 if (squaredist >= light->SquareReach)
01482 continue;
01483
01484 dist = (float)sqrt(squaredist);
01485
01486 cone = (-DotProduct(dir, &LightDelta) / dist - 1) * light->ConeMul + 1;
01487 if (cone <= 0)
01488 continue;
01489
01490 FTOL(((1 - squaredist / light->SquareReach) * cone) * 256, mul);
01491
01492 v->r += (light->r * mul) >> 8;
01493 v->g += (light->g * mul) >> 8;
01494 v->b += (light->b * mul) >> 8;
01495 }
01496 }
01497
01499
01501
01502 void SetModelLightSpotNormal(MODEL *model, LIGHT *light, VEC *pos, VEC *dir)
01503 {
01504 MODEL_VERTEX *v;
01505 float squaredist, dist, ang, cone;
01506 long mul;
01507 short i;
01508
01509
01510
01511 v = model->VertPtr;
01512 for (i = 0 ; i < model->VertNum ; i++, v++)
01513 {
01514 LightDelta.v[X] = pos->v[X] - v->x;
01515 LightDelta.v[Y] = pos->v[Y] - v->y;
01516 LightDelta.v[Z] = pos->v[Z] - v->z;
01517
01518 ang = DotProduct(&LightDelta, (VEC*)&v->nx);
01519 if (ang <= 0)
01520 {
01521 v->r = v->g = v->b = 0;
01522 continue;
01523 }
01524
01525 squaredist = DotProduct(&LightDelta, &LightDelta);
01526 if (squaredist >= light->SquareReach)
01527 {
01528 v->r = v->g = v->b = 0;
01529 continue;
01530 }
01531
01532 dist = (float)sqrt(squaredist);
01533
01534 cone = (-DotProduct(dir, &LightDelta) / dist - 1) * light->ConeMul + 1;
01535 if (cone <= 0)
01536 {
01537 v->r = v->g = v->b = 0;
01538 continue;
01539 }
01540
01541 FTOL(((1 - squaredist / light->SquareReach) * (ang / dist) * cone) * 256, mul);
01542
01543 v->r = (light->r * mul) >> 8;
01544 v->g = (light->g * mul) >> 8;
01545 v->b = (light->b * mul) >> 8;
01546 }
01547 }
01548
01550
01552
01553 void AddModelLightSpotNormal(MODEL *model, LIGHT *light, VEC *pos, VEC *dir)
01554 {
01555 MODEL_VERTEX *v;
01556 float squaredist, dist, ang, cone;
01557 long mul;
01558 short i;
01559
01560
01561
01562 v = model->VertPtr;
01563 for (i = 0 ; i < model->VertNum ; i++, v++)
01564 {
01565 LightDelta.v[X] = pos->v[X] - v->x;
01566 LightDelta.v[Y] = pos->v[Y] - v->y;
01567 LightDelta.v[Z] = pos->v[Z] - v->z;
01568
01569 ang = DotProduct(&LightDelta, (VEC*)&v->nx);
01570 if (ang <= 0)
01571 continue;
01572
01573 squaredist = DotProduct(&LightDelta, &LightDelta);
01574 if (squaredist >= light->SquareReach)
01575 continue;
01576
01577 dist = (float)sqrt(squaredist);
01578
01579 cone = (-DotProduct(dir, &LightDelta) / dist - 1) * light->ConeMul + 1;
01580 if (cone <= 0)
01581 continue;
01582
01583 FTOL(((1 - squaredist / light->SquareReach) * (ang / dist) * cone) * 256, mul);
01584
01585 v->r += (light->r * mul) >> 8;
01586 v->g += (light->g * mul) >> 8;
01587 v->b += (light->b * mul) >> 8;
01588 }
01589 }
01590
01592
01594
01595 void AddModelLightSimple(MODEL *model, VEC *pos)
01596 {
01597 long i, j, mul;
01598 LIGHT *l;
01599 float squaredist, dist, cone;
01600 long r, g, b;
01601 MODEL_VERTEX *v;
01602
01603
01604
01605 l = Light;
01606 for (i = 0 ; i < AffectObjectLightCount ; i++)
01607 {
01608 l = AffectObjectLights[i];
01609
01610
01611
01612 r = g = b = 0;
01613
01614 LightDelta.v[X] = l->x - pos->v[X];
01615 LightDelta.v[Y] = l->y - pos->v[Y];
01616 LightDelta.v[Z] = l->z - pos->v[Z];
01617
01618 squaredist = DotProduct(&LightDelta, &LightDelta);
01619 if (squaredist < l->SquareReach)
01620 {
01621 if (l->Type == LIGHT_OMNI || l->Type == LIGHT_OMNINORMAL)
01622 {
01623 FTOL((1 - squaredist / l->SquareReach) * 256, mul);
01624
01625 r = (l->r * mul) >> 8;
01626 g = (l->g * mul) >> 8;
01627 b = (l->b * mul) >> 8;
01628 }
01629 else
01630 {
01631 dist = (float)sqrt(squaredist);
01632
01633 cone = (-DotProduct(&l->DirMatrix.mv[Z], &LightDelta) / dist - 1) * l->ConeMul + 1;
01634 if (cone > 0)
01635 {
01636 FTOL(((1 - squaredist / l->SquareReach) * cone) * 256, mul);
01637
01638 r = (l->r * mul) >> 8;
01639 g = (l->g * mul) >> 8;
01640 b = (l->b * mul) >> 8;
01641 }
01642 }
01643 }
01644
01645
01646
01647 if (!i)
01648 {
01649 v = model->VertPtr;
01650 for (j = 0 ; j < model->VertNum ; j++, v++)
01651 {
01652 v->r = r;
01653 v->g = g;
01654 v->b = b;
01655 }
01656 }
01657
01658
01659
01660 else if (r | g | b)
01661 {
01662 v = model->VertPtr;
01663 for (j = 0 ; j < model->VertNum ; j++, v++)
01664 {
01665 v->r += r;
01666 v->g += g;
01667 v->b += b;
01668 }
01669 }
01670 }
01671 }
01672
01674
01676
01677 void AddPermLightInstance(LIGHT *light)
01678 {
01679 short i, j;
01680 INSTANCE *inst;
01681 VEC vec, pos, dir;
01682 MODEL *model;
01683 POLY_RGB *savergb;
01684
01685
01686
01687 if (!light->Flag) return;
01688
01689
01690
01691 ProcessOneLight(light);
01692
01693
01694
01695 inst = Instances;
01696
01697 for (i = 0 ; i < InstanceNum ; i++, inst++)
01698 {
01699
01700
01701
01702 if (inst->Flag & INSTANCE_HIDE)
01703 continue;
01704
01705
01706
01707 if (inst->Flag & INSTANCE_NO_FILE_LIGHTS)
01708 continue;
01709
01710
01711
01712 for (j = 0 ; j < InstanceModels[inst->Model].Count ; j++)
01713 {
01714
01715
01716
01717 if (inst->Box.Xmin > light->Xmax || inst->Box.Xmax < light->Xmin || inst->Box.Ymin > light->Ymax || inst->Box.Ymax < light->Ymin || inst->Box.Zmin > light->Zmax || inst->Box.Zmax < light->Zmin)
01718 continue;
01719
01720
01721
01722 model = &InstanceModels[inst->Model].Models[j];
01723 savergb = model->PolyRGB;
01724 model->PolyRGB = inst->rgb[j];
01725
01726 SubVector((VEC*)&light->x, &inst->WorldPos, &vec);
01727 TransposeRotVector(&inst->WorldMatrix, &vec, &pos);
01728
01729 switch (light->Type)
01730 {
01731 case LIGHT_OMNI:
01732 AddModelLightPermOmni(model, light, &pos);
01733 break;
01734 case LIGHT_OMNINORMAL:
01735 AddModelLightPermOmniNormal(model, light, &pos);
01736 break;
01737 case LIGHT_SPOT:
01738 TransposeRotVector(&inst->WorldMatrix, &light->DirMatrix.mv[Z], &dir);
01739 AddModelLightPermSpot(model, light, &pos, &dir);
01740 break;
01741 case LIGHT_SPOTNORMAL:
01742 TransposeRotVector(&inst->WorldMatrix, &light->DirMatrix.mv[Z], &dir);
01743 AddModelLightPermSpotNormal(model, light, &pos, &dir);
01744 break;
01745 }
01746 model->PolyRGB = savergb;
01747 }
01748 }
01749 }
01750
01752
01754
01755 void LoadLights(char *file)
01756 {
01757 long i;
01758 FILE *fp;
01759 LIGHT *light;
01760 FILELIGHT fl;
01761
01762
01763
01764 fp = fopen(file, "rb");
01765
01766
01767
01768 if (!fp)
01769 {
01770 fp = fopen(file, "wb");
01771 if (!fp) return;
01772 i = 0;
01773 fwrite(&i, sizeof(i), 1, fp);
01774 fclose(fp);
01775 fp = fopen(file, "rb");
01776 if (!fp) return;
01777 }
01778
01779
01780
01781 fread(&i, sizeof(i), 1, fp);
01782
01783 for ( ; i ; i--)
01784 {
01785
01786
01787
01788 light = AllocLight();
01789 if (!light) break;
01790
01791
01792
01793 fread(&fl, sizeof(fl), 1, fp);
01794
01795 if (EditMode == EDIT_LIGHTS)
01796 {
01797 VecMulScalar((VEC*)&fl.x, EditScale);
01798 fl.Reach *= EditScale;
01799 }
01800
01801 light->Flag = fl.Flag;
01802 light->Type = fl.Type;
01803 light->Speed = fl.Speed;
01804
01805 light->x = fl.x;
01806 light->y = fl.y;
01807 light->z = fl.z;
01808
01809 light->Reach = fl.Reach;
01810
01811 CopyMatrix(&fl.DirMatrix, &light->DirMatrix);
01812
01813 light->Cone = fl.Cone;
01814
01815 light->r = (long)fl.r;
01816 light->g = (long)fl.g;
01817 light->b = (long)fl.b;
01818
01819
01820
01821 if (EditMode != EDIT_LIGHTS && EditMode != EDIT_INSTANCES && !(light->Flag & LIGHT_FLICKER))
01822 {
01823
01824
01825
01826 if (light->Flag & LIGHT_FIXED)
01827 {
01828 AddPermLight(light);
01829 light->Flag &= ~LIGHT_FIXED;
01830 }
01831
01832
01833
01834 AddPermLightInstance(light);
01835
01836
01837
01838 if (!(light->Flag & LIGHT_MOVING))
01839 FreeLight(light);
01840 }
01841 }
01842
01843
01844
01845 fclose(fp);
01846 }
01847
01849
01851
01852 void SaveLights(char *file)
01853 {
01854 long num, i;
01855 FILE *fp;
01856 LIGHT *light;
01857 FILELIGHT fl;
01858 char bak[256];
01859
01860
01861
01862 memcpy(bak, file, strlen(file) - 3);
01863 wsprintf(bak + strlen(file) - 3, "li-");
01864 remove(bak);
01865 rename(file, bak);
01866
01867
01868
01869 fp = fopen(file, "wb");
01870 if (!fp) return;
01871
01872
01873
01874 for (i = num = 0 ; i < LIGHT_MAX ; i++) if (Light[i].Flag & LIGHT_FILE) num++;
01875
01876
01877
01878 fwrite(&num, sizeof(num), 1, fp);
01879
01880
01881
01882 light = Light;
01883 for (i = 0 ; i < LIGHT_MAX ; i++, light++) if (light->Flag & LIGHT_FILE)
01884 {
01885
01886
01887
01888 fl.Flag = light->Flag & ~LIGHT_OFF;
01889 fl.Type = light->Type;
01890 fl.Speed = light->Speed;
01891
01892 fl.x = light->x;
01893 fl.y = light->y;
01894 fl.z = light->z;
01895
01896 fl.Reach = light->Reach;
01897
01898 CopyMatrix(&light->DirMatrix, &fl.DirMatrix);
01899
01900 fl.Cone = light->Cone;
01901
01902 fl.r = (float)light->r;
01903 fl.g = (float)light->g;
01904 fl.b = (float)light->b;
01905
01906
01907
01908 fwrite(&fl, sizeof(fl), 1, fp);
01909 }
01910
01911
01912
01913 Box("Saved Light File:", file, MB_OK);
01914 fclose(fp);
01915 }
01916
01918
01920
01921 void DrawFileLights(void)
01922 {
01923 long i, col;
01924 LIGHT *light;
01925 MAT *lmat;
01926 VEC tempvec;
01927 MODEL *model;
01928
01929
01930
01931 light = Light;
01932 for (i = 0 ; i < LIGHT_MAX ; i++, light++) if (light->Flag & LIGHT_FILE)
01933 {
01934
01935
01936
01937 if (light->Type == LIGHT_SPOT || light->Type == LIGHT_SPOTNORMAL)
01938 {
01939 model = &LightSpotModel;
01940 lmat = &light->DirMatrix;
01941 }
01942 else
01943 {
01944 model = &LightOmniModel;
01945 lmat = &IdentityMatrix;
01946 }
01947
01948 DrawModel(model, lmat, (VEC*)&light->x, MODEL_PLAIN);
01949
01950
01951
01952 if (light == CurrentEditLight)
01953 {
01954 if (LightAxisType)
01955 DrawAxis(&IdentityMatrix, (VEC*)&light->x);
01956 else
01957 DrawAxis(&CAM_MainCamera->WMatrix, (VEC*)&light->x);
01958 }
01959
01960
01961
01962 if (light->Type == LIGHT_SPOT || light->Type == LIGHT_SPOTNORMAL)
01963 {
01964 tempvec.v[X] = light->x + light->DirMatrix.m[LX] * light->Reach;
01965 tempvec.v[Y] = light->y + light->DirMatrix.m[LY] * light->Reach;
01966 tempvec.v[Z] = light->z + light->DirMatrix.m[LZ] * light->Reach;
01967
01968 col = (long)(light->r * 65536 + light->g * 256 + light->b);
01969 DrawLine((VEC*)&light->x, &tempvec, col, 0);
01970 }
01971 }
01972 }
01973
01975
01977
01978 void EditFileLights(void)
01979 {
01980 short i, n;
01981 float z, xrad, yrad, sx, sy;
01982 VEC vec, vec2;
01983 VEC r, u, l, r2, u2, l2;
01984 MAT mat;
01985 LIGHT *light;
01986
01987
01988
01989 if (CAM_MainCamera->Type != CAM_EDIT)
01990 {
01991 CurrentEditLight = NULL;
01992 return;
01993 }
01994
01995
01996
01997 if (Keys[DIK_LCONTROL] && Keys[DIK_F4] && !LastKeys[DIK_F4])
01998 {
01999 SaveLights(GetLevelFilename("lit", FILENAME_MAKE_BODY | FILENAME_GAME_SETTINGS));
02000 }
02001
02002
02003
02004 if (!CurrentEditLight && Keys[DIK_RETURN] && !LastKeys[DIK_RETURN])
02005 {
02006 n = -1;
02007 z = RenderSettings.FarClip;
02008
02009 for (i = 0 ; i < LIGHT_MAX ; i++) if (Light[i].Flag & LIGHT_FILE)
02010 {
02011 RotTransVector(&ViewMatrix, &ViewTrans, (VEC*)&Light[i].x, &vec);
02012
02013 if (vec.v[Z] < RenderSettings.NearClip || vec.v[Z] >= RenderSettings.FarClip) continue;
02014
02015 sx = vec.v[X] * RenderSettings.GeomPers / vec.v[Z] + REAL_SCREEN_XHALF;
02016 sy = vec.v[Y] * RenderSettings.GeomPers / vec.v[Z] + REAL_SCREEN_YHALF;
02017
02018 xrad = (24 * RenderSettings.GeomPers) / vec.v[Z];
02019 yrad = (24 * RenderSettings.GeomPers) / vec.v[Z];
02020
02021 if (MouseXpos > sx - xrad && MouseXpos < sx + xrad && MouseYpos > sy - yrad && MouseYpos < sy + yrad)
02022 {
02023 if (vec.v[Z] < z)
02024 {
02025 n = i;
02026 z = vec.v[Z];
02027 }
02028 }
02029 }
02030 if (n != -1)
02031 {
02032 CurrentEditLight = &Light[n];
02033 return;
02034 }
02035 }
02036
02037
02038
02039
02040 if (Keys[DIK_INSERT] && !LastKeys[DIK_INSERT])
02041 {
02042 if ((light = AllocLight()))
02043 {
02044 vec.v[X] = 0;
02045 vec.v[Y] = 0;
02046 vec.v[Z] = 256;
02047 RotVector(&CAM_MainCamera->WMatrix, &vec, &vec2);
02048 light->x = CAM_MainCamera->WPos.v[X] + vec2.v[X];
02049 light->y = CAM_MainCamera->WPos.v[Y] + vec2.v[Y];
02050 light->z = CAM_MainCamera->WPos.v[Z] + vec2.v[Z];
02051
02052 light->Reach = 512;
02053
02054 RotMatrixZYX(&light->DirMatrix, 0.25f, 0, 0);
02055
02056 light->Cone = 90;
02057
02058 light->r = 0;
02059 light->g = 0;
02060 light->b = 0;
02061
02062 light->Flag = LIGHT_FILE | LIGHT_FIXED | LIGHT_MOVING;
02063 light->Type = LIGHT_OMNI;
02064 light->Speed = 1;
02065
02066 CurrentEditLight = light;
02067 }
02068 }
02069
02070
02071
02072 if (!CurrentEditLight) return;
02073
02074
02075
02076 if (Keys[DIK_RETURN] && !LastKeys[DIK_RETURN])
02077 {
02078 CurrentEditLight = NULL;
02079 return;
02080 }
02081
02082
02083
02084 if (Keys[DIK_DELETE] && !LastKeys[DIK_DELETE])
02085 {
02086 FreeLight(CurrentEditLight);
02087 CurrentEditLight = NULL;
02088 return;
02089 }
02090
02091
02092
02093 if (Keys[DIK_SPACE] && !LastKeys[DIK_SPACE])
02094 {
02095 if (Keys[DIK_LSHIFT])
02096 CurrentEditLight->Type--;
02097 else
02098 CurrentEditLight->Type++;
02099
02100 if (CurrentEditLight->Type > 4) CurrentEditLight->Type = 3;
02101 if (CurrentEditLight->Type == 4) CurrentEditLight->Type = 0;
02102 }
02103
02104
02105
02106 if (Keys[DIK_NUMPADENTER] && !LastKeys[DIK_NUMPADENTER])
02107 {
02108 switch (CurrentEditLight->Flag & (LIGHT_FIXED | LIGHT_MOVING))
02109 {
02110 case LIGHT_FIXED | LIGHT_MOVING:
02111 CurrentEditLight->Flag &= ~LIGHT_MOVING;
02112 break;
02113 case LIGHT_FIXED:
02114 CurrentEditLight->Flag &= ~LIGHT_FIXED;
02115 CurrentEditLight->Flag |= LIGHT_MOVING;
02116 break;
02117 case LIGHT_MOVING:
02118 CurrentEditLight->Flag |= LIGHT_FIXED;
02119 break;
02120 }
02121 }
02122
02123
02124
02125 if (Keys[DIK_NUMPADPLUS] && CurrentEditLight->Reach < 8192) CurrentEditLight->Reach += 16;
02126 if (Keys[DIK_NUMPADMINUS] && CurrentEditLight->Reach > 0) CurrentEditLight->Reach -= 16;
02127
02128
02129
02130 if (CurrentEditLight->Type == LIGHT_SPOT || CurrentEditLight->Type == LIGHT_SPOTNORMAL)
02131 {
02132 if (Keys[DIK_NUMPADSTAR] && CurrentEditLight->Cone < 180) CurrentEditLight->Cone++;
02133 if (Keys[DIK_NUMPADSLASH] && CurrentEditLight->Cone > 1) CurrentEditLight->Cone--;
02134 }
02135
02136
02137
02138 if (Keys[DIK_LSHIFT]) LastKeys[DIK_NUMPAD4] = LastKeys[DIK_NUMPAD5] = LastKeys[DIK_NUMPAD6] = LastKeys[DIK_NUMPAD7] = LastKeys[DIK_NUMPAD8] = LastKeys[DIK_NUMPAD9] = 0;
02139
02140 if (Keys[DIK_NUMPAD4] && !LastKeys[DIK_NUMPAD4] && CurrentEditLight->r > -1024) CurrentEditLight->r--;
02141 if (Keys[DIK_NUMPAD7] && !LastKeys[DIK_NUMPAD7] && CurrentEditLight->r < 1024) CurrentEditLight->r++;
02142
02143 if (Keys[DIK_NUMPAD5] && !LastKeys[DIK_NUMPAD5] && CurrentEditLight->g > -1024) CurrentEditLight->g--;
02144 if (Keys[DIK_NUMPAD8] && !LastKeys[DIK_NUMPAD8] && CurrentEditLight->g < 1024) CurrentEditLight->g++;
02145
02146 if (Keys[DIK_NUMPAD6] && !LastKeys[DIK_NUMPAD6] && CurrentEditLight->b > -1024) CurrentEditLight->b--;
02147 if (Keys[DIK_NUMPAD9] && !LastKeys[DIK_NUMPAD9] && CurrentEditLight->b < 1024) CurrentEditLight->b++;
02148
02149
02150
02151 if (Keys[DIK_TAB] && !LastKeys[DIK_TAB])
02152 {
02153 if (Keys[DIK_LSHIFT]) LightAxis--;
02154 else LightAxis++;
02155 if (LightAxis == -1) LightAxis = 5;
02156 if (LightAxis == 6) LightAxis = 0;
02157 }
02158
02159
02160
02161 if (Keys[DIK_LALT] && !LastKeys[DIK_LALT])
02162 LightAxisType ^= 1;
02163
02164
02165
02166 if (Keys[DIK_O] && !LastKeys[DIK_O])
02167 CurrentEditLight->Flag ^= LIGHT_OFF;
02168
02169
02170
02171 if (Keys[DIK_F] && !LastKeys[DIK_F])
02172 CurrentEditLight->Flag ^= LIGHT_FLICKER;
02173
02174
02175
02176 if (Keys[DIK_EQUALS] && CurrentEditLight->Speed < 255) CurrentEditLight->Speed++;
02177 if (Keys[DIK_MINUS] && CurrentEditLight->Speed > 1) CurrentEditLight->Speed--;
02178
02179
02180
02181 if (MouseLeft && !MouseLastLeft && Keys[DIK_LSHIFT])
02182 {
02183 if ((light = AllocLight()))
02184 {
02185 memcpy(light, CurrentEditLight, sizeof(LIGHT));
02186 CurrentEditLight = light;
02187 return;
02188 }
02189 }
02190
02191
02192
02193 if (MouseLeft)
02194 {
02195 RotTransVector(&ViewMatrix, &ViewTrans, (VEC*)&CurrentEditLight->x, &vec);
02196
02197 switch (LightAxis)
02198 {
02199 case LIGHT_AXIS_XY:
02200 vec.v[X] = MouseXrel * vec.v[Z] / RenderSettings.GeomPers + CameraEditXrel;
02201 vec.v[Y] = MouseYrel * vec.v[Z] / RenderSettings.GeomPers + CameraEditYrel;
02202 vec.v[Z] = CameraEditZrel;
02203 break;
02204 case LIGHT_AXIS_XZ:
02205 vec.v[X] = MouseXrel * vec.v[Z] / RenderSettings.GeomPers + CameraEditXrel;
02206 vec.v[Y] = CameraEditYrel;
02207 vec.v[Z] = -MouseYrel * vec.v[Z] / RenderSettings.GeomPers + CameraEditZrel;
02208 break;
02209 case LIGHT_AXIS_ZY:
02210 vec.v[X] = CameraEditXrel;
02211 vec.v[Y] = MouseYrel * vec.v[Z] / RenderSettings.GeomPers + CameraEditYrel;
02212 vec.v[Z] = MouseXrel * vec.v[Z] / RenderSettings.GeomPers + CameraEditZrel;
02213 break;
02214 case LIGHT_AXIS_X:
02215 vec.v[X] = MouseXrel * vec.v[Z] / RenderSettings.GeomPers + CameraEditXrel;
02216 vec.v[Y] = CameraEditYrel;
02217 vec.v[Z] = CameraEditZrel;
02218 break;
02219 case LIGHT_AXIS_Y:
02220 vec.v[X] = CameraEditXrel;
02221 vec.v[Y] = MouseYrel * vec.v[Z] / RenderSettings.GeomPers + CameraEditYrel;
02222 vec.v[Z] = CameraEditZrel;
02223 break;
02224 case LIGHT_AXIS_Z:
02225 vec.v[X] = CameraEditXrel;
02226 vec.v[Y] = CameraEditYrel;
02227 vec.v[Z] = -MouseYrel * vec.v[Z] / RenderSettings.GeomPers + CameraEditZrel;
02228 break;
02229 }
02230
02231 if (LightAxisType == 1)
02232 {
02233 SetVector(&vec2, vec.v[X], vec.v[Y], vec.v[Z]);
02234 }
02235 else
02236 {
02237 RotVector(&CAM_MainCamera->WMatrix, &vec, &vec2);
02238 }
02239
02240 CurrentEditLight->x += vec2.v[X];
02241 CurrentEditLight->y += vec2.v[Y];
02242 CurrentEditLight->z += vec2.v[Z];
02243 }
02244
02245
02246
02247 if (MouseRight) if (CurrentEditLight->Type == LIGHT_SPOT || CurrentEditLight->Type == LIGHT_SPOTNORMAL)
02248 {
02249 RotVector(&ViewMatrix, &CurrentEditLight->DirMatrix.mv[X], &r);
02250 RotVector(&ViewMatrix, &CurrentEditLight->DirMatrix.mv[Y], &u);
02251 RotVector(&ViewMatrix, &CurrentEditLight->DirMatrix.mv[Z], &l);
02252
02253 RotMatrixZYX(&mat, MouseYrel / 1024, -MouseXrel / 1024, 0);
02254
02255 RotVector(&mat, &r, &r2);
02256 RotVector(&mat, &u, &u2);
02257 RotVector(&mat, &l, &l2);
02258
02259 RotVector(&CAM_MainCamera->WMatrix, &r2, &CurrentEditLight->DirMatrix.mv[X]);
02260 RotVector(&CAM_MainCamera->WMatrix, &u2, &CurrentEditLight->DirMatrix.mv[Y]);
02261 RotVector(&CAM_MainCamera->WMatrix, &l2, &CurrentEditLight->DirMatrix.mv[Z]);
02262
02263 NormalizeMatrix(&CurrentEditLight->DirMatrix);
02264 }
02265 }
02266
02268
02270
02271 void DisplayLightInfo(LIGHT *light)
02272 {
02273 char buf[128];
02274
02275
02276
02277 DumpText(450, 0, 8, 16, 0xffff00, LightNames[light->Type]);
02278
02279
02280
02281 DumpText(450, 24, 8, 16, 0x00ffff, LightFlags[(light->Flag & (LIGHT_FIXED | LIGHT_MOVING)) - 1]);
02282
02283
02284
02285 wsprintf(buf, "RGB %ld %ld %ld", light->r, light->g, light->b);
02286 DumpText(450, 48, 8, 16, 0x00ff00, buf);
02287
02288
02289
02290 wsprintf(buf, "Reach %d", (short)light->Reach);
02291 DumpText(450, 72, 8, 16, 0xff0000, buf);
02292
02293
02294
02295 wsprintf(buf, "Flicker %s", LightFlickerNames[(light->Flag & LIGHT_FLICKER) != 0]);
02296 DumpText(450, 96, 8, 16, 0xff00ff, buf);
02297
02298
02299
02300 wsprintf(buf, "Flicker Speed %d", light->Speed);
02301 DumpText(450, 120, 8, 16, 0x0000ff, buf);
02302
02303
02304
02305 wsprintf(buf, "Axis %s - %s", LightAxisNames[LightAxis], LightAxisTypeNames[LightAxisType]);
02306 DumpText(450, 144, 8, 16, 0xffff00, buf);
02307
02308
02309
02310 if (light->Type == LIGHT_SPOT || light->Type == LIGHT_SPOTNORMAL)
02311 {
02312 wsprintf(buf, "Cone %d", (short)light->Cone);
02313 DumpText(450, 168, 8, 16, 0x00ffff, buf);
02314 }
02315 }
02316
02318
02320
02321 void LoadEditLightModels(void)
02322 {
02323 LoadModel("edit\\omni.m", &LightOmniModel, -1, 1, LOADMODEL_FORCE_TPAGE, 100);
02324 LoadModel("edit\\spot.m", &LightSpotModel, -1, 1, LOADMODEL_FORCE_TPAGE, 100);
02325 }
02326
02328
02330
02331 void FreeEditLightModels(void)
02332 {
02333 FreeModel(&LightOmniModel, 1);
02334 FreeModel(&LightSpotModel, 1);
02335 }
02336
02338
02340
02341
02342
02343
02344
02345
02346
02347
02348
02349
02350
02351
02352
02353
02354
02355
02356
02357
02358
02359
02360
02361
02362
02363
02364
02365
02366
02367
02368
02369
02370
02371
02372
02373
02374
02375
02376
02377
02378
02379
02380
02381
02382
02383
02384
02385
02386
02387
02388
02389
02390
02391
02392
02393
02394