|
11 | 11 | #include <malloc.h> |
12 | 12 | #include <string.h> |
13 | 13 |
|
| 14 | +#define IS_STRAIGHT_SURFACE(surfid) (((surfid) > -1) && ((surfid) & 0xFFFFE000) == 0 && ((surfid) & 0x1FFF) < m_numStraights) |
| 15 | +#define IS_CURVED_SURFACE(surfid) (((surfid) > -1) && ((surfid) & 0xFFFFE000) == 0x4000 && ((surfid) & 0x1FFF) < m_numCurves) |
| 16 | +#define IS_JUNCTION_SURFACE(surfid) (((surfid) > -1) && ((surfid) & 0xFFFFE000) == 0x2000 && ((surfid) & 0x1FFF) < m_numJunctions) |
| 17 | + |
14 | 18 | sdPlane g_defaultPlane = { 0, 0, 0, 0, 2048 }; |
15 | 19 | sdPlane g_seaPlane = { 9, 0, 16384, 0, 2048 }; |
16 | 20 |
|
@@ -71,6 +75,29 @@ short* SdGetBSP(sdNode* node, XZPAIR* pos) |
71 | 75 | return (short*)node; |
72 | 76 | } |
73 | 77 |
|
| 78 | +sdPlane* FindRoadInBSP(sdNode* node, sdPlane* base) |
| 79 | +{ |
| 80 | + sdPlane* plane; |
| 81 | + |
| 82 | + while (true) |
| 83 | + { |
| 84 | + if (*(int*)node > -1) |
| 85 | + { |
| 86 | + base += *(int*)node; |
| 87 | + return (base->surfaceType < 32) ? nullptr : base; |
| 88 | + } |
| 89 | + |
| 90 | + plane = FindRoadInBSP(node + 1, base); |
| 91 | + |
| 92 | + if (plane != nullptr) |
| 93 | + break; |
| 94 | + |
| 95 | + node += node->offset; |
| 96 | + } |
| 97 | + |
| 98 | + return plane; |
| 99 | +} |
| 100 | + |
74 | 101 | // walk the heightmap to get a cPosition |
75 | 102 | sdPlane* CDriver2LevelRegion::SdGetCell(const VECTOR_NOPAD& cPosition, int& sdLevel, sdBspCallback bspWalker) const |
76 | 103 | { |
@@ -162,6 +189,139 @@ sdPlane* CDriver2LevelRegion::SdGetCell(const VECTOR_NOPAD& cPosition, int& sdLe |
162 | 189 | return plane; |
163 | 190 | } |
164 | 191 |
|
| 192 | +// walk heightmap for nearest road |
| 193 | +int CDriver2LevelRegion::RoadInCell(VECTOR_NOPAD& position) const |
| 194 | +{ |
| 195 | + int moreLevels; |
| 196 | + sdPlane* plane; |
| 197 | + short* check; |
| 198 | + short* buffer; |
| 199 | + |
| 200 | + XYPAIR cellPos; |
| 201 | + |
| 202 | + cellPos.x = position.vx - 512; |
| 203 | + cellPos.y = position.vz - 512; |
| 204 | + |
| 205 | + sdPlane* planeData = (sdPlane*)((char*)buffer + buffer[1]); |
| 206 | + short* bspData = (short*)((char*)buffer + buffer[2]); |
| 207 | + sdNode* nodeData = (sdNode*)((char*)buffer + buffer[3]); |
| 208 | + |
| 209 | + check = &buffer[(cellPos.x >> 10 & 63) + |
| 210 | + (cellPos.y >> 10 & 63) * 64 + 4]; |
| 211 | + |
| 212 | + if (*check == -1) |
| 213 | + return -1; |
| 214 | + |
| 215 | + if (m_owner->m_format == LEV_FORMAT_DRIVER2_ALPHA16) |
| 216 | + { |
| 217 | + // FIXME: check if this is redundant! |
| 218 | + if (*check & 0xE000) |
| 219 | + { |
| 220 | + if (*check & 0x2000) |
| 221 | + { |
| 222 | + // check surface has overlapping planes flag (aka multiple levels) |
| 223 | + moreLevels = (*check & 0x8000) != 0; |
| 224 | + |
| 225 | + if (moreLevels) |
| 226 | + check = &bspData[(*check & 0x1fff) + 1]; |
| 227 | + |
| 228 | + do |
| 229 | + { |
| 230 | + if (moreLevels && check[-1] == -0x8000) |
| 231 | + moreLevels = 0; |
| 232 | + |
| 233 | + // check if it's has BSP properties |
| 234 | + // basically it determines surface bounds |
| 235 | + if (*check & 0x4000) |
| 236 | + { |
| 237 | + sdNode* search = &nodeData[*check & 0x1fff]; // 0x3fff in final |
| 238 | + |
| 239 | + while (search->node < 0) |
| 240 | + { |
| 241 | + plane = FindRoadInBSP(search + 1, planeData); |
| 242 | + |
| 243 | + if (plane != nullptr) |
| 244 | + break; |
| 245 | + |
| 246 | + search += search->offset; |
| 247 | + } |
| 248 | + |
| 249 | + if (plane != nullptr) |
| 250 | + break; |
| 251 | + } |
| 252 | + else |
| 253 | + { |
| 254 | + plane = &planeData[*check]; |
| 255 | + |
| 256 | + if (plane->surfaceType >= 32) |
| 257 | + break; |
| 258 | + } |
| 259 | + |
| 260 | + check += 2; |
| 261 | + } while (true); |
| 262 | + } |
| 263 | + else |
| 264 | + { |
| 265 | + plane = nullptr; |
| 266 | + } |
| 267 | + } |
| 268 | + else |
| 269 | + { |
| 270 | + plane = &planeData[*check]; |
| 271 | + } |
| 272 | + } |
| 273 | + else |
| 274 | + { |
| 275 | + if (*check & 0x8000) |
| 276 | + { |
| 277 | + moreLevels = (*check & 0x6000) == 0x2000; |
| 278 | + |
| 279 | + if (moreLevels) |
| 280 | + check = &bspData[(*check & 0x1fff) + 1]; |
| 281 | + |
| 282 | + do |
| 283 | + { |
| 284 | + if (moreLevels && check[-1] == 0x8000) |
| 285 | + moreLevels = 0; |
| 286 | + |
| 287 | + if (*check & 0x4000) |
| 288 | + { |
| 289 | + plane = FindRoadInBSP(&nodeData[*check & 0x3fff], planeData); |
| 290 | + |
| 291 | + if (plane != nullptr) |
| 292 | + break; |
| 293 | + } |
| 294 | + else |
| 295 | + { |
| 296 | + plane = &planeData[*check]; |
| 297 | + |
| 298 | + if (plane->surfaceType >= 32) |
| 299 | + break; |
| 300 | + } |
| 301 | + |
| 302 | + check += 2; |
| 303 | + } while (true); |
| 304 | + } |
| 305 | + else if (!(*check & 0xE000)) |
| 306 | + { |
| 307 | + plane = &planeData[*check]; |
| 308 | + } |
| 309 | + else |
| 310 | + plane = nullptr; |
| 311 | + } |
| 312 | + |
| 313 | + if (plane == nullptr) |
| 314 | + return -1; |
| 315 | + |
| 316 | + if (plane->surfaceType >= 32) |
| 317 | + { |
| 318 | + position.vy = SdHeightOnPlane(position, plane, ((CDriver2LevelMap*)m_owner)->m_curves) + 256; |
| 319 | + return plane->surfaceType - 32; |
| 320 | + } |
| 321 | + |
| 322 | + return -1; |
| 323 | +} |
| 324 | + |
165 | 325 | void CDriver2LevelRegion::FreeAll() |
166 | 326 | { |
167 | 327 | if (!m_loaded) |
@@ -289,7 +449,7 @@ void CDriver2LevelRegion::ReadHeightmapData(const SPOOL_CONTEXT& ctx) |
289 | 449 | } |
290 | 450 | else |
291 | 451 | { |
292 | | - MsgError("Incorrect format or read error\n"); |
| 452 | + MsgError("Incorrect road map format or read error\n"); |
293 | 453 | } |
294 | 454 | } |
295 | 455 |
|
@@ -528,6 +688,99 @@ int CDriver2LevelMap::MapHeight(const VECTOR_NOPAD& position) const |
528 | 688 | return 0; |
529 | 689 | } |
530 | 690 |
|
| 691 | +int CDriver2LevelMap::GetRoadIndex(VECTOR_NOPAD& position) const |
| 692 | +{ |
| 693 | + VECTOR_NOPAD cellPos; |
| 694 | + XZPAIR cell; |
| 695 | + int level = 0; |
| 696 | + |
| 697 | + cellPos.vx = position.vx - 512; // FIXME: is that a quarter of a cell? |
| 698 | + cellPos.vy = position.vy; |
| 699 | + cellPos.vz = position.vz - 512; |
| 700 | + |
| 701 | + WorldPositionToCellXZ(cell, cellPos); |
| 702 | + CDriver2LevelRegion* region = (CDriver2LevelRegion*)GetRegion(cell); |
| 703 | + |
| 704 | + return region->RoadInCell(position); |
| 705 | +} |
| 706 | + |
| 707 | +// [A] custom function for working with roads in very optimized way |
| 708 | +bool CDriver2LevelMap::GetSurfaceRoadInfo(DRIVER2_ROAD_INFO& outRoadInfo, int surfId) const |
| 709 | +{ |
| 710 | + DRIVER2_CURVE* curve; |
| 711 | + DRIVER2_STRAIGHT* straight; |
| 712 | + DRIVER2_JUNCTION* junction; |
| 713 | + |
| 714 | + outRoadInfo.surfId = surfId; |
| 715 | + |
| 716 | + if (IS_CURVED_SURFACE(surfId)) |
| 717 | + { |
| 718 | + outRoadInfo.curve = curve = GetCurve(surfId); |
| 719 | + outRoadInfo.ConnectIdx = &curve->ConnectIdx; |
| 720 | + outRoadInfo.NumLanes = curve->NumLanes; |
| 721 | + outRoadInfo.LaneDirs = curve->LaneDirs; |
| 722 | + outRoadInfo.AILanes = curve->AILanes; |
| 723 | + return true; |
| 724 | + } |
| 725 | + else if (IS_STRAIGHT_SURFACE(surfId)) |
| 726 | + { |
| 727 | + outRoadInfo.straight = straight = GetStraight(surfId); |
| 728 | + outRoadInfo.ConnectIdx = &straight->ConnectIdx; |
| 729 | + outRoadInfo.NumLanes = straight->NumLanes; |
| 730 | + outRoadInfo.LaneDirs = straight->LaneDirs; |
| 731 | + outRoadInfo.AILanes = straight->AILanes; |
| 732 | + return true; |
| 733 | + } |
| 734 | + else if (IS_JUNCTION_SURFACE(surfId)) |
| 735 | + { |
| 736 | + outRoadInfo.junction = junction = GetJunction(surfId); |
| 737 | + outRoadInfo.ConnectIdx = &junction->ExitIdx; |
| 738 | + outRoadInfo.flags = junction->flags; |
| 739 | + return true; |
| 740 | + } |
| 741 | + |
| 742 | + return false; |
| 743 | +} |
| 744 | + |
| 745 | +DRIVER2_STRAIGHT* CDriver2LevelMap::GetStraight(int index) const |
| 746 | +{ |
| 747 | + if(IS_STRAIGHT_SURFACE(index)) |
| 748 | + return &m_straights[index & 0x1fff]; |
| 749 | + |
| 750 | + return nullptr; |
| 751 | +} |
| 752 | + |
| 753 | +DRIVER2_CURVE* CDriver2LevelMap::GetCurve(int index) const |
| 754 | +{ |
| 755 | + if (IS_CURVED_SURFACE(index)) |
| 756 | + return &m_curves[index & 0x1fff]; |
| 757 | + |
| 758 | + return nullptr; |
| 759 | +} |
| 760 | + |
| 761 | +DRIVER2_JUNCTION* CDriver2LevelMap::GetJunction(int index) const |
| 762 | +{ |
| 763 | + if (IS_JUNCTION_SURFACE(index)) |
| 764 | + return &m_junctions[index & 0x1fff]; |
| 765 | + |
| 766 | + return nullptr; |
| 767 | +} |
| 768 | + |
| 769 | +int CDriver2LevelMap::GetNumStraights() const |
| 770 | +{ |
| 771 | + return m_numStraights; |
| 772 | +} |
| 773 | + |
| 774 | +int CDriver2LevelMap::GetNumCurves() const |
| 775 | +{ |
| 776 | + return m_numCurves; |
| 777 | +} |
| 778 | + |
| 779 | +int CDriver2LevelMap::GetNumJunctions() const |
| 780 | +{ |
| 781 | + return m_numJunctions; |
| 782 | +} |
| 783 | + |
531 | 784 | //------------------------------------------------------------- |
532 | 785 | // returns first cell object of cell |
533 | 786 | //------------------------------------------------------------- |
|
0 commit comments