1 2 // render.h [pengine] 3 4 // Copyright 2004-2006 Jasmine Langridge, jas@jareiko.net 5 // License: GPL version 2 (see included gpl.txt) 6 7 8 #include <cmath> 9 #include "vbuffer.h" 10 11 12 13 struct PParticle_s { 14 vec3f pos,linvel; 15 float life; 16 17 vec2f orix,oriy; // orientation vectors (2d) 18 }; 19 20 class PParticleSystem { 21 protected: 22 float colorstart[4],colorend[4]; 23 float startsize, endsize; 24 float decay; 25 const PTexture *tex; 26 GLenum blendparam1, blendparam2; 27 28 std::vector<PParticle_s> part; 29 30 public: PParticleSystem()31 PParticleSystem() { 32 colorstart[0] = colorstart[1] = colorstart[2] = colorstart[3] = 1.0; 33 colorend[0] = colorend[1] = colorend[2] = 1.0; colorend[3] = 0.0; 34 startsize = 0.0; 35 endsize = 1.0; 36 decay = 1.0; 37 tex = nullptr; 38 blendparam1 = GL_SRC_ALPHA; 39 blendparam2 = GL_ONE; 40 } 41 42 public: setColorStart(float r,float g,float b,float a)43 void setColorStart(float r, float g, float b, float a) { 44 colorstart[0] = r; colorstart[1] = g; colorstart[2] = b; colorstart[3] = a; 45 } setColorEnd(float r,float g,float b,float a)46 void setColorEnd(float r, float g, float b, float a) { 47 colorend[0] = r; colorend[1] = g; colorend[2] = b; colorend[3] = a; 48 } setColor(float r,float g,float b)49 void setColor(float r, float g, float b) { 50 colorstart[0] = colorend[0] = r; 51 colorstart[1] = colorend[1] = g; 52 colorstart[2] = colorend[2] = b; 53 } setSize(float start,float end)54 void setSize(float start, float end) { 55 startsize = start; endsize = end; 56 } setDecay(float _decay)57 void setDecay(float _decay) { 58 decay = _decay; 59 } setTexture(const PTexture * texptr)60 void setTexture(const PTexture *texptr) { 61 tex = texptr; 62 } setBlend(GLenum b1,GLenum b2)63 void setBlend(GLenum b1, GLenum b2) { 64 blendparam1 = b1; 65 blendparam2 = b2; 66 } 67 68 void addParticle(const vec3f &pos, const vec3f &linvel); 69 70 void tick(float delta); 71 72 friend class PSSRender; 73 }; 74 75 struct PVert_tv { 76 vec2f st; 77 vec3f xyz; 78 }; 79 80 #define PTEXT_HZA_LEFT 0x00000000 // default 81 #define PTEXT_HZA_CENTER 0x00000001 82 #define PTEXT_HZA_RIGHT 0x00000002 83 #define PTEXT_VTA_BOTTOM 0x00000000 // default 84 #define PTEXT_VTA_CENTER 0x00000100 85 #define PTEXT_VTA_TOP 0x00000200 86 87 class PSSRender : public PSubsystem { 88 private: 89 vec3f cam_pos; 90 mat44f cam_orimat; 91 92 public: 93 PSSRender(PApp &parentApp); 94 ~PSSRender(); 95 96 void tick(float delta, const vec3f &eyepos, const mat44f &eyeori, const vec3f &eyevel); 97 98 void render(PParticleSystem *psys); 99 100 void drawModel(PModel &model, PSSEffect &ssEffect, PSSTexture &ssTexture); 101 102 void drawText(const std::string &text, uint32 flags); 103 vec2f getTextDims(const std::string &text); 104 }; 105 106 107 108 class PSSTexture : public PSubsystem { 109 private: 110 PResourceList<PTexture> texlist; 111 112 public: 113 PSSTexture(PApp &parentApp); 114 ~PSSTexture(); 115 116 PTexture *loadTexture(const std::string &name, bool genMipmaps = true, bool clamp = false); 117 }; 118 119 120 class PImage { 121 private: 122 uint8 *data; 123 int cx,cy,cc; 124 125 public: PImage()126 PImage () : data (nullptr) { } PImage(const std::string & filename)127 PImage (const std::string &filename) : data (nullptr) { load (filename); } PImage(int _cx,int _cy,int _cc)128 PImage (int _cx, int _cy, int _cc) : data (nullptr) { load (_cx, _cy, _cc); } 129 ~PImage (); 130 131 void load (const std::string &filename); 132 void load (int _cx, int _cy, int _cc); 133 void unload (); 134 135 void expandChannels(); 136 getcx()137 int getcx() const { return cx; } getcy()138 int getcy() const { return cy; } getcc()139 int getcc() const { return cc; } getData()140 uint8 *getData() { return data; } 141 getData()142 const uint8 * getData() const 143 { 144 return data; 145 } 146 getByte(int i)147 uint8 & getByte(int i) 148 { 149 return data[i]; 150 } 151 getByte(int i)152 uint8 getByte(int i) const 153 { 154 return data[i]; 155 } 156 swap(PImage & other)157 void swap (PImage &other) throw () 158 { 159 { uint8 *tmp = data; data = other.data; other.data = tmp; } 160 { int tmp = cx; cx = other.cx; other.cx = tmp; } 161 { int tmp = cy; cy = other.cy; other.cy = tmp; } 162 { int tmp = cc; cc = other.cc; other.cc = tmp; } 163 } 164 }; 165 166 167 class PTexture : public PResource { 168 private: 169 GLuint texid; 170 GLenum textarget; 171 172 public: PTexture()173 PTexture () : texid (0) { } PTexture(const std::string & filename,bool genMipmaps,bool clamp)174 PTexture (const std::string &filename, bool genMipmaps, bool clamp) : texid (0) { load (filename, genMipmaps, clamp); } PTexture(PImage & img,bool genMipmaps,bool clamp)175 PTexture (PImage &img, bool genMipmaps, bool clamp) : texid (0) { load (img, genMipmaps, clamp); } ~PTexture()176 ~PTexture() { unload (); } 177 178 void load (const std::string &filename, bool genMipmaps, bool clamp); 179 void load(PImage &img, bool genMipmaps = true, bool clamp = false); 180 void loadPiece(PImage &img, int offx, int offy, int sizex, int sizey, bool genMipmaps = true, bool clamp = false); 181 void loadAlpha(const std::string &filename, bool genMipmaps = true, bool clamp = false); 182 void loadAlpha(PImage &img, bool genMipmaps = true, bool clamp = false); 183 void loadCubeMap(const std::string &filenamePrefix, const std::string &filenameSuffix, bool genMipmaps = true); 184 void unload(); 185 186 void bind() const; 187 188 static void unbind(); 189 }; 190 191 192 193 194 class PSSEffect : public PSubsystem { 195 private: 196 PResourceList<PEffect> fxlist; 197 198 public: 199 PSSEffect(PApp &parentApp); 200 ~PSSEffect(); 201 202 PEffect *loadEffect(const std::string &name); 203 }; 204 205 206 207 #define CULLFACE_NONE 0 208 #define CULLFACE_CW 1 209 #define CULLFACE_CCW 2 210 211 #define BLEND_NONE 0 212 #define BLEND_ADD 1 213 #define BLEND_MULTIPLY 2 214 #define BLEND_ALPHA 3 215 #define BLEND_PREMULTALPHA 4 216 217 218 struct fx_renderstate_s { 219 bool depthtest; 220 221 bool lighting; 222 bool lightmodeltwoside; 223 224 struct { 225 GLenum func; 226 float ref; 227 } alphatest; 228 229 int cullface; 230 231 int blendmode; 232 233 struct { 234 int texindex; 235 } texunit[1]; 236 }; 237 238 239 struct fx_pass_s { 240 fx_renderstate_s rs; 241 }; 242 243 struct fx_technique_s { 244 std::string name; 245 246 std::vector<fx_pass_s> pass; 247 248 bool validated; 249 bool textures_ready; 250 fx_technique_sfx_technique_s251 fx_technique_s() { 252 validated = false; 253 textures_ready = false; 254 } 255 }; 256 257 struct fx_texture_s { 258 std::string name; 259 260 std::string filename; 261 262 GLenum type; 263 264 // This is filled in just before rendering 265 PTexture *texobject; 266 fx_texture_sfx_texture_s267 fx_texture_s() { 268 texobject = nullptr; 269 } 270 }; 271 272 273 class PEffect : public PResource { 274 private: 275 // resources 276 std::vector<fx_texture_s> tex; 277 278 // techniques 279 std::vector<fx_technique_s> tech; 280 281 int cur_tech; 282 283 public: 284 PEffect(const std::string &filename); 285 ~PEffect(); 286 287 void unload(); 288 289 void loadFX(const std::string &filename); 290 void loadMTL(const std::string &filename); 291 292 int getNumTechniques(); 293 bool validateTechnique(int technique); 294 const std::string &getTechniqueName(int technique); 295 bool findTechnique(const std::string &techname, int *technique); 296 297 bool setCurrentTechnique(int technique); 298 int getCurrentTechnique(); 299 300 bool setFirstValidTechnique(); 301 302 bool renderBegin(int *numPasses, PSSTexture &sstex); 303 void renderPass(int pass); 304 void renderEnd(); 305 306 private: 307 void migrateRenderState(fx_renderstate_s *rs_old, fx_renderstate_s *rs_new); 308 }; 309 310 311 312 313 class PSSModel : public PSubsystem { 314 private: 315 PResourceList<PModel> modlist; 316 317 public: 318 PSSModel(PApp &parentApp); 319 ~PSSModel(); 320 321 PModel *loadModel(const std::string &name); 322 }; 323 324 325 class PFace { 326 public: 327 vec3f facenormal; 328 uint32 vt[3]; 329 uint32 tc[3]; 330 uint32 nr[3]; 331 332 public: 333 // PFace(vec 334 }; 335 336 337 class PMesh { 338 public: 339 std::vector<vec3f> vert; 340 std::vector<vec2f> texco; 341 std::vector<vec3f> norm; 342 std::vector<PFace> face; 343 344 std::string fxname; 345 PEffect *effect; 346 }; 347 348 349 class PModel : public PResource { 350 public: 351 std::vector<PMesh> mesh; 352 353 std::pair<vec3f, vec3f> getExtents() const; 354 355 public: 356 PModel (const std::string &filename, float globalScale = 1.0); 357 358 private: 359 void loadASE (const std::string &filename, float globalScale); 360 void loadOBJ (const std::string &filename, float globalScale); 361 }; 362 363 struct PTerrainFoliageBand { 364 float middle, range; 365 float density; 366 int trycount; 367 float scale; 368 369 /* 370 float scalemin; 371 float scalemax; 372 */ 373 374 PTexture *sprite_tex; 375 int sprite_count; 376 }; 377 378 struct PTerrainFoliage { 379 vec3f pos; 380 float ang; 381 float scale; 382 }; 383 384 struct PTerrainFoliageSet { 385 std::vector<PTerrainFoliage> inst; 386 387 PVBuffer buff[2]; 388 int numvert, numelem; 389 }; 390 391 struct PRoadSignSet { 392 std::vector<PTerrainFoliage> inst; 393 PVBuffer buff[2]; 394 int numvert; 395 int numelem; 396 }; 397 398 struct PTerrainTile { 399 int posx, posy; 400 int lru_counter; 401 402 PVBuffer vert; 403 int numverts; 404 405 PTexture tex; 406 407 vec3f mins,maxs; // AABB 408 409 // 410 411 std::vector<PTerrainFoliageSet> foliage; 412 std::vector<PRoadSignSet> roadsignset; 413 }; 414 415 /// 416 /// @brief Loads road information. 417 /// 418 class RoadMap 419 { 420 public: 421 422 RoadMap() = default; 423 load(const PImage & img)424 bool load(const PImage &img) 425 { 426 if (img.getData() == nullptr) 427 return false; 428 429 bx = img.getcx(); 430 by = img.getcy(); 431 bitmap.clear(); 432 bitmap.reserve(img.getcx() * img.getcy()); 433 434 const std::size_t tb = img.getcx() * img.getcy() * img.getcc(); // Total Bytes 435 436 for (auto pb = img.getData(); pb != img.getData() + tb; pb += img.getcc()) 437 { 438 bool is_road = true; 439 440 for (int i=0; i < img.getcc(); ++i) 441 if (pb[i] != 0xFF) // the road is white 442 { 443 is_road = false; 444 break; 445 } 446 447 bitmap.push_back(is_road); 448 } 449 450 return true; 451 } 452 is_loaded()453 bool is_loaded() const 454 { 455 return !bitmap.empty(); 456 } 457 458 /// 459 /// @brief Decides if provided point is on the road. 460 /// @param px X coordinate of the point. 461 /// @param py Y coordinate of the point. 462 /// @param ms Map size. 463 /// @see `getMapSize()`. 464 /// @returns Whether or not the point is on the road. 465 /// @retval true If no roadmap was loaded. 466 /// isOnRoad(float px,float py,float ms)467 bool isOnRoad(float px, float py, float ms) const 468 { 469 if (bitmap.empty()) 470 return true; 471 472 if (px >= ms) 473 { 474 do 475 px -= ms; 476 while (px > ms); 477 } 478 else 479 if (px < 0) 480 { 481 do 482 px += ms; 483 while (px < 0); 484 } 485 486 if (py >= ms) 487 { 488 do 489 py -= ms; 490 while (py > ms); 491 } 492 else 493 if (py < 0) 494 { 495 do 496 py += ms; 497 while (py < 0); 498 } 499 500 long int x = std::lround(px * bx / ms); 501 long int y = std::lround(py * by / ms); 502 503 CLAMP_UPPER(x, bx - 1); 504 CLAMP_UPPER(y, by - 1); 505 return bitmap.at(y * bx + x); 506 } 507 508 private: 509 510 std::vector<bool> bitmap; 511 int bx; 512 int by; 513 }; 514 515 struct road_sign 516 { 517 public: 518 519 // 520 // being smart here makes the rest of the code more complicated; 521 // so let's be stupid for now... 522 // 523 #if 0 524 struct road_sign_location 525 { 526 public: 527 528 float x; 529 float y; 530 float deg; 531 532 road_sign_location(float x=0, float y=0, float deg=0): 533 x(x), y(y), deg(deg) 534 { 535 } 536 }; 537 538 std::vector<road_sign_location> location; 539 #endif 540 541 PTexture *sprite = nullptr; 542 // PTexture *front = nullptr; 543 // PTexture *back = nullptr; 544 float scale = 1.0f; 545 float x = 0.0f; 546 float y = 0.0f; 547 float deg = 0.0f; 548 }; 549 550 class PTerrain // TODO: make this RAII conformant 551 { 552 protected: 553 bool loaded; 554 555 int tilesize, tilecount, totsize, totmask, totsizesq; 556 557 float scale_hz, scale_vt, scale_hz_inv, scale_vt_inv, scale_tile_inv; 558 559 int cmaptotsize, cmaptilesize, cmaptotmask; 560 561 //std::vector<uint8> hmap; 562 std::vector<float> hmap; 563 564 PImage cmap; 565 PImage tmap; ///< Terrain map. 566 RoadMap rmap; ///< Road map. 567 568 std::vector<float> fmap; 569 std::vector<PTerrainFoliageBand> foliageband; 570 std::vector<road_sign> roadsigns; 571 572 std::list<PTerrainTile> tile; 573 574 // tiles share index buffers 575 PVBuffer ind; 576 int numinds; 577 578 PTexture *tex_hud_map; 579 580 protected: 581 582 PTerrainTile *getTile(int x, int y); 583 getInterp(float x,float y,float * data)584 float getInterp(float x, float y, float *data) { 585 x *= scale_hz_inv; 586 int xi = (int)x; 587 if (x < 0.0) xi--; 588 x -= (float)xi; 589 int xiw = xi & totmask, xiw2 = (xiw+1) & totmask; 590 591 y *= scale_hz_inv; 592 int yi = (int)y; 593 if (y < 0.0) yi--; 594 y -= (float)yi; 595 int yiw = yi & totmask, yiw2 = (yiw+1) & totmask; 596 597 const int cx = totsize; 598 599 float xv1,xv2; 600 if (y > 0.0) { 601 if (y < 1.0) { 602 if (x < y) { 603 xv1 = data[yiw*cx+xiw]; 604 xv2 = INTERP(data[yiw2*cx+xiw],data[yiw2*cx+xiw2],x/y); 605 } else { 606 xv1 = INTERP(data[yiw*cx+xiw],data[yiw*cx+xiw2],(x-y)/(1.0-y)); 607 xv2 = data[yiw2*cx+xiw2]; 608 } 609 return INTERP(xv1,xv2,y); 610 } else { 611 return INTERP(data[yiw2*cx+xiw],data[yiw2*cx+xiw2],x); 612 } 613 } else { 614 return INTERP(data[yiw*cx+xiw],data[yiw*cx+xiw2],x); 615 } 616 } 617 618 public: 619 PTerrain(XMLElement *element, const std::string &filepath, PSSTexture &ssTexture); 620 ~PTerrain(); 621 622 void unload(); 623 624 void render(const vec3f &campos, const mat44f &camorim); 625 626 void drawSplat(float x, float y, float scale, float angle); 627 628 629 struct ContactInfo { 630 vec3f pos; 631 vec3f normal; 632 }; 633 634 /// 635 /// @brief Returns whether or not the given position is on road. 636 /// @param [in] pos Position to be checked. 637 /// @returns Whether or not `pos` is on the road. 638 /// @retval true If no roadmap was loaded. 639 /// @see `RoadMap`. 640 /// getRmapOnRoad(const vec3f & pos)641 bool getRmapOnRoad(const vec3f &pos) const 642 { 643 return rmap.isOnRoad(pos.x, pos.y, getMapSize()); 644 } 645 646 /// 647 /// @brief Returns the color of the pixel in the colormap that corresponds 648 /// to the given position in the terrain. 649 /// @note The height component Z is ignored. 650 /// @todo Should check if cmap.getcc() returns at least 3? 651 /// @todo Should check if cmap.getcx() == cmap.getcy()? 652 /// @todo Should remove paranoid clampings? 653 /// @todo Should actually measure performance of float vs int. 654 /// @param [in] pos Position in the terrain. 655 /// @returns Color in OpenGL-style RGB. 656 /// getCmapColor(const vec3f & pos)657 vec3f getCmapColor(const vec3f &pos) const 658 { 659 vec3f r; 660 #if 0 661 const float ms = getMapSize(); 662 float px = pos.x; 663 float py = pos.y; 664 #else 665 const int ms = static_cast<int> (getMapSize()); 666 int px = static_cast<int> (pos.x); 667 int py = static_cast<int> (pos.y); 668 #endif 669 if (px >= ms) 670 { 671 do 672 px -= ms; 673 while (px > ms); 674 } 675 else 676 if (px < 0) 677 { 678 do 679 px += ms; 680 while (px < 0); 681 } 682 683 if (py >= ms) 684 { 685 do 686 py -= ms; 687 while (py > ms); 688 } 689 else 690 if (py < 0) 691 { 692 do 693 py += ms; 694 while (py < 0); 695 } 696 697 long int x = std::lround(px * cmap.getcx() / getMapSize()); 698 long int y = std::lround(py * cmap.getcy() / getMapSize()); 699 700 CLAMP_UPPER(x, cmap.getcx() - 1); 701 CLAMP_UPPER(y, cmap.getcy() - 1); 702 r.x = cmap.getByte((y * cmap.getcx() + x) * cmap.getcc() + 0) / 255.0f; 703 r.y = cmap.getByte((y * cmap.getcx() + x) * cmap.getcc() + 1) / 255.0f; 704 r.z = cmap.getByte((y * cmap.getcx() + x) * cmap.getcc() + 2) / 255.0f; 705 return r; 706 } 707 708 /// 709 /// @brief Returns the road surface type corresponding to the given position 710 /// in the terrain. 711 /// @note The height component Z is ignored. 712 /// @todo Should remove paranoid clampings? 713 /// @todo Should actually measure performance of float vs int. (int should be intrinsecally faster) 714 /// @param [in] pos Position in the terrain. 715 /// @returns Terrain type. 716 /// getRoadSurface(const vec3f & pos)717 TerrainType getRoadSurface(const vec3f &pos) const 718 { 719 if (tmap.getData() == nullptr) 720 return TerrainType::Unknown; 721 #if 0 722 const float ms = getMapSize(); 723 float px = pos.x; 724 float py = pos.y; 725 #else 726 const int ms = static_cast<int> (getMapSize()); 727 int px = static_cast<int> (pos.x); 728 int py = static_cast<int> (pos.y); 729 #endif 730 if (px >= ms) 731 { 732 do 733 px -= ms; 734 while (px > ms); 735 } 736 else 737 if (px < 0) 738 { 739 do 740 px += ms; 741 while (px < 0); 742 } 743 744 if (py >= ms) 745 { 746 do 747 py -= ms; 748 while (py > ms); 749 } 750 else 751 if (py < 0) 752 { 753 do 754 py += ms; 755 while (py < 0); 756 } 757 758 long int x = std::lround(px * tmap.getcx() / getMapSize()); 759 long int y = std::lround(py * tmap.getcy() / getMapSize()); 760 rgbcolor temp; 761 762 CLAMP_UPPER(x, tmap.getcx() - 1); 763 CLAMP_UPPER(y, tmap.getcy() - 1); 764 temp.r = tmap.getByte((y * tmap.getcx() + x) * tmap.getcc() + 0); 765 temp.g = tmap.getByte((y * tmap.getcx() + x) * tmap.getcc() + 1); 766 temp.b = tmap.getByte((y * tmap.getcx() + x) * tmap.getcc() + 2); 767 return PUtil::decideRoadSurface(temp); 768 } 769 770 /// 771 /// @brief get the information about a contact point (its coordinates and normal) with the ground 772 /// getContactInfo(ContactInfo & tci)773 void getContactInfo(ContactInfo &tci) { 774 float x = tci.pos.x * scale_hz_inv; 775 int xi = (int)x; 776 if (x < 0.0) xi--; 777 x -= (float)xi; 778 int xiw = xi & totmask, xiw2 = (xi+1) & totmask; 779 780 float y = tci.pos.y * scale_hz_inv; 781 int yi = (int)y; 782 if (y < 0.0) yi--; 783 y -= (float)yi; 784 int yiw = yi & totmask, yiw2 = (yi+1) & totmask; 785 786 float *data = &hmap[0]; 787 const int cx = totsize; 788 789 float xv1,xv2; 790 if (y > 0.0) { 791 if (y < 1.0) { 792 if (x < y) { 793 tci.normal.x = data[yiw2*cx+xiw] - data[yiw2*cx+xiw2]; 794 tci.normal.y = data[yiw*cx+xiw] - data[yiw2*cx+xiw]; 795 xv1 = data[yiw*cx+xiw]; 796 xv2 = INTERP(data[yiw2*cx+xiw],data[yiw2*cx+xiw2],x/y); 797 } else { 798 tci.normal.x = data[yiw*cx+xiw] - data[yiw*cx+xiw2]; 799 tci.normal.y = data[yiw*cx+xiw2] - data[yiw2*cx+xiw2]; 800 xv1 = INTERP(data[yiw*cx+xiw],data[yiw*cx+xiw2],(x-y)/(1.0-y)); 801 xv2 = data[yiw2*cx+xiw2]; 802 } 803 tci.pos.z = INTERP(xv1,xv2,y); 804 } else { 805 tci.normal.x = data[yiw2*cx+xiw] - data[yiw2*cx+xiw2]; 806 tci.normal.y = data[yiw*cx+xiw] - data[yiw2*cx+xiw]; 807 tci.pos.z = INTERP(data[yiw2*cx+xiw],data[yiw2*cx+xiw2],x); 808 } 809 } else { 810 tci.normal.x = data[yiw*cx+xiw] - data[yiw*cx+xiw2]; 811 tci.normal.y = data[yiw*cx+xiw2] - data[yiw2*cx+xiw2]; 812 tci.pos.z = INTERP(data[yiw*cx+xiw],data[yiw*cx+xiw2],x); 813 } 814 tci.normal.z = scale_hz; 815 tci.normal.normalize(); 816 } 817 getHeight(float x,float y)818 float getHeight(float x, float y) { 819 return getInterp(x, y, &hmap[0]); 820 } 821 getFoliageLevel(float x,float y)822 float getFoliageLevel(float x, float y) { 823 return getInterp(x, y, &fmap[0]); 824 } 825 getHUDMapTexture()826 PTexture *getHUDMapTexture() { return tex_hud_map; } 827 getMapSize()828 float getMapSize() const { return totsize * scale_hz; } 829 }; 830 831 832 833 834