package org.jrenner.fps;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g3d.Material;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute;
import com.badlogic.gdx.graphics.g3d.model.MeshPart;
import com.badlogic.gdx.graphics.g3d.model.Node;
import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder;
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Quaternion;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.bullet.collision.btBvhTriangleMeshShape;
import com.badlogic.gdx.physics.bullet.collision.btCollisionObject;
import com.badlogic.gdx.physics.bullet.collision.btCollisionShape;
import com.badlogic.gdx.physics.bullet.collision.btStaticPlaneShape;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.GdxRuntimeException;
import org.jrenner.fps.headless.HeadlessModel;
import static com.badlogic.gdx.graphics.VertexAttributes.Usage;
import static com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder.VertexInfo;
public class LevelBuilder {
public static LevelBuilder inst;
private static Array<Model> models;
public static Array<LevelStatic> staticPieces; // server only: stores level geometry for sending to clients on connect
private static ModelBuilder mb;
public static void init() {
staticPieces = new Array<>();
groundPieces = new Array<>();
mb = new ModelBuilder();
models = new Array<>();
}
public LevelBuilder() {
inst = this;
}
public static Array<ModelInstance> staticGeometry;
public static Array<ModelInstance> groundPieces;
public static final float groundPieceSize = 100f;
public static void createLevel() {
// graphical representation of the ground
Log.debug("createLevel - create ground");
if (Main.isClient()) {
ModelBuilder mb = new ModelBuilder();
mb.begin();
Vector3 bl = new Vector3();
Vector3 tl = new Vector3();
Vector3 tr = new Vector3();
Vector3 br = new Vector3();
Vector3 norm = new Vector3(0f, 1f, 0f);
// the size of each rect that makes up the ground
Texture groundTex = Assets.manager.get("textures/ground1.jpg", Texture.class);
Material groundMat = new Material(TextureAttribute.createDiffuse(groundTex));
MeshPartBuilder mpb = mb.part("ground", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal | Usage.TextureCoordinates, groundMat);
float u1 = 0f;
float v1 = 0f;
float u2 = groundPieceSize / 5f;
float v2 = groundPieceSize / 5f;
mpb.setUVRange(u1, v1, u2, v2);
bl.set(0, 0, 0);
tl.set(0, 0, groundPieceSize);
tr.set(groundPieceSize, 0, groundPieceSize);
br.set(groundPieceSize, 0, 0);
//mpb.rect(bl, tl, tr, br, norm);
int divisions = ((int) groundPieceSize) / 4;
mpb.patch(bl, tl, tr, br, norm, divisions, divisions);
Model groundModel = mb.end();
models.add(groundModel);
groundPieces.clear();
int count = 0;
for (int x = 0; x < GameWorld.WORLD_WIDTH; x += groundPieceSize) {
for (int z = 0; z < GameWorld.WORLD_DEPTH; z += groundPieceSize) {
count++;
ModelInstance groundPiece = new ModelInstance(groundModel);
groundPiece.transform.setToTranslation(x, 0f, z);
groundPieces.add(groundPiece);
}
}
Log.debug("createLevel - created " + count + " groundPieces");
}
// physical representation of the ground
btCollisionObject groundObj = new btCollisionObject();
btCollisionShape groundShape = new btStaticPlaneShape(Vector3.Y, 0f);
groundObj.setCollisionShape(groundShape);
Physics.applyStaticGeometryCollisionFlags(groundObj);
Physics.inst.addStaticGeometryToWorld(groundObj);
if (Main.isServer()) {
Log.debug("createLevel - create static models");
// server creates static models here, client will create the models when received from server upon connection
createStaticModels(25);
}
Log.debug("createLevel - create boxes");
Box.createBoxes(10);
}
/** client builds statics, probably based on info from server */
public static void buildStatics(LevelStatic[] statics) {
if (staticGeometry == null) {
staticGeometry = new Array<>();
}
Log.debug("client building statics received from server: " + statics.length);
ModelBuilder mb = new ModelBuilder();
mb.begin();
for (LevelStatic stat : statics) {
Model model = Assets.manager.get(stat.modelName, Model.class);
setupStaticModel(model.meshParts, stat.mtx, true);
Node node = mb.node("piece", model);
stat.mtx.getTranslation(tmp);
node.translation.set(tmp);
node.rotation.set(stat.mtx.getRotation(q));
}
Model finalModel = mb.end();
ModelInstance instance = new ModelInstance(finalModel);
staticGeometry.add(instance);
}
private static Matrix4 mtx = new Matrix4();
private static Vector3 tmp = new Vector3();
private static Quaternion q = new Quaternion();
/** bullet bodies are offset from model instances by a 90 degree rotation on X axis, boolean set to true handles this */
private static void setupStaticModel(Array<MeshPart> meshParts, Matrix4 matrix, boolean performVisualToPhysicalRotation) {
//Log.debug("create static model at: " + matrix.getTranslation(tmp));
btCollisionObject obj = new btCollisionObject();
btCollisionShape shape = new btBvhTriangleMeshShape(meshParts);
obj.setCollisionShape(shape);
Physics.applyStaticGeometryCollisionFlags(obj);
mtx.set(matrix);
if (performVisualToPhysicalRotation) {
mtx.rotate(Vector3.X, -90);
}
obj.setWorldTransform(mtx);
Physics.inst.addStaticGeometryToWorld(obj);
}
private static void createStaticModels(int number) {
staticGeometry = new Array<>();
if (Main.isClient()) {
mb = new ModelBuilder();
mb.begin();
}
Array<String> modelChoices = new Array<>();
modelChoices.addAll("models/gate.g3db", "models/strange-ramp2.g3db");
//Array<Model> modelChoices = new Array<>();
//modelChoices.add(Assets.manager.get("gate.g3db", Model.class));
//modelChoices.add(Assets.manager.get("strange-ramp1.g3db", Model.class));
//modelChoices.add(Assets.manager.get("strange-ramp2.g3db", Model.class));
Quaternion quat = new Quaternion();
Matrix4 mtx = new Matrix4();
Vector3 translation = new Vector3();
//float lo = 10f;
//float hi = GameWorld.WORLD_WIDTH;
float x = 20f;
float z = 0f;
for (int i = 0; i < number; i++) {
x = MathUtils.random(GameWorld.WORLD_WIDTH);
z = MathUtils.random(GameWorld.WORLD_DEPTH);
quat.setEulerAngles(MathUtils.random(360f), MathUtils.random(360f), MathUtils.random(360f));
String modelName = modelChoices.random();
// bullet builds its physics shape using meshparts
Array<MeshPart> meshParts = null;
Node node = null;
if (Main.isClient()) {
Model model = Assets.manager.get(modelName, Model.class);
meshParts = model.meshParts;
node = mb.node("thing", model);
} else { // the regular Model class requires a gl context, doesn't work on headless
HeadlessModel model = Assets.manager.get(modelName, HeadlessModel.class);
meshParts = model.meshParts;
}
translation.set(x, 0f, z);
mtx.set(quat);
mtx.setTranslation(translation);
setupStaticModel(meshParts, mtx, true);
if (Main.isServer()) {
LevelStatic levelStatic = new LevelStatic();
levelStatic.modelName = modelName;
levelStatic.mtx.set(mtx);
staticPieces.add(levelStatic);
}
if (Main.isClient()) {
node.translation.set(translation);
node.rotation.set(quat);
}
}
if (Main.isClient()) {
Model model = mb.end();
ModelInstance modelInst = new ModelInstance(model);
staticGeometry.add(modelInst);
}
}
public static void dispose() {
for (Model model : models) {
Tools.dispose(model);
}
models.clear();
}
}