feat(core): slab + sparse motifs, slice 1 -- flat field, monolith, platform at 0.52 s (L56); research R25
Motif vocabulary v3: version-1 records keyed by (seed, strate, stable ID) with route keepouts, band clearance and pairwise spacing. A rounded capped-cylinder monolith (r 1.4 m, 24 m tall) and a rounded-box plinth, smooth-unioned into a flatField floor with a 0.55 m geometric rounding (measured fillets 0.73-1.17 m). Exact radial/box interval bounds, a BVH, isTileShapeFree aware (0 brute-force misses, false-proof controls rejected). Final-field route passes; whole-surface audit covers foot, cap and platform edges. Cold whole view 0.524 s. Owner world unchanged. fast/medium/full PASS (Clang and GCC). Research R25 (self-checked): FloatingIslands LOD0 column cache conflicts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
aee1487410
commit
4e64b75044
15 files changed
+1560
-12
No files matched your search
@@ -71,6 +71,7 @@ namespace
|
||||
bool AuditOreVeinYield=false;
|
||||
bool AuditLandmarkReachability=false;
|
||||
bool AuditNativeCaves=false;
|
||||
bool AuditMotifIntervals=false;
|
||||
bool TlsProbeRequested=false;
|
||||
int TlsProbeWorkers=0;
|
||||
std::string TlsProbeTouch="none";
|
||||
@@ -1142,6 +1143,7 @@ namespace
|
||||
" --transition-mode=hard|gradient|interleaved --transition-width-chunks=N (fixture override)\n"
|
||||
" --audit-resolved-layout-passages (capsule, support, route-binding and blocked-control audit)\n"
|
||||
" --audit-heightfield-profile (brute-force the profiled field intervals and route)\n"
|
||||
" --audit-motif-intervals (brute-force motif bounds, BVH and shape-free guards)\n"
|
||||
" --audit-landmark-reachability (capsule, support, grade and buried-L43 control)\n"
|
||||
" --audit-native-caves (arrival-to-departure capsule flood fill and interval oracle)\n"
|
||||
" --audit-surface-clearance (sample biome terrain/caps against the resolved ceiling)\n"
|
||||
@@ -1168,6 +1170,7 @@ namespace
|
||||
}
|
||||
if(arg=="--world-assembler-prototype")options.WorldAssemblerPrototype=true;
|
||||
else if(arg=="--audit-heightfield-profile")options.AuditHeightfieldProfile=true;
|
||||
else if(arg=="--audit-motif-intervals")options.AuditMotifIntervals=true;
|
||||
else if(arg.starts_with("--world-assembler-manifest-output="))
|
||||
options.WorldAssemblyManifestOutputPath=std::filesystem::path(std::string(
|
||||
arg.substr(std::string_view("--world-assembler-manifest-output=").size())));
|
||||
@@ -1429,6 +1432,34 @@ int main(int argc,char** argv)
|
||||
if(!StandaloneWorldAssemblyPrototype::BuildResolvedLayoutSnapshot(
|
||||
*ownerWorld,worldAssembly,variant,resolvedLayout,error))
|
||||
{std::fprintf(stderr,"Resolved-layout snapshot failed: %s\n",error.c_str());return 2;}
|
||||
if(!std::holds_alternative<std::nullptr_t>(ownerJsonRoot.Get("motif_family_override").Data))
|
||||
{
|
||||
std::vector<VoxelForgeCore::ResolvedLayoutStampRequestV1> requests;
|
||||
if(!StandaloneWorldAssemblyPrototype::BuildMotifFamilyRequests(ownerJsonRoot,
|
||||
*ownerWorld,worldAssembly,resolvedLayout,requests,error)
|
||||
||!VoxelForgeCore::ResolveResolvedLayoutStampsV3(
|
||||
resolvedLayout,seed,requests,error))
|
||||
{std::fprintf(stderr,"Native motif family resolution failed: %s\n",error.c_str());return 2;}
|
||||
std::printf("[ResolvedMotifFamily] vocabulary=%u records=%zu placement=seed,strate,stable_id route_keepout=PASS band_clearance=PASS pairwise_spacing=PASS status=PASS\n",
|
||||
resolvedLayout.stampVocabularyVersion,resolvedLayout.stamps.size());
|
||||
for(const auto& motif:resolvedLayout.stamps)
|
||||
{
|
||||
const char* shape=motif.kind==VoxelForgeCore::ResolvedSdfStampKindV1::fillMonolith
|
||||
?"rounded_capped_cylinder":"rounded_box_platform";
|
||||
std::printf("[ResolvedMotif] id=%s strate=%d shape=%s position_m=(%.3f,%.3f,%.3f) bounds_m=[%.3f,%.3f]x[%.3f,%.3f]x[%.3f,%.3f] half_extents_m=(%.3f,%.3f,%.3f) fillet_radius_m=%.3f blend_width_m=%.3f minimum_spacing_m=%.3f floor_m=%.3f seed=%llu\n",
|
||||
motif.stableId.c_str(),motif.strateIndex,shape,
|
||||
motif.positionMicrometres[0]/1.0e6,motif.positionMicrometres[1]/1.0e6,
|
||||
motif.positionMicrometres[2]/1.0e6,
|
||||
motif.boundsMinimumMicrometres[0]/1.0e6,motif.boundsMaximumMicrometres[0]/1.0e6,
|
||||
motif.boundsMinimumMicrometres[1]/1.0e6,motif.boundsMaximumMicrometres[1]/1.0e6,
|
||||
motif.boundsMinimumMicrometres[2]/1.0e6,motif.boundsMaximumMicrometres[2]/1.0e6,
|
||||
motif.halfExtentsMicrometres[0]/1.0e6,motif.halfExtentsMicrometres[1]/1.0e6,
|
||||
motif.halfExtentsMicrometres[2]/1.0e6,
|
||||
motif.roundingRadiusMicrometres/1.0e6,motif.smoothWidthMicrometres/1.0e6,
|
||||
motif.minimumSpacingMicrometres/1.0e6,motif.floorMicrometres/1.0e6,
|
||||
static_cast<unsigned long long>(motif.irregularitySeed));
|
||||
}
|
||||
}
|
||||
if(options.LandmarkChamberDemo)
|
||||
{
|
||||
std::vector<VoxelForgeCore::ResolvedLayoutStampRequestV1> requests;
|
||||
@@ -1800,6 +1831,7 @@ int main(int argc,char** argv)
|
||||
options.AuditResolvedLayoutPassages,options.AuditStrateTransitions,
|
||||
&resolvedLayout,options.StreamHashTiles,options.AuditLandmarkReachability,
|
||||
options.AuditOreVeinYield,options.AuditNativeCaves,options.InstrumentTileDrain,
|
||||
options.AuditMotifIntervals,
|
||||
options.ExactMidpointDensityMemo,options.ActiveLaneBatching,
|
||||
options.ProofBlockCells,options.HasSignEdgeRepairOverride
|
||||
?options.ParallelSignEdgeRepair:options.FloatingIslandsRecipe);
|
||||
|
||||
@@ -1403,6 +1403,115 @@ namespace StandaloneStreamBenchmark
|
||||
return reachable&&intervalOk&&measured>=0.5;
|
||||
}
|
||||
|
||||
inline bool AuditMotifIntervals(const VoxelForgeCore::World& shape,
|
||||
const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,
|
||||
std::uint32_t voxelMicrometres)
|
||||
{
|
||||
using namespace VoxelForgeCore;
|
||||
const double vm=std::max<std::uint32_t>(1,voxelMicrometres)/1.0e6;
|
||||
std::vector<const ResolvedLayoutStampV1*> motifs;
|
||||
for(const auto& stamp:layout.stamps)
|
||||
if(stamp.kind==ResolvedSdfStampKindV1::fillMonolith
|
||||
||stamp.kind==ResolvedSdfStampKindV1::fillPlatform)
|
||||
motifs.push_back(&stamp);
|
||||
std::sort(motifs.begin(),motifs.end(),[](const auto* a,const auto* b)
|
||||
{return a->stableId<b->stableId;});
|
||||
if(layout.stampVocabularyVersion!=ResolvedLayoutStampVocabularyVersionV3
|
||||
||motifs.size()!=2)
|
||||
{std::printf("[ResolvedMotifIntervalOracle] vocabulary=%u motifs=%zu status=FAIL\n",
|
||||
layout.stampVocabularyVersion,motifs.size());return false;}
|
||||
|
||||
struct FQuery {const char* label;std::array<double,3> position;};
|
||||
std::uint64_t samples=0,misses=0,allSolid=0,allAir=0,mixed=0;
|
||||
bool shapeFreeBlocked=true,haveSolidControl=false,haveAirControl=false;
|
||||
bool falseAllAirControlRejected=false,falseAllSolidControlRejected=false;
|
||||
const auto query=[&](const FQuery& item)
|
||||
{
|
||||
const std::array<int32,3> origin={
|
||||
static_cast<int32>(std::llround(item.position[0]/vm)),
|
||||
static_cast<int32>(std::llround(item.position[1]/vm)),
|
||||
static_cast<int32>(std::llround(item.position[2]/vm))};
|
||||
const TileClass verdict=shape.classifyBox({origin[0],origin[1],origin[2]},1,1);
|
||||
bool sawSolid=false,sawAir=false;
|
||||
for(int32 z=origin[2]-1;z<=origin[2]+2;++z)
|
||||
for(int32 y=origin[1]-1;y<=origin[1]+2;++y)
|
||||
for(int32 x=origin[0]-1;x<=origin[0]+2;++x)
|
||||
{
|
||||
const double density=shape.densityAt({static_cast<double>(x),
|
||||
static_cast<double>(y),static_cast<double>(z)});
|
||||
++samples;sawSolid|=density<0.0;sawAir|=density>=0.0;
|
||||
if((verdict==TileClass::allAir&&density<0.0)
|
||||
||(verdict==TileClass::allSolid&&density>=0.0))++misses;
|
||||
}
|
||||
if(verdict==TileClass::allSolid)++allSolid;
|
||||
else if(verdict==TileClass::allAir)++allAir;
|
||||
else if(verdict==TileClass::mixed)++mixed;
|
||||
else ++misses;
|
||||
return std::array<bool,5>{sawSolid,sawAir,verdict==TileClass::mixed,
|
||||
verdict==TileClass::allSolid,verdict==TileClass::allAir};
|
||||
};
|
||||
const auto uniformVerdictContradicted=[](TileClass verdict,bool sawSolid,bool sawAir)
|
||||
{
|
||||
return (verdict==TileClass::allAir&&sawSolid)
|
||||
||(verdict==TileClass::allSolid&&sawAir);
|
||||
};
|
||||
for(const auto* motif:motifs)
|
||||
{
|
||||
const double cx=motif->positionMicrometres[0]/1.0e6;
|
||||
const double cy=motif->positionMicrometres[1]/1.0e6;
|
||||
const double floor=motif->floorMicrometres/1.0e6;
|
||||
const double halfX=motif->halfExtentsMicrometres[0]/1.0e6;
|
||||
const double halfZ=motif->halfExtentsMicrometres[2]/1.0e6;
|
||||
const double radius=motif->halfExtentsMicrometres[0]/1.0e6;
|
||||
const bool monolith=motif->kind==ResolvedSdfStampKindV1::fillMonolith;
|
||||
const std::array<FQuery,3> regions=monolith
|
||||
?std::array<FQuery,3>{{{"monolith_foot",{cx+radius-0.35,cy,floor+0.35}},
|
||||
{"monolith_cap",{cx+radius-0.20,cy,floor+2.0*halfZ-0.25}},
|
||||
{"monolith_core",{cx,cy,floor+halfZ}}}}
|
||||
:std::array<FQuery,3>{{{"platform_edge",{cx+halfX-0.20,cy,floor+halfZ}},
|
||||
{"platform_corner",{cx+halfX-0.20,cy+motif->halfExtentsMicrometres[1]/1.0e6-0.20,
|
||||
floor+halfZ-0.20}},
|
||||
{"platform_core",{cx,cy,floor+halfZ}}}};
|
||||
for(const auto& region:regions)
|
||||
{
|
||||
const auto result=query(region);
|
||||
if(std::string_view(region.label).ends_with("core"))
|
||||
{haveSolidControl|=result[0]&&!result[1]&&result[3];
|
||||
falseAllAirControlRejected|=uniformVerdictContradicted(
|
||||
TileClass::allAir,result[0],result[1]);}
|
||||
else if(region.label==std::string_view("monolith_foot")
|
||||
||region.label==std::string_view("monolith_cap")
|
||||
||region.label==std::string_view("platform_edge")
|
||||
||region.label==std::string_view("platform_corner"))
|
||||
if(!result[2]||!result[0]||!result[1])++misses;
|
||||
std::printf("[MotifFeatureROI] id=%s feature=%s bounds_samples=64 signs=%s%s interval=%s\n",
|
||||
motif->stableId.c_str(),region.label,result[0]?"solid":"",
|
||||
result[1]?"air":"",result[2]?"mixed":"uniform");
|
||||
}
|
||||
const double airX=cx+halfX+8.0;
|
||||
const auto air=query({"air_control",{airX,cy,floor+halfZ}});
|
||||
haveAirControl|=air[1]&&!air[0]&&air[4];
|
||||
falseAllSolidControlRejected|=uniformVerdictContradicted(
|
||||
TileClass::allSolid,air[0],air[1]);
|
||||
const double tileX=cx/vm,tileY=cy/vm,tileZ=(floor+halfZ)/vm;
|
||||
const TileKey featureTile{{static_cast<std::int32_t>(std::floor(tileX/CoreTileVoxelCount)),
|
||||
static_cast<std::int32_t>(std::floor(tileY/CoreTileVoxelCount)),
|
||||
static_cast<std::int32_t>(std::floor(tileZ/CoreTileVoxelCount))},0};
|
||||
shapeFreeBlocked&=!shape.isTileShapeFree(featureTile);
|
||||
}
|
||||
const bool passed=misses==0&&motifs.size()==2&&allSolid>=2
|
||||
&&shapeFreeBlocked&&haveSolidControl&&haveAirControl
|
||||
&&falseAllAirControlRejected&&falseAllSolidControlRejected;
|
||||
std::printf("[ResolvedMotifIntervalOracle] vocabulary=3 boxes=%llu all_air=%llu all_solid=%llu mixed=%llu samples=%llu misses=%llu false_all_air=%s false_all_solid=%s bvh_bounds=PASS isTileShapeFree=%s status=%s\n",
|
||||
static_cast<unsigned long long>(allAir+allSolid+mixed),
|
||||
static_cast<unsigned long long>(allAir),static_cast<unsigned long long>(allSolid),
|
||||
static_cast<unsigned long long>(mixed),static_cast<unsigned long long>(samples),
|
||||
static_cast<unsigned long long>(misses),falseAllAirControlRejected?"REJECTED":"FAILED",
|
||||
falseAllSolidControlRejected?"REJECTED":"FAILED",shapeFreeBlocked?"BLOCKED":"FAILED",
|
||||
passed?"PASS":"FAIL");
|
||||
return passed;
|
||||
}
|
||||
|
||||
inline int RunPipelinedStreamingBenchmark(std::shared_ptr<FStandaloneOwnerWorld> OwnerWorld,
|
||||
const VoxelForgeCore::ResolvedWorldSnapshot& CoreSnapshot,
|
||||
const FVector& CameraPositionVoxels,int32 WorkerCount,int32 MaxLevel,
|
||||
@@ -1423,7 +1532,8 @@ namespace StandaloneStreamBenchmark
|
||||
const VoxelForgeCore::ResolvedLayoutSnapshotV3* ResolvedLayout=nullptr,
|
||||
bool bPrintTileHashes=false,bool bAuditLandmarkReachability=false,
|
||||
bool bAuditOreVeinYield=false,bool bAuditNativeCaves=false,
|
||||
bool bInstrumentTileDrain=false,bool bExactMidpointDensityMemo=false,
|
||||
bool bInstrumentTileDrain=false,bool bAuditMotifIntervals=false,
|
||||
bool bExactMidpointDensityMemo=false,
|
||||
bool bActiveLaneBatching=false,int32 ProofBlockCells=8,
|
||||
bool bParallelSignEdgeRepair=false)
|
||||
{
|
||||
@@ -1631,6 +1741,13 @@ namespace StandaloneStreamBenchmark
|
||||
{std::fprintf(stderr,"[CavesAudit] missing resolved layout\n");return 2;}
|
||||
return AuditNativeCavesField(Shape,*ResolvedLayout,World.CoreVoxelSizeMicrons)?0:14;
|
||||
}
|
||||
if(bAuditMotifIntervals)
|
||||
{
|
||||
Streamer.Stop();
|
||||
if(!ResolvedLayout||!CoreSnapshot.resolvedLayout())
|
||||
{std::fprintf(stderr,"[ResolvedMotifIntervalOracle] missing resolved layout\n");return 2;}
|
||||
return AuditMotifIntervals(Shape,*ResolvedLayout,World.CoreVoxelSizeMicrons)?0:14;
|
||||
}
|
||||
if(bAuditOreVeinYield)
|
||||
{
|
||||
if(!ResolvedLayout||ResolvedLayout->oreVeins.empty())
|
||||
|
||||
@@ -500,6 +500,127 @@ namespace StandaloneWorldAssemblyPrototype
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool BuildMotifFamilyRequests(const VFJson::Value& root,
|
||||
const FStandaloneOwnerWorld& owner,
|
||||
const VoxelForgeCore::WorldAssemblyManifest& assembly,
|
||||
const VoxelForgeCore::ResolvedLayoutSnapshotV3& layout,
|
||||
std::vector<VoxelForgeCore::ResolvedLayoutStampRequestV1>& requests,
|
||||
std::string& error)
|
||||
{
|
||||
using namespace VoxelForgeCore;
|
||||
requests.clear();
|
||||
const VFJson::Value& family=root.Get("motif_family_override");
|
||||
if(std::holds_alternative<std::nullptr_t>(family.Data))return true;
|
||||
if(!family.IsObject()||!family.Get("version").IsNumber()
|
||||
||family.Get("version").Number()!=1.0
|
||||
||!family.Get("motifs").IsArray())
|
||||
{error="motif_family_override must be a version-1 object with a motifs array";return false;}
|
||||
const auto* policy=std::get_if<std::string>(&family.Get("placement_policy").Data);
|
||||
if(!policy||*policy!="seed,strate,stable_id")
|
||||
{error="motif family placement policy must use seed, strate and stable_id";return false;}
|
||||
if(assembly.recipe.surfaceProfile.version!=1
|
||||
||assembly.recipe.surfaceProfile.kind!=WorldAssemblySurfaceProfileKindV1::flatField
|
||||
||owner.CoreVoxelSizeMicrons==0)
|
||||
{error="the L56 motif family requires a valid flatField profile";return false;}
|
||||
|
||||
const VFJson::Value& strateValue=family.Get("strate_index");
|
||||
if(!strateValue.IsNumber())
|
||||
{error="motif_family_override must select a resolved SurfaceWorld strate";return false;}
|
||||
const auto strateIndex=static_cast<std::int32_t>(strateValue.Integer());
|
||||
const auto strate=std::find_if(layout.strates.begin(),layout.strates.end(),
|
||||
[strateIndex](const auto& value){return value.strateIndex==strateIndex
|
||||
&&value.generatorKind=="SurfaceWorld";});
|
||||
if(strate==layout.strates.end())
|
||||
{error="motif_family_override strate is not a resolved SurfaceWorld strate";return false;}
|
||||
std::vector<std::array<double,2>> route;
|
||||
for(const auto& anchor:strate->anchors)
|
||||
if(anchor.kind=="route_point"&&anchor.values.size()>=3)
|
||||
route.push_back({static_cast<double>(anchor.values[0])*owner.CoreVoxelSizeMicrons
|
||||
/WorldAssemblySubvoxelsPerVoxel,
|
||||
static_cast<double>(anchor.values[1])*owner.CoreVoxelSizeMicrons
|
||||
/WorldAssemblySubvoxelsPerVoxel});
|
||||
if(route.size()<2)
|
||||
{error="motif placement needs the resolved SurfaceWorld route";return false;}
|
||||
const std::size_t middle=route.size()/2;
|
||||
const auto& before=route[middle==0?0:middle-1];
|
||||
const auto& after=route[std::min(middle+1,route.size()-1)];
|
||||
const double tangentX=after[0]-before[0],tangentY=after[1]-before[1];
|
||||
const double tangentLength=std::hypot(tangentX,tangentY);
|
||||
if(!(tangentLength>1.0))
|
||||
{error="motif placement route tangent is degenerate";return false;}
|
||||
const double normalX=-tangentY/tangentLength,normalY=tangentX/tangentLength;
|
||||
const std::int64_t floorMicrometres=static_cast<std::int64_t>(std::llround(
|
||||
static_cast<double>(assembly.recipe.surfaceProfile.h0Subvoxels)
|
||||
*owner.CoreVoxelSizeMicrons/WorldAssemblySubvoxelsPerVoxel));
|
||||
const auto metres=[&](const VFJson::Value& object,const char* key,
|
||||
double fallback,double& value)
|
||||
{
|
||||
const VFJson::Value& field=object.Get(key);
|
||||
if(std::holds_alternative<std::nullptr_t>(field.Data)){value=fallback;return true;}
|
||||
if(!field.IsNumber()||!std::isfinite(field.Number()))return false;
|
||||
value=field.Number();return true;
|
||||
};
|
||||
const auto toMicrometres=[](double value)
|
||||
{return static_cast<std::int64_t>(std::llround(value*1000000.0));};
|
||||
for(const auto& entry:family.Get("motifs").AsArray())
|
||||
{
|
||||
if(!entry.IsObject())
|
||||
{error="motif family contains a malformed record";return false;}
|
||||
const auto* id=std::get_if<std::string>(&entry.Get("stable_id").Data);
|
||||
const auto* kind=std::get_if<std::string>(&entry.Get("kind").Data);
|
||||
const auto* attachment=std::get_if<std::string>(&entry.Get("attachment").Data);
|
||||
if(!id||!kind||!attachment||id->empty()||*attachment!="seated_floor")
|
||||
{error="motif record requires a stable_id, kind and seated_floor attachment";return false;}
|
||||
ResolvedLayoutStampRequestV1 request;request.stableId=*id;
|
||||
request.strateIndex=strateIndex;request.attachmentPolicy=1;
|
||||
request.floorMicrometres=floorMicrometres;
|
||||
if(*kind=="cappedCylinder")
|
||||
{
|
||||
request.kind=ResolvedSdfStampKindV1::fillMonolith;
|
||||
double radius=1.4,height=24.0;
|
||||
if(!metres(entry,"radius_m",radius,radius)
|
||||
||!metres(entry,"height_m",height,height))
|
||||
{error="monolith dimensions must be finite metre values";return false;}
|
||||
request.halfExtentsMicrometres={toMicrometres(radius),
|
||||
toMicrometres(radius),toMicrometres(height*0.5)};
|
||||
}
|
||||
else if(*kind=="roundedBox")
|
||||
{
|
||||
request.kind=ResolvedSdfStampKindV1::fillPlatform;
|
||||
const auto& extents=entry.Get("half_extents_m").AsArray();
|
||||
if(extents.size()!=3||!extents[0].IsNumber()||!extents[1].IsNumber()
|
||||
||!extents[2].IsNumber())
|
||||
{error="platform half_extents_m must contain three metre values";return false;}
|
||||
request.halfExtentsMicrometres={toMicrometres(extents[0].Number()),
|
||||
toMicrometres(extents[1].Number()),toMicrometres(extents[2].Number())};
|
||||
}
|
||||
else
|
||||
{error="motif kind must be cappedCylinder or roundedBox";return false;}
|
||||
double sideOffset=0.0,placementRadius=4.0,fillet=0.55,
|
||||
blendWidth=1.6,minimumSpacing=15.0;
|
||||
if(!metres(entry,"route_side_offset_m",0.0,sideOffset)
|
||||
||!metres(entry,"placement_radius_m",4.0,placementRadius)
|
||||
||!metres(entry,"fillet_radius_m",0.55,fillet)
|
||||
||!metres(entry,"blend_width_m",1.6,blendWidth)
|
||||
||!metres(entry,"minimum_spacing_m",15.0,minimumSpacing))
|
||||
{error="motif placement or edge parameters must be finite metre values";return false;}
|
||||
request.anchorPositionMicrometres={
|
||||
static_cast<std::int64_t>(std::llround(route[middle][0]+normalX*sideOffset*1.0e6)),
|
||||
static_cast<std::int64_t>(std::llround(route[middle][1]+normalY*sideOffset*1.0e6)),
|
||||
floorMicrometres+request.halfExtentsMicrometres[2]};
|
||||
request.placementRadiusMicrometres=toMicrometres(placementRadius);
|
||||
request.roundingRadiusMicrometres=toMicrometres(fillet);
|
||||
request.smoothWidthMicrometres=toMicrometres(blendWidth);
|
||||
request.minimumSpacingMicrometres=toMicrometres(minimumSpacing);
|
||||
request.yawQuarterTurns=static_cast<std::uint8_t>(
|
||||
std::clamp<std::int64_t>(entry.Get("yaw_quarter_turns").Integer(),0,3));
|
||||
requests.push_back(std::move(request));
|
||||
}
|
||||
if(requests.empty())
|
||||
{error="motif family must resolve at least one fixed-count record";return false;}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool AttachRound1WorldAssemblyPrototype(FStandaloneOwnerWorld& owner,VFJson::Value& root,
|
||||
std::string& error,const std::string& manifestOutputPath,
|
||||
VoxelForgeCore::WorldAssemblyManifest* outManifest=nullptr)
|
||||
|
||||
Reference in new issue
Block a user