Package net.anzix.fsz.voxelworld

Source Code of net.anzix.fsz.voxelworld.VoxelBlockGenerator

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package net.anzix.fsz.voxelworld;

import net.anzix.fsz.voxelworld.layers.LayerBlock;
import com.ardor3d.bounding.BoundingBox;
import com.ardor3d.math.FastMath;
import com.ardor3d.math.Vector3;
import com.ardor3d.math.Vector4;
import com.ardor3d.math.type.ReadOnlyVector3;
import com.ardor3d.scenegraph.Mesh;
import com.ardor3d.scenegraph.MeshData;
import com.ardor3d.util.geom.BufferUtils;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.logging.Level;
import net.anzix.fsz.voxelworld.octree.OctreeRoot;

/**
*
* @author kovacsandras
*/
public class VoxelBlockGenerator extends AbstractBlockGenerator {
   
    public enum GeneratorStatus { READY, INITIALIZATION, DENSITY_GENERATION, MESH_GENERATION, ADDING_MESH_TO_SCENE, FINALIZING }
    private GeneratorStatus status;
    public GeneratorStatus getStatus() {
        return status;
    }
    @Override
    public String toString() {
        return status.toString();
    }
    private VoxelBlock block;
    private VoxelBlockPool pool;
    //private DetailLevel level;
    private ArrayList<Quad> quads;
    private float densities[][][];
    private Feature densitySources [][][];
    private Vector3 basePosition;
    private Voxel voxels[][][];

    public VoxelBlockGenerator(VoxelBlockPool pool) {

        status = GeneratorStatus.READY;
        this.pool = pool;
        block = null;
       
        quads = new ArrayList<Quad>();

        int blockSize = VoxelWorld.BLOCK_SIZE;

        densities = new float[blockSize + 2][blockSize + 2][blockSize + 2];
        densitySources = new Feature[blockSize + 2][blockSize + 2][blockSize + 2];
        voxels = new Voxel[blockSize + 1][blockSize + 1][blockSize + 1];
    }

    @Override
    public boolean tryToAttachABlock(AbstractBlock block) {
       
        if (this.block == null) {
            this.block = (VoxelBlock)block;
            return true;
        } else {
            return false; //busy
        }
    }

    @Override
    public AbstractBlock getBlock() {
        return block;
    }

    public void init() {
             
        int blockSize = VoxelWorld.BLOCK_SIZE;
       
        for (int x = 0; x < blockSize + 1; x++) {
            for (int y = 0; y < blockSize + 1; y++) {
                for (int z = 0; z < blockSize + 1; z++) {
                    voxels[x][y][z] = null;
                }
            }
        }
        for (int x = 0; x < blockSize + 2; x++) {
            for (int y = 0; y < blockSize + 2; y++) {
                for (int z = 0; z < blockSize + 2; z++) {
                    densities[x][y][z] = -1.0f;
                    densitySources[x][y][z] = null;
                }
            }
        }
       
        quads.clear();
       
        voxelCounter = 0;
        basePosition = block.getPosition().getAsWorldCoords(pool.getLevel());
    }
   
    @Override
    public void generate() {
       
        if (block == null || block.getStatus() != VoxelBlock.BlockStatus.UNDER_GENERATION) {
            VoxelBlockPool.logInvalidBlockStatus(block, "UNDER_GENERATION");
            return;
        }

        try {
           
            ArrayList<Feature> localFeatures = pool.getWorld().getLocalFeatures(block, pool.getLevel());
            if (localFeatures.size() > 0) {

                status = GeneratorStatus.INITIALIZATION;
                init();

                status = GeneratorStatus.DENSITY_GENERATION;
                LayerBlock layerBlock = block.getRelatedLayerBlock();
                Iterator it = localFeatures.iterator();
                while(it.hasNext()) {
                    Feature feature = (Feature)it.next();
                    feature.applyDensities(basePosition, densities, densitySources,pool.getLevel(), layerBlock);
                }

                status = GeneratorStatus.MESH_GENERATION;
                generateQuadsAndVoxels();

                status = GeneratorStatus.ADDING_MESH_TO_SCENE;
                addModelToScene();
               
                Iterator it2 = localFeatures.iterator();
                while(it2.hasNext()) {
                    Feature feature = (Feature)it2.next();
                    feature.attachAdditionalNonVoxelModels(pool.getLevel(),block);
                }

            }           

            status = GeneratorStatus.FINALIZING;
            pool.finalizeBlockGeneration(block, true);

        } catch (Exception ex) {
            VoxelWorld.logger.log(Level.SEVERE,"Unhandled exception while generating a block.");

            status = GeneratorStatus.FINALIZING;
            pool.finalizeBlockGeneration(block, false);

        } catch (OutOfMemoryError err) {
            VoxelWorld.logger.log(Level.SEVERE,"Out of memory errorr while generating a block.");

            status = GeneratorStatus.FINALIZING;
            pool.finalizeBlockGeneration(block, false);

        }



        status = GeneratorStatus.READY;
        this.block = null; //i am ready to work again
    }
   
