Flipping normals


I’m working currently on importing mirror modifier from blender.

And I came accross something strange.

When I mirror an object (a cube for example) I create cloned mesh object and properly translate all its vertices.

In result I have another mesh (in the same geometry) whose vertices are on the other side of [0, 0, 0] point along the specified axis.

The problem appears when the model has a material set.

The newly created object is black. It can be nicely seen when the base mesh has textures on it.

The problem is with normals. The vertices are mirrored but the normals remain the same.

So I altered normals the same way I altered vertices and … nothing changed.

No matter how I change normals I cannot see any difference.

I cloned the basic mesh so my normals buffer is already there.

I do not create new buffer, just replace the needed data by getting the i’th float changing it an replacing to the same position.

It works with positions but seems like not working with normals.

Does anyone have any idea why it happens ??

You’re saying this only happens with Lighting.j3md right? Unshaded materials are not effected?

Maybe you need to regenerate tangents on the new mesh?

Tangents are not calculated so there is no tangent buffer.

And the original mesh looks ok. Only the mirrored isn’t good.

Unshaded materials do not need normals to calculate lights so there is no difference between mirrored objects.

Below you can find the code of the whole method:


protected Node applyMirrorModifierData(Node node, Modifier modifier, DataRepository dataRepository) {

Map<String, Object> modifierData = (Map<String, Object>) modifier.getJmeModifierRepresentation();

int flag = ((Number) modifierData.get(“flag”)).intValue();

float[] mirrorFactor = new float[] {

(flag & 0x08) != 0 ? -1.0f : 1.0f,

(flag & 0x10) != 0 ? -1.0f : 1.0f,

(flag & 0x20) != 0 ? -1.0f : 1.0f


//calculating the center of the mirror

float[] center = new float[] { 0.0f, 0.0f, 0.0f };

Pointer pObject = (Pointer) modifierData.get(“mirrorob”);

if (pObject != null) {

Structure objectStructure;

try {

objectStructure = pObject.fetchData(dataRepository.getInputStream()).get(0);

ObjectHelper objectHelper = dataRepository.getHelper(ObjectHelper.class);

Node object = (Node) objectHelper.toObject(objectStructure, dataRepository);

if (object != null) {

Vector3f translation = object.getWorldTranslation();

center[0] = translation.x;

center[1] = translation.y;

center[2] = translation.z;


} catch (BlenderFileException e) {

LOGGER.severe("Cannot load mirror’s reference object. Cause: " + e.getLocalizedMessage());



//reading the flags

float tolerance = ((Number) modifierData.get(“tolerance”)).floatValue();

boolean mirrorU = (flag & 0x01) != 0;

boolean mirrorV = (flag & 0x02) != 0;

// boolean mirrorVGroup = (flag & 0x20) != 0;

//here is actual mirroring operation

List<Geometry> geometriesToAdd = new ArrayList<Geometry>();

for (int mirrorIndex = 0; mirrorIndex < 3; ++mirrorIndex) {

if (mirrorFactor[mirrorIndex] == -1.0f) {//perform operation for each axis seperately and only if it is necessary

for (Spatial spatial : node.getChildren()) {

if (spatial instanceof Geometry) {

//cloning the original mesh

Mesh mesh = ((Geometry) spatial).getMesh();

Mesh clone = mesh.deepClone();

// getting buffers

FloatBuffer position = mesh.getFloatBuffer(Type.Position);

FloatBuffer bindPosePosition = mesh.getFloatBuffer(Type.BindPosePosition);

FloatBuffer clonePosition = clone.getFloatBuffer(Type.Position);

FloatBuffer cloneBindPosePosition = clone.getFloatBuffer(Type.BindPosePosition);

FloatBuffer cloneNormals = clone.getFloatBuffer(Type.Normal);

FloatBuffer cloneBindPoseNormals = clone.getFloatBuffer(Type.BindPoseNormal);

// modyfying data

for (int i = mirrorIndex; i < clonePosition.limit(); i += 3) {

float value = clonePosition.get(i);

float d = center[mirrorIndex] - value;

if (Math.abs(d) <= tolerance) {

clonePosition.put(i, center[mirrorIndex]);

cloneBindPosePosition.put(i, center[mirrorIndex]);

position.put(i, center[mirrorIndex]);

bindPosePosition.put(i, center[mirrorIndex]);

} else {

clonePosition.put(i, value + 2.0f * d);

cloneBindPosePosition.put(i, value + 2.0f * d);


cloneNormals.put(i, -cloneNormals.get(i));

cloneBindPoseNormals.put(i, -cloneNormals.get(i));


//mirroring UV coordinates

if(mirrorU) {

FloatBuffer cloneUVs = (FloatBuffer) clone.getBuffer(Type.TexCoord).getData();

for(int i=0;i<cloneUVs.limit();i+=2) {

cloneUVs.put(i, 1.0f - cloneUVs.get(i));



if(mirrorV) {

FloatBuffer cloneUVs = (FloatBuffer) clone.getBuffer(Type.TexCoord).getData();

for(int i=1;i<cloneUVs.limit();i+=2) {

cloneUVs.put(i, 1.0f - cloneUVs.get(i));



//adding geometry to add list

Geometry geometry = new Geometry(null, clone);

geometry.setMaterial(((Geometry) spatial).getMaterial());




// adding meshes to node

for (Geometry geometry : geometriesToAdd) {






return node;



One thing to note is that when you just copy the data and mirror the points you will flip the faces’ winding order.

When you flip the triangle along only one axis, the order could get flipped from counter-clockwise to clockwise, and then that face would then become a candidate for face culling. To avoid the issue, you may have to flip the order of indices in the index buffer, for example the indices of the original triangle are: 0, 1, 2. If you’re flipping one or three axes, you may have to reorder the indices like so: 0, 2, 1.

1 Like

That was it :slight_smile:

Thanks a lot. Changing indexes helped :slight_smile: