的几年前曾经写过一个随机生成城镇的算法,生成的城镇房间从门可以进入内部行走,房间之间的走廊也可以走通,每个房间都包含一个寻路节点。但是有个缺点,模型面数很简陋,主要靠贴图来表现效果。随机生成后手动编辑改变也比较困难。
最近看到一个 叫城镇叠叠乐的游戏比较有创意,英文名townscaper,用它生成的模型比较精致,打算研究一下其使用到的算法。
townsaper主要使用的是Marchingcube算法,根据一个体素8个顶点是否为空对应256种情形(模型)来拼接最终的模型。普通的Marchingcube算法考虑到对称性共有15种基本构型( 考虑二义性是23种)。在townscaper中由于上下不具有对称性,一共有51种基本构型。
首先对体素的顶点进行编号,根据编号产生标记值(2的编号次方),8个顶点的标记值相加为体素的编号。
//右手系
//顶点编号
// +----------+
// /|V4 /|v5 y+
// / | / | |
// +----------+ | |
// V7| | |v6| |
// | | | | |
// | | | | +-------> x+
// | +-------|--+ /
// | / V0 | / V1 /
// |/ |/ z+
// +----------+
// V3 V2
//顶点标记
// +----------+
// /|v16 /|v32
// / | / |
// +----------+ |
// v128| | v|64|
// | | | |
// | | | |
// | +-------|--+
// | /v1 | /v2
// |/ |/
// +----------+
// v8 v4
下面列举了必须用到的模型,mesh: 18 rot[129, 72, 36] mirror[ 33, 24, 132, 66]; 表示18号体素模型经过旋转90/180/270度可以产生129,72,36号体素模型,经过旋转后再x轴镜像又可以产生33, 24, 132, 66号体素模型。
//mesh: 1 rot[ 8, 4, 2] mirror[ , , , ]; ( 1 = 0) ( 1 = 1)
//mesh: 3 rot[ 9, 12, 6] mirror[ , , , ]; ( 3 = 0 + 1) ( 3 = 1 + 2)
//mesh: 5 rot[ 10, , ] mirror[ , , , ]; ( 5 = 0 + 2) ( 5 = 1 + 4)
//mesh: 7 rot[ 11, 13, 14] mirror[ , , , ]; ( 7 = 0 + 1 + 2) ( 7 = 1 + 2 + 4)
//mesh: 15 rot[ , , ] mirror[ , , , ]; ( 15 = 0 + 1 + 2 + 3) ( 15 = 1 + 2 + 4 + 8)
//mesh: 16 rot[128, 64, 32] mirror[ , , , ]; ( 16 = 4) ( 16 = 16)
//mesh: 17 rot[136, 68, 34] mirror[ , , , ]; ( 17 = 0 + 4) ( 17 = 1 + 16)
//mesh: 18 rot[129, 72, 36] mirror[ 33, 24, 132, 66]; ( 18 = 1 + 4) ( 18 = 2 + 16)
//mesh: 19 rot[137, 76, 38] mirror[ 35, 25, 140, 70]; ( 19 = 0 + 1 + 4) ( 19 = 1 + 2 + 16)
//mesh: 20 rot[130, 65, 40] mirror[ , , , ]; ( 20 = 2 + 4) ( 20 = 4 + 16)
//mesh: 21 rot[138, 69, 42] mirror[ , , , ]; ( 21 = 0 + 2 + 4) ( 21 = 1 + 4 + 16)
//mesh: 22 rot[131, 73, 44] mirror[ 41, 28, 134, 67]; ( 22 = 1 + 2 + 4) ( 22 = 2 + 4 + 16)
//mesh: 23 rot[139, 77, 46] mirror[ 43, 29, 142, 71]; ( 23 = 0 + 1 + 2 + 4) ( 23 = 1 + 2 + 4 + 16)
//mesh: 26 rot[133, 74, 37] mirror[ , , , ]; ( 26 = 1 + 3 + 4) ( 26 = 2 + 8 + 16)
//mesh: 27 rot[141, 78, 39] mirror[ , , , ]; ( 27 = 0 + 1 + 3 + 4) ( 27 = 1 + 2 + 8 + 16)
//mesh: 30 rot[135, 75, 45] mirror[ , , , ]; ( 30 = 1 + 2 + 3 + 4) ( 30 = 2 + 4 + 8 + 16)
//mesh: 31 rot[143, 79, 47] mirror[ , , , ]; ( 31 = 0 + 1 + 2 + 3 + 4) ( 31 = 1 + 2 + 4 + 8 + 16)
//mesh: 48 rot[144, 192, 96] mirror[ , , , ]; ( 48 = 4 + 5) ( 48 = 16 + 32)
//mesh: 49 rot[152, 196, 98] mirror[ 50, 145, 200, 100]; ( 49 = 0 + 4 + 5) ( 49 = 1 + 16 + 32)
//mesh: 51 rot[153, 204, 102] mirror[ , , , ]; ( 51 = 0 + 1 + 4 + 5) ( 51 = 1 + 2 + 16 + 32)
//mesh: 52 rot[146, 193, 104] mirror[ 56, 148, 194, 97]; ( 52 = 2 + 4 + 5) ( 52 = 4 + 16 + 32)
//mesh: 53 rot[154, 197, 106] mirror[ 58, 149, 202, 101]; ( 53 = 0 + 2 + 4 + 5) ( 53 = 1 + 4 + 16 + 32)
//mesh: 54 rot[147, 201, 108] mirror[ 57, 156, 198, 99]; ( 54 = 1 + 2 + 4 + 5) ( 54 = 2 + 4 + 16 + 32)
//mesh: 55 rot[155, 205, 110] mirror[ 59, 157, 206, 103]; ( 55 = 0 + 1 + 2 + 4 + 5) ( 55 = 1 + 2 + 4 + 16 + 32)
//mesh: 60 rot[150, 195, 105] mirror[ , , , ]; ( 60 = 2 + 3 + 4 + 5) ( 60 = 4 + 8 + 16 + 32)
//mesh: 61 rot[158, 199, 107] mirror[ 62, 151, 203, 109]; ( 61 = 0 + 2 + 3 + 4 + 5) ( 61 = 1 + 4 + 8 + 16 + 32)
//mesh: 63 rot[159, 207, 111] mirror[ , , , ]; ( 63 = 0 + 1 + 2 + 3 + 4 + 5) ( 63 = 1 + 2 + 4 + 8 + 16 + 32)
//mesh: 80 rot[160, , ] mirror[ , , , ]; ( 80 = 4 + 6) ( 80 = 16 + 64)
//mesh: 81 rot[168, 84, 162] mirror[ , , , ]; ( 81 = 0 + 4 + 6) ( 81 = 1 + 16 + 64)
//mesh: 82 rot[161, 88, 164] mirror[ , , , ]; ( 82 = 1 + 4 + 6) ( 82 = 2 + 16 + 64)
//mesh: 83 rot[169, 92, 166] mirror[163, 89, 172, 86]; ( 83 = 0 + 1 + 4 + 6) ( 83 = 1 + 2 + 16 + 64)
//mesh: 85 rot[170, , ] mirror[ , , , ]; ( 85 = 0 + 2 + 4 + 6) ( 85 = 1 + 4 + 16 + 64)
//mesh: 87 rot[171, 93, 174] mirror[ , , , ]; ( 87 = 0 + 1 + 2 + 4 + 6) ( 87 = 1 + 2 + 4 + 16 + 64)
//mesh: 90 rot[165, , ] mirror[ , , , ]; ( 90 = 1 + 3 + 4 + 6) ( 90 = 2 + 8 + 16 + 64)
//mesh: 91 rot[173, 94, 167] mirror[ , , , ]; ( 91 = 0 + 1 + 3 + 4 + 6) ( 91 = 1 + 2 + 8 + 16 + 64)
//mesh: 95 rot[175, , ] mirror[ , , , ]; ( 95 = 0 + 1 + 2 + 3 + 4 + 6) ( 95 = 1 + 2 + 4 + 8 + 16 + 64)
//mesh: 112 rot[176, 208, 224] mirror[ , , , ]; (112 = 4 + 5 + 6) (112 = 16 + 32 + 64)
//mesh: 113 rot[184, 212, 226] mirror[178, 209, 232, 116]; (113 = 0 + 4 + 5 + 6) (113 = 1 + 16 + 32 + 64)
//mesh: 114 rot[177, 216, 228] mirror[ , , , ]; (114 = 1 + 4 + 5 + 6) (114 = 2 + 16 + 32 + 64)
//mesh: 115 rot[185, 220, 230] mirror[179, 217, 236, 118]; (115 = 0 + 1 + 4 + 5 + 6) (115 = 1 + 2 + 16 + 32 + 64)
//mesh: 117 rot[186, 213, 234] mirror[ , , , ]; (117 = 0 + 2 + 4 + 5 + 6) (117 = 1 + 4 + 16 + 32 + 64)
//mesh: 119 rot[187, 221, 238] mirror[ , , , ]; (119 = 0 + 1 + 2 + 4 + 5 + 6) (119 = 1 + 2 + 4 + 16 + 32 + 64)
//mesh: 120 rot[180, 210, 225] mirror[ , , , ]; (120 = 3 + 4 + 5 + 6) (120 = 8 + 16 + 32 + 64)
//mesh: 121 rot[188, 214, 227] mirror[182, 211, 233, 124]; (121 = 0 + 3 + 4 + 5 + 6) (121 = 1 + 8 + 16 + 32 + 64)
//mesh: 122 rot[181, 218, 229] mirror[ , , , ]; (122 = 1 + 3 + 4 + 5 + 6) (122 = 2 + 8 + 16 + 32 + 64)
//mesh: 123 rot[189, 222, 231] mirror[183, 219, 237, 126]; (123 = 0 + 1 + 3 + 4 + 5 + 6) (123 = 1 + 2 + 8 + 16 + 32 + 64)
//mesh: 125 rot[190, 215, 235] mirror[ , , , ]; (125 = 0 + 2 + 3 + 4 + 5 + 6) (125 = 1 + 4 + 8 + 16 + 32 + 64)
//mesh: 127 rot[191, 223, 239] mirror[ , , , ]; (127 = 0 + 1 + 2 + 3 + 4 + 5 + 6) (127 = 1 + 2 + 4 + 8 + 16 + 32 + 64)
//mesh: 240 rot[ , , ] mirror[ , , , ]; (240 = 4 + 5 + 6 + 7) (240 = 16 + 32 + 64 + 128)
//mesh: 241 rot[248, 244, 242] mirror[ , , , ]; (241 = 0 + 4 + 5 + 6 + 7) (241 = 1 + 16 + 32 + 64 + 128)
//mesh: 243 rot[249, 252, 246] mirror[ , , , ]; (243 = 0 + 1 + 4 + 5 + 6 + 7) (243 = 1 + 2 + 16 + 32 + 64 + 128)
//mesh: 245 rot[250, , ] mirror[ , , , ]; (245 = 0 + 2 + 4 + 5 + 6 + 7) (245 = 1 + 4 + 16 + 32 + 64 + 128)
//mesh: 247 rot[251, 253, 254] mirror[ , , , ]; (247 = 0 + 1 + 2 + 4 + 5 + 6 + 7) (247 = 1 + 2 + 4 + 16 + 32 + 64 + 128)
经过以上处理后,美术的工作量大大减少,townscaper使用的应该就是上述模型。
其实53个模型的制作依然是不小的工作量。 还有更进一步的算法继续减少建模工作量,可以利用反向思维将碎片自动添加到基本构型中。利用镜像和旋转对称性,我们只需要制作一个角落处的接缝碎片即可。
//碎片piece拼接规则:
//碎片名字 :"mTFAAFFAA-TFFFFFFF",
//第1个字母: r表示只旋转不镜像(rot),m表示带旋转加镜像(mirror),
//第2~9个字母:[e0==true & e1,4,5==false & eother==any] ,其中T表示true、F表示false、A表示any。
//第10个字母: -表示除去
1号碎片"TFAAAAAA-NNNNNNNN",// 面1: e0true 、e1false 、 other any - except none
表示只要体素满足:顶点0为TRUE 并且 顶点1为FALSE 并且 排除无,那么体素的构型中就会包含该面片。
static const char* pieceName[PieceNum] =
{
"TFAAAAAA",// 面1: 0true - 1false - other any -
"FTAAAAAA",// 面2: 1true - 0false - other any -
"TAAAFAAA",// 面3: 0true - 4false - other any -
"FAAATAAA",// 面4: 4true - 0false - other any -
"AAAATFAA",// 面5: 4true - 5false - other any -
"AAAAFTAA",// 面6: 5true - 4false - other any -
"TFAAFFAA",// 面7: 0true - 1,4,5false - other any -
"FTAAFFAA",// 面8: 1true - 0,4,5false - other any -
"TAAAFTAA",// 面9: 0,5true - 4false - other any -
"ATAATFAA",// 面10: 1,4true - 5false - other any -
};
bool HasPiece(int voxel,int piece)
{
const char* name = pieceName[piece];
const char* rule = name;
//piece拼接规则:
//"mTFAAFFAA-TFFFFFFF",
//第1个字母: r表示只旋转不镜像(rot),m表示带旋转加镜像(mirror),
//第2~9个字母:[e0==true & e1,4,5==false & eother==any]
//第10个字母: -表示除去
if (name[0]=='r' || name[0]=='m')
{
rule = name+1;
bool match = true;
for (int i=0;i<8;++i,++rule)
{
if ('T'==(*rule) && ((voxel&(1<<i))==0))
{
//match = false;
return false;
}
if ('F'==(*rule) && ((voxel&(1<<i))!=0))
{
return false;
}
//if ('N'==(*name) )
//{
// return false;
//}
}
}
else
{
return false;
}
//extra
if (name[9]=='+') //todo 需要和相邻的voxel相关?
{
//rule = name+10;
}
//execpt
else if (name[9]=='-')
{
rule = name+10;
for (int i=0;i<8;++i,++rule)
{
if ('T'==(*rule) && ((voxel&(1<<i))==0))
{
//execpt = false;
return true;
}
if ('F'==(*rule) && ((voxel&(1<<i))!=0))
{
return true;
}
if ('N'==(*rule) )
{
return true;
}
//if ('A'==(*name))
//{
//}
}
}
else
{
return false;
}
return false;
}
遍历所有的面片判断是否添加到某体素基本构型中,即 产生了构型第一象限中的组成部分。构型 其它三个象限可以通过镜像或旋转变换到第一象限,逐个添加好面片后再将模型逆向变换回原来象限即可。
void MarchingTownSys::GenMovieLib(int stage,const char* filename)
{
MovieClip pieceLib;
pieceLib.LoadFromFile(filename);
Mesh* pieceMeshArr[PieceNum];
for (int i=0;i<PieceNum;i++)
{
MovieClip* movie = pieceLib.GetMovieClip(pieceName[i]);
if (movie)
{
pieceMeshArr[i] = movie->GetMesh();
}
else
{
pieceMeshArr[i] = NULL;
}
}
for (int voxel=0;voxel<256;voxel++)
{
MovieClip* movie = m_voxelMovies[stage][voxel];
if (movie)
{
Mesh* mesh = movie->GetMesh();
int vVertexNum = 0;
int trigonNum = 0;
int voxelTemp = voxel;
//四次旋转对称 计算容量
for (int r=0;r<4;r++)
{
for (int p=0;p<PieceNum;p++)
{
Mesh* pieceMesh = pieceMeshArr[p];
if (HasPiece(voxelTemp,p) && pieceMesh)
{
vVertexNum += pieceMesh->m_vVertexNum;
trigonNum += pieceMesh->m_trigonNum;
}
}
voxelTemp = MarchingTables::VoxelRotY90[voxelTemp];
}
mesh->Resize(vVertexNum,vVertexNum,trigonNum);
//四次旋转对称
vVertexNum = 0;
trigonNum = 0;
voxelTemp = voxel;
for (int r=0;r<4;r++)
{
for (int p=0;p<PieceNum;p++)
{
Mesh* pieceMesh = pieceMeshArr[p];
//旋转到一号角判断有无piece
if (HasPiece(voxelTemp,p) && pieceMesh)
{
//将piece逆向旋转添加到模型
int meshRot = (4-r)%4;
int mirror = 0;
Mesh::Vertex* vDstV = mesh->m_vVertexs + vVertexNum;
Mesh::TVertex* vDstT = mesh->m_tVertexs + vVertexNum;
Mesh::Vertex* vSrcV = pieceMesh->m_vVertexs;
Mesh::TVertex* vSrcT = pieceMesh->m_tVertexs;
float posX,posZ,nx,nz;
for (int i=0;i<pieceMesh->m_vVertexNum;i++)
{
posX = vSrcV->x;
posZ = vSrcV->z;
nx= vSrcV->nx;
nz= vSrcV->nz;
if (mirror)
{
posX *= -1;
nx *= -1;
}
switch(meshRot)
{
case 0:
vDstV->x = posX;
vDstV->z = posZ;
vDstV->nx = nx;
vDstV->nz = nz;
break;
case 1://90
vDstV->x = posZ;
vDstV->z = -posX;
vDstV->nx = nz;
vDstV->nz = -nx;
break;
case 2://180
vDstV->x = -posX;
vDstV->z = -posZ;
vDstV->nx = -nx;
vDstV->nz = -nz;
break;
case 3://270
vDstV->x = -posZ;
vDstV->z = posX;
vDstV->nx = -nz;
vDstV->nz = nx;
break;
}
vDstV->y = vSrcV->y;
vDstV->ny = vSrcV->ny;
vDstT->u = vSrcT->u;
vDstT->v = vSrcT->v;
vDstT->w = vSrcT->w;
vSrcV++;
vSrcT++;
vDstV++;
vDstT++;
}
int offsetIndex = vVertexNum;
vVertexNum += pieceMesh->m_vVertexNum;
Mesh::Trigon* srcTrigon = pieceMesh->m_trigons;
Mesh::Trigon* dstTrigon = mesh->m_trigons+trigonNum;
for (int i=0;i<pieceMesh->m_trigonNum;i++)
{
if (mirror)
{
//时针序
dstTrigon->vIndex[0] = srcTrigon->vIndex[2] + offsetIndex;
dstTrigon->vIndex[1] = srcTrigon->vIndex[1] + offsetIndex;
dstTrigon->vIndex[2] = srcTrigon->vIndex[0] + offsetIndex;
}
else
{
dstTrigon->vIndex[0] = srcTrigon->vIndex[0] + offsetIndex;
dstTrigon->vIndex[1] = srcTrigon->vIndex[1] + offsetIndex;
dstTrigon->vIndex[2] = srcTrigon->vIndex[2] + offsetIndex;
}
srcTrigon++;
dstTrigon++;
}
trigonNum += pieceMesh->m_trigonNum;
}
}
voxelTemp = MarchingTables::VoxelRotY90[voxelTemp];
}
//for test
mesh->CreateBuff();
}
}
}
本算法用到的一套模型碎片:
也可以不预先将碎片拼接成基本体素,而是在最终拼接模型时直接使用碎片拼接这样更方便处理随机效果(在多套模型中随机取一个即可,给相邻的碎片使用相同的随机种子,这样就不会出现裂缝)。
通过四边形变形技术将正方形的Marchingcube变为不规则的形状,可以使建筑物不总是看起来像方块。这一步使用的是FFD2*2*2算法,或者简单的双线性差值也可以做到。(也可以使用FD3*3*3,中心控制点是可以随机偏移的,不会导致裂缝出现)
效果图:
源码:
//========================================================
// @Date: 2016.05
// @File: Include/Math/MarchingTowns.h
// @Brief: MarchingTowns
// @Author: LouLei
// @Email: twopointfive@163.com
// @Copyright (Crapell) - All Rights Reserved
//========================================================
#ifndef __MarchingTowns__H__
#define __MarchingTowns__H__
#include "Math/MathLib.h"
#include "Texture.h"
namespace RendSys
{
class MovieClip;
class Mesh;
};
class VertexBuffer;
class IndexBuffer;
class MarchingTownSys
{
public:
typedef int IndexInt;
#define FVF_VERTEX (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1)
struct SVertex
{
float pos[3];
float normal[3];
float uvw[3];
};
class VoxelBlock;
class EnergyCell
{
public:
int IntersectRay (const vec3 &start, const vec3 &end, vec3 &resPos);
//3~6边形面围成5~8面体 将能量点围在中间,两个能量点连线穿过多面体的面。 边界位置的面可能不完整(只有两个点)不被记录
// e e
// : .
// +----------+
// /|v4 : . /|v5
// / | : . / | y+
// +----------+ | |
// v7| | :. |v6| |
// e...|..|...e...|..|....e |
// | | .: | | +-------> x+
// | +-------|--+ /
// | / v0 : | / v1 /
// |/ . : |/ z+
// +----------+
// v3. : v2
// . e
// e
class Face
{
public:
bool IntersectRay (const vec3 &start, const vec3 &end, vec3 &resPos);
VoxelBlock* vox[6]; //多面体顶点为邻接体素的中心
int vNum;
EnergyCell* nei; //穿过面连接到另一个能量点
//多面体的边穿过两个相邻体素的公共面
};
float energy;
unsigned int seed;
vec3I index;
vec3 pos;
Face faces[8];
int faceNum;
};
class VoxelBlock
{
public:
bool IsPointIn(const vec3& point) const;
// +----------+
// /|e4 /|e5
// / | / | y+
// +----------+ | |
// e7| | |e6| |
// | | v | | |
// | | | | +-------> x+
// | +-------|--+ /
// | / e0 | / e1 /
// |/ |/ z+
// +----------+
// e3 e2
vec3I index;
vec3 cen;
int voxelCase;
EnergyCell* energy8[8]; //平行六面体boundbox ffd自由变形
int column; //柱子
// +-----------------+
// /| /|
// / | / | y+
// / | / | |
// +-----------------+ | |
// | | | | |
// | | | | |
// | |f0 f1 | |f2 +-------> x+
// | +--------+----|---+ /
// | / | / /
// | +f3 +f4 | +f5 z+
// |/ |/
// +--------+--------+
// f6 f7 f8
vec3 ffd3[9];
//for debug
vec4 planes[6];
};
enum Stage
{
Normal = 0, //普通墙体 平房顶
Tileroof = 1, //瓦片屋顶
Pierground = 2, //桥墩地基
Handrail = 3, //扶手栏杆
StageNum,
};
class MarchingPiece
{
public:
MarchingPiece();
bool HasMirror();
RendSys::Mesh* GetMesh(unsigned int seed);
bool Partof(int voxel);
char name[32];//去掉rand后缀
int randNum;
RendSys::Mesh* mesh[8]; //同类模型最多8个随机外观
};
public:
MarchingTownSys();
~MarchingTownSys();
//!voxelGridSize:体素格子大小,8个格子是一个体素。 正四面体:8个势能格子全1,8格子周围全0。
void Init(const vec3I& voxelGridSize,const vec3& groundExtend);
void SetMovieLib(const char* movieFile);
void SetPieceLib(const char* movieFile);
void Free();
void DistortStandard(float cornerDistort,float cenDistort);
void Rand();
void Clear();
void Update();
void Render();
void RenderEdit();
void SetGroundPos(const vec3& pos);
//protected:
//!计算势能
float GetGridEnergy(int cx,int cy,int cz);
void SetEnergy(float val,int seed,int cx,int cy,int cz);
void MakeBound();
//last 即使为空也拣选
EnergyCell* PickGrid(const vec3& rayPos,const vec3& rayDir,bool last=true);
void ClearEnergys();
void GenSurface();
//方案一:
//!计算体素面片
bool GenVoxelFragment(int stage,VoxelBlock* block);
void GenMovieLibWithPiece(int stage,const char* filename);//从piece预生成lib,随机性不好
void SetTableMovie(int stage,RendSys::MovieClip* movie,const char* name);
//方案二: 冗余顶点比方案一多 最好焊接一下顶点 接头处不好建模时可以使用装饰物遮挡
//!计算体素面片
bool GenVoxelFragmentWithPiece(int stage,VoxelBlock* block);
void GenVoxelMeshWithPiece(int stage,int voxelCase,RendSys::Mesh* mesh, unsigned int seed[8]);
void SetTablePiece(int stage,RendSys::MovieClip* movie,const char* name);
//
bool AddMeshFFD2(int meshRot,int mirror,RendSys::Mesh* mesh, VoxelBlock* block);
bool AddMeshFFD3(int meshRot,int mirror,RendSys::Mesh* mesh, VoxelBlock* block);
//计算法线
void ComputeNormal();
//计算环境光遮蔽
void ComputeLumen(SVertex *pVertex,int cx,int cy,int cz);
//EnergyGrid 和VoxelGrid 偏离了半格
inline vec3 EnergyToWorld (const vec3 &energyGrid);
inline vec3 WorldToEnergyI(const vec3 &World);
inline vec3 WorldToEnergyF(const vec3 &World);
public:
//方案一:
RendSys::MovieClip* m_movieLib;
RendSys::MovieClip* m_voxelMovies[StageNum][256];
//方案二:
RendSys::MovieClip* m_pieceLib;
int PieceNum;
#define PieceNum PieceNum
MarchingPiece pieceArr[256];
//柱子 柱子模型需要坐在原点
RendSys::MovieClip* m_columnMovies[256];
bool m_visible;
int m_seed;
int FFDSTYLE;
float m_cornerDistort;
float m_cenDistort;
vec3 m_halfGroundExtend;
vec3 m_groundCen;
vec3 m_groundMin;
vec3 m_groundMax;
float m_threshould;
//!一个体素voxel格子 占 8个1/4势能energy格子
vec3I EnergyGridSize;
int EnergyGridSizeXY;
vec3I VoxelGridSize; //==EnergyGridSize-1
int VoxelGridSizeXY;
vec3 VoxelGridExtend;
vec3 HalfVoxelGridExtend;
EnergyCell* m_gridEnergys; //EnergyGridSize
VoxelBlock* m_voxels; //VoxelGridSize
int IndexsMax;
SVertex* m_vertexs;
int m_vertexNum;
IndexInt* m_indexs;
int m_indexNum;
VertexBuffer* m_vertexBuffer;
IndexBuffer* m_indexBuffer;
int m_brushType; //0 添加 删除 随机外观
int m_continueBrush;
vec3 m_pickedPos;
EnergyCell* m_pickedEnergyCell;
int m_pickedEnergyFace;
VoxelBlock* m_pickedVoxelBlock;
char debugStr[512];
TexturePtr debugTexture;
};
inline vec3 MarchingTownSys ::EnergyToWorld(const vec3 &energyGrid)
{
//EnergyGrid 从 m_groundMin - HalfVoxelGridExtend开始
//VoxelGrid 从 m_groundMin开始
return energyGrid.Mult(VoxelGridExtend) + m_groundMin - HalfVoxelGridExtend;
}
inline vec3 MarchingTownSys ::WorldToEnergyI(const vec3 &World)
{
vec3 energyGrid = (World - m_groundMin + HalfVoxelGridExtend) / VoxelGridExtend;
energyGrid.x = floor(energyGrid.x);
energyGrid.y = floor(energyGrid.y);
energyGrid.z = floor(energyGrid.z);
return energyGrid;
}
inline vec3 MarchingTownSys ::WorldToEnergyF(const vec3 &World)
{
vec3 energyGrid = (World - m_groundMin + HalfVoxelGridExtend) / VoxelGridExtend;
energyGrid.x = (energyGrid.x);
energyGrid.y = (energyGrid.y);
energyGrid.z = (energyGrid.z);
return energyGrid;
}
#endif