    /*public void generateTile(double[][] heightMap) {
       
        try {
           
            ArrayList<Feature> localFeatures = pool.getWorld().getLocalFeatures(block, pool.getLevel());
            if (localFeatures.size() > 0) {

                status = GeneratorStatus.INITIALIZATION;
                init();

                status = GeneratorStatus.DENSITY_GENERATION;
                LayerBlock layerBlock = block.getRelatedLayerBlock();
                Iterator it = localFeatures.iterator();
                while(it.hasNext()) {
                    Feature feature = (Feature)it.next();
                    feature.applyDensities(basePosition, densities, densitySources,pool.getLevel(), layerBlock);
                }

                double maxHeight = 32;
                status = GeneratorStatus.ADDING_MESH_TO_SCENE;
                for (int x = 0; x< VoxelWorld.BLOCK_SIZE; x++)
                    for (int z = 0; z< VoxelWorld.BLOCK_SIZE; z++)
                    {
                        if (heightMap[x][z] <  )
                    }
               
            }           
          

        } catch (Exception ex) {
            VoxelWorld.logger.log(Level.SEVERE,"Unhandled exception while generating a block.");

            status = GeneratorStatus.FINALIZING;
            pool.finalizeBlockGeneration(block, false);

        } catch (OutOfMemoryError err) {
            VoxelWorld.logger.log(Level.SEVERE,"Out of memory errorr while generating a block.");

            status = GeneratorStatus.FINALIZING;
            pool.finalizeBlockGeneration(block, false);

        }



        status = GeneratorStatus.READY;
        this.block = null; //i am ready to work again
    } */
   
    private transient int i1x, i2x, i3x, i4x;
    private transient int i1y, i2y, i3y, i4y;
    private transient int i1z, i2z, i3z, i4z;
    private transient float baseDensity, otherDensity;
    private transient Vector3 crossingPoint = new Vector3();
    private transient double levelScale = 0;
    private transient double voxelArraySize = 0;

    public void generateQuadsAndVoxels() {

        int blockSize = VoxelWorld.BLOCK_SIZE;
        levelScale = pool.getLevel().getScale();
        voxelArraySize = blockSize + 1;
       
        for (int x = 0; x < blockSize + 2; x++) {
            for (int y = 0; y < blockSize + 2; y++) {
                for (int z = 0; z < blockSize + 2; z++) {
                    baseDensity = densities[x][y][z];
                    if (baseDensity > 0.0) {
                        if (x < blockSize + 1) {
                            otherDensity = densities[x + 1][y][z];
                            if (otherDensity <= 0) {
                                setCoordsXPlus(x, y, z);
                                addQuad();
                            }
                        }
                        if (y < blockSize + 1) {
                            otherDensity = densities[x][y + 1][z];
                            if (otherDensity <= 0) {
                                setCoordsYPlus(x, y, z);
                                addQuad();
                            }
                        }
                        if (z < blockSize + 1) {
                            otherDensity = densities[x][y][z + 1];
                            if (otherDensity <= 0) {
                                setCoordsZPlus(x, y, z);
                                addQuad();
                            }
                        }
                    } else if (baseDensity < 0.0) {
                        if (x < blockSize + 1) {
                            otherDensity = densities[x + 1][y][z];
                            if (otherDensity >= 0) {
                                setCoordsXMinus(x, y, z);
                                addQuad();
                            }
                        }
                        if (y < blockSize + 1) {
                            otherDensity = densities[x][y + 1][z];
                            if (otherDensity >= 0) {
                                setCoordsYMinus(x, y, z);
                                addQuad();
                            }
                        }
                        if (z < blockSize + 1) {
                            otherDensity = densities[x][y][z + 1];
                            if (otherDensity >= 0) {
                                setCoordsZMinus(x, y, z);
                                addQuad();
                            }
                        }
                    } else {
                        if (x < blockSize + 1) {
                            otherDensity = densities[x + 1][y][z];
                            if (otherDensity < 0) {
                                setCoordsXPlus(x, y, z);
                                addQuad();
                            } else if (otherDensity > 0) {
                                setCoordsXMinus(x, y, z);
                                addQuad();
                            }
                        }
                        if (y < blockSize + 1) {
                            otherDensity = densities[x][y + 1][z];
                            if (otherDensity < 0) {
                                setCoordsYPlus(x, y, z);
                                addQuad();
                            } else if (otherDensity > 0) {
                                setCoordsYMinus(x, y, z);
                                addQuad();
                            }
                        }
                        if (z < blockSize + 1) {
                            otherDensity = densities[x][y][z + 1];
                            if (otherDensity < 0) {
                                setCoordsZPlus(x, y, z);
                                addQuad();
                            } else if (otherDensity < 0) {
                                setCoordsZMinus(x, y, z);
                                addQuad();
                            }
                        }
                    }

                }
            }
        }
    }

    public void setCoordsXPlus(int x, int y, int z) {
        i1x = x;
        i1y = y;
        i1z = z;

        i2x = x;
        i2y = (y - 1);
        i2z = z;

        i3x = x;
        i3y = (y - 1);
        i3z = (z - 1);

        i4x = x;
        i4y = y;
        i4z = (z - 1);

        double f = baseDensity / (baseDensity - otherDensity);
        crossingPoint.set(x + f, y, z);
    }

    public void setCoordsXMinus(int x, int y, int z) {
        i1x = x;
        i1y = y;
        i1z = z;

        i2x = x;
        i2y = y;
        i2z = (z - 1);

        i3x = x;
        i3y = (y - 1);
        i3z = (z - 1);

        i4x = x;
        i4y = (y - 1);
        i4z = z;

        double f = -baseDensity / (-baseDensity + otherDensity);
        crossingPoint.set(x + f, y, z);
    }

    public void setCoordsYPlus(int x, int y, int z) {
        i1x = x;
        i1y = y;
        i1z = z;

        i2x = x;
        i2y = y;
        i2z = (z - 1);

        i3x = (x - 1);
        i3y = y;
        i3z = (z - 1);

        i4x = (x - 1);
        i4y = y;
        i4z = z;

        double f = baseDensity / (baseDensity - otherDensity);
        crossingPoint.set(x, y + f, z);
    }

    public void setCoordsYMinus(int x, int y, int z) {
        i1x = x;
        i1y = y;
        i1z = z;

        i2x = (x - 1);
        i2y = y;
        i2z = z;

        i3x = (x - 1);
        i3y = y;
        i3z = (z - 1);

        i4x = x;
        i4y = y;
        i4z = (z - 1);

        double f = -baseDensity / (-baseDensity + otherDensity);
        crossingPoint.set(x, y + f, z);
    }

    public void setCoordsZPlus(int x, int y, int z) {
        i1x = x;
        i1y = y;
        i1z = z;

        i2x = (x - 1);
        i2y = y;
        i2z = z;

        i3x = (x - 1);
        i3y = (y - 1);
        i3z = z;

        i4x = x;
        i4y = (y - 1);
        i4z = z;

        double f = baseDensity / (baseDensity - otherDensity);
        crossingPoint.set(x, y, z + f);
    }

    public void setCoordsZMinus(int x, int y, int z) {
        i1x = x;
        i1y = y;
        i1z = z;

        i2x = x;
        i2y = (y - 1);
        i2z = z;

        i3x = (x - 1);
        i3y = (y - 1);
        i3z = z;

        i4x = (x - 1);
        i4y = y;
        i4z = z;

        double f = -baseDensity / (-baseDensity + otherDensity);
        crossingPoint.set(x, y, z + f);
    }

