I’m working on an “infinite” minesweeper project right now I’m making a sprite for each cell.
I’m using different uv coordinates per cell type (single texture atlas).
Since THREE Sprite class makes all instance share the same geometry, and since UV are stored under geometry.attributes.uv
I’m making a different geometry per cell to be able to use sprite.geometry.attributes.uv.setXY()
individually.
Sprite of a Grid instance to show a chunk of the board:
for (var y = 0; y < this.h; y++) {
for (var x = 0; x < this.w; x++) {
var sprite = new THREE.Sprite(material);
var cell = get_cell_at(this.originX+x, this.originY+y)
sprite.geometry = new THREE.PlaneBufferGeometry()
sprite.center = new THREE.Vector2(0,0)
sprite.geometry.attributes.uv.setXY(0, get_uv(cell, 0, COOR_X), get_uv(cell, 0, COOR_Y))
sprite.geometry.attributes.uv.setXY(1, get_uv(cell, 1, COOR_X), get_uv(cell, 1, COOR_Y))
sprite.geometry.attributes.uv.setXY(3, get_uv(cell, 2, COOR_X), get_uv(cell, 2, COOR_Y))
sprite.geometry.attributes.uv.setXY(2, get_uv(cell, 3, COOR_X), get_uv(cell, 3, COOR_Y))
sprite.geometry.attributes.uv.needsUpdate = true;
sprite.position.x = this.originX + x
sprite.position.y = this.originY + y
sprite.position.z = 0
this.sprites.push(sprite)
scene.add(sprite);
}
}
And orthographic camera moves around and triggers the mapping/unmapping of chunks:
In red is the camera view moving around the board, in blue are the aligned chunks. At zoom = 1, the size of the camera view is the same as the chunk so I just need 4 chunks loaded at any time.
When a chunk goes out of view and a new one enters it, I just “move” and reset the old one like so:
move_to(originX, originY) {
this.originX = originX
this.originY = originY
for (var y = 0; y < this.h; y++) {
for (var x = 0; x < this.w; x++) {
const i = y*this.w + x
var s = this.sprites[i]
s.position.x = this.originX+x
s.position.y = this.originY+y
var cell = get_cell_at(this.originX+x, this.originY+y)
s.geometry.attributes.uv.setXY(0, get_uv(cell, 0, COOR_X), get_uv(cell, 0, COOR_Y))
s.geometry.attributes.uv.setXY(1, get_uv(cell, 1, COOR_X), get_uv(cell, 1, COOR_Y))
s.geometry.attributes.uv.setXY(3, get_uv(cell, 2, COOR_X), get_uv(cell, 2, COOR_Y))
s.geometry.attributes.uv.setXY(2, get_uv(cell, 3, COOR_X), get_uv(cell, 3, COOR_Y))
s.geometry.attributes.uv.needsUpdate = true;
}
}
}
Although this all works, it’s not as fast and smooth as I’d like it to be. Especially as you zoom out and more chunks are needed to fill the view (red rectange >> blue ones).
How would you optimize this?
- a) I could cache a chunk as 1 texture (is there a simple way to create texture from my three.js grid? or do I need to use some js image creation lib)
- b) instanciante all cells to make it use different position and uv attributes. is this possible with three.js?
- c) have 1 geometry for a complete grid chunk… but for a vertex shared by multiple cells I need different UV depending on which cell I’m filling… is this impossible?
- any other ideas? what seems better to you, and are there any profiling tools for firefox/chrome/three.js for webgl
My current prototype is here http://zbeul.ist/dev/frontend/render.html
You can zoom & pan with the mouse (chunk loading at zoom < 1 is not fully implemented yet)