这是一些代码,我写了大约5年前,最近正在研究。我们的目标是制作带有纹理的墙壁。如果墙壁上有交叉点,内部的纹理会被稍微压缩,外部的纹理也会稍微扩展,这样图案就能正确地贴出,并且可以有一些特殊的花纹排列在一起。下面是一个正在使用的例子:

下面是实现生成它的核心算法的怪物。它创建4个点的四边形,并将它们发送到QuadGeometry类,后者构建实际的顶点/索引缓冲区。它还会生成墙壁的顶部和边缘(正如你可以从TODOs看到的那样,我从未完成这个部分)。
using System.Collections.Generic;
using masques.map;
using masques.util;
using SharpDX;
namespace masques.graphics
{
public static class WallGeometry
{
private static Vector3 vec3(float x, float y, float z) { return new Vector3(x, y, z); }
private static int lookLeftAndRight(MapWalls walls, int x, int y, int z)
{
int c = 0;
if(x > 0 && walls.X.exists(x - 1, y, z)) c += 1;
if(x < walls.width && walls.X.exists(x, y, z)) c += 2;
return c;
}
private static int lookUpAndDown(MapWalls walls, int x, int y, int z)
{
int c = 0;
if(y > 0 && walls.Y.exists(x, y - 1, z)) c += 1;
if(y < walls.height && walls.Y.exists(x, y, z)) c += 2;
return c;
}
private static void adjustCorners(ref float a, ref float b, int corners, float adjustment, bool pushOut)
{
if(corners == 1) { a -= adjustment; if(pushOut) b += adjustment; }
else if(corners == 2) { b -= adjustment; if(pushOut) a += adjustment; }
else if(corners == 3) { a -= adjustment; b -= adjustment; }
}
// TODO edge and top materials not always the defaults
// TODO don't put top on wall if wall/tile is above
public static void build(MapWalls walls, QuadGeometry geo) { build(walls, geo, 0, 0, 0, walls.width, walls.height, walls.layers, MapWalls.EDGE_SIZE, 1, true); }
public static void build(MapWalls walls, QuadGeometry geo, int x0, int y0, int z0, int x1, int y1, int z1, float edgeSize, float wallHeight, bool includeBorders)
{
if(x1 <= x0 || y1 <= y0) return;
int w = walls.width, h = walls.height;
const ushort edgeMat = 1;
const ushort topMat = 1;
/***********************************************************************************************
* X WALLS *
***********************************************************************************************/
int y0x = includeBorders ? y0 : y0 + 1;
int y1x = includeBorders ? y1 + 1 : y1;
foreach(KeyValuePair<IntPoint, WallSegment> kvp in walls.X)
{
int x = kvp.Key.x;
int y = kvp.Key.y;
int z = kvp.Key.z;
if(x < x0 || x >= x1 || y < y0x || y >= y1x || z < z0 || z >= z1)
continue;
WallSegment segment = kvp.Value;
float px1 = x, px1a = px1, px1b = px1;
float px2 = x + 1, px2a = px2, px2b = px2;
float py = y;
float py1 = py - edgeSize;
float py2 = py + edgeSize;
float pz = z;
int corners1 = lookUpAndDown(walls, x, y, z);
int corners2 = lookUpAndDown(walls, x + 1, y, z);
bool isWallStart = x == 0 || !walls.X.exists(x - 1, y, z);
bool isWallEnd = x == w - 1 || !walls.X.exists(x + 1, y, z);
adjustCorners(ref px1a, ref px1b, corners1, -edgeSize, isWallStart);
adjustCorners(ref px2a, ref px2b, corners2, +edgeSize, isWallEnd);
float tx1a = px1a, tx1b = px1b, tx2a = px2a, tx2b = px2b;
// if we're the beginning of a wall...
if(isWallStart)
{
// If there are no Y intersections here, add an edge
if(corners1 == 0) geo.add(vec3(px1a, py2, pz), vec3(px1a, py1, pz), vec3(px1a, py2, pz + wallHeight), vec3(px1a, py1, pz + wallHeight), 0, 1, 0, 1, edgeMat);
}
else if(corners1 != 3)
{
tx1a = px1;
tx1b = px1;
}
// if we're at the end of a wall...
if(isWallEnd)
{
// If there are no Y intersections here, add an edge
if(corners2 == 0) geo.add(vec3(px2a, py1, pz), vec3(px2a, py2, pz), vec3(px2a, py1, pz + wallHeight), vec3(px2a, py2, pz + wallHeight), 0, 1, 0, 1, edgeMat);
}
else if(corners2 != 3)
{
tx2a = px2;
tx2b = px2;
}
// top
if(!walls.X.exists(x, y, z + 1))
{
geo.add(vec3(tx2b, py2, pz + wallHeight), vec3(tx1b, py2, pz + wallHeight), vec3(tx2a, py1, pz + wallHeight), vec3(tx1a, py1, pz + wallHeight), 0, 1, 0, 1, topMat);
}
// faces
geo.add(vec3(px1a, py1, pz), vec3(px2a, py1, pz), vec3(px1a, py1, pz + wallHeight), vec3(px2a, py1, pz + wallHeight), x + 1, x, z, z + 1, segment.back);
geo.add(vec3(px2b, py2, pz), vec3(px1b, py2, pz), vec3(px2b, py2, pz + wallHeight), vec3(px1b, py2, pz + wallHeight), w - x, w - x - 1, z, z + 1, segment.front);
}
/***********************************************************************************************
* Y WALLS *
***********************************************************************************************/
int x0y = includeBorders ? x0 : x0 + 1;
int x1y = includeBorders ? x1 + 1 : x1;
foreach(KeyValuePair<IntPoint, WallSegment> kvp in walls.Y)
{
int x = kvp.Key.x;
int y = kvp.Key.y;
int z = kvp.Key.z;
if(x < x0y || x >= x1y || y < y0 || y >= y1 || z < z0 || z >= z1)
continue;
WallSegment segment = kvp.Value;
float px = x;
float px1 = px - edgeSize;
float px2 = px + edgeSize;
float py1 = y, py1a = py1, py1b = py1;
float py2 = py1 + 1, py2a = py2, py2b = py2;
float pz = z;
int corners1 = lookLeftAndRight(walls, x, y, z);
int corners2 = lookLeftAndRight(walls, x, y + 1, z);
bool isWallStart = y == 0 || !walls.Y.exists(x, y - 1, z);
bool isWallEnd = y == h - 1 || !walls.Y.exists(x, y + 1, z);
adjustCorners(ref py1a, ref py1b, corners1, -edgeSize, isWallStart);
adjustCorners(ref py2a, ref py2b, corners2, +edgeSize, isWallEnd);
float ty1a = py1a, ty1b = py1b, ty2a = py2a, ty2b = py2b;
// if we're the beginning of a wall...
if(isWallStart)
{
// If there are no X intersections here, add an edge
if(corners1 == 0) geo.add(vec3(px1, py1a, pz), vec3(px2, py1a, pz), vec3(px1, py1a, pz + wallHeight), vec3(px2, py1a, pz + wallHeight), 0, 1, 0, 1, edgeMat);
}
else
{
ty1a = py1;
ty1b = py1;
}
// if we're at the end of a wall...
if(isWallEnd)
{
// If there are no X intersections here, add an edge
if(corners2 == 0) geo.add(vec3(px2, py2a, pz), vec3(px1, py2a, pz), vec3(px2, py2a, pz + wallHeight), vec3(px1, py2a, pz + wallHeight), 0, 1, 0, 1, edgeMat);
}
else
{
ty2a = py2;
ty2b = py2;
}
// top
if(!walls.Y.exists(x, y, z + 1))
{
geo.add(vec3(px2, ty2b, pz + wallHeight), vec3(px1, ty2a, pz + wallHeight), vec3(px2, ty1b, pz + wallHeight), vec3(px1, ty1a, pz + wallHeight), 0, 1, 0, 1, topMat);
}
// wall faces
geo.add(vec3(px1, py2a, pz), vec3(px1, py1a, pz), vec3(px1, py2a, pz + wallHeight), vec3(px1, py1a, pz + wallHeight), h - y, h - y - 1, z, z + 1, segment.back);
geo.add(vec3(px2, py1b, pz), vec3(px2, py2b, pz), vec3(px2, py1b, pz + wallHeight), vec3(px2, py2b, pz + wallHeight), y + 1, y, z, z + 1, segment.front);
}
}
}
}我正在考虑把它转换成联合,它使用Y-up而不是Z-up,因为这是一种非常有效/简单的方法,可以把简单的建筑物组合在一起,但是在我这样做之前,稍微清理一下可能是有意义的。特别是,我正在寻找一种很好的方法,把完全重复的X墙和Y墙部分组合起来。也许可以用一种不同的方式来解决这些问题(比如总是在X中生成,然后将坐标交换到Y墙中),尽管这样做并不简单。
有什么建议吗,还是说这是无法挽救的?
发布于 2018-02-22 11:43:52
关于CleanCode,最好的做法是:
https://codereview.stackexchange.com/questions/188083
复制相似问题