    private void addQuad() {

        Voxel voxel1 = addVoxel(i1x, i1y, i1z);
        Voxel voxel2 = addVoxel(i2x, i2y, i2z);
        Voxel voxel3 = addVoxel(i3x, i3y, i3z);
        Voxel voxel4 = addVoxel(i4x, i4y, i4z);
        if (voxel1 != null && voxel2 != null
                && voxel3 != null && voxel4 != null) {
            quads.add(new Quad(voxel1, voxel2, voxel3, voxel4));
        }

    }
   
    private int voxelCounter;

    private Voxel addVoxel(int x, int y, int z) {
        if (x>=0 && y>=0 && z>=0 && x<voxelArraySize && y<voxelArraySize && z<voxelArraySize) {
            Voxel voxel = voxels[x][y][z];
            Vector3 vertexPosition = new Vector3(crossingPoint).multiplyLocal(levelScale).addLocal(basePosition);
            Vector4 material = new Vector4(x/32.0f,y/32.0f,z/32.0f,1.0);
            if (voxel == null) {              
                voxels[x][y][z] = new Voxel(vertexPosition,material,voxelCounter);
                voxelCounter++;
            } else {
                voxels[x][y][z].add(vertexPosition,material);
            }
           
            return voxels[x][y][z];
        } else {
            //nem kell vele számolni
            return null;
        }

    }

    public Vector3 computeVoxelNormal(int x, int y, int z, ReadOnlyVector3 position) {
       
        Vector3 local = new Vector3(position);
        local.subtractLocal(basePosition);
        local.divideLocal(pool.getLevel().getScale());
        try {
            float d000 = densities[x][y][z];
            float d100 = densities[x + 1][y][z];
            float d010 = densities[x][y + 1][z];
            float d110 = densities[x + 1][y + 1][z];
            float d001 = densities[x][y][z + 1];
            float d101 = densities[x + 1][y][z + 1];
            float d011 = densities[x][y + 1][z + 1];
            float d111 = densities[x + 1][y + 1][z + 1];

            double sx1 = local.getX() - x;
            double sx0 = 1.0 - sx1;
            double sy1 = local.getY() - y;
            double sy0 = 1.0 - sy1;
            double sz1 = local.getZ() - z;
            double sz0 = 1.0 - sz1;

            double dx0 = (d100 - d000) * sy0 + (d110 - d010) * sy1;
            double dx1 = (d101 - d001) * sy0 + (d111 - d011) * sy1;
            double dx = dx0 * sz0 + dx1 * sz1;

            double dy0 = (d010 - d000) * sz0 + (d011 - d001) * sz1;
            double dy1 = (d110 - d100) * sz0 + (d111 - d101) * sz1;
            double dy = dy0 * sx0 + dy1 * sx1;

            double dz0 = (d001 - d000) * sx0 + (d101 - d100) * sx1;
            double dz1 = (d011 - d010) * sx0 + (d111 - d110) * sx1;
            double dz = dz0 * sy0 + dz1 * sy1;

            Vector3 normal = new Vector3(-dx, -dy, -dz);
            normal.normalizeLocal();
            return normal;
        } catch (Exception ex) {
            VoxelWorld.logger.log(Level.SEVERE,"NORMÁLGENERÁLÁSI HIBA");
            return new Vector3(0,1,0);
        }
    }

