org.gearvrf.scene_objects.GVRCylinderSceneObject.java Source code

Java tutorial

Introduction

Here is the source code for org.gearvrf.scene_objects.GVRCylinderSceneObject.java

Source

/* Copyright 2015 Samsung Electronics Co., LTD
 *
 * 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.gearvrf.scene_objects;

import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
import org.gearvrf.GVRSceneObject;
import org.gearvrf.GVRRenderData;
import org.gearvrf.GVRContext;
import org.gearvrf.GVRMesh;
import org.gearvrf.utility.Log;

public class GVRCylinderSceneObject extends GVRSceneObject {

    @SuppressWarnings("unused")
    private static final String TAG = Log.tag(GVRCylinderSceneObject.class);

    private static final int STACK_NUMBER = 10;
    private static final int SLICE_NUMBER = 36;
    private static final float BASE_RADIUS = 0.5f;
    private static final float TOP_RADIUS = 0.5f;
    private static final float HEIGHT = 1.0f;

    private float[] vertices;
    private float[] normals;
    private float[] texCoords;
    private char[] indices;
    private int vertexCount = 0;
    private int texCoordCount = 0;
    private char indexCount = 0;
    private int triangleCount = 0;

    /**
     * Constructs a cylinder scene object with a height of 1, radius of 0.5,2
     * stacks, and 36 slices.
     * 
     * @param gvrContext
     *            current {@link GVRContext}
     */
    public GVRCylinderSceneObject(GVRContext gvrContext) {
        super(gvrContext);

        generateCylinder(BASE_RADIUS, TOP_RADIUS, HEIGHT, STACK_NUMBER, SLICE_NUMBER);

        GVRMesh mesh = new GVRMesh(gvrContext);
        mesh.setVertices(vertices);
        mesh.setNormals(normals);
        mesh.setTexCoords(texCoords);
        mesh.setTriangles(indices);

        GVRRenderData renderData = new GVRRenderData(gvrContext);
        attachRenderData(renderData);
        renderData.setMesh(mesh);
    }

    /**
     * Constructs a cylinder scene object.
     * 
     * @param gvrContext
     *            current {@link GVRContext}
     * @param bottomRadius
     *            radius for the bottom of the cylinder
     * @param topRadius
     *            radius for the top of the cylinder
     * @param height
     *            height of the cylinder
     * @param stackNumber
     *            number of quads high to make the cylinder.
     * @param sliceNumber
     *            number of quads around to make the cylinder.
     */
    public GVRCylinderSceneObject(GVRContext gvrContext, float bottomRadius, float topRadius, float height,
            int stackNumber, int sliceNumber) {
        super(gvrContext);
        // assert height, numStacks, numSlices > 0
        if (height <= 0 || stackNumber <= 0 || sliceNumber <= 0) {
            throw new IllegalArgumentException(
                    "height, numStacks, and numSlices must be > 0.  Values passed were: height=" + height
                            + ", numStacks=" + stackNumber + ", numSlices=" + sliceNumber);
        }

        // assert numCaps > 0
        if (bottomRadius <= 0 && topRadius <= 0) {
            throw new IllegalArgumentException(
                    "bottomRadius and topRadius must be >= 0 and at least one of bottomRadius or topRadius must be > 0.  Values passed were: bottomRadius="
                            + bottomRadius + ", topRadius=" + topRadius);
        }

        generateCylinder(bottomRadius, topRadius, height, stackNumber, sliceNumber);

        GVRMesh mesh = new GVRMesh(gvrContext);
        mesh.setVertices(vertices);
        mesh.setNormals(normals);
        mesh.setTexCoords(texCoords);
        mesh.setTriangles(indices);

        GVRRenderData renderData = new GVRRenderData(gvrContext);
        attachRenderData(renderData);
        renderData.setMesh(mesh);
    }

    private void generateCylinder(float bottomRadius, float topRadius, float height, int stackNumber,
            int sliceNumber) {

        int capNumber = 2;
        if (bottomRadius == 0) {
            capNumber--;
        }

        if (topRadius == 0) {
            capNumber--;
        }

        int capVertexNumber = 3 * sliceNumber;
        int bodyVertexNumber = 4 * sliceNumber * stackNumber;
        int vertexNumber = (capNumber * capVertexNumber) + bodyVertexNumber;
        int triangleNumber = (capNumber * capVertexNumber) + (6 * sliceNumber * stackNumber);
        float halfHeight = height / 2.0f;

        vertices = new float[3 * vertexNumber];
        normals = new float[3 * vertexNumber];
        texCoords = new float[2 * triangleNumber];
        indices = new char[triangleNumber];

        // top cap
        // 3 * numSlices
        if (topRadius > 0) {
            createCap(topRadius, halfHeight, sliceNumber, 1.0f);
        }

        // cylinder body
        // 4 * numSlices * numStacks
        createBody(bottomRadius, topRadius, height, stackNumber, sliceNumber);

        // bottom cap
        // 3 * numSlices
        if (bottomRadius > 0) {
            createCap(bottomRadius, -halfHeight, sliceNumber, -1.0f);
        }

    }

    private void createCap(float radius, float height, int sliceNumber, float normalDirection) {
        for (int slice = 0; slice < sliceNumber; slice++) {
            double theta0 = ((slice) / sliceNumber) * 2.0 * Math.PI;
            double theta1 = ((slice + 1) / sliceNumber) * 2.0 * Math.PI;

            float y = height;
            float x0 = (float) (radius * Math.cos(theta0));
            float z0 = (float) (radius * Math.sin(theta0));
            float x1 = (float) (radius * Math.cos(theta1));
            float z1 = (float) (radius * Math.sin(theta1));

            float s0 = 1.0f - ((float) (slice) / sliceNumber);
            float s1 = 1.0f - ((float) (slice + 1) / sliceNumber);
            float s2 = (s0 + s1) / 2.0f;

            vertices[vertexCount + 0] = x0;
            vertices[vertexCount + 1] = y;
            vertices[vertexCount + 2] = z0;
            vertices[vertexCount + 3] = x1;
            vertices[vertexCount + 4] = y;
            vertices[vertexCount + 5] = z1;
            vertices[vertexCount + 6] = 0.0f;
            vertices[vertexCount + 7] = y;
            vertices[vertexCount + 8] = 0.0f;

            normals[vertexCount + 0] = 0.0f;
            normals[vertexCount + 1] = normalDirection;
            normals[vertexCount + 2] = 0.0f;
            normals[vertexCount + 3] = 0.0f;
            normals[vertexCount + 4] = normalDirection;
            normals[vertexCount + 5] = 0.0f;
            normals[vertexCount + 6] = 0.0f;
            normals[vertexCount + 7] = normalDirection;
            normals[vertexCount + 8] = 0.0f;

            texCoords[texCoordCount + 0] = s0;
            texCoords[texCoordCount + 1] = 0.0f;

            texCoords[texCoordCount + 2] = s1;
            texCoords[texCoordCount + 3] = 0.0f;

            texCoords[texCoordCount + 4] = s2;
            texCoords[texCoordCount + 5] = 1.0f;

            if (normalDirection > 0) {
                indices[indexCount + 0] = (char) (triangleCount + 1);
                indices[indexCount + 1] = (char) (triangleCount + 0);
                indices[indexCount + 2] = (char) (triangleCount + 2);
            } else {
                indices[indexCount + 0] = (char) (triangleCount + 0);
                indices[indexCount + 1] = (char) (triangleCount + 1);
                indices[indexCount + 2] = (char) (triangleCount + 2);
            }

            vertexCount += 9;
            texCoordCount += 6;
            indexCount += 3;
            triangleCount += 3;
        }
    }

    private void createBody(float bottomRadius, float topRadius, float height, int stackNumber, int sliceNumber) {
        float difference = bottomRadius - topRadius;
        float halfHeight = height / 2.0f;

        for (int stack = 0; stack < stackNumber; stack++) {

            int initVertexCount = vertexCount;

            float stackPercentage0 = ((float) (stack) / stackNumber);
            float stackPercentage1 = ((float) (stack + 1) / stackNumber);

            float t0 = 1.0f - stackPercentage0;
            float t1 = 1.0f - stackPercentage1;
            float y0 = -halfHeight + (stackPercentage0 * height);
            float y1 = -halfHeight + (stackPercentage1 * height);

            float nx, ny, nz;
            for (int slice = 0; slice < sliceNumber; slice++) {
                float slicePercentage0 = ((float) (slice) / sliceNumber);
                float slicePercentage1 = ((float) (slice + 1) / sliceNumber);
                double theta0 = slicePercentage0 * 2.0 * Math.PI;
                double theta1 = slicePercentage1 * 2.0 * Math.PI;
                double cosTheta0 = Math.cos(theta0);
                double sinTheta0 = Math.sin(theta0);
                double cosTheta1 = Math.cos(theta1);
                double sinTheta1 = Math.sin(theta1);

                float radius = (bottomRadius - (difference * stackPercentage0));
                float x0 = (float) (radius * cosTheta0);
                float z0 = (float) (-radius * sinTheta0);
                float x1 = (float) (radius * cosTheta1);
                float z1 = (float) (-radius * sinTheta1);

                radius = (bottomRadius - (difference * stackPercentage1));
                float x2 = (float) (radius * cosTheta0);
                float z2 = (float) (-radius * sinTheta0);
                float x3 = (float) (radius * cosTheta1);
                float z3 = (float) (-radius * sinTheta1);

                float s0 = slicePercentage0;
                float s1 = slicePercentage1;

                vertices[vertexCount + 0] = x0;
                vertices[vertexCount + 1] = y0;
                vertices[vertexCount + 2] = z0;

                vertices[vertexCount + 3] = x1;
                vertices[vertexCount + 4] = y0;
                vertices[vertexCount + 5] = z1;

                vertices[vertexCount + 6] = x2;
                vertices[vertexCount + 7] = y1;
                vertices[vertexCount + 8] = z2;

                vertices[vertexCount + 9] = x3;
                vertices[vertexCount + 10] = y1;
                vertices[vertexCount + 11] = z3;

                // calculate normal
                Vector3D v1 = new Vector3D(x1 - x0, 0, z1 - z0);
                Vector3D v2 = new Vector3D(x2 - x0, y1 - y0, z2 - z0);
                Vector3D v3 = v1.crossProduct(v2).normalize();

                nx = (float) v3.getX();
                ny = (float) v3.getY();
                nz = (float) v3.getZ();
                normals[vertexCount + 0] = nx;
                normals[vertexCount + 1] = ny;
                normals[vertexCount + 2] = nz;
                normals[vertexCount + 3] = nx;
                normals[vertexCount + 4] = ny;
                normals[vertexCount + 5] = nz;
                normals[vertexCount + 6] = nx;
                normals[vertexCount + 7] = ny;
                normals[vertexCount + 8] = nz;
                normals[vertexCount + 9] = nx;
                normals[vertexCount + 10] = ny;
                normals[vertexCount + 11] = nz;

                texCoords[texCoordCount + 0] = s0;
                texCoords[texCoordCount + 1] = t0;

                texCoords[texCoordCount + 2] = s1;
                texCoords[texCoordCount + 3] = t0;

                texCoords[texCoordCount + 4] = s0;
                texCoords[texCoordCount + 5] = t1;

                texCoords[texCoordCount + 6] = s1;
                texCoords[texCoordCount + 7] = t1;

                indices[indexCount + 0] = (char) (triangleCount + 0); // 0
                indices[indexCount + 1] = (char) (triangleCount + 1); // 1
                indices[indexCount + 2] = (char) (triangleCount + 2); // 2

                indices[indexCount + 3] = (char) (triangleCount + 2); // 2
                indices[indexCount + 4] = (char) (triangleCount + 1); // 1
                indices[indexCount + 5] = (char) (triangleCount + 3); // 3

                vertexCount += 12;
                texCoordCount += 8;
                indexCount += 6;
                triangleCount += 4;
            }

            for (int i = initVertexCount; i < vertexCount - 12; i += 12) {
                Vector3D v1 = new Vector3D(normals[i + 3], normals[i + 4], normals[i + 5]);
                Vector3D v2 = new Vector3D(normals[i + 12], normals[i + 13], normals[i + 14]);
                Vector3D v3 = v1.add(v2).normalize();
                nx = (float) v3.getX();
                ny = (float) v3.getY();
                nz = (float) v3.getZ();
                normals[i + 3] = nx;
                normals[i + 4] = ny;
                normals[i + 5] = nz;
                normals[i + 12] = nx;
                normals[i + 13] = ny;
                normals[i + 14] = nz;

                v1 = new Vector3D(normals[i + 9], normals[i + 10], normals[i + 11]);
                v2 = new Vector3D(normals[i + 18], normals[i + 19], normals[i + 20]);
                v3 = v1.add(v2).normalize();
                nx = (float) v3.getX();
                ny = (float) v3.getY();
                nz = (float) v3.getZ();
                normals[i + 9] = nx;
                normals[i + 10] = ny;
                normals[i + 11] = nz;
                normals[i + 18] = nx;
                normals[i + 19] = ny;
                normals[i + 20] = nz;
            }
            int i1 = vertexCount - 12;
            Vector3D v1 = new Vector3D(normals[i1 + 3], normals[i1 + 4], normals[i1 + 5]);
            int i2 = initVertexCount;
            Vector3D v2 = new Vector3D(normals[i2 + 0], normals[i2 + 1], normals[i2 + 2]);
            Vector3D v3 = v1.add(v2).normalize();
            nx = (float) v3.getX();
            ny = (float) v3.getY();
            nz = (float) v3.getZ();
            normals[i1 + 3] = nx;
            normals[i1 + 4] = ny;
            normals[i1 + 5] = nz;
            normals[i2 + 0] = nx;
            normals[i2 + 1] = ny;
            normals[i2 + 2] = nz;

            v1 = new Vector3D(normals[i1 + 9], normals[i1 + 10], normals[i1 + 11]);
            v2 = new Vector3D(normals[i2 + 6], normals[i2 + 7], normals[i2 + 8]);
            v3 = v1.add(v2).normalize();
            nx = (float) v3.getX();
            ny = (float) v3.getY();
            nz = (float) v3.getZ();
            normals[i1 + 9] = nx;
            normals[i1 + 10] = ny;
            normals[i1 + 11] = nz;
            normals[i2 + 6] = nx;
            normals[i2 + 7] = ny;
            normals[i2 + 8] = nz;
        }
    }
}