How to mirror a mesh without getting its faces upside down?

All topics about ZGameEditor goes here.

Moderator: Moderators

Post Reply
User avatar
Ats
Posts: 933
Joined: Fri Sep 28, 2012 10:05 am
Contact:

How to mirror a mesh without getting its faces upside down?

Post by Ats »

Hello 🙂

I made a whole boat from a single 2D mesh:
Screenshot 2026-01-04 113029.png
Screenshot 2026-01-04 113029.png (23.58 KiB) Viewed 7202 times

I was quite happy with it until I realized that the hole will never be symmetrical:
Screenshot 2026-01-04 113349.png
Screenshot 2026-01-04 113349.png (16.88 KiB) Viewed 7202 times

So I remade only half of the boat from a 2D mesh and mirrored it using scale.X = -1.0 to get the other half, but now the faces on the mirrored half are flipped:
Screenshot 2026-01-04 113801.png
Screenshot 2026-01-04 113801.png (13.68 KiB) Viewed 7202 times
I can mitigate the problem by using a DrawBackFace material, and everything looks correct, but I was wondering if there is some OpenGL “magic” to do the mirroring properly?
User avatar
Kjell
Posts: 1985
Joined: Sat Feb 23, 2008 11:15 pm

Re: How to mirror a mesh without getting its faces upside down?

Post by Kjell »

Hi Ats,
Ats wrote: Sun Jan 04, 2026 10:42 amI can mitigate the problem by using a DrawBackFace material, and everything looks correct
You don't want to enable DrawBackFace when it's not ( really ) needed.
Ats wrote: Sun Jan 04, 2026 10:42 amI was wondering if there is some OpenGL “magic” to do the mirroring properly?
You can toggle the "winding" between clockwise and counterclockwise in OpenGL using glFrontFace(GL_CW), or you can switch between back-face and front-face culling using glCullFace. Here's an example:

Code: Select all

<ZApplication Name="App" Caption="ZGameEditor application" FileVersion="2">
  <OnLoaded>
    <ZExternalLibrary ModuleName="opengl32">
      <Source>
<![CDATA[//

const int GL_FRONT = 0x0404;
const int GL_BACK = 0x0405;

//

void glCullFace(int mode){}]]>
      </Source>
    </ZExternalLibrary>
    <SpawnModel Model="Demo"/>
  </OnLoaded>
  <Content>
    <Model Name="Demo" RotationVelocity="0.1 0 0">
      <OnRender>
        <UseMaterial Material="DemoMaterial"/>
        <RenderMesh Mesh="HalfMesh"/>
        <RenderTransform Scale="-1 1 1"/>
        <ZExpression>
          <Expression>
<![CDATA[//

glCullFace(GL_FRONT);]]>
          </Expression>
        </ZExpression>
        <RenderMesh Mesh="HalfMesh"/>
        <ZExpression>
          <Expression>
<![CDATA[//

glCullFace(GL_BACK);]]>
          </Expression>
        </ZExpression>
      </OnRender>
    </Model>
    <Material Name="DemoMaterial" Shading="2"/>
    <Mesh Name="HalfMesh">
      <Producers>
        <MeshBox XCount="3" YCount="3" Grid2DOnly="255"/>
        <MeshExpression>
          <Expression>
<![CDATA[//

V.X -= 1;
V.Z = sin(abs(V.X)+V.Y)/2;]]>
          </Expression>
        </MeshExpression>
      </Producers>
    </Mesh>
  </Content>
