wip(standalone): GPU step 5 -- far GPU work 646 -> 112 ms; hybrid whole view equal to CPU
Whole far field on the RX 7900 XTX: 112 ms total (connector rings 61 ms). Cold hybrid 2.303 s vs all-CPU 2.314 s (within noise): the far field is no longer on the wall-clock critical path. 0 sign/topology differences vs CPU and WARP incl. 127,401 protected and 353 thin-feature cells; repeat GPU runs identical; far survival passes on GPU meshes; CPU hashes unchanged. Open: CPU/GPU vertex distance mean 1.15 mm but max 64.56 cm, 247 tiles over the quantisation bound. 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
247682a8e0
commit
0e730cdbba
9 files changed
+697
-260
No files matched your search
@@ -138,7 +138,13 @@ RWStructuredBuffer<uint> CellCases : register(u0);
|
||||
RWStructuredBuffer<uint> CellLocalOffsets : register(u1);
|
||||
RWStructuredBuffer<uint> GroupVertexCounts : register(u2);
|
||||
|
||||
struct FarFieldSampleRange { uint offset; uint count; };
|
||||
struct FarFieldSpatialBin
|
||||
{
|
||||
int3 coord;
|
||||
uint offset;
|
||||
uint count;
|
||||
uint padding;
|
||||
};
|
||||
struct FarFieldConnectorPanel
|
||||
{
|
||||
float3 start; float radius;
|
||||
@@ -159,8 +165,8 @@ struct FarConnectorRing
|
||||
float3 padding;
|
||||
};
|
||||
StructuredBuffer<VF_FieldPrimitive> FarShapes : register(t10);
|
||||
StructuredBuffer<FarFieldSampleRange> FarSampleShapeRanges : register(t11);
|
||||
StructuredBuffer<uint> FarSampleShapeIds : register(t12);
|
||||
StructuredBuffer<FarFieldSpatialBin> FarSpatialBins : register(t11);
|
||||
StructuredBuffer<uint> FarSpatialBinShapeIds : register(t12);
|
||||
StructuredBuffer<uint> FarProtectedAir : register(t13);
|
||||
StructuredBuffer<uint> FarProtectedSolid : register(t14);
|
||||
RWStructuredBuffer<float> FarFieldDensity : register(u7);
|
||||
@@ -178,8 +184,45 @@ cbuffer FarFieldConstants : register(b1)
|
||||
float FarStrataHi;
|
||||
float FarStrataBlend;
|
||||
uint FarSampleCount;
|
||||
uint FarEvalCounterEnabled;
|
||||
uint FarSpatialBinMask;
|
||||
uint FarSpatialBinSize;
|
||||
uint FarSpatialBinCapacity;
|
||||
};
|
||||
|
||||
RWStructuredBuffer<uint> FarFieldEvalCounters : register(u6);
|
||||
|
||||
uint FarFieldBinHash(int3 coord)
|
||||
{
|
||||
uint hash = 2166136261u;
|
||||
hash = (hash ^ asuint(coord.x)) * 16777619u;
|
||||
hash = (hash ^ asuint(coord.y)) * 16777619u;
|
||||
hash = (hash ^ asuint(coord.z)) * 16777619u;
|
||||
hash ^= hash >> 16; hash *= 0x7feb352du; hash ^= hash >> 15;
|
||||
hash *= 0x846ca68bu; hash ^= hash >> 16;
|
||||
return hash;
|
||||
}
|
||||
|
||||
FarFieldSpatialBin FarFieldFindBin(float3 position)
|
||||
{
|
||||
FarFieldSpatialBin emptyBin;
|
||||
emptyBin.coord = int3(0,0,0);
|
||||
emptyBin.offset = 0;
|
||||
emptyBin.count = 0;
|
||||
emptyBin.padding = 0;
|
||||
const int3 coord = int3(floor(position / max(1.0f, (float)FarSpatialBinSize)));
|
||||
uint slot = FarFieldBinHash(coord) & FarSpatialBinMask;
|
||||
[loop]
|
||||
for (uint probe = 0; probe < FarSpatialBinCapacity; ++probe)
|
||||
{
|
||||
const FarFieldSpatialBin bin = FarSpatialBins[slot];
|
||||
if (bin.count == 0) return emptyBin;
|
||||
if (all(bin.coord == coord)) return bin;
|
||||
slot = (slot + 1) & FarSpatialBinMask;
|
||||
}
|
||||
return emptyBin;
|
||||
}
|
||||
|
||||
[numthreads(64,1,1)]
|
||||
void EvaluateFarFieldCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
{
|
||||
@@ -190,13 +233,14 @@ void EvaluateFarFieldCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
const uint z = index / (SampleDim * SampleDim);
|
||||
const float3 position = float3(OriginX, OriginY, OriginZ)
|
||||
+ float3(int(x) - 1, int(y) - 1, int(z) - 1) * StepVoxels;
|
||||
const FarFieldSampleRange range = FarSampleShapeRanges[index];
|
||||
const FarFieldSpatialBin range = FarFieldFindBin(position);
|
||||
float cave = 100000.0f;
|
||||
float overlay = -100000.0f;
|
||||
uint distanceEvaluations = 0;
|
||||
[loop]
|
||||
for (uint item = 0; item < range.count; ++item)
|
||||
{
|
||||
const VF_FieldPrimitive shape = FarShapes[FarSampleShapeIds[range.offset + item]];
|
||||
const VF_FieldPrimitive shape = FarShapes[FarSpatialBinShapeIds[range.offset + item]];
|
||||
if ((shape.flags & VF_FIELD_PRIMITIVE_CARVE) != 0)
|
||||
{
|
||||
if (position.x < shape.minVoxel[0] - 6 || position.x > shape.maxVoxel[0] + 6
|
||||
@@ -205,6 +249,7 @@ void EvaluateFarFieldCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
continue;
|
||||
float distance = VF_FieldPrimitiveDistance(shape,
|
||||
position.x, position.y, position.z);
|
||||
++distanceEvaluations;
|
||||
if ((shape.flags & VF_FIELD_PRIMITIVE_HAS_FLOOR) != 0)
|
||||
{
|
||||
const float floorZ = VF_FieldPrimitiveFloorAt(shape, position.x, position.y);
|
||||
@@ -219,6 +264,7 @@ void EvaluateFarFieldCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
{
|
||||
overlay = max(overlay, -VF_FieldPrimitiveDistance(shape,
|
||||
position.x, position.y, position.z));
|
||||
++distanceEvaluations;
|
||||
}
|
||||
}
|
||||
const float base = min(position.z - FarStrataLo, FarStrataHi - position.z);
|
||||
@@ -231,16 +277,21 @@ void EvaluateFarFieldCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
if (protectedAir != 0) density = magnitude;
|
||||
else if (protectedSolid != 0) density = -magnitude;
|
||||
FarFieldDensity[index] = density;
|
||||
if (FarEvalCounterEnabled != 0) FarFieldEvalCounters[index] = distanceEvaluations;
|
||||
}
|
||||
|
||||
float FarRockDistanceAt(float3 position)
|
||||
float FarRockDistanceAt(float3 position,int3 cachedBinCoord,FarFieldSpatialBin cachedRange)
|
||||
{
|
||||
float cave = 100000.0f;
|
||||
float overlay = -100000.0f;
|
||||
const int3 positionBinCoord=int3(floor(position/max(1.0f,(float)FarSpatialBinSize)));
|
||||
FarFieldSpatialBin range;
|
||||
if(all(positionBinCoord==cachedBinCoord))range=cachedRange;
|
||||
else range=FarFieldFindBin(position);
|
||||
[loop]
|
||||
for (uint shapeId = 0; shapeId < FarShapeCount; ++shapeId)
|
||||
for (uint item = 0; item < range.count; ++item)
|
||||
{
|
||||
const VF_FieldPrimitive shape = FarShapes[shapeId];
|
||||
const VF_FieldPrimitive shape = FarShapes[FarSpatialBinShapeIds[range.offset + item]];
|
||||
if ((shape.flags & VF_FIELD_PRIMITIVE_CARVE) != 0)
|
||||
{
|
||||
if (position.x < shape.minVoxel[0] - 6 || position.x > shape.maxVoxel[0] + 6
|
||||
@@ -269,29 +320,43 @@ float FarRockDistanceAt(float3 position)
|
||||
|
||||
float FarConnectorRadius(float3 center, float3 direction, float nominalRadius)
|
||||
{
|
||||
// The coarse far field is a signed-distance composition. Two bounded
|
||||
// distance steps follow its connector wall closely without a CPU readback
|
||||
// or a long radial scan for each sparse panel ring.
|
||||
// Keep the bounded signed-distance refinement, but stop once the residual
|
||||
// reaches the same acceptance bound used by the original fallback. A
|
||||
// spatial-bin cache avoids re-hashing nearby probes.
|
||||
const float3 nominalPosition=center+direction*nominalRadius;
|
||||
const int3 cachedBinCoord=int3(floor(nominalPosition/max(1.0f,(float)FarSpatialBinSize)));
|
||||
const FarFieldSpatialBin cachedRange=FarFieldFindBin(nominalPosition);
|
||||
const float maxOffset=max(16.0f,nominalRadius*0.75f);
|
||||
const float minRadius=max(0.0f,nominalRadius-maxOffset);
|
||||
const float maxRadius=nominalRadius+maxOffset;
|
||||
float radius=nominalRadius;
|
||||
[unroll]
|
||||
for(uint refine=0;refine<3;++refine)
|
||||
float residual=FarRockDistanceAt(center+direction*radius,cachedBinCoord,cachedRange);
|
||||
bool converged=abs(residual)<=1.0f;
|
||||
if(!converged)
|
||||
{
|
||||
const float distance=FarRockDistanceAt(center+direction*radius);
|
||||
const float nextDistance=FarRockDistanceAt(center+direction*(radius+4.0f));
|
||||
// Most connector fields are close to a unit-gradient SDF along the
|
||||
// radial ray. Try that correction first and verify it before accepting.
|
||||
radius=clamp(radius-residual,minRadius,maxRadius);
|
||||
residual=FarRockDistanceAt(center+direction*radius,cachedBinCoord,cachedRange);
|
||||
converged=abs(residual)<=1.0f;
|
||||
}
|
||||
[unroll]
|
||||
for(uint refine=0;refine<2&&!converged;++refine)
|
||||
{
|
||||
const float distance=residual;
|
||||
const float nextDistance=FarRockDistanceAt(center+direction*(radius+4.0f),cachedBinCoord,cachedRange);
|
||||
const float slope=(nextDistance-distance)*0.25f;
|
||||
const float correction=abs(slope)>0.125f?distance/slope:distance;
|
||||
radius=clamp(radius-correction,minRadius,maxRadius);
|
||||
residual=FarRockDistanceAt(center+direction*radius,cachedBinCoord,cachedRange);
|
||||
converged=abs(residual)<=1.0f;
|
||||
}
|
||||
const float residual=FarRockDistanceAt(center+direction*radius);
|
||||
if(abs(residual)>1.0f)
|
||||
{
|
||||
const float searchMin=max(0.0f,nominalRadius-maxOffset-8.0f);
|
||||
const float searchMax=nominalRadius+maxOffset;
|
||||
float previous=searchMin;
|
||||
float previousDistance=FarRockDistanceAt(center+direction*previous);
|
||||
float previousDistance=FarRockDistanceAt(center+direction*previous,cachedBinCoord,cachedRange);
|
||||
float bestRadius=radius;
|
||||
float bestDifference=100000.0f;
|
||||
[loop]
|
||||
@@ -299,7 +364,7 @@ float FarConnectorRadius(float3 center, float3 direction, float nominalRadius)
|
||||
{
|
||||
const float distance=min(searchMin+sample*8.0f,searchMax);
|
||||
if(distance<=previous+1.0e-5f)break;
|
||||
const float currentDistance=FarRockDistanceAt(center+direction*distance);
|
||||
const float currentDistance=FarRockDistanceAt(center+direction*distance,cachedBinCoord,cachedRange);
|
||||
if((previousDistance>=0.0f)!=(currentDistance>=0.0f))
|
||||
{
|
||||
float lo=previous,hi=distance;
|
||||
@@ -308,7 +373,7 @@ float FarConnectorRadius(float3 center, float3 direction, float nominalRadius)
|
||||
for(uint refine=0;refine<8;++refine)
|
||||
{
|
||||
const float mid=(lo+hi)*0.5f;
|
||||
if((FarRockDistanceAt(center+direction*mid)>=0.0f)==previousSolid)lo=mid;
|
||||
if((FarRockDistanceAt(center+direction*mid,cachedBinCoord,cachedRange)>=0.0f)==previousSolid)lo=mid;
|
||||
else hi=mid;
|
||||
}
|
||||
const float crossing=(lo+hi)*0.5f;
|
||||
@@ -324,65 +389,136 @@ float FarConnectorRadius(float3 center, float3 direction, float nominalRadius)
|
||||
return radius;
|
||||
}
|
||||
|
||||
[numthreads(64,1,1)]
|
||||
void BuildFarConnectorRingsCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
uint FarConnectorPanelForRing(uint ringId)
|
||||
{
|
||||
const uint ringId = dispatchId.x;
|
||||
if (ringId == 0)
|
||||
{
|
||||
const uint maxVerticesPerTile = CellCount * 15;
|
||||
const uint slot = maxVerticesPerTile > 0 ? OutputBaseVertex / maxVerticesPerTile : 0;
|
||||
const uint panelBaseVertex = slot * FarConnectorPanelVertexCapacity;
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset + 20, FarConnectorVertexCount);
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset + 24, FarConnectorVertexCount > 0 ? 1 : 0);
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset + 28, panelBaseVertex);
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset + 32, 0);
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset + 36, 0);
|
||||
}
|
||||
if (ringId >= FarConnectorRingCount) return;
|
||||
|
||||
FarFieldConnectorPanel connector = FarConnectorPanels[0];
|
||||
uint low = 0;
|
||||
uint high = FarConnectorCount;
|
||||
[loop]
|
||||
for (uint i = 0; i < FarConnectorCount; ++i)
|
||||
while (low < high)
|
||||
{
|
||||
const FarFieldConnectorPanel candidate = FarConnectorPanels[i];
|
||||
if (ringId >= candidate.ringOffset && ringId < candidate.ringOffset + candidate.ringCount)
|
||||
const uint middle = low + ((high - low) >> 1);
|
||||
const FarFieldConnectorPanel candidate = FarConnectorPanels[middle];
|
||||
if (ringId < candidate.ringOffset) high = middle;
|
||||
else if (ringId >= candidate.ringOffset + candidate.ringCount) low = middle + 1;
|
||||
else return middle;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint FarConnectorPanelForVertex(uint vertexId)
|
||||
{
|
||||
uint low = 0;
|
||||
uint high = FarConnectorCount;
|
||||
[loop]
|
||||
while (low < high)
|
||||
{
|
||||
const uint middle = low + ((high - low) >> 1);
|
||||
const FarFieldConnectorPanel candidate = FarConnectorPanels[middle];
|
||||
const uint candidateVertexCount = (candidate.ringCount - 1) * 24;
|
||||
if (vertexId < candidate.vertexOffset) high = middle;
|
||||
else if (vertexId >= candidate.vertexOffset + candidateVertexCount) low = middle + 1;
|
||||
else return middle;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
groupshared float FarConnectorGroupRadii[64];
|
||||
groupshared float4 FarConnectorGroupCenterT[16];
|
||||
groupshared float4 FarConnectorGroupURadius[16];
|
||||
groupshared float4 FarConnectorGroupV[16];
|
||||
|
||||
[numthreads(64,1,1)]
|
||||
void BuildFarConnectorRingsCS(uint3 groupId : SV_GroupID,
|
||||
uint3 groupThreadId : SV_GroupThreadID)
|
||||
{
|
||||
// Four lanes cooperate on one ring. Lane zero loads the panel and builds
|
||||
// its shared frame once; each lane then refines one radial direction.
|
||||
// One 64-thread group is one AMD wave, keeping radial work local.
|
||||
const uint ringId = groupId.x * 16 + groupThreadId.x / 4;
|
||||
const uint radialId = groupThreadId.x & 3;
|
||||
const uint ringSlot = groupThreadId.x / 4;
|
||||
if (radialId == 0)
|
||||
{
|
||||
const bool validRing = ringId < FarConnectorRingCount;
|
||||
if (validRing)
|
||||
{
|
||||
connector = candidate;
|
||||
break;
|
||||
const FarFieldConnectorPanel connector = FarConnectorPanels[FarConnectorPanelForRing(ringId)];
|
||||
const float3 start = connector.start;
|
||||
const float3 delta = connector.end - start;
|
||||
const float connectorLength = length(delta);
|
||||
const float3 axis = delta / max(connectorLength, 1.0e-8f);
|
||||
float3 u = cross(axis, float3(0,0,1));
|
||||
if (dot(u,u) < 0.01f) u = cross(axis, float3(0,1,0));
|
||||
u = normalize(u);
|
||||
const float3 v = normalize(cross(axis,u));
|
||||
const uint localRing = ringId - connector.ringOffset;
|
||||
float t = 0;
|
||||
if (localRing == 0) t = connector.tMin;
|
||||
else if (localRing + 1 == connector.ringCount) t = connector.tMax;
|
||||
else t = ((float)(connector.axialSampleStart + localRing - 1) + 0.5f)
|
||||
/ max(1.0f, (float)connector.axialSampleCount);
|
||||
const float3 center = start + delta * t;
|
||||
FarConnectorGroupCenterT[ringSlot] = float4(center,t);
|
||||
FarConnectorGroupURadius[ringSlot] = float4(u,connector.radius);
|
||||
FarConnectorGroupV[ringSlot] = float4(v,0);
|
||||
}
|
||||
else
|
||||
{
|
||||
FarConnectorGroupCenterT[ringSlot] = 0;
|
||||
FarConnectorGroupURadius[ringSlot] = 0;
|
||||
FarConnectorGroupV[ringSlot] = 0;
|
||||
}
|
||||
if (ringId == 0)
|
||||
{
|
||||
const uint maxVerticesPerTile = CellCount * 15;
|
||||
const uint slot = maxVerticesPerTile > 0 ? OutputBaseVertex / maxVerticesPerTile : 0;
|
||||
const uint panelBaseVertex = slot * FarConnectorPanelVertexCapacity;
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset + 20, FarConnectorVertexCount);
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset + 24, FarConnectorVertexCount > 0 ? 1 : 0);
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset + 28, panelBaseVertex);
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset + 32, 0);
|
||||
FarConnectorDrawArgs.Store(OutputArgsByteOffset + 36, 0);
|
||||
}
|
||||
}
|
||||
const float3 start = connector.start;
|
||||
const float3 delta = connector.end - start;
|
||||
const float connectorLength = length(delta);
|
||||
const float3 axis = delta / max(connectorLength, 1.0e-8f);
|
||||
float3 u = cross(axis, float3(0,0,1));
|
||||
if (dot(u,u) < 0.01f) u = cross(axis, float3(0,1,0));
|
||||
u = normalize(u);
|
||||
const float3 v = normalize(cross(axis,u));
|
||||
const uint localRing = ringId - connector.ringOffset;
|
||||
float t;
|
||||
if (localRing == 0) t = connector.tMin;
|
||||
else if (localRing + 1 == connector.ringCount) t = connector.tMax;
|
||||
else t = ((float)(connector.axialSampleStart + localRing - 1) + 0.5f)
|
||||
/ max(1.0f, (float)connector.axialSampleCount);
|
||||
const float3 center = start + delta * t;
|
||||
const float ru = FarConnectorRadius(center,u,connector.radius);
|
||||
const float rmu = FarConnectorRadius(center,-u,connector.radius);
|
||||
const float rv = FarConnectorRadius(center,v,connector.radius);
|
||||
const float rmv = FarConnectorRadius(center,-v,connector.radius);
|
||||
FarConnectorRing ring;
|
||||
ring.corners[0] = float4(center + u*ru + v*rv, 0);
|
||||
ring.corners[1] = float4(center - u*rmu + v*rv, 0);
|
||||
ring.corners[2] = float4(center - u*rmu - v*rmv, 0);
|
||||
ring.corners[3] = float4(center + u*ru - v*rmv, 0);
|
||||
ring.normals[0] = float4(-normalize(u+v),0);
|
||||
ring.normals[1] = float4(-normalize(-u+v),0);
|
||||
ring.normals[2] = float4(-normalize(-u-v),0);
|
||||
ring.normals[3] = float4(-normalize(u-v),0);
|
||||
ring.t = t;
|
||||
ring.padding = 0;
|
||||
FarConnectorRingOutput[ringId] = ring;
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
const bool validRing = ringId < FarConnectorRingCount;
|
||||
const float4 centerT = FarConnectorGroupCenterT[ringSlot];
|
||||
const float4 uRadius = FarConnectorGroupURadius[ringSlot];
|
||||
const float3 center = centerT.xyz;
|
||||
const float3 u = uRadius.xyz;
|
||||
const float3 v = FarConnectorGroupV[ringSlot].xyz;
|
||||
const float3 direction = radialId == 0 ? u : radialId == 1 ? -u : radialId == 2 ? v : -v;
|
||||
const float radius = validRing ? FarConnectorRadius(center,direction,uRadius.w) : 0;
|
||||
FarConnectorGroupRadii[groupThreadId.x] = radius;
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
if (!validRing) return;
|
||||
|
||||
if (radialId == 0) FarConnectorRingOutput[ringId].t = centerT.w;
|
||||
const uint ringBase = groupThreadId.x - radialId;
|
||||
const float ru = FarConnectorGroupRadii[ringBase + 0];
|
||||
const float rmu = FarConnectorGroupRadii[ringBase + 1];
|
||||
const float rv = FarConnectorGroupRadii[ringBase + 2];
|
||||
const float rmv = FarConnectorGroupRadii[ringBase + 3];
|
||||
if (radialId == 0)
|
||||
{
|
||||
FarConnectorRingOutput[ringId].corners[0] = float4(center + u*ru + v*rv, 0);
|
||||
FarConnectorRingOutput[ringId].normals[0] = float4(-normalize(u+v),0);
|
||||
}
|
||||
else if (radialId == 1)
|
||||
{
|
||||
FarConnectorRingOutput[ringId].corners[1] = float4(center - u*rmu + v*rv, 0);
|
||||
FarConnectorRingOutput[ringId].normals[1] = float4(-normalize(-u+v),0);
|
||||
}
|
||||
else if (radialId == 2)
|
||||
{
|
||||
FarConnectorRingOutput[ringId].corners[2] = float4(center - u*rmu - v*rmv, 0);
|
||||
FarConnectorRingOutput[ringId].normals[2] = float4(-normalize(-u-v),0);
|
||||
}
|
||||
else
|
||||
{
|
||||
FarConnectorRingOutput[ringId].corners[3] = float4(center + u*ru - v*rmv, 0);
|
||||
FarConnectorRingOutput[ringId].normals[3] = float4(-normalize(u-v),0);
|
||||
}
|
||||
}
|
||||
|
||||
[numthreads(64,1,1)]
|
||||
@@ -390,18 +526,7 @@ void EmitFarConnectorPanelsCS(uint3 dispatchId : SV_DispatchThreadID)
|
||||
{
|
||||
const uint vertexId = dispatchId.x;
|
||||
if (vertexId >= FarConnectorVertexCount) return;
|
||||
FarFieldConnectorPanel connector = FarConnectorPanels[0];
|
||||
[loop]
|
||||
for (uint i = 0; i < FarConnectorCount; ++i)
|
||||
{
|
||||
const FarFieldConnectorPanel candidate = FarConnectorPanels[i];
|
||||
const uint candidateCount = (candidate.ringCount - 1) * 24;
|
||||
if (vertexId >= candidate.vertexOffset && vertexId < candidate.vertexOffset + candidateCount)
|
||||
{
|
||||
connector = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const FarFieldConnectorPanel connector = FarConnectorPanels[FarConnectorPanelForVertex(vertexId)];
|
||||
const uint localVertex = vertexId - connector.vertexOffset;
|
||||
const uint interval = localVertex / 24;
|
||||
const uint cornerInQuad = localVertex % 6;
|
||||
|
||||
Reference in new issue
Block a user