SpeedTree: read the .spt collision and composite texcoord blocks
The CSpeedTreeRT stand-in now returns the collision objects stored in the .spt (12000 section: spheres and cylinders) instead of one fixed trunk cylinder, and takes leaf texture rects from the 10000 section instead of hand-picked atlas rects (trees without leaf maps use the frond rect). SpeedGrass/BoundaryShapeManager are N_A (no 40250 caller). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
17cb7326c1
commit
ed61b2d20f
@@ -571,3 +571,5 @@
|
|||||||
{"date": "2026-09-28T05:29:23+00:00", "unit": "EterLib/GrpImageTexture.cpp", "action": "unit K: DXT DDS textures upload the file's own mip levels (40250 CreateDDSTexture: max(1,dwMipMapCount), dwLinearSize>>2i Copy) instead of a regenerated chain; non-DDS keep the D3DX_DEFAULT full chain; mem: textures one level; invented is_ui_tex no-mip rule (icon/ui/locale/<=128px) deleted; dxt_decode_test registered in ctest with mip-level cases", "evidence": "ctest -j3 36/36; fake-server spawn frame 900: near terrain diff 0.00, far terrain 2.7/255 (minified levels now from the file), rest is UI timing (area title/chat); far-terrain look NEEDS_LIVE"}
|
{"date": "2026-09-28T05:29:23+00:00", "unit": "EterLib/GrpImageTexture.cpp", "action": "unit K: DXT DDS textures upload the file's own mip levels (40250 CreateDDSTexture: max(1,dwMipMapCount), dwLinearSize>>2i Copy) instead of a regenerated chain; non-DDS keep the D3DX_DEFAULT full chain; mem: textures one level; invented is_ui_tex no-mip rule (icon/ui/locale/<=128px) deleted; dxt_decode_test registered in ctest with mip-level cases", "evidence": "ctest -j3 36/36; fake-server spawn frame 900: near terrain diff 0.00, far terrain 2.7/255 (minified levels now from the file), rest is UI timing (area title/chat); far-terrain look NEEDS_LIVE"}
|
||||||
{"date": "2026-09-28T05:33:21+00:00", "unit": "SpeedTreeLib/SpeedTreeWrapper.cpp", "action": "SpeedTree follow-up closed without code change: .spt 0x4E25 is one composite rect per file (87/87, independent of 1-3 leaf maps) and overlays on the atlases put it on the billboard tree image for most species, so it is not the leaf-map texcoords; the hand-picked leaf rects stay DIVERGENT, note updated", "evidence": "byte dump of b1_baobab_rt.spt tail (0x4E20/0x4E22 name/0x4E23 b/0x4E24 b/0x4E25 8f/0x4E21); count over 87 .spt files; atlas overlays of 9 species"}
|
{"date": "2026-09-28T05:33:21+00:00", "unit": "SpeedTreeLib/SpeedTreeWrapper.cpp", "action": "SpeedTree follow-up closed without code change: .spt 0x4E25 is one composite rect per file (87/87, independent of 1-3 leaf maps) and overlays on the atlases put it on the billboard tree image for most species, so it is not the leaf-map texcoords; the hand-picked leaf rects stay DIVERGENT, note updated", "evidence": "byte dump of b1_baobab_rt.spt tail (0x4E20/0x4E22 name/0x4E23 b/0x4E24 b/0x4E25 8f/0x4E21); count over 87 .spt files; atlas overlays of 9 species"}
|
||||||
{"date": "2026-09-30T03:07:37+00:00", "unit": "MilesLib/*", "action": "MilesLib platform port: 40250 SoundBase/SoundData/SoundInstance2D/3D/Stream/SoundManager*/Type copied verbatim (only includes + one unused QPC local differ) over an AIL_* software mixer (MilesAil.cpp: 2D balance pan, 3D inverse distance min 1/max 200 + listener pan, threaded stream decode, loop counts, decompress_ASI/ADPCM to float WAV); host NativeAudioEngine drains MtMilesMix; CSoundManager Create/Destroy in PythonBoot (IsNoSoundCard), listener+Update from PythonApplicationCamera, Save/RestoreVolume on window focus, CSoundData::SetPackMode in PackInitialize; old AudioCommands/SoundInstance bridge deleted; 142/142 PORTED", "evidence": "port_miles_test ok (2D/3D/stream fades/DataToInstance); ctest all pass except pre-existing flaky login_flow; desktop live client MT_AUDIO_TRACE peaks 1.17/0.70 at login and ~0.02 in game; Android device not attached, NEEDS device run"}
|
{"date": "2026-09-30T03:07:37+00:00", "unit": "MilesLib/*", "action": "MilesLib platform port: 40250 SoundBase/SoundData/SoundInstance2D/3D/Stream/SoundManager*/Type copied verbatim (only includes + one unused QPC local differ) over an AIL_* software mixer (MilesAil.cpp: 2D balance pan, 3D inverse distance min 1/max 200 + listener pan, threaded stream decode, loop counts, decompress_ASI/ADPCM to float WAV); host NativeAudioEngine drains MtMilesMix; CSoundManager Create/Destroy in PythonBoot (IsNoSoundCard), listener+Update from PythonApplicationCamera, Save/RestoreVolume on window focus, CSoundData::SetPackMode in PackInitialize; old AudioCommands/SoundInstance bridge deleted; 142/142 PORTED", "evidence": "port_miles_test ok (2D/3D/stream fades/DataToInstance); ctest all pass except pre-existing flaky login_flow; desktop live client MT_AUDIO_TRACE peaks 1.17/0.70 at login and ~0.02 in game; Android device not attached, NEEDS device run"}
|
||||||
|
{"date": "2026-09-30T03:19:50+00:00", "unit": "SpeedTreeLib/SpeedTreeWrapper.cpp", "action": "CSpeedTreeRT stand-in reads the .spt collision block (12000: 12002 sphere, 12003 cylinder) and the composite texcoord block (10000: 10002 leaf maps, 10003 frond, 10004 billboard) instead of a fixed trunk cylinder and hand-picked leaf rects; GetCollisionObjectCount/GetCollisionObject DIVERGENT -> PORTED; SetupLeafBuffers note updated (earlier 'no per-leaf-map texcoords' conclusion was wrong, the rects are in section 10000)", "evidence": "port_speedtree_test ok; token layout checked over 118 .spt extracted from the 40250 packs; fake-server Yongan view (camera 4500/3/0): tree crown now drawn from its own leaf-map cells, the wrong fruit/autumn cell is gone"}
|
||||||
|
{"date": "2026-09-30T03:19:50+00:00", "unit": "SpeedTreeLib/SpeedGrassRT.cpp", "action": "SpeedGrassRT/SpeedGrassWrapper/BoundaryShapeManager N_A: compiled (USE_SPEEDGRASS) but no caller outside those files", "evidence": "LC_ALL=C grep over ClientVS22/source for CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager"}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"reference": "SpeedTreeLib/BoundaryShapeManager.cpp",
|
||||||
|
"reference_sha256": "6fdfc42ca249991740567658bc0dced8dedddb387e2f4e1dcbad53b0d38fc964",
|
||||||
|
"priority": "P4",
|
||||||
|
"contracts": [],
|
||||||
|
"functions": {
|
||||||
|
"CBoundaryShapeManager::CBoundaryShapeManager": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CBoundaryShapeManager::~CBoundaryShapeManager": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CBoundaryShapeManager::LoadBsfFile": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CBoundaryShapeManager::PointInside": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CBoundaryShapeManager::PointInShape": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CBoundaryShapeManager::RandomPoint": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
{
|
||||||
|
"reference": "SpeedTreeLib/SpeedGrassRT.cpp",
|
||||||
|
"reference_sha256": "1fbd873c76a7aa57935035ea45a720527b9fd8ee478352806934fbb5f9c86d42",
|
||||||
|
"priority": "P4",
|
||||||
|
"contracts": [],
|
||||||
|
"functions": {
|
||||||
|
"VecInterpolate": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::SBlade::SBlade": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::SRegion::SRegion": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::CSpeedGrassRT": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::~CSpeedGrassRT": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::DeleteRegions": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::GetRegions": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::ParseBsfFile": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::CustomPlacement": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::GetLodParams": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::SetLodParams": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::Cull": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::SetWindDirection": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::GetWindDirection": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::GetCameraPos": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::SetCamera": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::SetPerspective": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::CreateRegions": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::RotateAxisFromIdentity": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::ComputeFrustum": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::ComputeUnitBillboard": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::ConvertCoordsToCell": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassRT::OutsideFrustum": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"reference": "SpeedTreeLib/SpeedGrassWrapper.cpp",
|
||||||
|
"reference_sha256": "c737f71f0115acabec0eb1ba4cf4a0ec9a169c8ac5f9ac32a789d2194ac00f53",
|
||||||
|
"priority": "P4",
|
||||||
|
"contracts": [],
|
||||||
|
"functions": {
|
||||||
|
"CSpeedGrassWrapper::CSpeedGrassWrapper": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassWrapper::~CSpeedGrassWrapper": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassWrapper::Draw": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassWrapper::InitFromBsfFile": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassWrapper::Color": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassWrapper::Height": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
},
|
||||||
|
"CSpeedGrassWrapper::InitGraphics": {
|
||||||
|
"status": "N_A",
|
||||||
|
"note": "compiled in 40250 (Constants.h defines USE_SPEEDGRASS) but unreachable: CSpeedGrassRT/CSpeedGrassWrapper/CBoundaryShapeManager are referenced only inside SpeedGrass*/BoundaryShapeManager* (grep over ClientVS22/source); nothing to port"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -68,7 +68,8 @@
|
|||||||
"impl": [
|
"impl": [
|
||||||
"src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::SetupLeafBuffers"
|
"src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::SetupLeafBuffers"
|
||||||
],
|
],
|
||||||
"note": "verbatim. DIVERGENT input: the stand-in's leaf clusters and composite-atlas rects are hand-picked (the .spt stores no per-leaf-map texcoords: leaf entries 0x0FA0-0x0FA7 carry colour/path/origin/size only, and the single 8-float 0x4E25 rect in the 0x4E20 composite block, one per file for 1-3 leaf maps, frames the billboard cell in most atlases; billboards never render in 40250 since SetLodLevel(1.0)); NEEDS_LIVE"
|
"note": "verbatim. Leaf texture coordinates are the .spt composite-atlas rects (10000 section: 10002 leaf maps, 10003 frond map, 10004 billboard, 8 floats per card, v flipped under SetTextureFlip), replacing the hand-picked rects; a tree without leaf maps (palms, ferns, bare winter trees) draws the proxy clusters with its frond rect. DIVERGENT input remains: cluster positions/sizes are the stand-in's procedural proxy, not the closed engine's; NEEDS_LIVE",
|
||||||
|
"test": "tests/port/port_speedtree_test.cpp"
|
||||||
},
|
},
|
||||||
"CSpeedTreeWrapper::Advance": {
|
"CSpeedTreeWrapper::Advance": {
|
||||||
"status": "PORTED",
|
"status": "PORTED",
|
||||||
@@ -196,18 +197,20 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"CSpeedTreeWrapper::GetCollisionObjectCount": {
|
"CSpeedTreeWrapper::GetCollisionObjectCount": {
|
||||||
"status": "DIVERGENT",
|
"status": "PORTED",
|
||||||
"impl": [
|
"impl": [
|
||||||
"src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::GetCollisionObjectCount"
|
"src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::GetCollisionObjectCount"
|
||||||
],
|
],
|
||||||
"note": "verbatim; the stand-in returns one trunk CO_CYLINDER (radius 35, height 750), not the .spt collision objects"
|
"note": "verbatim; the CSpeedTreeRT stand-in returns the .spt collision block as stored (12000 section: 12002 sphere x y z r, 12003 cylinder x y z r h; 1-5 objects, none for aloe/fern ground plants, checked on all 118 pack .spt); the wrapper passes no size override (LoadTree fSize -1), so no SetTreeSize rescale applies",
|
||||||
|
"test": "tests/port/port_speedtree_test.cpp"
|
||||||
},
|
},
|
||||||
"CSpeedTreeWrapper::GetCollisionObject": {
|
"CSpeedTreeWrapper::GetCollisionObject": {
|
||||||
"status": "DIVERGENT",
|
"status": "PORTED",
|
||||||
"impl": [
|
"impl": [
|
||||||
"src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::GetCollisionObject"
|
"src/port/SpeedTreeLib/SpeedTreeWrapper.cpp:CSpeedTreeWrapper::GetCollisionObject"
|
||||||
],
|
],
|
||||||
"note": "verbatim; the stand-in returns one trunk CO_CYLINDER (radius 35, height 750), not the .spt collision objects"
|
"note": "verbatim; the CSpeedTreeRT stand-in returns the .spt collision block as stored (12000 section: 12002 sphere x y z r, 12003 cylinder x y z r h; 1-5 objects, none for aloe/fern ground plants, checked on all 118 pack .spt); the wrapper passes no size override (LoadTree fSize -1), so no SetTreeSize rescale applies",
|
||||||
|
"test": "tests/port/port_speedtree_test.cpp"
|
||||||
},
|
},
|
||||||
"CSpeedTreeWrapper::GetPosition": {
|
"CSpeedTreeWrapper::GetPosition": {
|
||||||
"status": "PORTED",
|
"status": "PORTED",
|
||||||
|
|||||||
@@ -12,11 +12,15 @@
|
|||||||
// inactive; there is only that LOD.
|
// inactive; there is only that LOD.
|
||||||
// - GetTextures returns bare file names: the wrapper prefixes the .spt directory.
|
// - GetTextures returns bare file names: the wrapper prefixes the .spt directory.
|
||||||
// - GetTreeSize reads the Size / SizeVariance header tokens (2001 / 2002) of the .spt.
|
// - GetTreeSize reads the Size / SizeVariance header tokens (2001 / 2002) of the .spt.
|
||||||
|
// - GetCollisionObject returns the .spt collision block (12000 section) as stored: 12002 sphere
|
||||||
|
// (x, y, z, radius), 12003 cylinder (x, y, z, radius, height); a tree without the section has none.
|
||||||
|
// - Leaf texture coordinates are the .spt composite-atlas rects (10000 section), v flipped under
|
||||||
|
// SetTextureFlip.
|
||||||
// What is not the SDK (DIVERGENT; the notes are on the wrapper in audit/port-map/SpeedTreeLib/):
|
// What is not the SDK (DIVERGENT; the notes are on the wrapper in audit/port-map/SpeedTreeLib/):
|
||||||
// - Branch and leaf geometry cannot be generated from the .spt parameters without the closed
|
// - Branch and leaf geometry cannot be generated from the .spt parameters without the closed
|
||||||
// engine; it is the previous procedural proxy (species hints from the file name, 1000-unit
|
// engine; it is the previous procedural proxy (species hints from the file name, 1000-unit
|
||||||
// height, crossed-branch trunk, hand-picked composite atlas rects), no fronds.
|
// height, crossed-branch trunk, leaf clusters), no fronds; a tree without leaf maps draws its
|
||||||
// - Collision objects are one trunk cylinder; the .spt collision block is not parsed.
|
// proxy clusters with its frond-map rect.
|
||||||
// - Materials are the SDK defaults (white); the .spt material block is not parsed.
|
// - Materials are the SDK defaults (white); the .spt material block is not parsed.
|
||||||
#include "SpeedTreeLib/StdAfx.h"
|
#include "SpeedTreeLib/StdAfx.h"
|
||||||
|
|
||||||
@@ -116,47 +120,56 @@ uint32_t species_seed(const std::string & s)
|
|||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Composite-atlas leaf rects, D3D image space (v down).
|
// Composite-atlas rect, D3D image space (v down).
|
||||||
struct UVRect
|
struct UVRect
|
||||||
{
|
{
|
||||||
float u0 = 0.0f, v0 = 0.0f, u1 = 1.0f, v1 = 1.0f;
|
float u0 = 0.0f, v0 = 0.0f, u1 = 1.0f, v1 = 1.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<UVRect> foliage_rects(const std::string & species, const std::string & composite)
|
// .spt texture-coordinate block: 10000, then 10002 <count> leaf maps, 10003 <count> frond maps, 10004
|
||||||
|
// <count> billboards, each entry 8 floats: the four card corners (+r,+u) (-r,+u) (-r,-u) (+r,-u) as
|
||||||
|
// (u, v) in the composite atlas with v up (the SDK flips v under SetTextureFlip), then 10001. Checked on
|
||||||
|
// the 118 .spt files of the 40250 packs: 0-2 leaf maps, always 1 frond map, 0-1 billboards.
|
||||||
|
struct SSptTexCoords
|
||||||
{
|
{
|
||||||
if (composite.empty())
|
std::vector<UVRect> vLeaves, vFronds, vBillboards;
|
||||||
return { { 0.0f, 0.0f, 1.0f, 1.0f } };
|
};
|
||||||
const std::string s = to_lower_str(species);
|
|
||||||
const std::string c = to_lower_str(composite);
|
bool read_spt_texcoords(const unsigned char * p, size_t n, SSptTexCoords & out)
|
||||||
const bool fall = s.find("fall") != std::string::npos;
|
{
|
||||||
const bool winter = s.find("winter") != std::string::npos;
|
auto i32_at = [&](size_t o) {
|
||||||
if (c.find("b1") != std::string::npos)
|
int32_t v = 0;
|
||||||
|
std::memcpy(&v, p + o, 4);
|
||||||
|
return v;
|
||||||
|
};
|
||||||
|
for (size_t o = 0; o + 8 <= n; ++o)
|
||||||
{
|
{
|
||||||
if (fall)
|
if (i32_at(o) != 10000 || i32_at(o + 4) != 10002)
|
||||||
return { { 0.00f, 0.05f, 0.25f, 0.25f }, { 0.25f, 0.25f, 0.50f, 0.50f } };
|
continue;
|
||||||
return { { 0.25f, 0.02f, 0.50f, 0.23f }, { 0.25f, 0.18f, 0.50f, 0.36f }, { 0.00f, 0.27f, 0.27f, 0.49f } };
|
SSptTexCoords coords;
|
||||||
}
|
size_t c = o + 4;
|
||||||
if (c.find("b2") != std::string::npos)
|
for (int32_t token : { 10002, 10003, 10004 })
|
||||||
{
|
{
|
||||||
if (fall)
|
if (c + 8 > n || i32_at(c) != token)
|
||||||
return { { 0.00f, 0.00f, 0.25f, 0.25f }, { 0.25f, 0.25f, 0.50f, 0.50f } };
|
return false;
|
||||||
return { { 0.50f, 0.38f, 0.75f, 0.63f }, { 0.50f, 0.63f, 0.75f, 0.88f }, { 0.00f, 0.38f, 0.25f, 0.62f } };
|
const int32_t count = i32_at(c + 4);
|
||||||
}
|
c += 8;
|
||||||
if (c.find("b3") != std::string::npos)
|
if (count < 0 || count > 64 || c + size_t(count) * 32 > n)
|
||||||
|
return false;
|
||||||
|
std::vector<UVRect> & rects = token == 10002 ? coords.vLeaves : token == 10003 ? coords.vFronds : coords.vBillboards;
|
||||||
|
for (int32_t i = 0; i < count; ++i, c += 32)
|
||||||
{
|
{
|
||||||
if (fall)
|
float f[8];
|
||||||
return { { 0.25f, 0.25f, 0.50f, 0.50f } };
|
std::memcpy(f, p + c, sizeof(f));
|
||||||
return { { 0.00f, 0.25f, 0.25f, 0.50f }, { 0.00f, 0.50f, 0.25f, 0.75f }, { 0.25f, 0.50f, 0.50f, 0.75f } };
|
rects.push_back({ f[2], 1.0f - f[1], f[0], 1.0f - f[5] });
|
||||||
}
|
}
|
||||||
if (c.find("n1") != std::string::npos)
|
|
||||||
{
|
|
||||||
if (winter)
|
|
||||||
return { { 0.00f, 0.36f, 0.50f, 0.58f }, { 0.25f, 0.55f, 0.52f, 0.75f } };
|
|
||||||
return { { 0.00f, 0.72f, 0.28f, 0.96f }, { 0.25f, 0.74f, 0.53f, 0.97f } };
|
|
||||||
}
|
}
|
||||||
if (c.find("n2") != std::string::npos)
|
if (c + 4 > n || i32_at(c) != 10001)
|
||||||
return { { 0.00f, 0.48f, 0.27f, 0.75f }, { 0.26f, 0.73f, 0.58f, 1.00f }, { 0.75f, 0.48f, 1.00f, 0.80f } };
|
return false;
|
||||||
return { { 0.0f, 0.0f, 1.0f, 1.0f } };
|
out = std::move(coords);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// .spt header: 1000 <len> "__IdvSpt_02_", 1002, 2000 <len> <branch texture>, 2001 <float size>,
|
// .spt header: 1000 <len> "__IdvSpt_02_", 1002, 2000 <len> <branch texture>, 2001 <float size>,
|
||||||
@@ -186,6 +199,59 @@ bool read_spt_size(const unsigned char * p, size_t n, float & size, float & vari
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SCollisionObject
|
||||||
|
{
|
||||||
|
CSpeedTreeRT::ECollisionObjectType eType = CSpeedTreeRT::CO_SPHERE;
|
||||||
|
float afPosition[3] = { 0.0f, 0.0f, 0.0f };
|
||||||
|
float afDimensions[3] = { 0.0f, 0.0f, 0.0f };
|
||||||
|
};
|
||||||
|
|
||||||
|
// .spt collision block: 12000, then per object 12002 <x y z radius> (sphere) or 12003 <x y z radius
|
||||||
|
// height> (cylinder) as floats, then 12001. The section sits after the texture-coordinate block (10000
|
||||||
|
// .. 10001) and before 13000; small ground plants (aloe flowers, ferns) have none. Checked on all 118
|
||||||
|
// .spt files of the 40250 packs: 1-5 objects, only these two tokens.
|
||||||
|
void read_spt_collision(const unsigned char * p, size_t n, std::vector<SCollisionObject> & out)
|
||||||
|
{
|
||||||
|
auto i32_at = [&](size_t o) {
|
||||||
|
int32_t v = 0;
|
||||||
|
std::memcpy(&v, p + o, 4);
|
||||||
|
return v;
|
||||||
|
};
|
||||||
|
for (size_t o = 0; o + 8 <= n; ++o)
|
||||||
|
{
|
||||||
|
if (i32_at(o) != 12000)
|
||||||
|
continue;
|
||||||
|
const int32_t next = i32_at(o + 4);
|
||||||
|
if (next != 12001 && next != 12002 && next != 12003)
|
||||||
|
continue;
|
||||||
|
std::vector<SCollisionObject> objects;
|
||||||
|
size_t c = o + 4;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (c + 4 > n)
|
||||||
|
return;
|
||||||
|
const int32_t t = i32_at(c);
|
||||||
|
c += 4;
|
||||||
|
if (t == 12001)
|
||||||
|
break;
|
||||||
|
const size_t count = t == 12002 ? 4 : t == 12003 ? 5 : 0;
|
||||||
|
if (count == 0 || c + count * 4 > n)
|
||||||
|
return;
|
||||||
|
SCollisionObject obj;
|
||||||
|
obj.eType = t == 12002 ? CSpeedTreeRT::CO_SPHERE : CSpeedTreeRT::CO_CYLINDER;
|
||||||
|
float f[5] = {};
|
||||||
|
std::memcpy(f, p + c, count * 4);
|
||||||
|
c += count * 4;
|
||||||
|
std::memcpy(obj.afPosition, f, sizeof(obj.afPosition));
|
||||||
|
obj.afDimensions[0] = f[3];
|
||||||
|
obj.afDimensions[1] = count == 5 ? f[4] : 0.0f;
|
||||||
|
objects.push_back(obj);
|
||||||
|
}
|
||||||
|
out = std::move(objects);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct SStaticLight
|
struct SStaticLight
|
||||||
{
|
{
|
||||||
float afAttributes[16] = {
|
float afAttributes[16] = {
|
||||||
@@ -258,7 +324,8 @@ struct STreeData
|
|||||||
std::vector<const float *> vLeafTexPtrs, vLeafCoordPtrs;
|
std::vector<const float *> vLeafTexPtrs, vLeafCoordPtrs;
|
||||||
|
|
||||||
float afBoundingBox[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
|
float afBoundingBox[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
|
||||||
float fTrunkRadius = 35.0f, fTrunkHeight = 600.0f;
|
std::vector<SCollisionObject> vCollisionObjects;
|
||||||
|
SSptTexCoords texCoords;
|
||||||
float afBranchMaterial[13], afFrondMaterial[13], afLeafMaterial[13];
|
float afBranchMaterial[13], afFrondMaterial[13], afLeafMaterial[13];
|
||||||
float fLeafLightingAdjustment = 0.75f;
|
float fLeafLightingAdjustment = 0.75f;
|
||||||
bool bComputed = false;
|
bool bComputed = false;
|
||||||
@@ -285,6 +352,8 @@ struct STreeData
|
|||||||
strCompositeTexture = basename_only(info.composite_texture);
|
strCompositeTexture = basename_only(info.composite_texture);
|
||||||
strSelfShadowTexture = basename_only(info.self_shadow_texture);
|
strSelfShadowTexture = basename_only(info.self_shadow_texture);
|
||||||
read_spt_size(p, n, fFileSize, fFileVariance);
|
read_spt_size(p, n, fFileSize, fFileVariance);
|
||||||
|
read_spt_collision(p, n, vCollisionObjects);
|
||||||
|
read_spt_texcoords(p, n, texCoords);
|
||||||
fSize = fFileSize;
|
fSize = fFileSize;
|
||||||
fVariance = fFileVariance;
|
fVariance = fFileVariance;
|
||||||
}
|
}
|
||||||
@@ -366,7 +435,11 @@ struct STreeData
|
|||||||
|
|
||||||
void BuildLeaves(float H, bool conifer, bool palm)
|
void BuildLeaves(float H, bool conifer, bool palm)
|
||||||
{
|
{
|
||||||
const std::vector<UVRect> rects = foliage_rects(strName, strCompositeTexture);
|
// Leaf clusters use the tree's leaf maps; a tree without leaf maps (palms, ferns, bare winter
|
||||||
|
// trees) gets its foliage from fronds in the SDK, which the proxy stands in for with the frond map.
|
||||||
|
std::vector<UVRect> rects = texCoords.vLeaves.empty() ? texCoords.vFronds : texCoords.vLeaves;
|
||||||
|
if (rects.empty())
|
||||||
|
rects.push_back(UVRect());
|
||||||
const uint32_t seed = species_seed(strName);
|
const uint32_t seed = species_seed(strName);
|
||||||
const int count = palm ? 16 : 24;
|
const int count = palm ? 16 : 24;
|
||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
@@ -418,8 +491,6 @@ struct STreeData
|
|||||||
afBoundingBox[3] = R;
|
afBoundingBox[3] = R;
|
||||||
afBoundingBox[4] = R;
|
afBoundingBox[4] = R;
|
||||||
afBoundingBox[5] = H;
|
afBoundingBox[5] = H;
|
||||||
fTrunkRadius = H * 0.035f;
|
|
||||||
fTrunkHeight = H * 0.75f;
|
|
||||||
|
|
||||||
// PORT: the Godot host draws its own trees unless the native terrain renderer is on; the tree
|
// PORT: the Godot host draws its own trees unless the native terrain renderer is on; the tree
|
||||||
// then has no geometry so the forest submits nothing.
|
// then has no geometry so the forest submits nothing.
|
||||||
@@ -846,18 +917,23 @@ void CSpeedTreeRT::GetBoundingBox(float * pBounds) const
|
|||||||
std::memcpy(pBounds, m_pImpl->pTree->afBoundingBox, 6 * sizeof(float));
|
std::memcpy(pBounds, m_pImpl->pTree->afBoundingBox, 6 * sizeof(float));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int CSpeedTreeRT::GetCollisionObjectCount(void) { return 1; }
|
unsigned int CSpeedTreeRT::GetCollisionObjectCount(void)
|
||||||
|
|
||||||
void CSpeedTreeRT::GetCollisionObject(unsigned int /*nIndex*/, ECollisionObjectType & eType, float * pPosition, float * pDimensions)
|
|
||||||
{
|
{
|
||||||
const STreeData & tree = *m_pImpl->pTree;
|
return static_cast<unsigned int>(m_pImpl->pTree->vCollisionObjects.size());
|
||||||
eType = CO_CYLINDER;
|
}
|
||||||
|
|
||||||
|
void CSpeedTreeRT::GetCollisionObject(unsigned int nIndex, ECollisionObjectType & eType, float * pPosition, float * pDimensions)
|
||||||
|
{
|
||||||
|
const auto & objects = m_pImpl->pTree->vCollisionObjects;
|
||||||
|
if (nIndex >= objects.size())
|
||||||
|
{
|
||||||
|
SetError("SpeedTreeRT: collision object index out of range");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto & o = objects[nIndex];
|
||||||
|
eType = o.eType;
|
||||||
if (pPosition)
|
if (pPosition)
|
||||||
pPosition[0] = pPosition[1] = pPosition[2] = 0.0f;
|
std::memcpy(pPosition, o.afPosition, sizeof(o.afPosition));
|
||||||
if (pDimensions)
|
if (pDimensions)
|
||||||
{
|
std::memcpy(pDimensions, o.afDimensions, sizeof(o.afDimensions));
|
||||||
pDimensions[0] = tree.fTrunkRadius;
|
|
||||||
pDimensions[1] = tree.fTrunkHeight;
|
|
||||||
pDimensions[2] = 0.0f;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,6 +200,11 @@ if(BUILD_TESTING AND CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME)
|
|||||||
target_link_libraries(port_miles_test PRIVATE port_platform)
|
target_link_libraries(port_miles_test PRIVATE port_platform)
|
||||||
add_test(NAME port.miles COMMAND $<TARGET_FILE:port_miles_test>)
|
add_test(NAME port.miles COMMAND $<TARGET_FILE:port_miles_test>)
|
||||||
|
|
||||||
|
# SpeedTreeLib: the CSpeedTreeRT stand-in's .spt size and collision blocks.
|
||||||
|
add_executable(port_speedtree_test ${PROJECT_SOURCE_DIR}/tests/port/port_speedtree_test.cpp)
|
||||||
|
target_link_libraries(port_speedtree_test PRIVATE port_platform)
|
||||||
|
add_test(NAME port.speedtree COMMAND $<TARGET_FILE:port_speedtree_test>)
|
||||||
|
|
||||||
# 批次 2V1: Packet.h struct sizes against the classic wire layouts.
|
# 批次 2V1: Packet.h struct sizes against the classic wire layouts.
|
||||||
add_executable(port_packet_test ${PROJECT_SOURCE_DIR}/tests/port/port_packet_test.cpp
|
add_executable(port_packet_test ${PROJECT_SOURCE_DIR}/tests/port/port_packet_test.cpp
|
||||||
${PROJECT_SOURCE_DIR}/tests/port/port_packet_test_wire.cpp)
|
${PROJECT_SOURCE_DIR}/tests/port/port_packet_test_wire.cpp)
|
||||||
|
|||||||
@@ -0,0 +1,220 @@
|
|||||||
|
// SpeedTreeLib: the CSpeedTreeRT stand-in (platform/SpeedTreeLib/SpeedTreeRT.cpp) reads the .spt size and
|
||||||
|
// collision blocks the 40250 wrapper asks for (GetTreeSize, GetCollisionObject in OnUpdateCollisionData).
|
||||||
|
// The blocks are built here in the .spt token layout of the 40250 pack files.
|
||||||
|
#include "SpeedTreeLib/StdAfx.h"
|
||||||
|
|
||||||
|
#include <SpeedTreeRT.h>
|
||||||
|
#include "../../src/platform/EterLib/RenderCommands3D.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
static int g_failures = 0;
|
||||||
|
#define CHECK(cond) \
|
||||||
|
do { \
|
||||||
|
if (!(cond)) { \
|
||||||
|
std::fprintf(stderr, "%s:%d: CHECK(%s)\n", __FILE__, __LINE__, #cond); \
|
||||||
|
++g_failures; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#define CHECK_NEAR(a, b) CHECK(std::fabs(double(a) - double(b)) < 1e-3)
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Block
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> bytes;
|
||||||
|
void i32(int32_t v) { bytes.insert(bytes.end(), reinterpret_cast<unsigned char *>(&v), reinterpret_cast<unsigned char *>(&v) + 4); }
|
||||||
|
void f32(float v) { bytes.insert(bytes.end(), reinterpret_cast<unsigned char *>(&v), reinterpret_cast<unsigned char *>(&v) + 4); }
|
||||||
|
void str(const std::string & s)
|
||||||
|
{
|
||||||
|
i32(int32_t(s.size()));
|
||||||
|
bytes.insert(bytes.end(), s.begin(), s.end());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1000 "__IdvSpt_02_", 1002, 2000 <bark>, 2001 size, 2002 variance, then filler up to the collision block.
|
||||||
|
Block header(float size, float variance)
|
||||||
|
{
|
||||||
|
Block b;
|
||||||
|
b.i32(1000);
|
||||||
|
b.str("__IdvSpt_02_");
|
||||||
|
b.i32(1002);
|
||||||
|
b.i32(2000);
|
||||||
|
b.str("BaobabBark.tga");
|
||||||
|
b.i32(2001);
|
||||||
|
b.f32(size);
|
||||||
|
b.i32(2002);
|
||||||
|
b.f32(variance);
|
||||||
|
// texture-coordinate block: two leaf maps, one frond map, one billboard; corners (+r,+u) (-r,+u)
|
||||||
|
// (-r,-u) (+r,-u) with v up (b1_baobab_rt.spt)
|
||||||
|
b.i32(10000);
|
||||||
|
b.i32(10002);
|
||||||
|
b.i32(2);
|
||||||
|
for (float f : { 0.5f, 0.25f, 0.25f, 0.25f, 0.25f, 0.0f, 0.5f, 0.0f })
|
||||||
|
b.f32(f);
|
||||||
|
for (float f : { 0.25f, 0.25f, 0.0f, 0.25f, 0.0f, 0.0f, 0.25f, 0.0f })
|
||||||
|
b.f32(f);
|
||||||
|
b.i32(10003);
|
||||||
|
b.i32(1);
|
||||||
|
for (float f : { 0.625f, 0.735f, 0.5f, 0.735f, 0.5f, 0.613f, 0.625f, 0.613f })
|
||||||
|
b.f32(f);
|
||||||
|
b.i32(10004);
|
||||||
|
b.i32(1);
|
||||||
|
for (float f : { 0.75f, 0.985f, 0.625f, 0.985f, 0.625f, 0.735f, 0.75f, 0.735f })
|
||||||
|
b.f32(f);
|
||||||
|
b.i32(10001);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The same block without leaf maps (palms, ferns, bare winter trees).
|
||||||
|
Block header_no_leaves()
|
||||||
|
{
|
||||||
|
Block b;
|
||||||
|
b.i32(1000);
|
||||||
|
b.str("__IdvSpt_02_");
|
||||||
|
b.i32(1002);
|
||||||
|
b.i32(2000);
|
||||||
|
b.str("PalmBark.tga");
|
||||||
|
b.i32(2001);
|
||||||
|
b.f32(50.0f);
|
||||||
|
b.i32(2002);
|
||||||
|
b.f32(0.0f);
|
||||||
|
b.i32(10000);
|
||||||
|
b.i32(10002);
|
||||||
|
b.i32(0);
|
||||||
|
b.i32(10003);
|
||||||
|
b.i32(1);
|
||||||
|
for (float f : { 0.375f, 0.625f, 0.25f, 0.625f, 0.25f, 0.5f, 0.375f, 0.5f })
|
||||||
|
b.f32(f);
|
||||||
|
b.i32(10004);
|
||||||
|
b.i32(0);
|
||||||
|
b.i32(10001);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float * leaf_texcoords(CSpeedTreeRT & tree, unsigned i)
|
||||||
|
{
|
||||||
|
CSpeedTreeRT::SGeometry geometry;
|
||||||
|
tree.GetGeometry(geometry, SpeedTree_LeafGeometry);
|
||||||
|
CHECK(geometry.m_sLeaves0.m_usLeafCount > i);
|
||||||
|
return geometry.m_sLeaves0.m_usLeafCount > i ? geometry.m_sLeaves0.m_pLeafMapTexCoords[i] : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_leaf_texcoords()
|
||||||
|
{
|
||||||
|
SetNativeTerrainRenderEnabled(true);
|
||||||
|
CSpeedTreeRT::SetTextureFlip(true); // as CSpeedTreeWrapper::LoadTree
|
||||||
|
|
||||||
|
Block b = header(1100.0f, 150.0f);
|
||||||
|
CSpeedTreeRT tree;
|
||||||
|
CHECK(tree.LoadTree(b.bytes.data(), unsigned(b.bytes.size())));
|
||||||
|
CHECK(tree.Compute(nullptr, 1));
|
||||||
|
// leaf 0: leaf map 0, v flipped
|
||||||
|
if (const float * t = leaf_texcoords(tree, 0))
|
||||||
|
{
|
||||||
|
const float want[8] = { 0.5f, 0.75f, 0.25f, 0.75f, 0.25f, 1.0f, 0.5f, 1.0f };
|
||||||
|
for (int k = 0; k < 8; ++k)
|
||||||
|
CHECK_NEAR(t[k], want[k]);
|
||||||
|
}
|
||||||
|
// leaf 1: leaf map 1, mirrored in u by the proxy
|
||||||
|
if (const float * t = leaf_texcoords(tree, 1))
|
||||||
|
{
|
||||||
|
const float want[8] = { 0.0f, 0.75f, 0.25f, 0.75f, 0.25f, 1.0f, 0.0f, 1.0f };
|
||||||
|
for (int k = 0; k < 8; ++k)
|
||||||
|
CHECK_NEAR(t[k], want[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// no leaf maps: the proxy clusters use the frond map
|
||||||
|
Block palm = header_no_leaves();
|
||||||
|
CSpeedTreeRT palmTree;
|
||||||
|
CHECK(palmTree.LoadTree(palm.bytes.data(), unsigned(palm.bytes.size())));
|
||||||
|
CHECK(palmTree.Compute(nullptr, 1));
|
||||||
|
if (const float * t = leaf_texcoords(palmTree, 0))
|
||||||
|
{
|
||||||
|
const float want[8] = { 0.375f, 0.375f, 0.25f, 0.375f, 0.25f, 0.5f, 0.375f, 0.5f };
|
||||||
|
for (int k = 0; k < 8; ++k)
|
||||||
|
CHECK_NEAR(t[k], want[k]);
|
||||||
|
}
|
||||||
|
SetNativeTerrainRenderEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_collision()
|
||||||
|
{
|
||||||
|
Block b = header(1100.0f, 150.0f);
|
||||||
|
b.i32(12000);
|
||||||
|
b.i32(12003); // cylinder x y z radius height
|
||||||
|
b.f32(0.0f);
|
||||||
|
b.f32(0.0f);
|
||||||
|
b.f32(0.0f);
|
||||||
|
b.f32(164.47f);
|
||||||
|
b.f32(582.55f);
|
||||||
|
b.i32(12002); // sphere x y z radius
|
||||||
|
b.f32(-10.44f);
|
||||||
|
b.f32(18.5f);
|
||||||
|
b.f32(775.42f);
|
||||||
|
b.f32(450.15f);
|
||||||
|
b.i32(12001);
|
||||||
|
b.i32(13000);
|
||||||
|
b.i32(13001);
|
||||||
|
|
||||||
|
CSpeedTreeRT tree;
|
||||||
|
CHECK(tree.LoadTree(b.bytes.data(), unsigned(b.bytes.size())));
|
||||||
|
float size = 0.0f, variance = 0.0f;
|
||||||
|
tree.GetTreeSize(size, variance);
|
||||||
|
CHECK_NEAR(size, 1100.0f);
|
||||||
|
CHECK_NEAR(variance, 150.0f);
|
||||||
|
|
||||||
|
CHECK(tree.GetCollisionObjectCount() == 2);
|
||||||
|
CSpeedTreeRT::ECollisionObjectType type = CSpeedTreeRT::CO_BOX;
|
||||||
|
float pos[3] = {}, dim[3] = {};
|
||||||
|
tree.GetCollisionObject(0, type, pos, dim);
|
||||||
|
CHECK(type == CSpeedTreeRT::CO_CYLINDER);
|
||||||
|
CHECK_NEAR(pos[0], 0.0f);
|
||||||
|
CHECK_NEAR(pos[2], 0.0f);
|
||||||
|
CHECK_NEAR(dim[0], 164.47f);
|
||||||
|
CHECK_NEAR(dim[1], 582.55f);
|
||||||
|
tree.GetCollisionObject(1, type, pos, dim);
|
||||||
|
CHECK(type == CSpeedTreeRT::CO_SPHERE);
|
||||||
|
CHECK_NEAR(pos[0], -10.44f);
|
||||||
|
CHECK_NEAR(pos[1], 18.5f);
|
||||||
|
CHECK_NEAR(pos[2], 775.42f);
|
||||||
|
CHECK_NEAR(dim[0], 450.15f);
|
||||||
|
|
||||||
|
// instances share the tree data
|
||||||
|
CSpeedTreeRT * instance = tree.MakeInstance();
|
||||||
|
CHECK(instance->GetCollisionObjectCount() == 2);
|
||||||
|
delete instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_no_collision()
|
||||||
|
{
|
||||||
|
// aloe flowers / ferns: no 12000 section, no collision objects
|
||||||
|
Block b = header(50.0f, 0.0f);
|
||||||
|
b.i32(13000);
|
||||||
|
b.i32(13001);
|
||||||
|
CSpeedTreeRT tree;
|
||||||
|
CHECK(tree.LoadTree(b.bytes.data(), unsigned(b.bytes.size())));
|
||||||
|
CHECK(tree.GetCollisionObjectCount() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
test_collision();
|
||||||
|
test_no_collision();
|
||||||
|
test_leaf_texcoords();
|
||||||
|
if (g_failures)
|
||||||
|
{
|
||||||
|
std::fprintf(stderr, "port_speedtree_test: %d failure(s)\n", g_failures);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::printf("port_speedtree_test: ok\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user