</ZApplication>
However, why not simply start with a 2D mesh that has the triangles flipped for each symmetrical side? :wink:

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FileVersion="2">
  <OnLoaded>
    <SpawnModel Model="Demo"/>
  </OnLoaded>
  <Content>
    <Model Name="Demo" RotationVelocity="0.1 0 0">
      <OnRender>
        <UseMaterial Material="DemoMaterial"/>
        <RenderMesh Mesh="DemoMesh"/>
      </OnRender>
    </Model>
    <Material Name="DemoMaterial" Shading="2"/>
    <Mesh Name="DemoMesh">
      <Producers>
        <MeshBox XCount="3" YCount="3" Grid2DOnly="255"/>
        <MeshBox XCount="3" YCount="3" Grid2DOnly="255"/>
        <MeshTransform Position="2 0 0" Rotation="0 0 0.25"/>
        <MeshCombine/>
        <MeshExpression>
          <Expression>
<![CDATA[//

V.X -= 1;
V.Z = sin(abs(V.X)+V.Y)/2;]]>
          </Expression>
        </MeshExpression>
      </Producers>
    </Mesh>
  </Content>
</ZApplication>
K
User avatar
Ats
Posts: 933
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: How to mirror a mesh without getting its faces upside down?

Post by Ats »

Kjell wrote:However, why not simply start with a 2D mesh that has the triangles flipped for each symmetrical side? :wink:
Oh, I didn’t know that MeshExpression could apply to combined meshes, but that makes sense. Perfect for my boat :D

However, now I’m unable to apply a texture properly, and I’ve tried all possible combinations. Something I didn’t have any problems with before.
I believe this comes from the 2D mesh rotation. Do I have to recalculate the TexCoord manually?

Edit :
All right, I managed to apply the texture after calculating the TexCoord. But now, the normal is also kind of broken on the edge of the symmetry. Flipping the mesh correctly bring a lot of new troubles :lol:
User avatar
Kjell
Posts: 1985
Joined: Sat Feb 23, 2008 11:15 pm

Re: How to mirror a mesh without getting its faces upside down?

Post by Kjell »

Hi Ats,
Ats wrote: Sun Jan 04, 2026 7:41 pmDo I have to recalculate the TexCoord manually?
Simply swap the x and y components of the texture coordinates. For example, something like this:

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FileVersion="2">
  <OnLoaded>
    <SpawnModel Model="Demo"/>
  </OnLoaded>
  <OnUpdate>
    <ZExpression>
      <Expression>
<![CDATA[// Toggle between solid & wireframe over time

DemoMaterial.Shading = frac(App.Time) > 0.5 ? 2 : 0;]]>
      </Expression>
    </ZExpression>
  </OnUpdate>
  <Content>
    <Model Name="Demo" RotationVelocity="0.1 0.1 0">
      <OnRender>
        <UseMaterial Material="DemoMaterial"/>
        <RenderMesh Mesh="DemoMesh"/>
      </OnRender>
    </Model>
    <Mesh Name="DemoMesh">
      <Producers>
        <MeshBox XCount="3" YCount="3" Grid2DOnly="255"/>
        <MeshBox XCount="3" YCount="3" Grid2DOnly="255"/>
        <MeshExpression AutoNormals="0" HasTexCoords="255">
          <Expression>
<![CDATA[// Swizzle texture coordinates

float x = TexCoord.X;

TexCoord.X = TexCoord.Y;
TexCoord.Y = x;

// Swizzle vertex coordinates ( and offset x )

x = V.X;

V.X = 2-V.Y;
V.Y = x;]]>
          </Expression>
        </MeshExpression>
        <MeshCombine/>
        <MeshTransform Comment="Center mesh" Position="-1 0 0"/>
      </Producers>
    </Mesh>
    <Bitmap Name="DemoBitmap">
      <Producers>
        <BitmapExpression>
          <Expression>
<![CDATA[//

float u = 1.0-X;
float v = 0.5-Y;

float s = frac((round(X*4)+round(Y*4))*0.5);

Pixel.R = u-v+s;
Pixel.G = v+u;
Pixel.B = 24-sqrt(u*u+v*v)*64;]]>
          </Expression>
        </BitmapExpression>
      </Producers>
    </Bitmap>
    <Material Name="DemoMaterial" Shading="2">
      <Textures>
        <MaterialTexture Texture="DemoBitmap" TexCoords="1"/>
      </Textures>
    </Material>
  </Content>
</ZApplication>
Ats wrote: Sun Jan 04, 2026 7:41 pmBut now, the normal is also kind of broken on the edge of the symmetry.
Are you using the AutoNormals feature of MeshExpression? When combining multiple meshes their vertices stay separate from each other, even when they're at the exact same position. If you want the combined meshes to appear like a single surface, you can't rely ( solely ) on AutoNormals in that situation.

However, one cheap / hack-y fix is to "flatten" all normals for vertices that are at x=0 to the YZ-plane. Here's an example:

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" LightPosition="0.5 0.5 0.5" FileVersion="2">
  <OnLoaded>
    <ZLibrary>
      <Source>
<![CDATA[//

vec3 normalize3(float x, float y, float z)
{
  float s = sqrt(x*x+y*y+z*z);
  return s ? vector3(x/s, y/s, z/s) : vector3(x, y, z);
}]]>
      </Source>
    </ZLibrary>
    <SpawnModel Model="Demo"/>
  </OnLoaded>
  <Content>
    <Model Name="Demo">
      <OnUpdate>
        <ZExpression>
          <Expression>
<![CDATA[//

Demo.Rotation.Y = cos(App.Time)/16-0.0625;]]>
          </Expression>
        </ZExpression>
      </OnUpdate>
      <OnRender>
        <RenderTransformGroup Translate="0 1.5 0">
          <Children>
            <RenderMesh Name="DemoRenderMesh" Mesh="DemoMesh"/>
          </Children>
        </RenderTransformGroup>
        <RenderTransformGroup Translate="0 -1.5 0">
          <Children>
            <RenderMesh Name="DemoRenderMesh1" Mesh="DemoFixMesh"/>
          </Children>
        </RenderTransformGroup>
      </OnRender>
    </Model>
    <Mesh Name="DemoMesh">
      <Producers>
        <MeshBox XCount="3" YCount="3" Grid2DOnly="255"/>
        <MeshBox XCount="3" YCount="3" Grid2DOnly="255"/>
        <MeshTransform Position="2 0 0" Rotation="0 0 0.25"/>
        <MeshCombine/>
        <MeshExpression>
          <Expression>
<![CDATA[//

float x = (V.X-1)*PI/5;
float y = V.Y*PI/2;

V.X = V.X-1;
V.Y = sin(y)*cos(x);
V.Z = cos(y)*cos(x);]]>
          </Expression>
        </MeshExpression>
      </Producers>
    </Mesh>
    <Mesh Name="DemoFixMesh">
      <Producers>
        <MeshBox XCount="3" YCount="3" Grid2DOnly="255"/>
        <MeshBox XCount="3" YCount="3" Grid2DOnly="255"/>
        <MeshTransform Position="2 0 0" Rotation="0 0 0.25"/>
        <MeshCombine/>
        <MeshExpression>
          <Expression>
<![CDATA[//

float x = (V.X-1)*PI/5;
float y = V.Y*PI/2;

V.X = V.X-1;
V.Y = sin(y)*cos(x);
V.Z = cos(y)*cos(x);]]>
          </Expression>
        </MeshExpression>
        <MeshExpression AutoNormals="0">
          <Expression>
<![CDATA[//

if(V.X == 0)
{
  N = normalize3(0, V.Y, V.Z);
}]]>
          </Expression>
        </MeshExpression>
      </Producers>
    </Mesh>
  </Content>
</ZApplication>
K
User avatar
Ats
Posts: 933
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: How to mirror a mesh without getting its faces upside down?

Post by Ats »

Thanks for those examples, Kjell, they are very helpful to me.
Thanks to that, I’m currently calculating the normals only where the stitches happen between ocean chunks. The rest use auto-generated normals.
Post Reply