Symmetry/ Mirror Modifer . Uv transformations

hi i would like to submit my code so that it can be used by everyone, who knows someone might need it.

Symmetry



Current bugs :

  1. i dont know how to apply animationControl / skeletonControl to the new mesh.
  2. havent implemented “slice along mirror” operation yet.



    [java]

    package jme3test;



    import com.jme3.animation.AnimChannel;

    import com.jme3.animation.AnimControl;

    import com.jme3.animation.AnimEventListener;

    import com.jme3.animation.SkeletonControl;

    import com.jme3.app.SimpleApplication;

    import com.jme3.light.AmbientLight;

    import com.jme3.light.DirectionalLight;

    import com.jme3.math.ColorRGBA;

    import com.jme3.math.Vector3f;

    import com.jme3.scene.Geometry;

    import com.jme3.scene.Mesh;

    import com.jme3.scene.Node;

    import com.jme3.scene.Spatial;

    import com.jme3.util.TangentBinormalGenerator;

    import java.util.logging.Level;

    import java.util.logging.Logger;



    /** BUG : each mesh loses animation. Because i dont know how to attach the skeleton/animation control to new mesh.*/

    public class TestSymmetry extends SimpleApplication implements AnimEventListener

    {

    public static void main(String[] args)

    {

    TestSymmetry app = new TestSymmetry();

    app.start();

    }



    @Override

    public void simpleInitApp()

    {

    Logger.getLogger("").setLevel(Level.SEVERE);

    flyCam.setMoveSpeed(50);



    Node node = (Node)assetManager.loadModel(“Models/Sinbad/Sinbad.mesh.xml”);

    SkeletonControl skeleton = node.getControl(SkeletonControl.class);

    AnimControl animation = node.getControl(AnimControl.class);



    //apply symmetry modifier to all model parts.

    for(Spatial child : node.getChildren())

    {

    Geometry geom = (Geometry)child;

    Mesh mesh = geom.getMesh();

    MeshData data = new MeshData(mesh).createSymmetricMesh(Vector3f.ZERO, Vector3f.UNIT_Y, 0.1f);

    mesh = data.createMesh();

    geom.setMesh(mesh); //BUG : each mesh loses animation.

    }

    TangentBinormalGenerator.generate(node);

    rootNode.attachChild(node);



    AnimControl control = node.getControl(AnimControl.class);

    if (control != null) //if it has animation

    {

    control.addListener(this);

    AnimChannel channel = control.createChannel();

    channel.setAnim(“SliceHorizontal”);

    }



    DirectionalLight sun = new DirectionalLight();

    sun.setDirection(new Vector3f(1, 0, -2).normalizeLocal());

    sun.setColor(ColorRGBA.White);

    rootNode.addLight(sun);

    AmbientLight al = new AmbientLight();

    al.setColor(ColorRGBA.White.mult(1.5f));

    rootNode.addLight(al);



    cam.setLocation(new Vector3f(-5f, 2f, 0f));

    cam.lookAt(new Vector3f(0f, 1.5f, 0f), Vector3f.UNIT_Y);

    }



    public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName)

    {

    }



    public void onAnimChange(AnimControl control, AnimChannel channel, String animName)

    {

    }

    }

    [/java]



    [java]

    package jme3test;



    import com.jme3.material.Material;

    import com.jme3.math.Vector2f;

    import com.jme3.math.Vector3f;

    import com.jme3.scene.Geometry;

    import com.jme3.scene.Mesh;

    import com.jme3.scene.Node;

    import com.jme3.scene.VertexBuffer;

    import com.jme3.scene.VertexBuffer.Type;

    import com.jme3.scene.shape.Line;

    import com.jme3.util.BufferUtils;

    import java.nio.ByteBuffer;

    import java.nio.FloatBuffer;

    import java.nio.ShortBuffer;

    import jme3tools.optimize.GeometryBatchFactory;



    public final class Utilities

    {

    /**
  • Distance formula from Koen Samyn : http://knol.google.com/k/plane-equation-in-3d#

    *
  • @param pointToBeMeasured the Point you want to find its distance from plane.
  • @param anyPointOfPlane any plane’s point e.g plane’s origin
  • @param planeNormal the plane’s Normal at the “anyPointOfPlane”

    */

    public static float getDistanceOfPointFromPlane(Vector3f pointToBeMeasured, Vector3f anyPointOfPlane, Vector3f planeNormal)

    {

    return pointToBeMeasured.subtract(anyPointOfPlane).dot(planeNormal);

    }



    /**
  • Symmetry formula from Koen Samyn : http://knol.google.com/k/mirroring-a-point-on-a-3d-plane#
  • Same info on wikipedia : http://en.wikipedia.org/wiki/Reflection_(mathematics)

    *
  • @param pointToBeMirrored the Point you want to find its symmetric
  • @param planeOrigin the planeOrigin e.g (0,0,0)
  • @param planeNormal the planeNormal is a vector that points upward from the plane e.g Vector3f.UNIT_Z

    */

    public static Vector3f getSummetricPosition(Vector3f pointToBeMirrored, Vector3f planeOrigin, Vector3f planeNormal)

    {

    if (!planeNormal.isUnitVector())

    {

    throw new IllegalArgumentException("planeNormal " + planeNormal.toString() + " is not a unit vector");

    }

    Vector3f symmetricPoint = pointToBeMirrored.subtract(planeNormal.mult(2 * getDistanceOfPointFromPlane(pointToBeMirrored, planeOrigin, planeNormal)));

    return symmetricPoint;

    }



    public static float[] merge(float[] f1, float[] f2)

    {

    if (f1 == null) return f2;

    if (f2 == null) return f1;

    float[] f = new float[f1.length + f2.length];

    System.arraycopy(f1, 0, f, 0, f1.length);

    System.arraycopy(f2, 0, f, f1.length, f2.length);

    return f;

    }



    public static Type getTexCoordType(int i)

    {

    switch(i)

    {

    case 0: return Type.TexCoord;

    case 1: return Type.TexCoord2;

    case 2: return Type.TexCoord3;

    case 3: return Type.TexCoord4;

    case 4: return Type.TexCoord5;

    case 5: return Type.TexCoord6;

    case 6: return Type.TexCoord7;

    case 7: return Type.TexCoord8;

    default: throw new IllegalArgumentException("The specified tex coord type not found");

    }

    }



    public static Vector3f getVector3FromArray(float[] array,int i)

    {

    return new Vector3f(array[i*3],array[i*3+1],array[i*3+2]);

    }



    public static Vector2f getVector2FromArray(float[] array,int i)

    {

    return new Vector2f(array[i*2],array[i*2+1]);

    }



    public static void setInArray(Vector3f p1, float[] array, int i)

    {

    array[i*3] = p1.x;

    array[i*3+1] = p1.y;

    array[i*3+2] = p1.z;

    }



    public static void setInArray(Vector2f p1, float[] array, int i)

    {

    array[i*2] = p1.x;

    array[i*2+1] = p1.y;

    }



    public static Node createDebugNormals(Mesh mesh, Material mat)

    {

    Node debugNormals = new Node();



    VertexBuffer vertex = mesh.getBuffer(Type.Position);

    float[] vertexArray = BufferUtils.getFloatArray((FloatBuffer) vertex.getData());



    VertexBuffer normals = mesh.getBuffer(Type.Normal);

    float[] normalArray = BufferUtils.getFloatArray((FloatBuffer) normals.getData());



    for (int i = 0; i < vertexArray.length; i += 3)

    {

    Vector3f p1 = new Vector3f(vertexArray, vertexArray, vertexArray);

    Vector3f n1 = new Vector3f(normalArray, normalArray, normalArray);



    debugNormals.attachChild(createLine("DebugShowNormalsLine", mat, p1, p1.add(n1.mult(0.1f))));

    }



    GeometryBatchFactory.optimize(debugNormals);

    return debugNormals;

    }



    public static Geometry createLine(String name, Material mat, Vector3f startPosition, Vector3f endPosition)

    {

    Line line = new Line(startPosition, endPosition);

    Geometry lineGeom = new Geometry(name, line);

    lineGeom.setMaterial(mat);

    return lineGeom;

    }



    /**
  • Create a new short[] array and populate it with the given ShortBuffer’s
  • contents.

    *
  • @param buff
  •        the ShortBuffer to read from<br />
    
  • @return a new short array populated from the ShortBuffer

    */

    public static short[] getShortArray(ShortBuffer buff)

    {

    if (buff == null)

    {

    return null;

    }

    buff.clear();

    short[] inds = new short[buff.limit()];

    for (int x = 0; x < inds.length; x++)

    {

    inds[x] = buff.get();

    }

    return inds;

    }



    /**
  • Create a new byte[] array and populate it with the given ByteBuffer’s
  • contents.

    *
  • @param buff
  •        the ByteBuffer to read from<br />
    
  • @return a new byte array populated from the ByteBuffer

    /

    public static byte[] getByteArray(ByteBuffer buff)

    {

    if (buff == null)

    {

    return null;

    }

    buff.clear();

    byte[] inds = new byte[buff.limit()];

    for (int x = 0; x < inds.length; x++)

    {

    inds[x] = buff.get();

    }

    return inds;

    }

    }//Utilities

    [/java]



    [java]

    package jme3test;



    import com.jme3.math.FastMath;

    import com.jme3.math.Vector2f;

    import com.jme3.math.Vector3f;

    import com.jme3.scene.Mesh;

    import com.jme3.scene.VertexBuffer;

    import com.jme3.scene.VertexBuffer.Type;

    import com.jme3.util.BufferUtils;

    import java.nio.ByteBuffer;

    import java.util.HashSet;



    /
    * Immutable class that transforms the mesh.
  • It is immutable to minimize get/set/construct array operations, and doesnt copy data unless it has to modify it.
  • Since it is immutable remember to use the returned value, because the class internal state / method arguments dont change.
  • @toDo Implement "slice along mirror" for symmetry.
  • @toDo Support animation. Bug : Only half of the mesh has animation (the symmetric part has no animation).
  • @toDo ability to apply Transformations to specific points (e.g not affecting the whole mesh but a part of it).
  • @toDo select uvs, vertexes etc based on mouse click.

    /

    public final class MeshData

    {

    private float[] vertexArray;

    private float[] normalArray;

    private float[][] uvArrays = new float[MAX_TEX_COORDS][];

    private short[] indexArray;

    private float[] bonePositionArray;

    private float[] boneNormalArray;

    private float[] boneWeightArray;

    private byte[] boneIndexArray;

    public static final HashSet<Short> SELECT_ALL_INDEXES = null;

    public static final HashSet<Short> SELECT_NO_INDEXES = new HashSet<Short>();

    public static final int MAX_TEX_COORDS = 8;



    /
    * Creates an empty mesh /

    private MeshData()

    {

    }



    /
    * Creates a mesh without animation, and only 1 tex coord./

    public MeshData(float[] vertexArray, float[] normalArray, float[] uvArray, short[] indexArray)

    {

    this.vertexArray = vertexArray;

    this.normalArray = normalArray;

    this.uvArrays[0] = uvArray;

    this.indexArray = indexArray;

    }



    /** Creates a mesh with the specified values.
    /

    public MeshData(float[] vertexArray, float[] normalArray, float[][] uvArrays, short[] indexArray, float[] bonePositionArray, float[] boneNormalArray, float[] boneWeightArray, byte[] boneIndexArray)

    {

    this.vertexArray = vertexArray;

    this.normalArray = normalArray;

    for(int t=0;t<MAX_TEX_COORDS;t++) this.uvArrays[t] = uvArrays[t];

    this.indexArray = indexArray;

    this.bonePositionArray = bonePositionArray;

    this.boneNormalArray = boneNormalArray;

    this.boneWeightArray = boneWeightArray;

    this.boneIndexArray = boneIndexArray;

    }



    /** creates MeshData from mesh */

    public MeshData(Mesh mesh)

    {

    vertexArray = BufferUtils.getFloatArray(mesh.getFloatBuffer(Type.Position));

    normalArray = BufferUtils.getFloatArray(mesh.getFloatBuffer(Type.Normal));

    for(int i=0; i < MAX_TEX_COORDS; i++) uvArrays = BufferUtils.getFloatArray(mesh.getFloatBuffer(Utilities.getTexCoordType(i)));

    indexArray = Utilities.getShortArray(mesh.getShortBuffer(Type.Index));

    bonePositionArray = BufferUtils.getFloatArray(mesh.getFloatBuffer(Type.BindPosePosition));

    boneNormalArray = BufferUtils.getFloatArray(mesh.getFloatBuffer(Type.BindPoseNormal));

    boneWeightArray = BufferUtils.getFloatArray(mesh.getFloatBuffer(Type.BoneWeight));

    VertexBuffer boneIndexBuffer = mesh.getBuffer(Type.BoneIndex);

    if (boneIndexBuffer != null) boneIndexArray = (Utilities.getByteArray((ByteBuffer) boneIndexBuffer.getData()));

    }



    /**Merges 2 meshes into 1. The second’s mesh indexes change to point to their new position (after mesh1’s array).
  • @param shareSkeleton if the resulting mesh will have the skeleton of mesh1, (or mesh2 if mesh1 skeleton is null).

    /

    public MeshData merge(MeshData mesh2, boolean shareSkeleton)

    {

    MeshData mesh1 = this;



    MeshData resultMesh = new MeshData();

    resultMesh.vertexArray = Utilities.merge(mesh1.vertexArray, mesh2.vertexArray);

    resultMesh.normalArray = Utilities.merge(mesh1.normalArray, mesh2.normalArray);

    for (int i = 0; i < MAX_TEX_COORDS; i++)

    {

    resultMesh.uvArrays = Utilities.merge(mesh1.uvArrays, mesh2.uvArrays);

    }



    //make second’s mesh indexes point to their new position (after mesh1’s array).
    /

    resultMesh.indexArray = new short[mesh1.indexArray.length + mesh2.indexArray.length];

    System.arraycopy(mesh1.indexArray, 0, resultMesh.indexArray, 0, mesh1.indexArray.length);

    for (int i = 0; i < mesh2.indexArray.length; i++)

    {

    resultMesh.indexArray[mesh1.indexArray.length + i] = (short) (mesh2.indexArray + mesh1.getNumberOfVertexElements());

    }



    if (mesh1.bonePositionArray != null && mesh1.boneNormalArray != null && mesh1.bonePositionArray != null && mesh1.boneWeightArray != null)

    if (shareSkeleton)

    {

    if (mesh1.bonePositionArray != null) resultMesh.bonePositionArray = mesh1.bonePositionArray;

    else resultMesh.bonePositionArray = mesh2.bonePositionArray;



    if (mesh1.boneNormalArray != null) resultMesh.boneNormalArray = mesh1.boneNormalArray;

    else resultMesh.boneNormalArray = mesh2.boneNormalArray;



    if (mesh1.boneWeightArray != null) resultMesh.boneWeightArray = mesh1.boneWeightArray;

    else resultMesh.boneWeightArray = mesh2.boneWeightArray;



    if (mesh1.boneIndexArray != null) resultMesh.boneIndexArray = mesh1.boneIndexArray;

    else resultMesh.boneIndexArray = mesh2.boneIndexArray;

    }

    else

    {

    resultMesh.bonePositionArray = Utilities.merge(mesh1.bonePositionArray, mesh2.bonePositionArray);

    resultMesh.boneNormalArray = Utilities.merge(mesh1.boneNormalArray, mesh2.boneNormalArray);

    resultMesh.boneWeightArray = Utilities.merge(mesh1.boneWeightArray, mesh2.boneWeightArray);



    //make second’s mesh bone indexes point to their new position (after mesh1’s array).*/

    resultMesh.boneIndexArray = new byte[mesh1.boneIndexArray.length + mesh2.boneIndexArray.length];

    System.arraycopy(mesh1.boneIndexArray, 0, resultMesh.boneIndexArray, 0, mesh1.boneIndexArray.length);

    for (int i = 0; i < mesh2.boneIndexArray.length; i++)

    {

    resultMesh.boneIndexArray[mesh1.boneIndexArray.length + i] = (byte) (mesh2.boneIndexArray + mesh1.bonePositionArray.length / 3);

    }

    }



    return resultMesh;

    }



    /**
  • @param planeOrigin the planeOrigin e.g (0,0,0)
  • @param planeNormal the planeNormal is a vector that points upward from the plane e.g Vector3f.UNIT_Z

    /

    public MeshData mirror(Vector3f planeOrigin, Vector3f planeNormal, boolean mirrorSkeleton)

    {

    MeshData mesh = this;

    MeshData resultMesh = mesh.shallowClone();

    resultMesh.vertexArray = new float[mesh.vertexArray.length];

    resultMesh.normalArray = new float[mesh.normalArray.length];



    //mirror positions.

    for (int i = 0; i < mesh.getNumberOfVertexElements(); i++)

    {

    Vector3f position = mesh.getVertex(i);

    Vector3f summetricPosition = Utilities.getSummetricPosition(position, planeOrigin, planeNormal);

    resultMesh.setVertex(summetricPosition, i);

    }



    //mirror normals.

    for (int i = 0; i < mesh.getNumberOfNormalElements(); i++)

    {

    Vector3f normalPosition = mesh.getNormal(i);

    Vector3f summetricNormalPosition = Utilities.getSummetricPosition(normalPosition, planeOrigin, planeNormal);

    resultMesh.setNormal(summetricNormalPosition, i);

    }





    if (mirrorSkeleton && mesh.bonePositionArray != null && mesh.boneNormalArray != null && mesh.bonePositionArray != null && mesh.boneWeightArray != null)

    {

    resultMesh.bonePositionArray = new float[mesh.bonePositionArray.length];

    resultMesh.boneNormalArray = new float[mesh.boneNormalArray.length];



    //mirror bone positions.

    for (int i = 0; i < mesh.getNumberOfBonePositionElements(); i++)

    {

    Vector3f bonePosition = mesh.getBonePosition(i);

    Vector3f summetricBonePosition = Utilities.getSummetricPosition(bonePosition, planeOrigin, planeNormal);

    resultMesh.setBonePosition(summetricBonePosition, i);

    }



    //mirror bone normals.

    for (int i = 0; i < mesh.getNumberOfBoneNormalElements(); i++)

    {

    Vector3f boneNormalPosition = mesh.getBoneNormal(i);

    Vector3f summetricBoneNormalPosition = Utilities.getSummetricPosition(boneNormalPosition, planeOrigin, planeNormal);

    resultMesh.setBoneNormal(summetricBoneNormalPosition, i);

    }

    }



    resultMesh = resultMesh.flipIndexes();

    return resultMesh;

    }



    /
    * Applies symmetry modifier to mesh, and creates a new mesh from the 2 submeshes.
  • @param planeOrigin the planeOrigin e.g (0,0,0)
  • @param planeNormal the planeNormal is a vector that points upward from the plane e.g Vector3f.UNIT_Z
  • @param weldThreshold how much to smooth the vertices near the plane suggested amount from 0.001 until 0.1.
  •                  Set it to negative number to disable welding.<br />
    

/

public MeshData createSymmetricMesh(Vector3f planeOrigin, Vector3f planeNormal, float weldThreshold)

{

MeshData mesh = this.shallowClone();

MeshData mirrorMesh = mesh.mirror(planeOrigin, planeNormal, true);



float[] weldedMeshVertexArray = new float[mesh.vertexArray.length];

float[] weldedMirrorMeshVertexArray = new float[mirrorMesh.vertexArray.length];

float[] weldedMeshNormalArray = new float[mesh.normalArray.length];

float[] weldedMirrorMeshNormalArray = new float[mirrorMesh.normalArray.length];



for (int i = 0; i < mesh.getNumberOfIndexElements(); i++)

{

int index = mesh.getIndex(i);

Vector3f p1 = mesh.getVertex(index);

Vector3f mp1 = mirrorMesh.getVertex(index);

Vector3f n1 = mesh.getNormal(index);

Vector3f mn1 = mirrorMesh.getNormal(index);



if (Utilities.getDistanceOfPointFromPlane(p1, planeOrigin, planeNormal) <= weldThreshold)

{

p1 = p1.interpolate(mp1, 0.5f);

n1 = n1.interpolate(mn1, 0.5f);

mp1 = p1;

mn1 = n1;

}//if

Utilities.setInArray(p1, weldedMeshVertexArray, index);

Utilities.setInArray(mp1, weldedMirrorMeshVertexArray, index);

Utilities.setInArray(n1, weldedMeshNormalArray, index);

Utilities.setInArray(mn1, weldedMirrorMeshNormalArray, index);

}

mesh.vertexArray = weldedMeshVertexArray;

mesh.normalArray = weldedMeshNormalArray;

mirrorMesh.vertexArray = weldedMirrorMeshVertexArray;

mirrorMesh.normalArray = weldedMirrorMeshNormalArray;

MeshData resultMesh = mesh.merge(mirrorMesh, false);



return resultMesh;

}



/
* Changes the order of indexes, e.g from 1,2,3 it becomes 3,2,1.

  • This will allow an invisible mesh to be seen./

    public MeshData flipIndexes()

    {

    MeshData mesh = this;

    MeshData result = mesh.shallowClone();

    result.indexArray = new short[mesh.indexArray.length];

    for (int i = 0; i < mesh.indexArray.length; i += 3)

    {

    result.indexArray = mesh.indexArray;

    result.indexArray = mesh.indexArray;

    result.indexArray = mesh.indexArray;

    }

    return result;

    }



    /** @param TexCoordNumber which array of tex coords to affect. Usual choice : 0
    /

    public MeshData flipU(int TexCoordNumber)

    {

    MeshData mesh = this;

    MeshData result = mesh.shallowClone();

    result.uvArrays[TexCoordNumber] = new float[mesh.uvArrays[TexCoordNumber].length];

    for (int i = 0; i < mesh.uvArrays[TexCoordNumber].length; i += 2)

    {

    result.uvArrays[TexCoordNumber] = (1 - mesh.uvArrays[TexCoordNumber]) % 1;

    result.uvArrays[TexCoordNumber] = mesh.uvArrays[TexCoordNumber] % 1;

    }

    return result;

    }



    /** @param TexCoordNumber which array of tex coords to affect. Usual choice : 0*/

    public MeshData flipV(int TexCoordNumber)

    {

    MeshData mesh = this;

    MeshData result = mesh.shallowClone();

    result.uvArrays[TexCoordNumber] = new float[mesh.uvArrays[TexCoordNumber].length];

    for (int i = 0; i < mesh.uvArrays[TexCoordNumber].length; i += 2)

    {

    result.uvArrays[TexCoordNumber] = mesh.uvArrays[TexCoordNumber] % 1;

    result.uvArrays[TexCoordNumber] = (1 - mesh.uvArrays[TexCoordNumber]) % 1;

    }

    return result;

    }



    /** @param TexCoordNumber which array of tex coords to affect. Usual choice : 0*/

    public MeshData moveUV(Vector2f uvMoveOffset, int TexCoordNumber)

    {

    MeshData mesh = this;

    MeshData result = mesh.shallowClone();

    result.uvArrays[TexCoordNumber] = new float[mesh.uvArrays[TexCoordNumber].length];

    for (int i = 0; i < mesh.uvArrays[TexCoordNumber].length; i += 2)

    {

    result.uvArrays[TexCoordNumber] = (mesh.uvArrays[TexCoordNumber] + uvMoveOffset.x) % 1;

    result.uvArrays[TexCoordNumber] = (mesh.uvArrays[TexCoordNumber] + uvMoveOffset.y) % 1;

    }

    return result;

    }



    /** @param TexCoordNumber which array of tex coords to affect. Usual choice : 0*/

    public MeshData rotateUV(float uvRotateAngle, int TexCoordNumber)

    {

    MeshData mesh = this;

    MeshData result = mesh.shallowClone();

    result.uvArrays[TexCoordNumber] = new float[mesh.uvArrays[TexCoordNumber].length];

    for (int i = 0; i < mesh.uvArrays[TexCoordNumber].length; i += 2)

    {

    result.uvArrays[TexCoordNumber] = (mesh.uvArrays[TexCoordNumber] * FastMath.cos(uvRotateAngle) - mesh.uvArrays[TexCoordNumber] * FastMath.sin(uvRotateAngle)) % 1;

    result.uvArrays[TexCoordNumber] = (mesh.uvArrays[TexCoordNumber] * FastMath.sin(uvRotateAngle) + mesh.uvArrays[TexCoordNumber] * FastMath.cos(uvRotateAngle)) % 1;

    }

    return result;

    }



    /** @param TexCoordNumber which array of tex coords to affect. Usual choice : 0*/

    public MeshData scaleUV(Vector2f uvScaleAmount, int TexCoordNumber)

    {

    MeshData mesh = this;

    MeshData result = mesh.shallowClone();

    result.uvArrays[TexCoordNumber] = new float[mesh.uvArrays[TexCoordNumber].length];

    for (int i = 0; i < mesh.uvArrays[TexCoordNumber].length; i += 2)

    {

    result.uvArrays[TexCoordNumber] = (mesh.uvArrays[TexCoordNumber] * uvScaleAmount.x) % 1;

    result.uvArrays[TexCoordNumber] = (mesh.uvArrays[TexCoordNumber] * uvScaleAmount.y) % 1;

    }

    return result;

    }



    /** switches u with v.
  • @param TexCoordNumber which array of tex coords to affect. Usual choice : 0

    /

    public MeshData switchUV(int TexCoordNumber)

    {

    MeshData mesh = this;

    MeshData result = mesh.shallowClone();

    result.uvArrays[TexCoordNumber] = new float[mesh.uvArrays[TexCoordNumber].length];

    for (int i = 0; i < mesh.uvArrays[TexCoordNumber].length; i += 2)

    {

    result.uvArrays[TexCoordNumber] = result.uvArrays[TexCoordNumber];

    result.uvArrays[TexCoordNumber] = result.uvArrays[TexCoordNumber];

    }

    return result;

    }



    /
    * sets mesh to have the same data as this./

    public Mesh save(Mesh mesh)

    {

    if (vertexArray != null)

    {

    VertexBuffer vb = mesh.getBuffer(Type.Position);

    if (vb != null) vb.updateData(BufferUtils.createFloatBuffer(vertexArray));

    else mesh.setBuffer(Type.Position, 3, vertexArray);

    }



    if (normalArray != null)

    {

    VertexBuffer vb = mesh.getBuffer(Type.Normal);

    if (vb != null) vb.updateData(BufferUtils.createFloatBuffer(normalArray));

    else mesh.setBuffer(Type.Normal, 3, normalArray);

    }



    for (int i = 0; i < MAX_TEX_COORDS; i++)

    {

    if (uvArrays != null)

    {

    VertexBuffer vb = mesh.getBuffer(Utilities.getTexCoordType(i));

    if (vb != null) vb.updateData(BufferUtils.createFloatBuffer(uvArrays));

    else mesh.setBuffer(Type.TexCoord, 2, uvArrays);

    }

    }



    if (indexArray != null)

    {

    VertexBuffer vb = mesh.getBuffer(Type.Index);

    if (vb != null) vb.updateData(BufferUtils.createShortBuffer(indexArray));

    else mesh.setBuffer(Type.Index, 3, indexArray);

    }



    if (bonePositionArray != null)

    {

    VertexBuffer vb = mesh.getBuffer(Type.BindPosePosition);

    if (vb != null) vb.updateData(BufferUtils.createFloatBuffer(bonePositionArray));

    else mesh.setBuffer(Type.BindPosePosition, 3, bonePositionArray);

    }



    if (boneNormalArray != null)

    {

    VertexBuffer vb = mesh.getBuffer(Type.BindPoseNormal);

    if (vb != null) vb.updateData(BufferUtils.createFloatBuffer(boneNormalArray));

    else mesh.setBuffer(Type.BindPoseNormal, 3, boneNormalArray);

    }



    if (boneWeightArray != null)

    {

    VertexBuffer vb = mesh.getBuffer(Type.BoneWeight);

    if (vb != null) vb.updateData(BufferUtils.createFloatBuffer(boneWeightArray));

    else mesh.setBuffer(Type.BoneWeight, 4, boneWeightArray);

    }



    if (boneIndexArray != null)

    {

    VertexBuffer vb = mesh.getBuffer(Type.BoneIndex);

    if (vb != null) vb.updateData(BufferUtils.createByteBuffer(boneIndexArray));

    else mesh.setBuffer(Type.BoneIndex, 4, boneIndexArray);

    }



    if (vertexArray != null) mesh.updateBound();

    return mesh;

    }



    public Mesh createMesh()

    {

    Mesh mesh = new Mesh();

    return save(mesh);

    }



    private MeshData shallowClone()

    {

    return new MeshData(vertexArray, normalArray, uvArrays, indexArray, bonePositionArray, boneNormalArray, boneWeightArray, boneIndexArray);

    }



    public MeshData setIndexArray(short[] indexArray)

    {

    MeshData mesh = this.shallowClone();

    mesh.indexArray = indexArray;

    return mesh;

    }



    public MeshData setNormalArray(float[] normalArray)

    {

    MeshData mesh = this.shallowClone();

    mesh.normalArray = normalArray;

    return mesh;

    }



    /** @param TexCoordNumber which array of tex coords to affect. Usual choice : 0
    /

    public MeshData setUvArray(float[] uvArray, int TexCoordNumber)

    {

    MeshData mesh = this.shallowClone();

    mesh.uvArrays[TexCoordNumber] = uvArray;

    return mesh;

    }



    public MeshData setVertexArray(float[] vertexArray)

    {

    MeshData mesh = this.shallowClone();

    mesh.vertexArray = vertexArray;

    return mesh;

    }



    public MeshData setBonePositionArray(float[] bonePositionArray)

    {

    MeshData mesh = this.shallowClone();

    mesh.bonePositionArray = bonePositionArray;

    return mesh;

    }



    public MeshData setBoneNormalArray(float[] boneNormalArray)

    {

    MeshData mesh = this.shallowClone();

    mesh.boneNormalArray = boneNormalArray;

    return mesh;

    }



    public MeshData setBoneWeightArray(float[] boneWeightArray)

    {

    MeshData mesh = this.shallowClone();

    mesh.boneWeightArray = boneWeightArray;

    return mesh;

    }



    public MeshData setBoneIndexArray(byte[] boneIndexArray)

    {

    MeshData mesh = this.shallowClone();

    mesh.boneIndexArray = boneIndexArray;

    return mesh;

    }



    public Vector3f getVertex(int i)

    {

    return Utilities.getVector3FromArray(vertexArray, i);

    }



    public Vector3f getNormal(int i)

    {

    return Utilities.getVector3FromArray(normalArray, i);

    }



    /** @param TexCoordNumber which array of tex coords to affect. Usual choice : 0*/

    public Vector2f getUv(int i, int TexCoordNumber)

    {

    return Utilities.getVector2FromArray(uvArrays[TexCoordNumber], i);

    }



    public short getIndex(int i)

    {

    return indexArray;

    }



    public Vector3f getBonePosition(int i)

    {

    return Utilities.getVector3FromArray(bonePositionArray, i);

    }



    public Vector3f getBoneNormal(int i)

    {

    return Utilities.getVector3FromArray(boneNormalArray, i);

    }



    private void setVertex(Vector3f p, int i)

    {

    Utilities.setInArray(p, vertexArray, i);

    }



    private void setNormal(Vector3f p, int i)

    {

    Utilities.setInArray(p, normalArray, i);

    }



    /** @param TexCoordNumber which array of tex coords to affect. Usual choice : 0*/

    private void setUv(Vector2f p, int i, int TexCoordNumber)

    {

    Utilities.setInArray(p, uvArrays[TexCoordNumber], i);

    }



    private void setIndex(Short p, int i)

    {

    indexArray = p;

    }



    private void setBonePosition(Vector3f p, int i)

    {

    Utilities.setInArray(p, bonePositionArray, i);

    }



    private void setBoneNormal(Vector3f p, int i)

    {

    Utilities.setInArray(p, boneNormalArray, i);

    }



    public int getNumberOfVertexElements()

    {

    if (vertexArray == null) return 0;

    return vertexArray.length / 3;

    }



    public int getNumberOfNormalElements()

    {

    if (normalArray == null) return 0;

    return normalArray.length / 3;

    }



    /** @param TexCoordNumber which array of tex coords to affect. Usual choice : 0*/

    public int getNumberOfUvElements(int TexCoordNumber)

    {

    if (uvArrays[TexCoordNumber] == null) return 0;

    return uvArrays[TexCoordNumber].length / 2;

    }



    public int getNumberOfIndexElements()

    {

    if (indexArray == null) return 0;

    return indexArray.length;

    }



    public int getNumberOfBonePositionElements()

    {

    if (bonePositionArray == null) return 0;

    return bonePositionArray.length / 3;

    }



    public int getNumberOfBoneNormalElements()

    {

    if (boneNormalArray == null) return 0;

    return boneNormalArray.length / 3;

    }



    public float[] getVertexArrayCopy()

    {

    if (vertexArray == null) return null;

    return vertexArray.clone();

    }



    public float[] getNormalArrayCopy()

    {

    if (normalArray == null) return null;

    return normalArray.clone();

    }



    /** @param TexCoordNumber which array of tex coords to affect. Usual choice : 0*/

    public float[] getUvArrayCopy(int TexCoordNumber)

    {

    if (uvArrays[TexCoordNumber] == null) return null;

    return uvArrays[TexCoordNumber].clone();

    }



    public short[] getIndexArrayCopy()

    {

    if (indexArray == null) return null;

    return indexArray.clone();

    }



    public float[] getBonePositionArrayCopy()

    {

    if (bonePositionArray == null) return null;

    return bonePositionArray.clone();

    }



    public float[] getBoneNormalArrayCopy()

    {

    if (boneNormalArray == null) return null;

    return boneNormalArray.clone();

    }



    public float[] getBoneWeightArrayCopy()

    {

    if (boneWeightArray == null) return null;

    return boneWeightArray.clone();

    }



    public byte[] getBoneIndexArrayCopy()

    {

    if (boneIndexArray == null) return null;

    return boneIndexArray.clone();

    }

    }

    [/java]

It is probably best to do the mirroring operation on the vertex attributes you know like positions and normals, the rest of them can just be copied directly. Right now you’re somewhat hardcoding all the data types like you assume boneweights are floats although this could change. You can see how to copy them directly in GeometryBatchFactory.



Other than that, it looks good :slight_smile:

for portal like games, this is a gold mine :stuck_out_tongue: