Search in sources :

Example 1 with DynamicArray

use of com.jme3.scene.plugins.blender.file.DynamicArray in project jmonkeyengine by jMonkeyEngine.

the class ParticlesHelper method toParticleEmitter.

@SuppressWarnings("unchecked")
public ParticleEmitter toParticleEmitter(Structure particleSystem) throws BlenderFileException {
    ParticleEmitter result = null;
    Pointer pParticleSettings = (Pointer) particleSystem.getFieldValue("part");
    if (pParticleSettings.isNotNull()) {
        Structure particleSettings = pParticleSettings.fetchData().get(0);
        int totPart = ((Number) particleSettings.getFieldValue("totpart")).intValue();
        // draw type will be stored temporarily in the name (it is used during modifier applying operation)
        int drawAs = ((Number) particleSettings.getFieldValue("draw_as")).intValue();
        // P - point, L - line, N - None, B - Bilboard
        char nameSuffix;
        switch(drawAs) {
            case PART_DRAW_NOT:
                nameSuffix = 'N';
                // no need to generate particles in this case
                totPart = 0;
                break;
            case PART_DRAW_BB:
                nameSuffix = 'B';
                break;
            case PART_DRAW_OB:
            case PART_DRAW_GR:
                nameSuffix = 'P';
                // TODO: support groups and aobjects
                LOGGER.warning("Neither object nor group particles supported yet! Using point representation instead!");
                break;
            case PART_DRAW_LINE:
                nameSuffix = 'L';
                // TODO: support lines
                LOGGER.warning("Lines not yet supported! Using point representation instead!");
            default:
                // all others are rendered as points in blender
                nameSuffix = 'P';
        }
        result = new ParticleEmitter(particleSettings.getName() + nameSuffix, Type.Triangle, totPart);
        if (nameSuffix == 'N') {
            // no need to set anything else
            return result;
        }
        // setting the emitters shape (the shapes meshes will be set later during modifier applying operation)
        int from = ((Number) particleSettings.getFieldValue("from")).intValue();
        switch(from) {
            case PART_FROM_VERT:
                result.setShape(new EmitterMeshVertexShape());
                break;
            case PART_FROM_FACE:
                result.setShape(new EmitterMeshFaceShape());
                break;
            case PART_FROM_VOLUME:
                result.setShape(new EmitterMeshConvexHullShape());
                break;
            default:
                LOGGER.warning("Default shape used! Unknown emitter shape value ('from' parameter: " + from + ')');
        }
        // reading acceleration
        DynamicArray<Number> acc = (DynamicArray<Number>) particleSettings.getFieldValue("acc");
        result.setGravity(-acc.get(0).floatValue(), -acc.get(1).floatValue(), -acc.get(2).floatValue());
        // setting the colors
        result.setEndColor(new ColorRGBA(1f, 1f, 1f, 1f));
        result.setStartColor(new ColorRGBA(1f, 1f, 1f, 1f));
        // reading size
        float sizeFactor = nameSuffix == 'B' ? 1.0f : 0.3f;
        float size = ((Number) particleSettings.getFieldValue("size")).floatValue() * sizeFactor;
        result.setStartSize(size);
        result.setEndSize(size);
        // reading lifetime
        int fps = blenderContext.getBlenderKey().getFps();
        float lifetime = ((Number) particleSettings.getFieldValue("lifetime")).floatValue() / fps;
        float randlife = ((Number) particleSettings.getFieldValue("randlife")).floatValue() / fps;
        result.setLowLife(lifetime * (1.0f - randlife));
        result.setHighLife(lifetime);
        // preparing influencer
        ParticleInfluencer influencer;
        int phystype = ((Number) particleSettings.getFieldValue("phystype")).intValue();
        switch(phystype) {
            case PART_PHYS_NEWTON:
                influencer = new NewtonianParticleInfluencer();
                ((NewtonianParticleInfluencer) influencer).setNormalVelocity(((Number) particleSettings.getFieldValue("normfac")).floatValue());
                ((NewtonianParticleInfluencer) influencer).setVelocityVariation(((Number) particleSettings.getFieldValue("randfac")).floatValue());
                ((NewtonianParticleInfluencer) influencer).setSurfaceTangentFactor(((Number) particleSettings.getFieldValue("tanfac")).floatValue());
                ((NewtonianParticleInfluencer) influencer).setSurfaceTangentRotation(((Number) particleSettings.getFieldValue("tanphase")).floatValue());
                break;
            case PART_PHYS_BOIDS:
            case // TODO: support other influencers
            PART_PHYS_KEYED:
                LOGGER.warning("Boids and Keyed particles physic not yet supported! Empty influencer used!");
            case PART_PHYS_NO:
            default:
                influencer = new EmptyParticleInfluencer();
        }
        result.setParticleInfluencer(influencer);
    }
    return result;
}
Also used : ParticleEmitter(com.jme3.effect.ParticleEmitter) EmptyParticleInfluencer(com.jme3.effect.influencers.EmptyParticleInfluencer) Pointer(com.jme3.scene.plugins.blender.file.Pointer) EmitterMeshVertexShape(com.jme3.effect.shapes.EmitterMeshVertexShape) EmitterMeshFaceShape(com.jme3.effect.shapes.EmitterMeshFaceShape) NewtonianParticleInfluencer(com.jme3.effect.influencers.NewtonianParticleInfluencer) ColorRGBA(com.jme3.math.ColorRGBA) EmitterMeshConvexHullShape(com.jme3.effect.shapes.EmitterMeshConvexHullShape) DynamicArray(com.jme3.scene.plugins.blender.file.DynamicArray) Structure(com.jme3.scene.plugins.blender.file.Structure) EmptyParticleInfluencer(com.jme3.effect.influencers.EmptyParticleInfluencer) ParticleInfluencer(com.jme3.effect.influencers.ParticleInfluencer) NewtonianParticleInfluencer(com.jme3.effect.influencers.NewtonianParticleInfluencer)

Example 2 with DynamicArray

use of com.jme3.scene.plugins.blender.file.DynamicArray in project jmonkeyengine by jMonkeyEngine.

the class MeshHelper method loadUVCoordinates.

/**
     * The method loads the UV coordinates. The result is a map where the key is the user's UV set name and the values are UV coordinates.
     * But depending on the mesh type (triangle/quads or bmesh) the lists in the map have different meaning.
     * For bmesh they are enlisted just like they are stored in the blend file (in loops).
     * For traditional faces every 4 UV's should be assigned for a single face.
     * @param meshStructure
     *            the mesh structure
     * @return a map that sorts UV coordinates between different UV sets
     * @throws BlenderFileException
     *             an exception is thrown when problems with blend file occur
     */
@SuppressWarnings("unchecked")
public LinkedHashMap<String, List<Vector2f>> loadUVCoordinates(Structure meshStructure) throws BlenderFileException {
    LOGGER.log(Level.FINE, "Loading UV coordinates from mesh: {0}.", meshStructure.getName());
    LinkedHashMap<String, List<Vector2f>> result = new LinkedHashMap<String, List<Vector2f>>();
    if (this.isBMeshCompatible(meshStructure)) {
        // in this case the UV's are assigned to vertices (an array is the same length as the vertex array)
        Structure loopData = (Structure) meshStructure.getFieldValue("ldata");
        Pointer pLoopDataLayers = (Pointer) loopData.getFieldValue("layers");
        List<Structure> loopDataLayers = pLoopDataLayers.fetchData();
        for (Structure structure : loopDataLayers) {
            Pointer p = (Pointer) structure.getFieldValue("data");
            if (p.isNotNull() && ((Number) structure.getFieldValue("type")).intValue() == MeshHelper.UV_DATA_LAYER_TYPE_BMESH) {
                String uvSetName = structure.getFieldValue("name").toString();
                List<Structure> uvsStructures = p.fetchData();
                List<Vector2f> uvs = new ArrayList<Vector2f>(uvsStructures.size());
                for (Structure uvStructure : uvsStructures) {
                    DynamicArray<Number> loopUVS = (DynamicArray<Number>) uvStructure.getFieldValue("uv");
                    uvs.add(new Vector2f(loopUVS.get(0).floatValue(), loopUVS.get(1).floatValue()));
                }
                result.put(uvSetName, uvs);
            }
        }
    } else {
        // in this case UV's are assigned to faces (the array has the same legnth as the faces count)
        Structure facesData = (Structure) meshStructure.getFieldValue("fdata");
        Pointer pFacesDataLayers = (Pointer) facesData.getFieldValue("layers");
        if (pFacesDataLayers.isNotNull()) {
            List<Structure> facesDataLayers = pFacesDataLayers.fetchData();
            for (Structure structure : facesDataLayers) {
                Pointer p = (Pointer) structure.getFieldValue("data");
                if (p.isNotNull() && ((Number) structure.getFieldValue("type")).intValue() == MeshHelper.UV_DATA_LAYER_TYPE_FMESH) {
                    String uvSetName = structure.getFieldValue("name").toString();
                    List<Structure> uvsStructures = p.fetchData();
                    List<Vector2f> uvs = new ArrayList<Vector2f>(uvsStructures.size());
                    for (Structure uvStructure : uvsStructures) {
                        DynamicArray<Number> mFaceUVs = (DynamicArray<Number>) uvStructure.getFieldValue("uv");
                        uvs.add(new Vector2f(mFaceUVs.get(0).floatValue(), mFaceUVs.get(1).floatValue()));
                        uvs.add(new Vector2f(mFaceUVs.get(2).floatValue(), mFaceUVs.get(3).floatValue()));
                        uvs.add(new Vector2f(mFaceUVs.get(4).floatValue(), mFaceUVs.get(5).floatValue()));
                        uvs.add(new Vector2f(mFaceUVs.get(6).floatValue(), mFaceUVs.get(7).floatValue()));
                    }
                    result.put(uvSetName, uvs);
                }
            }
        }
    }
    return result;
}
Also used : ArrayList(java.util.ArrayList) Pointer(com.jme3.scene.plugins.blender.file.Pointer) LinkedHashMap(java.util.LinkedHashMap) Vector2f(com.jme3.math.Vector2f) DynamicArray(com.jme3.scene.plugins.blender.file.DynamicArray) ArrayList(java.util.ArrayList) List(java.util.List) Structure(com.jme3.scene.plugins.blender.file.Structure)

Example 3 with DynamicArray

use of com.jme3.scene.plugins.blender.file.DynamicArray in project jmonkeyengine by jMonkeyEngine.

the class MeshHelper method loadVerticesAndNormals.

/**
     * This method returns the vertices.
     * 
     * @param meshStructure
     *            the structure containing the mesh data
     * @return a list of two - element arrays, the first element is the vertex and the second - its normal
     * @throws BlenderFileException
     *             this exception is thrown when the blend file structure is somehow invalid or corrupted
     */
@SuppressWarnings("unchecked")
public void loadVerticesAndNormals(Structure meshStructure, List<Vector3f> vertices, List<Vector3f> normals) throws BlenderFileException {
    LOGGER.log(Level.FINE, "Loading vertices and normals from mesh: {0}.", meshStructure.getName());
    int count = ((Number) meshStructure.getFieldValue("totvert")).intValue();
    if (count > 0) {
        Pointer pMVert = (Pointer) meshStructure.getFieldValue("mvert");
        List<Structure> mVerts = pMVert.fetchData();
        Vector3f co = null, no = null;
        if (fixUpAxis) {
            for (int i = 0; i < count; ++i) {
                DynamicArray<Number> coordinates = (DynamicArray<Number>) mVerts.get(i).getFieldValue("co");
                co = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(2).floatValue(), -coordinates.get(1).floatValue());
                vertices.add(co);
                DynamicArray<Number> norm = (DynamicArray<Number>) mVerts.get(i).getFieldValue("no");
                no = new Vector3f(norm.get(0).shortValue() / 32767.0f, norm.get(2).shortValue() / 32767.0f, -norm.get(1).shortValue() / 32767.0f);
                normals.add(no);
            }
        } else {
            for (int i = 0; i < count; ++i) {
                DynamicArray<Number> coordinates = (DynamicArray<Number>) mVerts.get(i).getFieldValue("co");
                co = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(1).floatValue(), coordinates.get(2).floatValue());
                vertices.add(co);
                DynamicArray<Number> norm = (DynamicArray<Number>) mVerts.get(i).getFieldValue("no");
                no = new Vector3f(norm.get(0).shortValue() / 32767.0f, norm.get(1).shortValue() / 32767.0f, norm.get(2).shortValue() / 32767.0f);
                normals.add(no);
            }
        }
    }
    LOGGER.log(Level.FINE, "Loaded {0} vertices and normals.", vertices.size());
}
Also used : DynamicArray(com.jme3.scene.plugins.blender.file.DynamicArray) Vector3f(com.jme3.math.Vector3f) Pointer(com.jme3.scene.plugins.blender.file.Pointer) Structure(com.jme3.scene.plugins.blender.file.Structure)

Example 4 with DynamicArray

use of com.jme3.scene.plugins.blender.file.DynamicArray in project jmonkeyengine by jMonkeyEngine.

the class CurvesTemporalMesh method loadNurbSurface.

/**
     * This method loads the NURBS curve or surface.
     * @param nurb
     *            the NURBS data structure
     * @throws BlenderFileException
     *             an exception is thrown when problems with reading occur
     */
@SuppressWarnings("unchecked")
private void loadNurbSurface(Structure nurb, int materialIndex) throws BlenderFileException {
    // loading the knots
    List<Float>[] knots = new List[2];
    Pointer[] pKnots = new Pointer[] { (Pointer) nurb.getFieldValue("knotsu"), (Pointer) nurb.getFieldValue("knotsv") };
    for (int i = 0; i < knots.length; ++i) {
        if (pKnots[i].isNotNull()) {
            FileBlockHeader fileBlockHeader = blenderContext.getFileBlock(pKnots[i].getOldMemoryAddress());
            BlenderInputStream blenderInputStream = blenderContext.getInputStream();
            blenderInputStream.setPosition(fileBlockHeader.getBlockPosition());
            int knotsAmount = fileBlockHeader.getCount() * fileBlockHeader.getSize() / 4;
            knots[i] = new ArrayList<Float>(knotsAmount);
            for (int j = 0; j < knotsAmount; ++j) {
                knots[i].add(Float.valueOf(blenderInputStream.readFloat()));
            }
        }
    }
    // loading the flags and orders (basis functions degrees)
    int flag = ((Number) nurb.getFieldValue("flag")).intValue();
    boolean smooth = (flag & FLAG_SMOOTH) != 0;
    int flagU = ((Number) nurb.getFieldValue("flagu")).intValue();
    int flagV = ((Number) nurb.getFieldValue("flagv")).intValue();
    int orderU = ((Number) nurb.getFieldValue("orderu")).intValue();
    int orderV = ((Number) nurb.getFieldValue("orderv")).intValue();
    // loading control points and their weights
    int pntsU = ((Number) nurb.getFieldValue("pntsu")).intValue();
    int pntsV = ((Number) nurb.getFieldValue("pntsv")).intValue();
    List<Structure> bPoints = ((Pointer) nurb.getFieldValue("bp")).fetchData();
    List<List<Vector4f>> controlPoints = new ArrayList<List<Vector4f>>(pntsV);
    for (int i = 0; i < pntsV; ++i) {
        List<Vector4f> uControlPoints = new ArrayList<Vector4f>(pntsU);
        for (int j = 0; j < pntsU; ++j) {
            DynamicArray<Float> vec = (DynamicArray<Float>) bPoints.get(j + i * pntsU).getFieldValue("vec");
            if (blenderContext.getBlenderKey().isFixUpAxis()) {
                uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(2).floatValue(), -vec.get(1).floatValue(), vec.get(3).floatValue()));
            } else {
                uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(1).floatValue(), vec.get(2).floatValue(), vec.get(3).floatValue()));
            }
        }
        if ((flagU & 0x01) != 0) {
            for (int k = 0; k < orderU - 1; ++k) {
                uControlPoints.add(uControlPoints.get(k));
            }
        }
        controlPoints.add(uControlPoints);
    }
    if ((flagV & 0x01) != 0) {
        for (int k = 0; k < orderV - 1; ++k) {
            controlPoints.add(controlPoints.get(k));
        }
    }
    int originalVerticesAmount = vertices.size();
    int resolu = ((Number) nurb.getFieldValue("resolu")).intValue();
    if (knots[1] == null) {
        // creating the NURB curve
        Curve curve = new Curve(new Spline(controlPoints.get(0), knots[0]), resolu);
        FloatBuffer vertsBuffer = (FloatBuffer) curve.getBuffer(Type.Position).getData();
        beziers.add(new BezierLine(BufferUtils.getVector3Array(vertsBuffer), materialIndex, smooth, false));
    } else {
        // creating the NURB surface
        int resolv = ((Number) nurb.getFieldValue("resolv")).intValue();
        int uSegments = resolu * controlPoints.get(0).size() - 1;
        int vSegments = resolv * controlPoints.size() - 1;
        Surface nurbSurface = Surface.createNurbsSurface(controlPoints, knots, uSegments, vSegments, orderU, orderV, smooth);
        FloatBuffer vertsBuffer = (FloatBuffer) nurbSurface.getBuffer(Type.Position).getData();
        vertices.addAll(Arrays.asList(BufferUtils.getVector3Array(vertsBuffer)));
        FloatBuffer normalsBuffer = (FloatBuffer) nurbSurface.getBuffer(Type.Normal).getData();
        normals.addAll(Arrays.asList(BufferUtils.getVector3Array(normalsBuffer)));
        IndexBuffer indexBuffer = nurbSurface.getIndexBuffer();
        for (int i = 0; i < indexBuffer.size(); i += 3) {
            int index1 = indexBuffer.get(i) + originalVerticesAmount;
            int index2 = indexBuffer.get(i + 1) + originalVerticesAmount;
            int index3 = indexBuffer.get(i + 2) + originalVerticesAmount;
            faces.add(new Face(new Integer[] { index1, index2, index3 }, smooth, materialIndex, null, null, this));
        }
    }
}
Also used : ArrayList(java.util.ArrayList) Pointer(com.jme3.scene.plugins.blender.file.Pointer) FloatBuffer(java.nio.FloatBuffer) Spline(com.jme3.math.Spline) Surface(com.jme3.scene.shape.Surface) IndexBuffer(com.jme3.scene.mesh.IndexBuffer) Vector4f(com.jme3.math.Vector4f) ArrayList(java.util.ArrayList) List(java.util.List) BlenderInputStream(com.jme3.scene.plugins.blender.file.BlenderInputStream) Structure(com.jme3.scene.plugins.blender.file.Structure) Face(com.jme3.scene.plugins.blender.meshes.Face) FileBlockHeader(com.jme3.scene.plugins.blender.file.FileBlockHeader) Curve(com.jme3.scene.shape.Curve) DynamicArray(com.jme3.scene.plugins.blender.file.DynamicArray)

Example 5 with DynamicArray

use of com.jme3.scene.plugins.blender.file.DynamicArray in project jmonkeyengine by jMonkeyEngine.

the class Field method fill.

/**
     * This method fills the field wth data read from the input stream.
     * @param blenderInputStream
     *            the stream we read data from
     * @throws BlenderFileException
     *             an exception is thrown when the blend file is somehow invalid or corrupted
     */
public void fill(BlenderInputStream blenderInputStream) throws BlenderFileException {
    int dataToRead = 1;
    if (tableSizes != null && tableSizes.length > 0) {
        for (int size : tableSizes) {
            if (size <= 0) {
                throw new BlenderFileException("The field " + name + " has invalid table size: " + size);
            }
            dataToRead *= size;
        }
    }
    DataType dataType = pointerLevel == 0 ? DataType.getDataType(type, blenderContext) : DataType.POINTER;
    switch(dataType) {
        case POINTER:
            if (dataToRead == 1) {
                Pointer pointer = new Pointer(pointerLevel, function, blenderContext);
                pointer.fill(blenderInputStream);
                value = pointer;
            } else {
                Pointer[] data = new Pointer[dataToRead];
                for (int i = 0; i < dataToRead; ++i) {
                    Pointer pointer = new Pointer(pointerLevel, function, blenderContext);
                    pointer.fill(blenderInputStream);
                    data[i] = pointer;
                }
                value = new DynamicArray<Pointer>(tableSizes, data);
            }
            break;
        case CHARACTER:
            // and characters are very often used as byte number stores instead of real chars
            if (dataToRead == 1) {
                value = Byte.valueOf((byte) blenderInputStream.readByte());
            } else {
                Character[] data = new Character[dataToRead];
                for (int i = 0; i < dataToRead; ++i) {
                    data[i] = Character.valueOf((char) blenderInputStream.readByte());
                }
                value = new DynamicArray<Character>(tableSizes, data);
            }
            break;
        case SHORT:
            if (dataToRead == 1) {
                value = Integer.valueOf(blenderInputStream.readShort());
            } else {
                Number[] data = new Number[dataToRead];
                for (int i = 0; i < dataToRead; ++i) {
                    data[i] = Integer.valueOf(blenderInputStream.readShort());
                }
                value = new DynamicArray<Number>(tableSizes, data);
            }
            break;
        case INTEGER:
            if (dataToRead == 1) {
                value = Integer.valueOf(blenderInputStream.readInt());
            } else {
                Number[] data = new Number[dataToRead];
                for (int i = 0; i < dataToRead; ++i) {
                    data[i] = Integer.valueOf(blenderInputStream.readInt());
                }
                value = new DynamicArray<Number>(tableSizes, data);
            }
            break;
        case LONG:
            if (dataToRead == 1) {
                value = Long.valueOf(blenderInputStream.readLong());
            } else {
                Number[] data = new Number[dataToRead];
                for (int i = 0; i < dataToRead; ++i) {
                    data[i] = Long.valueOf(blenderInputStream.readLong());
                }
                value = new DynamicArray<Number>(tableSizes, data);
            }
            break;
        case FLOAT:
            if (dataToRead == 1) {
                value = Float.valueOf(blenderInputStream.readFloat());
            } else {
                Number[] data = new Number[dataToRead];
                for (int i = 0; i < dataToRead; ++i) {
                    data[i] = Float.valueOf(blenderInputStream.readFloat());
                }
                value = new DynamicArray<Number>(tableSizes, data);
            }
            break;
        case DOUBLE:
            if (dataToRead == 1) {
                value = Double.valueOf(blenderInputStream.readDouble());
            } else {
                Number[] data = new Number[dataToRead];
                for (int i = 0; i < dataToRead; ++i) {
                    data[i] = Double.valueOf(blenderInputStream.readDouble());
                }
                value = new DynamicArray<Number>(tableSizes, data);
            }
            break;
        case VOID:
            break;
        case STRUCTURE:
            if (dataToRead == 1) {
                Structure structure = blenderContext.getDnaBlockData().getStructure(type);
                structure.fill(blenderContext.getInputStream());
                value = structure;
            } else {
                Structure[] data = new Structure[dataToRead];
                for (int i = 0; i < dataToRead; ++i) {
                    Structure structure = blenderContext.getDnaBlockData().getStructure(type);
                    structure.fill(blenderContext.getInputStream());
                    data[i] = structure;
                }
                value = new DynamicArray<Structure>(tableSizes, data);
            }
            break;
        default:
            throw new IllegalStateException("Unimplemented filling of type: " + type);
    }
}
Also used : DataType(com.jme3.scene.plugins.blender.file.Structure.DataType)

Aggregations

DynamicArray (com.jme3.scene.plugins.blender.file.DynamicArray)7 Pointer (com.jme3.scene.plugins.blender.file.Pointer)6 Structure (com.jme3.scene.plugins.blender.file.Structure)6 ArrayList (java.util.ArrayList)4 List (java.util.List)3 Spline (com.jme3.math.Spline)2 Vector3f (com.jme3.math.Vector3f)2 Curve (com.jme3.scene.shape.Curve)2 FloatBuffer (java.nio.FloatBuffer)2 ParticleEmitter (com.jme3.effect.ParticleEmitter)1 EmptyParticleInfluencer (com.jme3.effect.influencers.EmptyParticleInfluencer)1 NewtonianParticleInfluencer (com.jme3.effect.influencers.NewtonianParticleInfluencer)1 ParticleInfluencer (com.jme3.effect.influencers.ParticleInfluencer)1 EmitterMeshConvexHullShape (com.jme3.effect.shapes.EmitterMeshConvexHullShape)1 EmitterMeshFaceShape (com.jme3.effect.shapes.EmitterMeshFaceShape)1 EmitterMeshVertexShape (com.jme3.effect.shapes.EmitterMeshVertexShape)1 ColorRGBA (com.jme3.math.ColorRGBA)1 Vector2f (com.jme3.math.Vector2f)1 Vector4f (com.jme3.math.Vector4f)1 CullHint (com.jme3.scene.Spatial.CullHint)1