    public void addModelToScene() {
       
        OctreeRoot octree = block.getOctree();

        Mesh mesh = new Mesh();
        MeshData meshData = mesh.getMeshData();
        int numOfQuads = quads.size();
        int numOfVoxels = voxelCounter;

        FloatBuffer vertexBuffer = BufferUtils.createVector3Buffer(numOfVoxels);
        IntBuffer indexBuffer = BufferUtils.createIntBuffer(numOfQuads * 6);
        FloatBuffer normalBuffer = BufferUtils.createVector3Buffer(numOfVoxels);
        // FloatBuffer texCoordBuffer = BufferUtils.createVector2Buffer(numOfVoxels);
        FloatBuffer colorBuffer  = BufferUtils.createColorBuffer(numOfVoxels);

        ArrayList<Integer> verticesWithGrass = new ArrayList<Integer>();
       
       
        int blockSize = VoxelWorld.BLOCK_SIZE;
       
        for (int x = 0; x < blockSize + 1; x++) {
            for (int y = 0; y < blockSize + 1; y++) {
                for (int z = 0; z < blockSize + 1; z++) {
                    Voxel voxel = voxels[x][y][z];
                    if (voxel != null) {
                        voxel.summarize();
                       
                        octree.insertVertex(x, y, z, voxel.index);
                       
                        int i3 = voxel.index * 3;
                       
                        vertexBuffer.put(i3+0, voxel.vertexPosition.getXf());
                        vertexBuffer.put(i3+1, voxel.vertexPosition.getYf());
                        vertexBuffer.put(i3+2, voxel.vertexPosition.getZf());

                        Vector3 normal = computeVoxelNormal(x,y,z,voxel.vertexPosition);
                        normalBuffer.put(i3+0, normal.getXf());
                        normalBuffer.put(i3+1, normal.getYf());
                        normalBuffer.put(i3+2, normal.getZf());  
                       
                        int i4 = voxel.index * 4;
                        colorBuffer.put(i4+0, voxel.materials.getXf());
                        colorBuffer.put(i4+1, voxel.materials.getYf());
                        colorBuffer.put(i4+2, voxel.materials.getZf());
                        colorBuffer.put(i4+3, voxel.materials.getWf());
                       
                        ///GRASS 
                        if (voxel.vertexPosition.getY() > 33 && voxel.vertexPosition.getYf() < 210 && normal.getY() > 0.7)
                            verticesWithGrass.add(voxel.index);
                    }
                }
            }
        }

        for (int q = 0; q < quads.size(); q++) {
            Quad quad = quads.get(q);

            indexBuffer.put(quad.voxel1.index);
            indexBuffer.put(quad.voxel2.index);
            indexBuffer.put(quad.voxel3.index);
            indexBuffer.put(quad.voxel1.index);
            indexBuffer.put(quad.voxel3.index);
            indexBuffer.put(quad.voxel4.index);
        }

        indexBuffer.rewind();
        //colorBuffer.flip();

        meshData.setVertexBuffer(vertexBuffer);
        meshData.setIndexBuffer(indexBuffer);
        meshData.setNormalBuffer(normalBuffer);
        meshData.setColorBuffer(colorBuffer);


        // meshData.setColorBuffer(colorBuffer);

        block.numOfTriangles = quads.size()*2;
        block.numOfVertices = voxelCounter;

        Vector3 center = basePosition;
        double halfSize = pool.getLevel().getScale() * VoxelWorld.BLOCK_SIZE / 2.0;
        center.addLocal(halfSize, halfSize, halfSize);
        mesh.setModelBound(new BoundingBox(center, halfSize, halfSize, halfSize));

        block.getVoxelNode().attachChild(mesh);
       
        //////////GRASS///////////////////
        if (pool.getLevel().getLevelID() == 0) {
            int numOfGrasses = verticesWithGrass.size();

            Mesh grassMesh = new Mesh();
            MeshData grassMeshData = grassMesh.getMeshData();

            FloatBuffer grassVertexBuffer = BufferUtils.createVector3Buffer(numOfGrasses * 4);
            IntBuffer grassIndexBuffer = BufferUtils.createIntBuffer(numOfGrasses * 6);
            FloatBuffer grassNormalBuffer = BufferUtils.createVector3Buffer(numOfGrasses * 4);
            FloatBuffer grassTexCoordBuffer = BufferUtils.createVector2Buffer(numOfGrasses * 4);
            //FloatBuffer grassBasePositionBuffer = BufferUtils.createVector2Buffer(numOfGrasses * 4);
            for (int i=0, size = verticesWithGrass.size(); i<size; i++) {

                int i3 = verticesWithGrass.get(i) * 3;
                int i4 = i * 4;

                float topY = 2.5f + (float) Math.random();
                float bottomY = -0.1f;
                float halfEidth = 1.5f + (float) Math.random();

                //double alpha = Math.random()*Math.PI;
                //float offsetX = (FastMath.sin(alpha) * halfEidth);
                //float offsetZ = (float)(FastMath.cos(alpha) * halfEidth);
                float offsetX = (float)Math.random()-0.5f;
                float offsetZ = (float)Math.random()-0.5f;
                grassVertexBuffer.put(vertexBuffer.get(i3 +0) + offsetX);
                grassVertexBuffer.put(vertexBuffer.get(i3 +1) + bottomY);
                grassVertexBuffer.put(vertexBuffer.get(i3 +2) + offsetZ);

                grassVertexBuffer.put(vertexBuffer.get(i3 +0) + offsetX);
                grassVertexBuffer.put(vertexBuffer.get(i3 +1) + topY);
                grassVertexBuffer.put(vertexBuffer.get(i3 +2) + offsetZ);

                grassVertexBuffer.put(vertexBuffer.get(i3 +0) + offsetX);
                grassVertexBuffer.put(vertexBuffer.get(i3 +1) + topY);
                grassVertexBuffer.put(vertexBuffer.get(i3 +2) + offsetZ);

                grassVertexBuffer.put(vertexBuffer.get(i3 +0) + offsetX);
                grassVertexBuffer.put(vertexBuffer.get(i3 +1) + bottomY);
                grassVertexBuffer.put(vertexBuffer.get(i3 +2) + offsetZ);
               
                /*grassVertexBuffer.put(vertexBuffer.get(i3 +0) + offsetX);
                grassVertexBuffer.put(vertexBuffer.get(i3 +1) + bottomY);
                grassVertexBuffer.put(vertexBuffer.get(i3 +2) + offsetZ);

                grassVertexBuffer.put(vertexBuffer.get(i3 +0) + offsetX);
                grassVertexBuffer.put(vertexBuffer.get(i3 +1) + topY);
                grassVertexBuffer.put(vertexBuffer.get(i3 +2) + offsetZ);

                grassVertexBuffer.put(vertexBuffer.get(i3 +0) - offsetX);
                grassVertexBuffer.put(vertexBuffer.get(i3 +1) + topY);
                grassVertexBuffer.put(vertexBuffer.get(i3 +2) - offsetZ);

                grassVertexBuffer.put(vertexBuffer.get(i3 +0) - offsetX);
                grassVertexBuffer.put(vertexBuffer.get(i3 +1) + bottomY);
                grassVertexBuffer.put(vertexBuffer.get(i3 +2) - offsetZ);*/

                float nx = normalBuffer.get(i3 +0);
                float ny = normalBuffer.get(i3 +1);
                float nz = normalBuffer.get(i3 +2);

                grassNormalBuffer.put(nx);
                grassNormalBuffer.put(ny);
                grassNormalBuffer.put(nz)
                grassNormalBuffer.put(nx);
                grassNormalBuffer.put(ny);
                grassNormalBuffer.put(nz)
                grassNormalBuffer.put(nx);
                grassNormalBuffer.put(ny);
                grassNormalBuffer.put(nz)
                grassNormalBuffer.put(nx);
                grassNormalBuffer.put(ny);
                grassNormalBuffer.put(nz)


                grassTexCoordBuffer.put(0);
                grassTexCoordBuffer.put(0);
                grassTexCoordBuffer.put(0);
                grassTexCoordBuffer.put(1);
                grassTexCoordBuffer.put(1);
                grassTexCoordBuffer.put(1);
                grassTexCoordBuffer.put(1);
                grassTexCoordBuffer.put(0);

                grassIndexBuffer.put(i4);
                grassIndexBuffer.put(i4+1);
                grassIndexBuffer.put(i4+2);
                grassIndexBuffer.put(i4);
                grassIndexBuffer.put(i4+2);
                grassIndexBuffer.put(i4+3);          
            }

            //grassIndexBuffer.rewind();
            //colorBuffer.flip();

            grassMeshData.setVertexBuffer(grassVertexBuffer);
            grassMeshData.setIndexBuffer(grassIndexBuffer);
            grassMeshData.setNormalBuffer(grassNormalBuffer);
            grassMeshData.setTextureBuffer(grassTexCoordBuffer,0);

            grassMesh.setModelBound(new BoundingBox(center, halfSize, halfSize, halfSize));

            block.getGrassNode().attachChild(grassMesh);           
        }
       
 
       
       
       
       
       
       
       
       
       
       
       
       
       
    }
   
   
   
}
TOP

Related Classes of net.anzix.fsz.voxelworld.VoxelBlockGenerator

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.