Created
August 23, 2020 15:55
-
-
Save devent/65f08bbf06f97217a3ad11fe70d7cd22 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright © 2020 Erwin Müller (erwin@muellerpublic.de) | |
* | |
* This program is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation, either version 3 of the License, or | |
* (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
package com.anrisoftware.dwarfhustle.gamemainmap.terrain.states; | |
import static java.lang.String.format; | |
import static org.apache.commons.math3.util.FastMath.round; | |
import java.nio.FloatBuffer; | |
import java.util.List; | |
import org.eclipse.collections.api.list.primitive.MutableIntList; | |
import org.eclipse.collections.api.map.primitive.MutableObjectIntMap; | |
import org.eclipse.collections.impl.factory.Lists; | |
import org.eclipse.collections.impl.factory.primitive.IntLists; | |
import org.eclipse.collections.impl.factory.primitive.ObjectIntMaps; | |
import org.eclipse.collections.impl.map.mutable.primitive.ObjectIntHashMap; | |
import com.jme3.bounding.BoundingBox; | |
import com.jme3.collision.CollisionResults; | |
import com.jme3.math.Ray; | |
import com.jme3.math.Vector2f; | |
import com.jme3.math.Vector3f; | |
import com.jme3.scene.Geometry; | |
import com.jme3.scene.Mesh; | |
import com.jme3.scene.VertexBuffer.Type; | |
import com.jme3.util.BufferUtils; | |
import lombok.AllArgsConstructor; | |
import lombok.Data; | |
public class TerrainBox { | |
@Data | |
@AllArgsConstructor | |
private static class QuadVertex { | |
Vector3f ver; | |
Vector3f nor; | |
Vector2f tex; | |
int x; | |
int y; | |
} | |
private final int size; | |
private final List<QuadVertex> quads; | |
private final MutableIntList inds; | |
private final Geometry geometry; | |
private final Mesh mesh; | |
private final Vector3f pos; | |
private final int[] indices = new int[30]; | |
private final int x; | |
private final int y; | |
private final BoundingBox boundingBox; | |
private TerrainBox a; | |
private TerrainBox b; | |
private TerrainBox c; | |
private TerrainBox d; | |
private FloatBuffer verbuff; | |
public TerrainBox(int size) { | |
this(new Geometry(), new Mesh(), size, Lists.mutable.ofInitialCapacity(calcSize(size)), | |
IntLists.mutable.empty(), new Vector3f(-size / 2 + 1, 0, -size / 2 + 1), | |
new BoundingBox(new Vector3f(1, 0, 1), size / 2f, size / 2f, 1f), 0, 0); | |
} | |
private static int calcSize(int size) { | |
return size * size + 2 * size + 1; | |
} | |
public TerrainBox(Geometry geometry, Mesh mesh, int size, List<QuadVertex> quads, MutableIntList inds, Vector3f pos, | |
BoundingBox boundingBox, int x, int y) { | |
this.geometry = geometry; | |
this.mesh = mesh; | |
this.size = size; | |
this.quads = quads; | |
this.inds = inds; | |
this.pos = pos; | |
this.boundingBox = boundingBox; | |
this.x = x; | |
this.y = y; | |
} | |
public TerrainBox build() { | |
buildQuads(null, size / 2 - 1, 1f / size); | |
buildMesh(); | |
return this; | |
} | |
public TerrainBox collideWith(Ray other, CollisionResults results) { | |
if (isLeaf()) { | |
return this; | |
} | |
if (a.boundingBox.collideWith(other, results) != 0) { | |
return a.collideWith(other, results); | |
} | |
if (b.boundingBox.collideWith(other, results) != 0) { | |
return b.collideWith(other, results); | |
} | |
if (c.boundingBox.collideWith(other, results) != 0) { | |
return c.collideWith(other, results); | |
} | |
if (d.boundingBox.collideWith(other, results) != 0) { | |
return d.collideWith(other, results); | |
} | |
return null; | |
} | |
public void setHeights(int[] xs, int[] ys, float height) { | |
for (int i = 0; i < xs.length; i++) { | |
var q = getPatch(xs[i] - (size / 2 - 1), ys[i] - (size / 2 - 1)); | |
setHeight(q.indices, height); | |
} | |
} | |
public void setHeight(int x, int y, float height) { | |
var q = getPatch(x - (size / 2 - 1), y - (size / 2 - 1)); | |
setHeight(q.indices, height); | |
} | |
public void setHeights(float[][] heights) { | |
for (int x = 0; x < heights.length; x++) { | |
for (int y = 0; y < heights[x].length; y++) { | |
var q = getPatch(x - (size / 2 - 1), y - (size / 2 - 1)); | |
setHeight(q.indices, heights[x][y]); | |
} | |
} | |
} | |
public void setHeight(int[] indices, float height) { | |
for (int i = 0; i < indices.length; i++) { | |
int ix = indices[i]; | |
var q = quads.get(indices[i]); | |
float y = q.ver.y + height; | |
verbuff.put(ix * 3 + 1, y); | |
} | |
mesh.getBuffer(Type.Position).setUpdateNeeded(); | |
} | |
public boolean isLeaf() { | |
return a == null && b == null && c == null && d == null; | |
} | |
public Mesh getMesh() { | |
return mesh; | |
} | |
public Geometry getGeometry() { | |
return geometry; | |
} | |
public int getX() { | |
return x; | |
} | |
public int getY() { | |
return y; | |
} | |
public TerrainBox getPatch(float x, float z) { | |
int h = size / 2; | |
if (h == 0) { | |
return this; | |
} | |
if (a.pos.x <= x && x < (h + a.pos.x) && a.pos.z <= z && z < (h + a.pos.z)) { | |
return a.getPatch(x, z); | |
} | |
if (b.pos.x <= x && x < (h + b.pos.x) && b.pos.z <= z && z < (h + b.pos.z)) { | |
return b.getPatch(x, z); | |
} | |
if (c.pos.x <= x && x < (h + c.pos.x) && c.pos.z <= z && z < (h + c.pos.z)) { | |
return c.getPatch(x, z); | |
} | |
if (d.pos.x <= x && x < (h + d.pos.x) && d.pos.z <= z && z < (h + d.pos.z)) { | |
return d.getPatch(x, z); | |
} | |
return null; | |
} | |
@Override | |
public String toString() { | |
if (isLeaf()) { | |
return format("%s[x=%d,y=%d,p=%s,b=%s]", getClass().getSimpleName(), x, y, pos, boundingBox); | |
} | |
return format("%s[%s,x=%d,y=%d,p=%s,b=%s,a=%s,b=%s,c=%s,d=%s]", getClass().getSimpleName(), geometry, x, y, pos, | |
boundingBox, a, b, c, d); | |
} | |
private void buildMesh() { | |
Vector3f[] v = new Vector3f[quads.size()]; | |
Vector2f[] t = new Vector2f[quads.size()]; | |
Vector3f[] n = new Vector3f[quads.size()]; | |
for (int i = 0; i < quads.size(); i++) { | |
v[i] = quads.get(i).ver; | |
t[i] = quads.get(i).tex; | |
n[i] = quads.get(i).nor; | |
} | |
int[] i = inds.toArray(); | |
verbuff = BufferUtils.createFloatBuffer(v); | |
mesh.setBuffer(Type.Position, 3, verbuff); | |
mesh.setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(n)); | |
mesh.setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(t)); | |
mesh.setBuffer(Type.Index, 1, BufferUtils.createIntBuffer(i)); | |
mesh.updateBound(); | |
geometry.setMesh(mesh); | |
} | |
private TerrainBox buildQuads(QuadVertex quad, int sizeh, float texf) { | |
if (size > 1) { | |
float h = size / 2f; | |
float h2 = size / 4f; | |
var posa = pos.add(0, 0, 0); | |
a = new TerrainBox(geometry, mesh, size / 2, quads, inds, posa, | |
new BoundingBox(pos.add(h2, 0, h2), h2, 0, h2), round(sizeh + posa.x), round(sizeh + posa.z)) | |
.buildQuads(null, sizeh, texf); | |
var posb = pos.add(h, 0, 0); | |
b = new TerrainBox(geometry, mesh, size / 2, quads, inds, posb, | |
new BoundingBox(pos.add(h2 + h2 + h2, 0, h2), h2, 0, h2), round(sizeh + posb.x), | |
round(sizeh + posb.z)).buildQuads(null, sizeh, texf); | |
Vector3f posc = pos.add(0, 0, h); | |
c = new TerrainBox(geometry, mesh, size / 2, quads, inds, posc, | |
new BoundingBox(pos.add(h2, 0, h2 + h2 + h2), h2, 0, h2), round(sizeh + posc.x), | |
round(sizeh + posc.z)).buildQuads(null, sizeh, texf); | |
Vector3f posd = pos.add(h, 0, h); | |
d = new TerrainBox(geometry, mesh, size / 2, quads, inds, posd, | |
new BoundingBox(pos.add(h2 + h2 + h2, 0, h2 + h2 + h2), h2, 0, h2), round(sizeh + posd.x), | |
round(sizeh + posd.z)).buildQuads(null, sizeh, texf); | |
} | |
if (size == 1) { | |
MutableObjectIntMap<QuadVertex> quadIndices = ObjectIntMaps.mutable.ofInitialCapacity(30); | |
setupIndicesTop(quadIndices, sizeh, texf, 0); | |
setupIndicesNorth(quadIndices, sizeh, texf, 6, -0.001f); | |
setupIndicesEast(quadIndices, sizeh, texf, 12, -0.001f); | |
setupIndicesSouth(quadIndices, sizeh, texf, 18, -0.001f); | |
setupIndicesWest(quadIndices, sizeh, texf, 24, -0.001f); | |
} | |
return this; | |
} | |
private void setupIndicesTop(MutableObjectIntMap<QuadVertex> quadIndices, int sizeh, float texf, int i) { | |
int h = size; | |
var posa = pos.add(0, 0, 0); | |
var nora = new Vector3f(0, 1, 0); | |
var texa = new Vector2f((sizeh + posa.x) * texf, 1f - (sizeh + posa.z) * texf); | |
var qa = new QuadVertex(posa, nora, texa, x, y); | |
var ia = addQuad(qa, quadIndices); | |
var posb = pos.add(0, 0, h); | |
var norb = new Vector3f(0, 1, 0); | |
var texb = new Vector2f((sizeh + posb.x) * texf, 1f - (sizeh + posb.z) * texf); | |
var qb = new QuadVertex(posb, norb, texb, x, y); | |
var ib = addQuad(qb, quadIndices); | |
var posc = pos.add(h, 0, h); | |
var norc = new Vector3f(0, 1, 0); | |
var texc = new Vector2f((sizeh + posc.x) * texf, 1f - (sizeh + posc.z) * texf); | |
var qc = new QuadVertex(posc, norc, texc, x, y); | |
var ic = addQuad(qc, quadIndices); | |
var posd = pos.add(h, 0, 0); | |
var nord = new Vector3f(0, 1, 0); | |
var texd = new Vector2f((sizeh + posd.x) * texf, 1f - (sizeh + posd.z) * texf); | |
var qd = new QuadVertex(posd, nord, texd, x, y); | |
var id = addQuad(qd, quadIndices); | |
inds.addAll(ia, ib, ic, ic, id, ia); | |
indices[i++] = ia; | |
indices[i++] = ib; | |
indices[i++] = ic; | |
indices[i++] = ic; | |
indices[i++] = id; | |
indices[i++] = ia; | |
} | |
private void setupIndicesNorth(MutableObjectIntMap<QuadVertex> quadIndices, int sizeh, float texf, int i, float b) { | |
int h = size; | |
var posa = pos.add(0, 0, 0); | |
var nora = new Vector3f(0, 0, 1); | |
var texa = new Vector2f((sizeh + posa.x) * texf, 1f - (sizeh + posa.z) * texf); | |
var qa = new QuadVertex(posa, nora, texa, x, y); | |
var ia = addQuad(qa, quadIndices); | |
var posb = pos.add(h, 0, 0); | |
var norb = new Vector3f(0, 0, 1); | |
var texb = new Vector2f((sizeh + posb.x) * texf, 1f - (sizeh + posb.z) * texf); | |
var qb = new QuadVertex(posb, norb, texb, x, y); | |
var ib = addQuad(qb, quadIndices); | |
var posc = pos.add(h, b, 0); | |
var norc = new Vector3f(0, 0, 1); | |
var texc = new Vector2f((sizeh + posc.x) * texf, 1f - (sizeh + posc.z) * texf); | |
var qc = new QuadVertex(posc, norc, texc, x, y); | |
var ic = addQuad(qc, quadIndices); | |
var posd = pos.add(0, b, 0); | |
var nord = new Vector3f(0, 0, 1); | |
var texd = new Vector2f((sizeh + posd.x) * texf, 1f - (sizeh + posd.z) * texf); | |
var qd = new QuadVertex(posd, nord, texd, x, y); | |
var id = addQuad(qd, quadIndices); | |
inds.addAll(ia, ib, id, id, ib, ic); | |
indices[i++] = ia; | |
indices[i++] = ib; | |
indices[i++] = id; | |
indices[i++] = id; | |
indices[i++] = ib; | |
indices[i++] = ic; | |
} | |
private void setupIndicesEast(MutableObjectIntMap<QuadVertex> quadIndices, int sizeh, float texf, int i, float b) { | |
int h = size; | |
var posa = pos.add(h, 0, 0); | |
var nora = new Vector3f(0, 0, 1); | |
var texa = new Vector2f((sizeh + posa.x) * texf, 1f - (sizeh + posa.z) * texf); | |
var qa = new QuadVertex(posa, nora, texa, x, y); | |
var ia = addQuad(qa, quadIndices); | |
var posb = pos.add(h, 0, h); | |
var norb = new Vector3f(0, 0, 1); | |
var texb = new Vector2f((sizeh + posb.x) * texf, 1f - (sizeh + posb.z) * texf); | |
var qb = new QuadVertex(posb, norb, texb, x, y); | |
var ib = addQuad(qb, quadIndices); | |
var posc = pos.add(h, b, h); | |
var norc = new Vector3f(0, 0, 1); | |
var texc = new Vector2f((sizeh + posc.x) * texf, 1f - (sizeh + posc.z) * texf); | |
var qc = new QuadVertex(posc, norc, texc, x, y); | |
var ic = addQuad(qc, quadIndices); | |
var posd = pos.add(h, b, 0); | |
var nord = new Vector3f(0, 0, 1); | |
var texd = new Vector2f((sizeh + posd.x) * texf, 1f - (sizeh + posd.z) * texf); | |
var qd = new QuadVertex(posd, nord, texd, x, y); | |
var id = addQuad(qd, quadIndices); | |
inds.addAll(ib, id, ia, ib, ic, id); | |
indices[i++] = ib; | |
indices[i++] = id; | |
indices[i++] = ia; | |
indices[i++] = ib; | |
indices[i++] = ic; | |
indices[i++] = id; | |
} | |
private void setupIndicesSouth(MutableObjectIntMap<QuadVertex> quadIndices, int sizeh, float texf, int i, float b) { | |
int h = size; | |
var posa = pos.add(0, 0, h); | |
var nora = new Vector3f(0, 0, -1); | |
var texa = new Vector2f((sizeh + posa.x) * texf, 1f - (sizeh + posa.z) * texf); | |
var qa = new QuadVertex(posa, nora, texa, x, y); | |
var ia = addQuad(qa, quadIndices); | |
var posb = pos.add(0, b, h); | |
var norb = new Vector3f(0, 0, -1); | |
var texb = new Vector2f((sizeh + posb.x) * texf, 1f - (sizeh + posb.z) * texf); | |
var qb = new QuadVertex(posb, norb, texb, x, y); | |
var ib = addQuad(qb, quadIndices); | |
var posc = pos.add(h, b, h); | |
var norc = new Vector3f(0, 0, -1); | |
var texc = new Vector2f((sizeh + posc.x) * texf, 1f - (sizeh + posc.z) * texf); | |
var qc = new QuadVertex(posc, norc, texc, x, y); | |
var ic = addQuad(qc, quadIndices); | |
var posd = pos.add(h, 0, h); | |
var nord = new Vector3f(0, 0, -1); | |
var texd = new Vector2f((sizeh + posd.x) * texf, 1f - (sizeh + posd.z) * texf); | |
var qd = new QuadVertex(posd, nord, texd, x, y); | |
var id = addQuad(qd, quadIndices); | |
inds.addAll(ia, ib, ic, ia, ic, id); | |
indices[i++] = ia; | |
indices[i++] = ib; | |
indices[i++] = ic; | |
indices[i++] = ia; | |
indices[i++] = ic; | |
indices[i++] = id; | |
} | |
private void setupIndicesWest(MutableObjectIntMap<QuadVertex> quadIndices, int sizeh, float texf, int i, float b) { | |
int h = size; | |
var posa = pos.add(0, b, 0); | |
var nora = new Vector3f(-1, 0, 0); | |
var texa = new Vector2f((sizeh + posa.x) * texf, 1f - (sizeh + posa.z) * texf); | |
var qa = new QuadVertex(posa, nora, texa, x, y); | |
var ia = addQuad(qa, quadIndices); | |
var posb = pos.add(0, b, h); | |
var norb = new Vector3f(-1, 0, 0); | |
var texb = new Vector2f((sizeh + posb.x) * texf, 1f - (sizeh + posb.z) * texf); | |
var qb = new QuadVertex(posb, norb, texb, x, y); | |
var ib = addQuad(qb, quadIndices); | |
var posc = pos.add(0, 0, h); | |
var norc = new Vector3f(-1, 0, 0); | |
var texc = new Vector2f((sizeh + posc.x) * texf, 1f - (sizeh + posc.z) * texf); | |
var qc = new QuadVertex(posc, norc, texc, x, y); | |
var ic = addQuad(qc, quadIndices); | |
var posd = pos.add(0, 0, 0); | |
var nord = new Vector3f(-1, 0, 0); | |
var texd = new Vector2f((sizeh + posd.x) * texf, 1f - (sizeh + posd.z) * texf); | |
var qd = new QuadVertex(posd, nord, texd, x, y); | |
var id = addQuad(qd, quadIndices); | |
inds.addAll(id, ib, ic, ib, id, ia); | |
indices[i++] = id; | |
indices[i++] = ib; | |
indices[i++] = ic; | |
indices[i++] = ib; | |
indices[i++] = id; | |
indices[i++] = ia; | |
} | |
private int addQuad(QuadVertex qa, MutableObjectIntMap<QuadVertex> quadIndices) { | |
var i = quadIndices.get(qa); | |
if (i == ObjectIntHashMap.EMPTY_VALUE) { | |
i = quads.size(); | |
quadIndices.put(qa, i); | |
quads.add(qa); | |
} | |
return i; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment