Package org.terasology.world.block.shapes

Source Code of org.terasology.world.block.shapes.JsonBlockShapeLoader$BlockShapeHandler$ColliderInfo

/*
* Copyright 2013 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.terasology.world.block.shapes;

import com.bulletphysics.collision.shapes.BoxShape;
import com.bulletphysics.collision.shapes.CollisionShape;
import com.bulletphysics.collision.shapes.CompoundShape;
import com.bulletphysics.collision.shapes.ConvexHullShape;
import com.bulletphysics.collision.shapes.SphereShape;
import com.bulletphysics.linearmath.Transform;
import com.bulletphysics.util.ObjectArrayList;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import gnu.trove.list.TIntList;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.procedure.TIntProcedure;
import org.terasology.asset.AssetLoader;
import org.terasology.math.Rotation;
import org.terasology.module.Module;
import org.terasology.utilities.gson.Vector2fTypeAdapter;
import org.terasology.utilities.gson.Vector3fTypeAdapter;
import org.terasology.world.block.BlockPart;

import javax.vecmath.Matrix4f;
import javax.vecmath.Vector2f;
import javax.vecmath.Vector3f;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.List;
import java.util.Locale;

/**
* @author Immortius
*/
public class JsonBlockShapeLoader implements AssetLoader<BlockShapeData> {
    private static final BoxShape CUBE_SHAPE = new BoxShape(new Vector3f(0.5f, 0.5f, 0.5f));
    private Gson gson;

    public JsonBlockShapeLoader() {
        gson = new GsonBuilder()
                .setPrettyPrinting()
                .registerTypeAdapter(BlockShapeData.class, new BlockShapeHandler())
                .registerTypeAdapter(BlockMeshPart.class, new BlockMeshPartHandler())
                .registerTypeAdapter(Vector3f.class, new Vector3fTypeAdapter())
                .registerTypeAdapter(Vector2f.class, new Vector2fTypeAdapter())
                .create();
    }

    @Override
    public BlockShapeData load(Module module, InputStream stream, List<URL> urls, List<URL> deltas) throws IOException {
        return gson.fromJson(new InputStreamReader(stream, Charsets.UTF_8), BlockShapeData.class);
    }

    private static class BlockShapeHandler implements JsonDeserializer<BlockShapeData> {

        public static final String DISPLAY_NAME = "displayName";
        public static final String PITCH_SYMMETRIC = "pitchSymmetric";
        public static final String YAW_SYMMETRIC = "yawSymmetric";
        public static final String ROLL_SYMMETRIC = "rollSymmetric";
        public static final String SYMMETRIC = "symmetric";
        public static final String CONVEX_HULL = "convexHull";
        public static final String COLLIDERS = "colliders";
        public static final String COLLISION = "collision";
        public static final String FULL_SIDE = "fullSide";
        public static final String TYPE = "type";
        public static final String AABB = "AABB";
        public static final String SPHERE = "Sphere";
        public static final String POSITION = "position";
        public static final String EXTENTS = "extents";
        public static final String RADIUS = "radius";

        @Override
        public BlockShapeData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            BlockShapeData shape = new BlockShapeData();
            JsonObject shapeObj = json.getAsJsonObject();

            if (shapeObj.has(DISPLAY_NAME)) {
                shape.setDisplayName(shapeObj.getAsJsonPrimitive(DISPLAY_NAME).getAsString());
            }

            for (BlockPart part : BlockPart.values()) {
                if (shapeObj.has(part.toString().toLowerCase(Locale.ENGLISH))) {
                    JsonObject meshObj = shapeObj.getAsJsonObject(part.toString().toLowerCase(Locale.ENGLISH));
                    shape.setMeshPart(part, (BlockMeshPart) context.deserialize(meshObj, BlockMeshPart.class));
                    if (part.isSide() && meshObj.has(FULL_SIDE)) {
                        shape.setBlockingSide(part.getSide(), meshObj.get(FULL_SIDE).getAsBoolean());
                    }
                }
            }

            if (shapeObj.has(COLLISION) && shapeObj.get(COLLISION).isJsonObject()) {
                JsonObject collisionInfo = shapeObj.get(COLLISION).getAsJsonObject();
                processCollision(context, shape, collisionInfo);
            } else {
                shape.setCollisionShape(CUBE_SHAPE);
                shape.setCollisionSymmetric(true);
            }
            return shape;
        }

        private void processCollision(JsonDeserializationContext context, BlockShapeData shape, JsonObject collisionInfo) {
            if (collisionInfo.has(PITCH_SYMMETRIC) && collisionInfo.get(PITCH_SYMMETRIC).isJsonPrimitive()
                    && collisionInfo.get(PITCH_SYMMETRIC).getAsJsonPrimitive().isBoolean()) {
                shape.setPitchSymmetric(collisionInfo.get(PITCH_SYMMETRIC).getAsBoolean());
            }
            if (collisionInfo.has(YAW_SYMMETRIC) && collisionInfo.get(YAW_SYMMETRIC).isJsonPrimitive()
                    && collisionInfo.get(YAW_SYMMETRIC).getAsJsonPrimitive().isBoolean()) {
                shape.setYawSymmetric(collisionInfo.get(YAW_SYMMETRIC).getAsBoolean());
            }
            if (collisionInfo.has(ROLL_SYMMETRIC) && collisionInfo.get(ROLL_SYMMETRIC).isJsonPrimitive()
                    && collisionInfo.get(ROLL_SYMMETRIC).getAsJsonPrimitive().isBoolean()) {
                shape.setRollSymmetric(collisionInfo.get(ROLL_SYMMETRIC).getAsBoolean());
            }

            if (collisionInfo.has(SYMMETRIC) && collisionInfo.get(SYMMETRIC).isJsonPrimitive()
                    && collisionInfo.get(SYMMETRIC).getAsJsonPrimitive().isBoolean()) {
                if (collisionInfo.get(SYMMETRIC).getAsBoolean()) {
                    shape.setCollisionSymmetric(true);
                }
            }

            if (collisionInfo.has(CONVEX_HULL) && collisionInfo.get(CONVEX_HULL).isJsonPrimitive()
                    && collisionInfo.get(CONVEX_HULL).getAsJsonPrimitive().isBoolean()) {
                ObjectArrayList<Vector3f> verts = buildVertList(shape);
                ConvexHullShape convexHull = new ConvexHullShape(verts);
                shape.setCollisionShape(convexHull);
            } else if (collisionInfo.has(COLLIDERS) && collisionInfo.get(COLLIDERS).isJsonArray()
                    && collisionInfo.get(COLLIDERS).getAsJsonArray().size() > 0) {
                JsonArray colliderArray = collisionInfo.get(COLLIDERS).getAsJsonArray();
                processColliders(context, colliderArray, shape);
            } else {
                shape.setCollisionShape(CUBE_SHAPE);
                shape.setCollisionSymmetric(true);
            }
        }

        private ObjectArrayList<Vector3f> buildVertList(BlockShapeData shape) {
            ObjectArrayList<Vector3f> result = new ObjectArrayList<>();
            for (BlockPart part : BlockPart.values()) {
                BlockMeshPart meshPart = shape.getMeshPart(part);
                if (meshPart != null) {
                    for (int i = 0; i < meshPart.size(); ++i) {
                        result.add(meshPart.getVertex(i));
                    }
                }
            }
            return result;
        }

        private void processColliders(JsonDeserializationContext context, JsonArray colliderArray, BlockShapeData shape) {

            List<ColliderInfo> colliders = Lists.newArrayList();
            for (JsonElement elem : colliderArray) {
                if (elem.isJsonObject()) {
                    JsonObject colliderObj = elem.getAsJsonObject();
                    if (colliderObj.has(TYPE) && colliderObj.get(TYPE).isJsonPrimitive() && colliderObj.getAsJsonPrimitive(TYPE).isString()) {
                        String type = colliderObj.get(TYPE).getAsString();
                        if (AABB.equals(type)) {
                            colliders.add(processAABBShape(context, colliderObj));
                        } else if (SPHERE.equals(type)) {
                            colliders.add(processSphereShape(context, colliderObj));
                        }
                    }
                }
            }
            if (colliders.size() > 1) {
                ColliderInfo info = processCompoundShape(colliders);
                shape.setCollisionShape(info.collisionShape);
                shape.setCollisionOffset(info.offset);
            } else if (colliders.size() == 1) {
                shape.setCollisionShape(colliders.get(0).collisionShape);
                shape.setCollisionOffset(colliders.get(0).offset);
            } else {
                shape.setCollisionShape(CUBE_SHAPE);
                shape.setCollisionOffset(new Vector3f(0, 0, 0));
                shape.setCollisionSymmetric(true);
            }
        }

