-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBlock.js
More file actions
252 lines (240 loc) · 9.74 KB
/
Block.js
File metadata and controls
252 lines (240 loc) · 9.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
import { asyncLoadResByUrl } from "./loadResources.js";
import { textureMipmapByTile, prepareTextureAarray, blockInventoryTexture } from "./processingPictures.js";
// 用32位储存方块id和data。高16位存data,低16位存id。
// 由于 data 中 0 出现的概率是最高的,因此放在高位。
// 这样做的目的是在转换为字符串后更短,在网络传输中有一定优势。
// TODO: 渲染和计算时需要频繁读取方块 此时进行位运算不如将id和bd分开两个普通数组存放快
// 例如在i5-5300U上遍历区块计算光照并重新渲染就要在这里耗时20ms以上 是难以接受的
class LongID extends Number {
constructor(id = 0, bd = 0) {
super(bd << 16 | id);
};
get id() { return this & 0xFFFF; };
get bd() { return this >>> 16 };
};
let defaultBlockTextureImg = null, blocksCfg = null;
const BlockRenderType = {
NORMAL: Symbol("block render type: normal"),
FLOWER: Symbol("block render type: flower"),
CACTUS: Symbol("block render type: cactus"),
FLUID: Symbol("block render type: fluid"),
};
// BLOCKS: block name -> block blockIDs: block id -> [db] -> block
const BLOCKS = {}, blockIDs = {};
const findEmptyIdSlot = () => {
for (let i = 0; i <= 0xFFFF; ++i)
if (!(i in blockIDs)) return i;
return -1;
};
class Block {
constructor(blockName, {
opacity = 15,
luminance = 0,
renderType = Block.renderType.NORMAL,
stackable = 64,
textureImg = defaultBlockTextureImg,
texture: textureCoord = [[16, 32]],
friction = 1,
id = findEmptyIdSlot(),
bd = 0,
showName = blockName.toLowerCase().replace(/_/g, " ").replace(/^\w|\s\w/g, w => w.toUpperCase()),
isLeaves = blockName.endsWith("leaves"),
isGlass = blockName.endsWith("glass"),
isFluid = renderType == Block.renderType.FLUID,
maxLevel = 8,
...others
} = {}) {
this.name = blockName;
this.isFluid = isFluid;
if (isFluid) this.maxLevel = maxLevel - 1;
// if (renderType == Block.renderType.FLUID)
// renderType = Block.renderType.NORMAL;
this.renderType = renderType;
this.vertices = Block.getVerticesByRenderType(renderType);
this.elements = Block.getElementsByRenderType(renderType);
this.texture = Block.getTexUVByTexCoord({
renderType, name: blockName,
coordinate: textureCoord,
texImg: textureImg,
});
this.opacity = opacity;
this.luminance = luminance;
this.stackable = stackable;
this.friction = friction;
this.id = id;
this.bd = bd;
this.longID = new LongID(id, bd);
blockIDs[this.longID] = this;
BLOCKS[blockName] = this;
this.texture.inventory = blockInventoryTexture(this);
this.showName = showName;
this.isLeaves = isLeaves; this.isGlass = isGlass;
for (let k in others) this[k] = others[k];
};
get isOpaque() { return this.opacity === 15; };
get isTransparent() { return this.opacity === 0; };
get isTranslucent() { return this.opacity !== 0 && this.opacity !== 15; };
static getTexUVByTexCoord({
renderType, name = "Block",
coordinate = [[16, 32]],
texImg = defaultBlockTextureImg,
} = {}) {
for (let texCoord of coordinate) {
let [x, y] = texCoord;
texCoord[0] = y - 1; texCoord[1] = x - 1;
}
const uv = {}, ans = { img: texImg, uv, coordinate, };
let xsize = 1 / 32, ysize = 1 / 16,
calculateOffset = i => texImg.mipmap? i / 4: 0,
// 有 texture4array 意味着是纹理数组 (webgl2)
dx = texImg.texture4array? texImg.texture4array.tileCount[0]: calculateOffset(xsize),
dy = texImg.texture4array? texImg.texture4array.tileCount[1]: calculateOffset(ysize),
cr2uv = texImg.texture4array? ([x, y]) => [
0, 0, (x + y * dx),
0, 1, (x + y * dx),
1, 1, (x + y * dx),
1, 0, (x + y * dx),
]: ([x, y]) => [
x*xsize+dx, y*ysize+dy, 0,
x*xsize+dx, (y+1)*ysize-dy, 0,
(x+1)*xsize-dx, (y+1)*ysize-dy, 0,
(x+1)*xsize-dx, y*ysize+dy, 0,
];
switch (renderType) {
case BlockRenderType.CACTUS:
case BlockRenderType.NORMAL: {
switch (coordinate.length) {
case 1: {
let uvw = cr2uv(coordinate[0]);
"x+,x-,y+,y-,z+,z-".split(",").map(k => uv[k] = uvw);
break; }
case 2: {
uv["y+"] = uv["y-"] = cr2uv(coordinate[0]);
let uvw = cr2uv(coordinate[1]);
"x+,x-,z+,z-".split(",").forEach(k => uv[k] = uvw);
break; }
case 3: {
uv["y+"] = cr2uv(coordinate[0]);
uv["y-"] = cr2uv(coordinate[1]);
let uvw = cr2uv(coordinate[2]);
"x+,x-,z+,z-".split(",").forEach(k => uv[k] = uvw);
break; }
case 4: {
uv["y+"] = cr2uv(coordinate[0]);
uv["y-"] = cr2uv(coordinate[1]);
uv["x+"] = uv["x-"] = cr2uv(coordinate[2]);
uv["z+"] = uv["z-"] = cr2uv(coordinate[3]);
break; }
case 6: {
"x+,x-,y+,y-,z+,z-".split(",").forEach((k, i) => uv[k] = cr2uv(coordinate[i]));
break; }
default: throw name + " texture translate error: array length";
}
break; }
case BlockRenderType.FLOWER: {
if (coordinate.length > 2) throw name + " texture translate error: array length";
let uvw = cr2uv(coordinate[0]);
uv["face"] = [...uvw, ...uvw];
break; }
case BlockRenderType.FLUID: {
if (coordinate.length > 2) throw name + " texture translate error: array length";
let uvw = cr2uv(coordinate[0]);
"x+,x-,y+,y-,z+,z-".split(",").forEach(k => uv[k] = uvw);
break; }
}
return ans;
};
static get renderType() {
return BlockRenderType;
};
static getVerticesByRenderType(renderType) {
return blocksCfg.vertices[renderType] || {};
};
static getElementsByRenderType(renderType) {
return Object.entries(this.getVerticesByRenderType(renderType)).map(([face, vs]) => ({[face]: (len => {
if (!len) return [];
let base = [0,1,2, 0,2,3], out = [];
for(let i=0,j=0; i<len; j=++i*4)
out.push(...base.map(x => x+j));
return out;
})(vs.length/12)})).reduce((ac, o) => ({...ac, ...o}), {});
};
static getBlockByBlockName(blockName) {
return BLOCKS[blockName] || null;
};
static getBlockByBlockIDandData(id, bd = 0) {
return blockIDs[new LongID(id, bd)] || blockIDs[id] || null;
};
static getBlockByBlockLongID(longID) {
let t = blockIDs[longID];
if (t) return t;
longID = longID instanceof LongID? longID: new LongID(longID);
return blockIDs[longID.id] || null;
};
static listBlocks() {
return Object.values(BLOCKS);
};
static get defaultBlockTextureImg() {
return defaultBlockTextureImg;
};
};
Block.preloaded = Promise.all([
asyncLoadResByUrl("./terrain-atlas.png").then(img => {
defaultBlockTextureImg = img;
if (isSupportWebGL2)
prepareTextureAarray(img);
else textureMipmapByTile(img);
}),
asyncLoadResByUrl("./blocks.json").then(obj => {
// index_renderType = [index -> BlockRenderType[render type]]
let index_renderType = obj.index_renderType = [];
Object.entries(obj.block_renderType_index).forEach(([type, i]) => {
index_renderType[i] = BlockRenderType[type.toUpperCase()];
});
// blocksCfg.blocks.renderType = BlockRenderType[render type]
Object.entries(obj.blocks).forEach(([, block]) => {
if ("renderType" in block)
block.renderType = index_renderType[block.renderType];
});
let brtv = obj.block_renderType_vertex;
obj.vertices = {
[BlockRenderType.NORMAL]:
("x+:2763,x-:0541,y+:0123,y-:4567,z+:1472,z-:3650")
.split(",").map(s => s.split(":"))
.map(([face, vs]) => {
return ({[face]: [...vs].map(i => brtv.normal[i]).reduce((ac, d) => {ac.push(...d); return ac;},[])});
})
.reduce((ac, o) => ({...ac, ...o}), {}),
[BlockRenderType.FLOWER]:
("face:14630572").split(",").map(s => s.split(":"))
.map(([face, vs]) => {
return ({[face]: [...vs].map(i => brtv.flower[i]).reduce((ac, d) => {ac.push(...d); return ac;},[])});
})
.reduce((ac, o) => ({...ac, ...o}), {}),
[BlockRenderType.CACTUS]:
("x+:12 13 14 15,x-:20 21 22 23,y+:0 1 2 3,y-:4 5 6 7,z+:8 9 10 11,z-:16 17 18 19")
.split(",").map(s => s.split(":"))
.map(([face, vs]) => {
return ({[face]: vs.split(" ").map(i => brtv.cactus[i]).reduce((ac, d) => {ac.push(...d); return ac;},[])});
})
.reduce((ac, o) => ({...ac, ...o}), {}),
[BlockRenderType.FLUID]:
("x+:2763,x-:0541,y+:0123,y-:4567,z+:1472,z-:3650")
.split(",").map(s => s.split(":"))
.map(([face, vs]) => {
return ({[face]: [...vs].map(i => brtv.fluid[i]).reduce((ac, d) => ac.concat(d), [])});
})
.reduce((ac, o) => ({...ac, ...o}), {}),
};
blocksCfg = obj;
}),
]).then(() => {
Object.entries(blocksCfg.blocks).forEach(([blockName, cfg]) => {
new Block(blockName, cfg);
});
});
export {
Block,
Block as default,
LongID,
};