wip(assembler): M1 round 2 -- surface recipe closer to legacy; native still slower and visibly buggy
Luna, M1 round 2 of 4: round 1's mismatch was an underfit recipe (two synthetic profiles, missing large-scale surface terms). Now: biome IDs match 4,096/4,096, mean height offset -1.366 vox, density MAE 274.04 -> 17.23 vox, sign mismatches 55.2% -> 18.14%. Bound audit 298 boxes / 8,046 points, 0 misses, negative control caught. Hash c5e0371612e927af at W1/W8/W16. Surface query 8-14 ms / 4,096 points. Matched 3D capture pairs added. Problems (my review of the captures + her timings): - matched 120-tile views: full-detail readiness 5.227 s native vs 2.882 s legacy -- SLOWER; - surface capture shows a rectangular flat-topped raised block with vertical walls (tile/patch-level height bug, inferred); - cliff capture is broken (floating shard, debris trail), not a cliff. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
1 parent
874c796c65
commit
fcb16fd265
15 files changed
+1375
-91
No files matched your search
@@ -7710,12 +7710,26 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
std::int64_t WorldAssemblyFixed(double voxels)
|
||||
{return static_cast<std::int64_t>(std::llround(voxels*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel));}
|
||||
|
||||
std::int64_t WorldAssemblyQ16(double value)
|
||||
{return static_cast<std::int64_t>(std::llround(value*65536.0));}
|
||||
|
||||
std::int64_t WorldAssemblyWavelength(double frequency,double fallbackVoxels)
|
||||
{
|
||||
const double wavelength=std::isfinite(frequency)&&frequency>0.0?1.0/frequency:fallbackVoxels;
|
||||
return std::max<std::int64_t>(1,WorldAssemblyFixed(wavelength));
|
||||
}
|
||||
|
||||
uint64 WorldAssemblyFeatureId(uint32 seed,int64 x,int64 y,uint32 kind)
|
||||
{
|
||||
uint64 hash=14695981039346656037ull;
|
||||
const auto add=[&](uint64 value,int bytes)
|
||||
{
|
||||
for(int i=0;i<bytes;++i){hash^=static_cast<uint8>(value>>(8*i));hash*=1099511628211ull;}
|
||||
};
|
||||
add(seed,4);add(static_cast<uint64>(x),8);add(static_cast<uint64>(y),8);add(kind,4);
|
||||
return hash?hash:1;
|
||||
}
|
||||
|
||||
bool AttachRound1WorldAssemblyPrototype(FStandaloneOwnerWorld& owner,VFJson::Value& root,
|
||||
std::string& error,const std::string& manifestOutputPath)
|
||||
{
|
||||
@@ -7738,10 +7752,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
const FBiomeContext biomeContext=owner.Manager->GetBiomeContextForChunk(chunk);
|
||||
const std::int64_t bottomVoxel=static_cast<std::int64_t>(bottomChunkZ)*CHUNK_SIZE;
|
||||
const std::int64_t heightVoxels=static_cast<std::int64_t>(topChunkZ-bottomChunkZ+1)*CHUNK_SIZE;
|
||||
const double baseHeight=static_cast<double>(bottomVoxel)
|
||||
+static_cast<double>(heightVoxels)*surface.BaseGroundRelative;
|
||||
const double elevation=std::max(16.0,static_cast<double>(surface.ElevationRange));
|
||||
const FBiomeMapParams& map=biomeContext.Map;
|
||||
const UVoxelStrateDefinition* definition=owner.Manager->GetStrateForChunk(chunk);
|
||||
|
||||
VoxelForgeCore::WorldAssemblyManifest manifest;
|
||||
auto& recipe=manifest.recipe;
|
||||
@@ -7750,34 +7762,140 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
recipe.biomeWarpSubvoxels=WorldAssemblyFixed(map.WarpStrength);
|
||||
recipe.biomeWarpWavelengthSubvoxels=WorldAssemblyWavelength(map.WarpFrequency,556.0);
|
||||
recipe.biomeBorderBlendSubvoxels=WorldAssemblyFixed(map.BorderBlend);
|
||||
VoxelForgeCore::WorldAssemblyBiomeProfileV1 lowland;
|
||||
lowland.stableId=0x1001u;
|
||||
lowland.baseHeightSubvoxels=WorldAssemblyFixed(baseHeight-elevation*0.15);
|
||||
lowland.continentAmplitudeSubvoxels=WorldAssemblyFixed(elevation*0.45);
|
||||
lowland.continentWavelengthSubvoxels=WorldAssemblyWavelength(surface.ContinentFrequency,800.0);
|
||||
lowland.mountainAmplitudeSubvoxels=WorldAssemblyFixed(elevation*0.12*surface.MountainStrength);
|
||||
lowland.mountainWavelengthSubvoxels=WorldAssemblyWavelength(surface.MountainFrequency,192.0);
|
||||
lowland.detailAmplitudeSubvoxels=WorldAssemblyFixed(std::max(1.0,static_cast<double>(surface.SurfaceRoughness)));
|
||||
lowland.detailWavelengthSubvoxels=WorldAssemblyWavelength(surface.DetailFrequency,24.0);
|
||||
VoxelForgeCore::WorldAssemblyBiomeProfileV1 upland=lowland;
|
||||
upland.stableId=0x1002u;
|
||||
upland.baseHeightSubvoxels=WorldAssemblyFixed(baseHeight+elevation*0.20);
|
||||
upland.continentAmplitudeSubvoxels=WorldAssemblyFixed(elevation*0.80);
|
||||
upland.continentWavelengthSubvoxels=WorldAssemblyWavelength(surface.ContinentFrequency*0.72,1120.0);
|
||||
upland.mountainAmplitudeSubvoxels=WorldAssemblyFixed(elevation*0.42*std::max(0.25,static_cast<double>(surface.MountainStrength)));
|
||||
upland.mountainWavelengthSubvoxels=WorldAssemblyWavelength(surface.MountainFrequency*0.75,256.0);
|
||||
upland.detailAmplitudeSubvoxels=WorldAssemblyFixed(std::max(2.0,static_cast<double>(surface.SurfaceRoughness)*1.5));
|
||||
upland.detailWavelengthSubvoxels=WorldAssemblyWavelength(surface.DetailFrequency*0.8,30.0);
|
||||
recipe.biomes={lowland,upland};
|
||||
recipe.biomeClimateReliefWavelengthSubvoxels=WorldAssemblyWavelength(map.ReliefFrequency,3333.0);
|
||||
recipe.biomeClimateMoistureWavelengthSubvoxels=WorldAssemblyWavelength(map.MoistureFrequency,2500.0);
|
||||
recipe.biomeClimateReliefContrastQ16=WorldAssemblyQ16(map.ReliefContrast);
|
||||
const auto makeProfile=[&](uint32 stableId,const FSurfaceGenerationParams& params,
|
||||
const FBiomeResolved* climate)
|
||||
{
|
||||
VoxelForgeCore::WorldAssemblyBiomeProfileV1 profile;
|
||||
profile.stableId=stableId;
|
||||
profile.baseHeightSubvoxels=WorldAssemblyFixed(static_cast<double>(bottomVoxel)
|
||||
+static_cast<double>(heightVoxels)*params.BaseGroundRelative);
|
||||
profile.continentAmplitudeSubvoxels=WorldAssemblyFixed(params.ElevationRange);
|
||||
profile.continentWavelengthSubvoxels=WorldAssemblyWavelength(params.ContinentFrequency,1000.0);
|
||||
profile.mountainAmplitudeSubvoxels=WorldAssemblyFixed(params.ElevationRange*params.MountainStrength);
|
||||
profile.mountainWavelengthSubvoxels=WorldAssemblyWavelength(params.MountainFrequency,500.0);
|
||||
profile.detailAmplitudeSubvoxels=WorldAssemblyFixed(params.SurfaceRoughness);
|
||||
profile.detailWavelengthSubvoxels=WorldAssemblyWavelength(params.DetailFrequency,25.0);
|
||||
profile.reliefMinimumQ16=climate?WorldAssemblyQ16(climate->ReliefMin):0;
|
||||
profile.reliefMaximumQ16=climate?WorldAssemblyQ16(climate->ReliefMax):65536;
|
||||
profile.moistureMinimumQ16=climate?WorldAssemblyQ16(climate->MoistureMin):0;
|
||||
profile.moistureMaximumQ16=climate?WorldAssemblyQ16(climate->MoistureMax):65536;
|
||||
profile.reliefStrengthQ16=WorldAssemblyQ16(params.ReliefStrength);
|
||||
profile.reliefContrastQ16=WorldAssemblyQ16(params.ReliefContrast);
|
||||
profile.reliefWavelengthSubvoxels=WorldAssemblyWavelength(params.ReliefFrequency,667.0);
|
||||
profile.heightWarpStrengthSubvoxels=WorldAssemblyFixed(params.HeightWarpStrength);
|
||||
profile.heightWarpWavelengthSubvoxels=WorldAssemblyWavelength(params.HeightWarpFrequency,125.0);
|
||||
profile.beachWidthSubvoxels=WorldAssemblyFixed(params.BeachWidth);
|
||||
profile.cliffStrengthQ16=WorldAssemblyQ16(params.CliffStrength);
|
||||
profile.cliffSlopeThresholdQ16=std::max<std::int64_t>(1,WorldAssemblyQ16(params.CliffSlopeThreshold));
|
||||
profile.cliffSharpnessQ16=WorldAssemblyQ16(params.CliffSharpness);
|
||||
profile.cliffSampleDistanceSubvoxels=WorldAssemblyFixed(std::max(0.5f,params.CliffSampleDist));
|
||||
const double structuralAbsBound=static_cast<double>(params.ElevationRange)
|
||||
+static_cast<double>(params.ElevationRange)*params.MountainStrength
|
||||
+2.0*params.SurfaceRoughness;
|
||||
const double cliffAbsBound=2.0*structuralAbsBound*params.CliffStrength*params.CliffSharpness;
|
||||
profile.cliffMaximumDisplacementSubvoxels=WorldAssemblyFixed(params.CliffStrength>0.0f
|
||||
?std::max(1.0,cliffAbsBound):0.0);
|
||||
profile.hasCapProfile=true;
|
||||
profile.capHeightSubvoxels=WorldAssemblyFixed(static_cast<double>(bottomVoxel)
|
||||
+static_cast<double>(heightVoxels)*params.CeilingRelative);
|
||||
profile.capWarpStrengthSubvoxels=WorldAssemblyFixed(params.CeilingWarpStrength);
|
||||
profile.capWarpWavelengthSubvoxels=WorldAssemblyWavelength(params.CeilingWarpFrequency,100.0);
|
||||
profile.capUndulationSubvoxels=WorldAssemblyFixed(params.CeilingUndulation);
|
||||
profile.capWavelengthSubvoxels=WorldAssemblyWavelength(params.CeilingUndulationFrequency,500.0);
|
||||
profile.capDetailAmplitudeSubvoxels=WorldAssemblyFixed(params.CeilingRoughness);
|
||||
profile.capDetailWavelengthSubvoxels=WorldAssemblyWavelength(params.CeilingRoughnessFrequency,25.0);
|
||||
profile.capRidgeAmplitudeSubvoxels=WorldAssemblyFixed(params.CeilingRidgeStrength);
|
||||
profile.capRidgeWavelengthSubvoxels=WorldAssemblyWavelength(params.CeilingRidgeFrequency,50.0);
|
||||
return profile;
|
||||
};
|
||||
if(biomeContext.IsValid()&&definition)
|
||||
{
|
||||
for(const FBiomeResolved& resolved:biomeContext.Biomes)
|
||||
{
|
||||
const UVoxelBiomeDefinition* biome=definition->Biomes.IsValidIndex(resolved.Index)
|
||||
?definition->Biomes[resolved.Index]:nullptr;
|
||||
FSurfaceGenerationParams params=surface;
|
||||
if(biome&&biome->bOverrideTerrain&&biome->GeneratorType==ECaveGeneratorType::SurfaceWorld)
|
||||
{
|
||||
params=biome->SurfaceParams;
|
||||
params.StrateTopWorldZ=surface.StrateTopWorldZ;
|
||||
params.StrateBottomWorldZ=surface.StrateBottomWorldZ;
|
||||
params.BoundarySealThickness=surface.BoundarySealThickness;
|
||||
params.BaseDensity=surface.BaseDensity;
|
||||
params.WaterLevelRelative=surface.WaterLevelRelative;
|
||||
}
|
||||
recipe.biomes.push_back(makeProfile(0x1001u+static_cast<uint32>(resolved.Index),params,&resolved));
|
||||
}
|
||||
}
|
||||
if(recipe.biomes.empty())recipe.biomes.push_back(makeProfile(0x1001u,surface,nullptr));
|
||||
|
||||
VoxelForgeCore::WorldAssemblyStratumPlanV1 stratum;
|
||||
stratum.strateIndex=owner.Manager->GetStrateIndex(
|
||||
static_cast<float>((static_cast<double>(surfaceChunkZ*CHUNK_SIZE+CHUNK_SIZE/2))*VOXEL_SIZE));
|
||||
stratum.topChunkZ=topChunkZ;
|
||||
stratum.bottomChunkZ=bottomChunkZ;
|
||||
stratum.capHeightSubvoxels=WorldAssemblyFixed(static_cast<double>(topChunkZ+1)*CHUNK_SIZE-4.0);
|
||||
stratum.capUndulationSubvoxels=0;
|
||||
stratum.capWavelengthSubvoxels=WorldAssemblyFixed(512.0);
|
||||
const double strateBottom=static_cast<double>(bottomVoxel);
|
||||
const double strateHeight=static_cast<double>(heightVoxels);
|
||||
stratum.capHeightSubvoxels=WorldAssemblyFixed(strateBottom+strateHeight*surface.CeilingRelative);
|
||||
stratum.capUndulationSubvoxels=WorldAssemblyFixed(surface.CeilingUndulation);
|
||||
stratum.capWavelengthSubvoxels=WorldAssemblyWavelength(surface.CeilingUndulationFrequency,250.0);
|
||||
stratum.capDetailAmplitudeSubvoxels=WorldAssemblyFixed(surface.CeilingRoughness);
|
||||
stratum.capDetailWavelengthSubvoxels=WorldAssemblyWavelength(surface.CeilingRoughnessFrequency,25.0);
|
||||
stratum.capRidgeAmplitudeSubvoxels=WorldAssemblyFixed(surface.CeilingRidgeStrength);
|
||||
stratum.capRidgeWavelengthSubvoxels=WorldAssemblyWavelength(surface.CeilingRidgeFrequency,50.0);
|
||||
stratum.waterSurfaceSubvoxels=WorldAssemblyFixed(strateBottom+strateHeight*surface.WaterLevelRelative);
|
||||
stratum.waterEnabled=definition&&definition->bHasWater&&surface.WaterLevelRelative>0.0f;
|
||||
|
||||
// Select a deterministic steep edge in the resolved owner field for a bounded shelf.
|
||||
// Selection d'une pente deterministe dans le champ resolu pour une saillie bornee.
|
||||
constexpr int probeSide=49,probeStep=32;
|
||||
std::array<float,probeSide*probeSide> heights{};
|
||||
std::array<uint8,probeSide*probeSide> valid{};
|
||||
for(int y=0;y<probeSide;++y)
|
||||
for(int x=0;x<probeSide;++x)
|
||||
{
|
||||
const float px=static_cast<float>((x-probeSide/2)*probeStep);
|
||||
const float py=static_cast<float>((y-probeSide/2)*probeStep);
|
||||
float ceiling=0.0f;
|
||||
const std::size_t index=static_cast<std::size_t>(y)*probeSide+x;
|
||||
valid[index]=owner.Generator->GetSurfaceHeightAt(px,py,topChunkZ,heights[index],ceiling)?1u:0u;
|
||||
}
|
||||
double steepest=0.0;int cliffX=0,cliffY=0;bool xAxis=true;int downhill=1;
|
||||
for(int y=1;y<probeSide-1;++y)
|
||||
for(int x=1;x<probeSide-1;++x)
|
||||
{
|
||||
const std::size_t center=static_cast<std::size_t>(y)*probeSide+x;
|
||||
if(!valid[center]||!valid[center-1]||!valid[center+1]||!valid[center-probeSide]||!valid[center+probeSide])continue;
|
||||
const double dx=static_cast<double>(heights[center+1]-heights[center-1]);
|
||||
const double dy=static_cast<double>(heights[center+probeSide]-heights[center-probeSide]);
|
||||
const double slope=(std::abs(dx)+std::abs(dy))/(4.0*probeStep);
|
||||
if(slope>steepest){steepest=slope;cliffX=(x-probeSide/2)*probeStep;cliffY=(y-probeSide/2)*probeStep;
|
||||
xAxis=std::abs(dx)>=std::abs(dy);downhill=(xAxis?dx:dy)>=0.0?-1:1;}
|
||||
}
|
||||
if(steepest>0.05)
|
||||
{
|
||||
float ground=0.0f,ceiling=0.0f;
|
||||
if(owner.Generator->GetSurfaceHeightAt(static_cast<float>(cliffX),static_cast<float>(cliffY),
|
||||
topChunkZ,ground,ceiling))
|
||||
{
|
||||
VoxelForgeCore::WorldAssemblyFeatureV1 shelf;
|
||||
shelf.stableId=WorldAssemblyFeatureId(recipe.seed,cliffX,cliffY,0x4f564552u);
|
||||
shelf.type=2;
|
||||
const int reach=72,width=36;
|
||||
const int minX=xAxis?cliffX+(downhill<0?-reach:0)-4:cliffX-width;
|
||||
const int maxX=xAxis?cliffX+(downhill>0?reach:0)+4:cliffX+width;
|
||||
const int minY=xAxis?cliffY-width:cliffY+(downhill<0?-reach:0)-4;
|
||||
const int maxY=xAxis?cliffY+width:cliffY+(downhill>0?reach:0)+4;
|
||||
shelf.minimumSubvoxels={WorldAssemblyFixed(minX),WorldAssemblyFixed(minY),
|
||||
WorldAssemblyFixed(std::max<double>(bottomVoxel,ground-12.0))};
|
||||
shelf.maximumSubvoxels={WorldAssemblyFixed(maxX),WorldAssemblyFixed(maxY),
|
||||
WorldAssemblyFixed(std::min<double>((topChunkZ+1)*CHUNK_SIZE,ground+5.0))};
|
||||
stratum.features.push_back(shelf);
|
||||
}
|
||||
}
|
||||
manifest.plan.strates.push_back(std::move(stratum));
|
||||
if(!manifest.finalize(error))return false;
|
||||
const std::string manifestText=manifest.ToJson();
|
||||
@@ -7802,12 +7920,13 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
if(!rootObject)
|
||||
{error="owner snapshot root is not a JSON object";return false;}
|
||||
(*rootObject)["world_assembly"]=std::move(manifestJson);
|
||||
std::printf("[WorldAssemblerPrototype] recipe_version=%u evaluator_version=%u strata=%zu biomes=%zu strate_index=%d chunk_z=%d..%d serialized_bytes=%zu recipe_hash=%016llx plan_hash=%016llx manifest_hash=%016llx source=owner_surface_params_and_biome_map route_points=0 features=0\n",
|
||||
std::printf("[WorldAssemblerPrototype] recipe_version=%u evaluator_version=%u strata=%zu biomes=%zu strate_index=%d chunk_z=%d..%d serialized_bytes=%zu recipe_hash=%016llx plan_hash=%016llx manifest_hash=%016llx source=owner_surface_params_and_biome_map route_points=%zu features=%zu\n",
|
||||
manifest.recipe.schemaVersion,manifest.recipe.evaluatorVersion,manifest.plan.strates.size(),
|
||||
manifest.recipe.biomes.size(),manifest.plan.strates.front().strateIndex,topChunkZ,bottomChunkZ,
|
||||
manifestText.size(),static_cast<unsigned long long>(manifest.recipe.contentHash),
|
||||
static_cast<unsigned long long>(manifest.plan.contentHash),
|
||||
static_cast<unsigned long long>(manifest.contentHash));
|
||||
static_cast<unsigned long long>(manifest.contentHash),manifest.plan.strates.front().route.size(),
|
||||
manifest.plan.strates.front().features.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -7831,11 +7950,17 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
static_cast<long long>(influence.maximumZSubvoxels),
|
||||
static_cast<long long>(detail.maximumAbsoluteDisplacementSubvoxels),
|
||||
static_cast<long long>(detail.minimumFeatureSizeSubvoxels),bounds.passed?"PASS":"FAIL");
|
||||
std::printf("[WorldAssemblerBoundAudit] boxes=%llu point_samples=%llu missed_bounds=%llu unknown_bounds=%llu deliberately_wrong_bound_detected=%s status=%s\n",
|
||||
std::printf("[WorldAssemblerBoundAudit] boxes=%llu point_samples=%llu missed_bounds=%llu unknown_bounds=%llu border_boxes=%llu cliff_boxes=%llu cap_boxes=%llu negative_coordinate_boxes=%llu exception_boxes=%llu exception_point_samples=%llu deliberately_wrong_bound_detected=%s status=%s\n",
|
||||
static_cast<unsigned long long>(bounds.boxesTested),
|
||||
static_cast<unsigned long long>(bounds.pointSamples),
|
||||
static_cast<unsigned long long>(bounds.missedBounds),
|
||||
static_cast<unsigned long long>(bounds.unknownBounds),
|
||||
static_cast<unsigned long long>(bounds.biomeBorderBoxes),
|
||||
static_cast<unsigned long long>(bounds.cliffBoxes),
|
||||
static_cast<unsigned long long>(bounds.capBoxes),
|
||||
static_cast<unsigned long long>(bounds.negativeCoordinateBoxes),
|
||||
static_cast<unsigned long long>(bounds.exceptionBoxes),
|
||||
static_cast<unsigned long long>(bounds.exceptionPointSamples),
|
||||
bounds.negativeControlDetected?"true":"false",bounds.passed?"PASS":"FAIL");
|
||||
|
||||
struct Query {std::int64_t x=0,y=0,z=0;};
|
||||
@@ -7889,6 +8014,8 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
add(static_cast<std::uint64_t>(sample.capHeightSubvoxels),8);
|
||||
add(sample.biomeId,4);add(sample.neighborBiomeId,4);add(sample.biomeWeightQ16,4);
|
||||
add(static_cast<std::uint64_t>(sample.densitySubvoxels),8);
|
||||
add(static_cast<std::uint64_t>(sample.waterSurfaceSubvoxels),8);
|
||||
add(static_cast<std::uint64_t>(sample.waterFillSubvoxels),8);
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
@@ -7908,6 +8035,139 @@ float3 Lit=Rock*(Ambient+0.88*Diffuse)+float3(0.50,0.69,0.82)*Head*0.75;
|
||||
std::printf("[WorldAssemblerQueryTiming] samples=%zu native_W1_ms=%.3f native_W8_ms=%.3f native_W16_ms=%.3f scope=point_evaluation_only\n",
|
||||
queries.size(),time1,time8,time16);
|
||||
|
||||
double ownerHeightSum=0.0,nativeHeightSum=0.0;
|
||||
double ownerHeightSquareSum=0.0,nativeHeightSquareSum=0.0,centeredCrossSum=0.0;
|
||||
double heightAbsoluteSum=0.0,heightMaximumError=0.0;
|
||||
std::vector<double> heightDeltas;
|
||||
heightDeltas.reserve(4096);
|
||||
std::uint64_t ownerBiomeSamples=0,ownerBiomeIdMismatches=0;
|
||||
const FBiomeContext ownerBiomeContext=owner.Manager->GetBiomeContextForChunk(
|
||||
FIntVector(0,0,plan.topChunkZ));
|
||||
std::uint64_t heightSamples=0;
|
||||
constexpr int heightProbeSide=64;
|
||||
constexpr double heightProbeSpan=3200.0;
|
||||
for(int y=0;y<heightProbeSide;++y)
|
||||
for(int x=0;x<heightProbeSide;++x)
|
||||
{
|
||||
const double xVoxel=(static_cast<double>(x)/(heightProbeSide-1)-0.5)*heightProbeSpan;
|
||||
const double yVoxel=(static_cast<double>(y)/(heightProbeSide-1)-0.5)*heightProbeSpan;
|
||||
float ownerGround=0.0f,ownerCap=0.0f;
|
||||
if(!owner.Generator->GetSurfaceHeightAt(static_cast<float>(xVoxel),
|
||||
static_cast<float>(yVoxel),plan.topChunkZ,ownerGround,ownerCap))continue;
|
||||
const auto native=block.point(
|
||||
static_cast<std::int64_t>(std::llround(xVoxel*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel)),
|
||||
static_cast<std::int64_t>(std::llround(yVoxel*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel)),0);
|
||||
const double nativeGround=static_cast<double>(native.groundHeightSubvoxels)
|
||||
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
|
||||
const double ownerGroundValue=ownerGround;
|
||||
ownerHeightSum+=ownerGroundValue;nativeHeightSum+=nativeGround;
|
||||
ownerHeightSquareSum+=ownerGroundValue*ownerGroundValue;
|
||||
nativeHeightSquareSum+=nativeGround*nativeGround;
|
||||
centeredCrossSum+=ownerGroundValue*nativeGround;
|
||||
const double difference=std::abs(nativeGround-ownerGroundValue);
|
||||
heightAbsoluteSum+=difference;heightMaximumError=std::max(heightMaximumError,difference);
|
||||
heightDeltas.push_back(nativeGround-ownerGroundValue);
|
||||
FVoxelBiomeQuery ownerBiome;
|
||||
owner.Generator->QueryBiomeAt(static_cast<float>(xVoxel),static_cast<float>(yVoxel),
|
||||
plan.topChunkZ,ownerBiome);
|
||||
if(ownerBiome.bHasBiomes&&ownerBiomeContext.Biomes.IsValidIndex(ownerBiome.DominantContextIndex))
|
||||
{
|
||||
const std::uint32_t ownerBiomeId=0x1001u+static_cast<std::uint32_t>(
|
||||
ownerBiomeContext.Biomes[ownerBiome.DominantContextIndex].Index);
|
||||
++ownerBiomeSamples;
|
||||
if(native.biomeId!=ownerBiomeId)++ownerBiomeIdMismatches;
|
||||
}
|
||||
++heightSamples;
|
||||
}
|
||||
if(heightSamples>0)
|
||||
{
|
||||
const double ownerMean=ownerHeightSum/heightSamples;
|
||||
const double nativeMean=nativeHeightSum/heightSamples;
|
||||
const double ownerVariance=std::max(0.0,ownerHeightSquareSum/heightSamples-ownerMean*ownerMean);
|
||||
const double nativeVariance=std::max(0.0,nativeHeightSquareSum/heightSamples-nativeMean*nativeMean);
|
||||
const double covariance=centeredCrossSum/heightSamples-ownerMean*nativeMean;
|
||||
const double slope=ownerVariance>0.0?covariance/ownerVariance:0.0;
|
||||
const double intercept=nativeMean-slope*ownerMean;
|
||||
const double correlation=(ownerVariance>0.0&&nativeVariance>0.0)
|
||||
?covariance/std::sqrt(ownerVariance*nativeVariance):0.0;
|
||||
double offsetCorrectedAbsoluteSum=0.0;
|
||||
for(const double delta:heightDeltas)offsetCorrectedAbsoluteSum+=std::abs(delta-(nativeMean-ownerMean));
|
||||
std::printf("[WorldAssemblerSurfaceShapeDiagnostic] samples=%llu owner_voxel_cm=%.1f core_voxel_cm=%.1f mean_owner_height=%.3f mean_native_height=%.3f mean_offset=%.3f mae=%.3f mae_after_mean_offset_removal=%.3f max_error=%.3f regression_native_equals_slope_owner_plus_offset_slope=%.5f offset=%.3f correlation=%.5f owner_biome_samples=%llu owner_biome_id_mismatches=%llu\n",
|
||||
static_cast<unsigned long long>(heightSamples),VOXEL_SIZE,25.0,
|
||||
ownerMean,nativeMean,nativeMean-ownerMean,heightAbsoluteSum/heightSamples,
|
||||
heightDeltas.empty()?0.0:offsetCorrectedAbsoluteSum/heightDeltas.size(),
|
||||
heightMaximumError,slope,intercept,correlation,
|
||||
static_cast<unsigned long long>(ownerBiomeSamples),
|
||||
static_cast<unsigned long long>(ownerBiomeIdMismatches));
|
||||
}
|
||||
|
||||
struct FWorldAssemblerCaptureSite
|
||||
{double X=0.0,Y=0.0,OwnerGround=0.0,NativeGround=0.0,Score=0.0;bool Found=false;};
|
||||
FWorldAssemblerCaptureSite borderSite,coastSite;
|
||||
const auto waterVoxel=static_cast<double>(plan.waterSurfaceSubvoxels)
|
||||
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel;
|
||||
for(int y=-1600;y<=1600;y+=64)
|
||||
for(int x=-1600;x<=1600;x+=64)
|
||||
{
|
||||
float ownerGround=0.0f,ownerCap=0.0f;
|
||||
if(!owner.Generator->GetSurfaceHeightAt(static_cast<float>(x),static_cast<float>(y),
|
||||
plan.topChunkZ,ownerGround,ownerCap))continue;
|
||||
FVoxelBiomeQuery ownerBiome;
|
||||
owner.Generator->QueryBiomeAt(static_cast<float>(x),static_cast<float>(y),
|
||||
plan.topChunkZ,ownerBiome);
|
||||
const auto native=block.point(static_cast<std::int64_t>(x)
|
||||
*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,static_cast<std::int64_t>(y)
|
||||
*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,0);
|
||||
if(ownerBiome.bHasBiomes&&ownerBiome.NeighborWeight>borderSite.Score)
|
||||
borderSite={static_cast<double>(x),static_cast<double>(y),ownerGround,
|
||||
static_cast<double>(native.groundHeightSubvoxels)
|
||||
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,
|
||||
ownerBiome.NeighborWeight,true};
|
||||
const double waterDistance=std::abs(static_cast<double>(ownerGround)-waterVoxel);
|
||||
if(!coastSite.Found||waterDistance<coastSite.Score)
|
||||
coastSite={static_cast<double>(x),static_cast<double>(y),ownerGround,
|
||||
static_cast<double>(native.groundHeightSubvoxels)
|
||||
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,
|
||||
waterDistance,true};
|
||||
}
|
||||
const auto printCaptureSite=[&](const char* name,const FWorldAssemblerCaptureSite& site)
|
||||
{
|
||||
if(!site.Found)return;
|
||||
const double cameraX=site.X/4.0,cameraY=(site.Y-140.0)/4.0;
|
||||
const double cameraZ=(site.OwnerGround+64.0)/4.0;
|
||||
std::printf("[WorldAssemblerCaptureSite] name=%s target_vox=(%.0f,%.0f,%.2f) owner_ground_vox=%.2f native_ground_vox=%.2f score=%.5f camera_m=(%.3f,%.3f,%.3f,0,-22)\n",
|
||||
name,site.X,site.Y,site.OwnerGround,site.OwnerGround,site.NativeGround,site.Score,
|
||||
cameraX,cameraY,cameraZ);
|
||||
};
|
||||
const auto originNativeColumn=block.point(0,0,0);
|
||||
float originOwnerGround=0.0f,originOwnerCap=0.0f;
|
||||
if(owner.Generator->GetSurfaceHeightAt(0.0f,0.0f,plan.topChunkZ,
|
||||
originOwnerGround,originOwnerCap))
|
||||
printCaptureSite("surface",{0.0,0.0,originOwnerGround,
|
||||
static_cast<double>(originNativeColumn.groundHeightSubvoxels)
|
||||
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,0.0,true});
|
||||
printCaptureSite("biome_border",borderSite);
|
||||
printCaptureSite("coast",coastSite);
|
||||
if(!plan.features.empty()&&plan.features.front().type==2)
|
||||
{
|
||||
const auto& feature=plan.features.front();
|
||||
const double fx=(feature.minimumSubvoxels[0]+feature.maximumSubvoxels[0])
|
||||
/(2.0*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
const double fy=(feature.minimumSubvoxels[1]+feature.maximumSubvoxels[1])
|
||||
/(2.0*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel);
|
||||
float ownerGround=0.0f,ownerCap=0.0f;
|
||||
if(owner.Generator->GetSurfaceHeightAt(static_cast<float>(fx),static_cast<float>(fy),
|
||||
plan.topChunkZ,ownerGround,ownerCap))
|
||||
{
|
||||
const auto native=block.point(static_cast<std::int64_t>(std::llround(fx
|
||||
*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel)),static_cast<std::int64_t>(std::llround(fy
|
||||
*VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel)),0);
|
||||
printCaptureSite("cliff",{fx,fy,ownerGround,
|
||||
static_cast<double>(native.groundHeightSubvoxels)
|
||||
/VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel,0.0,true});
|
||||
}
|
||||
}
|
||||
|
||||
double absoluteSum=0.0,maximumError=0.0;
|
||||
std::uint64_t signMismatches=0;
|
||||
const auto legacyStart=std::chrono::steady_clock::now();
|
||||
@@ -8520,6 +8780,11 @@ int main(int Argc,char** Argv)
|
||||
{
|
||||
const auto WindowShowStart=std::chrono::steady_clock::now();
|
||||
ShowWindow(App.Window,SW_SHOW);
|
||||
if(!App.CapturePath.empty())
|
||||
{
|
||||
BringWindowToTop(App.Window);
|
||||
SetForegroundWindow(App.Window);
|
||||
}
|
||||
std::printf("[ColdTimeline] stage=viewer_window_show duration_ms=%.3f\n",
|
||||
std::chrono::duration<double,std::milli>(
|
||||
std::chrono::steady_clock::now()-WindowShowStart).count());
|
||||
|
||||
Reference in new issue
Block a user