        private ColliderInfo processCompoundShape(List<ColliderInfo> colliders) {
            CompoundShape collisionShape = new CompoundShape();

            for (ColliderInfo collider : colliders) {
                Transform transform = new Transform(new Matrix4f(Rotation.none().getQuat4f(), collider.offset, 1.0f));
                collisionShape.addChildShape(transform, collider.collisionShape);
            }
            return new ColliderInfo(new Vector3f(), collisionShape);
        }

        private ColliderInfo processAABBShape(JsonDeserializationContext context, JsonObject colliderDef) {
            Vector3f offset = context.deserialize(colliderDef.get(POSITION), Vector3f.class);
            Vector3f extent = context.deserialize(colliderDef.get(EXTENTS), Vector3f.class);
            if (offset == null) {
                throw new JsonParseException("AABB Collider missing position");
            }
            if (extent == null) {
                throw new JsonParseException("AABB Collider missing extents");
            }
            extent.absolute();

            return new ColliderInfo(offset, new BoxShape(extent));
        }

        private ColliderInfo processSphereShape(JsonDeserializationContext context, JsonObject colliderDef) {
            Vector3f offset = context.deserialize(colliderDef.get(POSITION), Vector3f.class);
            float radius = colliderDef.get(RADIUS).getAsFloat();
            if (offset == null) {
                throw new JsonParseException("Sphere Collider missing position");
            }

            return new ColliderInfo(offset, new SphereShape(radius));
        }

        private static class ColliderInfo {
            public Vector3f offset;
            public CollisionShape collisionShape;

            public ColliderInfo(Vector3f offset, CollisionShape shape) {
                this.offset = offset;
                this.collisionShape = shape;
            }
        }
    }

    private static class BlockMeshPartHandler implements JsonDeserializer<BlockMeshPart> {

        @Override
        public BlockMeshPart deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            final JsonObject meshObj = json.getAsJsonObject();
            final Vector3f[] vertices = context.deserialize(meshObj.get("vertices"), Vector3f[].class);
            final Vector3f[] normals = context.deserialize(meshObj.get("normals"), Vector3f[].class);
            final Vector2f[] texCoords = context.deserialize(meshObj.get("texcoords"), Vector2f[].class);

            if (vertices == null) {
                throw new JsonParseException("Vertices missing");
            }
            if (normals == null) {
                throw new JsonParseException("Normals missing");
            }
            if (texCoords == null) {
                throw new JsonParseException("Texcoords missing");
            }
            if (!meshObj.has("faces")) {
                throw new JsonParseException("Faces missing");
            }

            if (vertices.length != normals.length || vertices.length != texCoords.length) {
                throw new JsonParseException("vertices, normals and texcoords must have the same length");
            }

            // Normalise the normals for safety
            for (Vector3f norm : normals) {
                norm.normalize();
            }

            int[][] faces = context.deserialize(meshObj.get("faces"), int[][].class);

            // Convert faces to indices via triangle fan
            TIntList indices = new TIntArrayList();
            for (int[] face : faces) {
                for (int tri = 0; tri < face.length - 2; tri++) {
                    indices.add(face[0]);
                    indices.add(face[tri + 1]);
                    indices.add(face[tri + 2]);
                }
            }

            // Check indices in bounds
            indices.forEach(new TIntProcedure() {
                @Override
                public boolean execute(int value) {
                    if (value < 0 || value >= vertices.length) {
                        throw new JsonParseException("Face value out of range: " + value + ", max vertex is " + (vertices.length - 1));
                    }
                    return true;
                }
            });

            return new BlockMeshPart(vertices, normals, texCoords, indices.toArray());
        }
    }

}
TOP

Related Classes of org.terasology.world.block.shapes.JsonBlockShapeLoader$BlockShapeHandler$ColliderInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.