在平面上进行索引包装' 3D'使用位掩码

时间:2015-09-22 11:04:30

标签: c# arrays

我的3D游戏世界存储在一个平面的字节数组中。世界宽度/长度为1024 x 1024,高度为256。所以数组长度为268435456个字节。要访问数组,我使用如下计算的索引指针:

WORLD_WIDTH_SHIFT = 10;
WORLD_HEIGHT_SHIFT = 8;

indexPointer = (((Px << WORLD_WIDTH_SHIFT) + Pz) << WORLD_HEIGHT_SHIFT) + Py;

由于性能原因,我在处理期间通过向索引值添加预定义常量来浏览数组。向西移动(正x轴)就像这样:

WEST = WORLD_WIDTH * WORLD_HEIGHT;
NORTH = WORLD_HEIGHT;
SOUTH = -WORLD_HEIGHT;
WEST = WORLD_WIDTH * WORLD_HEIGHT;
EAST = -WORLD_WIDTH * WORLD_HEIGHT;
UP = 1;
DOWN = -1;

indexPointer += WEST;

为了防止索引超出范围并且我愚蠢地认为会实现包装,我在结果中添加了一个掩码:

WORLD_ARRAY_SIZE = WORLD_WIDTH * WORLD_WIDTH * WORLD_HEIGHT;
WORLD_ARRAY_SIZE_MASK = WORLD_ARRAY_SIZE - 1;
WEST = WORLD_WIDTH * WORLD_HEIGHT;

indexPointer = (indexPointer + WEST) & WORLD_ARRAY_SIZE_MASK;

这在上面的x轴上运行良好,但在z轴(南北)上,我发现它后发现它的包装显然无法正常工作。如果我错了,请纠正我,但我需要一种方法在indexPointer中代表z坐标的位上执行位掩码。前8位是高度,接下来的10位是z coord,最后10位是x coord。有没有办法在不将10位提取到单独的字段然后执行加法和掩码然后必须重建最终指针的情况下执行此操作?

1 个答案:

答案 0 :(得分:1)

给出常数:

HIGH = 1;
NORTH = WORLD_WIDTH;
WEST = NORTH*WORLD_HEIGHT;

您可以修改“增量”功能:

public static void UpdatePointer (int indexPointer, int dir) {
    int mask0 = 0xffc0000;
    int mask1 = 0x003ff00;
    int mask2 = 0x00000ff;
    res = (indexPointer&mask0+dir)&mask0;
    res |= (indexPointer&mask1+dir)&mask1;
    res |= (indexPointer&mask2+dir)&mask2;
    return res;
}

因此,这或多或少取代indexPointer += update

<强>解释

问题是当你添加一个常量时,添加一个值会导致进位。你基本上想要将三个“整数”表示为一个整数。现在给定整数有值:

#bits 10   10    8
value 12 1023   15

并添加到第二个整数,那个将溢出导致:

#bits 10   10    8
value 13    0   15

虽然第二个“整数”具有正确的值,但第一个已经递增。

您可以通过屏蔽解决此问题。我们来看看第二个掩码指令:

(indexPointer&mask1+dir)&mask1;

(其他人相同)。

这里我们首先将掩码应用于indexPointer。这确保我们“提取”正确的整数。所以:

#bits 10   10    8
value 12 1023   15

现在变为:

#bits 10   10    8
value 0  1023    0

这有助于防止dir溢出第三个整数,从而导致第二个整数增加。

现在我们将dir“添加”到该值,结果为:

#bits 10   10    8
value  1    0    0

但现在我们再次屏蔽导致:

#bits 10   10    8
value  0    0    0

因此第二个标记指令已将第二个“整数”的值确定为零,并且对其他整数没有任何影响。

第一个面具也是如此,其中携带预防更为突出:

#bits 10   10    8 //initial state
value 12 1023   15

#bits 10   10    8 //masking with mask0
value 12    0    0

#bits 10   10    8 //adding dir
value 12    1    0

#bits 10   10    8 //masking again
value 12    0    0

如果您稍后对这三项操作执行操作,则最终状态为:

#bits 10   10    8 //final result
value 12    0   15

<强>替代:

另一种方法是使用“溢出”位。在这种情况下,indexPointer使用8+1+10+1+10 = 30位,其中一位用于溢出。在这种情况下,更新是:

indexPointer += dir;
indexPointer &= 0x3ff7feff;

其中0x3ff7feff是一个掩码,用于再次清除溢出位:

0x3ff7feff = 11 1111 1111   0   11 1111 1111   0   1111 1111

为了“使用”指针,你需要一个小巧的操作:

int realPointer = ((indexPointer&0x3ff00000)>>0x02)|((indexPointer&0x7fe00)>>0x01)|(indexPointer&0xff);

或更新功能读取:

public static void UpdatePointer (int indexPointer, int dir) {
    int temp = ((indexPointer&0xffc0000)<<0x02)|((indexPointer&0x3ff00)<<0x01)|(indexPointer&0xff);
    temp += dir;
    return ((temp&0x3ff00000)>>0x02)|((temp&0x7fe00)>>0x01)|(temp&0xff);
}
显然你需要在某个地方付出代价。