Cardboard VR

All topics about ZGameEditor goes here.

Moderator: Moderators

User avatar
Ats
Posts: 603
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Cardboard VR

Post by Ats »

Hi, I'm back to ZGE and this time I'm trying to make something out of the Google cardboard VR that takes dust on my shelter...
So I made a quick example, using an old example to set viewport, and the red globule from about.zge

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="Cardboard VR" ClearScreenMode="1" RenderPasses="2" ClearColor="0.24 0.24 0.24 1" ScreenMode="0" CameraPosition="0.1 0 6" FileVersion="2" AndroidPackageName="com.mydomain.ZGEVR" AndroidPortrait="2">
  <OnLoaded>
    <ZExternalLibrary ModuleName="opengl32">
      <BeforeInitExp>
<![CDATA[if(ANDROID) {
  this.ModuleName = "libGLESv1_CM.so";
}]]>
      </BeforeInitExp>
      <Source>
<![CDATA[//

void glViewport(int X, int Y, int Width, int Height){}]]>
      </Source>
    </ZExternalLibrary>
    <ZExpression>
      <Expression>
<![CDATA[App.RenderPasses = 2;

View[0,0] = 0;
View[0,1] = 0;
View[0,2] = App.ViewportWidth / 2;
View[0,3] = App.ViewportHeight;

View[1,0] = App.ViewportWidth / 2;
View[1,1] = 0;
View[1,2] = App.ViewportWidth / 2;
View[1,3] = App.ViewportHeight;]]>
      </Expression>
    </ZExpression>
    <SpawnModel Model="System" SpawnStyle="1"/>
  </OnLoaded>
  <OnUpdate>
    <Timer Interval="0.31">
      <OnTimer>
        <SpawnModel Model="SpinnerModel"/>
      </OnTimer>
    </Timer>
  </OnUpdate>
  <OnBeginRenderPass>
    <ZExpression>
      <Expression>
<![CDATA[// Only clear the screen on first pass

if(App.CurrentRenderPass) App.ClearScreenMode = 1;
else App.ClearScreenMode = 0;

// Calculate & set the viewport ratio

int P = App.CurrentRenderPass;

float W = View[P,2];
float H = View[P,3];

//App.CustomViewportRatio = W/H;

if(App.CurrentRenderPass == 0)
{
  App.CameraPosition.Z = 6;
  App.CameraPosition.X = -0.1;
  App.CameraPosition.Y = 0;
  App.CameraRotation.Y = 0;
}

if(App.CurrentRenderPass == 1)
{
  App.CameraPosition.Z = 6;
  App.CameraPosition.X = 0.1;
  App.CameraPosition.Y = 0;
  App.CameraRotation.Y = 0;
}]]>
      </Expression>
    </ZExpression>
  </OnBeginRenderPass>
  <Content>
    <Group Name="SpinnerGroup">
      <Children>
        <Model Name="SpinnerModel" Scale="2 2 1" RotationVelocity="0 0.5 0">
          <Definitions>
            <Variable Name="SpinnerTime"/>
          </Definitions>
          <OnUpdate>
            <Condition Expression="return CurrentModel.Position.Y&gt;5.5;">
              <OnTrue>
                <RemoveModel/>
              </OnTrue>
            </Condition>
            <ZExpression>
              <Expression>
<![CDATA[SpinnerTime+=App.DeltaTime*0.75;

float Temp1;
Temp1=-4 + SpinnerTime;
Temp1+=noise2(CurrentModel.Personality,SpinnerTime*0.5) * 1.0;
Temp1+=noise2((SpinnerTime+App.Time)*0.25,0)*3;
CurrentModel.Position.Y=Temp1;

Temp1=sin(SpinnerTime*2)*2;
Temp1+=noise2((SpinnerTime+App.Time)*0.2,0)*8;
CurrentModel.Position.X=Temp1;

Temp1=cos(SpinnerTime*2)*3;
CurrentModel.Position.Z=Temp1;]]>
              </Expression>
            </ZExpression>
          </OnUpdate>
          <OnRender>
            <UseMaterial Material="SpinnerMaterial"/>
            <RenderMesh Mesh="SpinnerMesh"/>
          </OnRender>
        </Model>
        <Mesh Name="SpinnerMesh">
          <Producers>
            <MeshSphere Scale="0.25 0.25 0.25" ZSamples="8" RadialSamples="8"/>
          </Producers>
        </Mesh>
        <Material Name="SpinnerMaterial" Shading="1" Color="0.5 0 0 1"/>
      </Children>
    </Group> <!-- SpinnerGroup -->

    <Camera Name="CameraLeftEye"/>
    <Camera Name="CameraRightEye"/>
    <Array Name="View" Type="1" Dimensions="1" SizeDim1="2" SizeDim2="4" Persistent="255">
      <Values>
<![CDATA[78DA6360800055260686F7501A990F001DD20254]]>
      </Values>
    </Array>
    <Model Name="System">
      <Comment>
<![CDATA[This is the easiest way to make sure glViewport is called before anything else.
Calling it in OnBeginRenderPass doesn't work since ZGE sets the viewport natively at the beginning of OnRender.]]>
      </Comment>
      <OnRender>
        <ZExpression>
          <Expression>
<![CDATA[//

int P = App.CurrentRenderPass;

int X = View[P,0];
int Y = View[P,1];
int W = View[P,2];
int H = View[P,3];

//

glViewport(X,Y,W,H);]]>
          </Expression>
        </ZExpression>
      </OnRender>
    </Model>
  </Content>
</ZApplication>
But on android, it renders a very tiny viewport at the bottom left of the screen. Something like 10x5 pixels...
Am I supposed to use renderpass like that or should I use the Camera component?

I'm going to search if there is a place on the internet where I can find the camera parameters for setting the distance between the eyes correctly between the two viewports.

Edit:
I managed to "adapt" the screen to the phone by setting directly 1920 and 1080 instead of App.ScreenWidth (or Viewport) and... It's working real good :o
Can't wait to add that to Omeganaut.
User avatar
Kjell
Posts: 1876
Joined: Sat Feb 23, 2008 11:15 pm

Re: Cardboard VR

Post by Kjell »

Hi Ats,

Keep in mind that you ( generally ) want to correct for lens distortion when targeting VR. Without lens distortion correction, you'll end up with a so-called "pincushion" effect.

Image

You can find a article discussing the most common techniques here.

K
User avatar
Ats
Posts: 603
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Cardboard VR

Post by Ats »

I read the article and I think I'll try to go with the second method (mesh based solution), since I'm doing that for Omeganaut, which has low poly. Direct vertex displacement seems to need a lot of vertex, and since I'm doing that for the low poly Omeganaut...

But before starting with that, I'd like to understand why App.ScreenWidth isn't working on Android when we start playing with the RenderPasses.
Also, I'm using 3 passes to render a single separative line over the two screen. Is that how I should do that?

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="Cardboard VR" ClearScreenMode="1" RenderPasses="3" ClearColor="0.24 0.24 0.24 1" ScreenMode="0" CameraPosition="0 1000 0" ClipFar="200" FileVersion="2" AndroidPackageName="com.mydomain.ZGEVR" AndroidPortrait="2">
  <OnLoaded>
    <ZExternalLibrary ModuleName="opengl32">
      <BeforeInitExp>
<![CDATA[if(ANDROID) {
  this.ModuleName = "libGLESv1_CM.so";
}]]>
      </BeforeInitExp>
      <Source>
<![CDATA[//

void glViewport(int X, int Y, int Width, int Height){}]]>
      </Source>
    </ZExternalLibrary>
    <ZLibrary HasInitializer="1">
      <Source>
<![CDATA[int screenW = 2560;
int screenH = 1440;

void setVR(int i)
{
  switch(i)
  {
    case 0:
    App.RenderPasses = 1;

    View[0,0] = 0;
    View[0,1] = 0;
    View[0,2] = screenW;
    View[0,3] = screenH;
    break;

    case 1:
    App.RenderPasses = 3;

    View[0,0] = 0;
    View[0,1] = 0;
    View[0,2] = screenW / 2;
    View[0,3] = screenH;

    View[1,0] = screenW / 2;
    View[1,1] = 0;
    View[1,2] = screenW / 2;
    View[1,3] = screenH;

    View[2,0] = 0;
    View[2,1] = 0;
    View[2,2] = screenW;
    View[2,3] = screenH;
    break;
  }
}]]>
      </Source>
    </ZLibrary>
    <SpawnModel Model="System" SpawnStyle="1"/>
  </OnLoaded>
  <OnUpdate>
    <Timer Interval="0.31">
      <OnTimer>
        <SpawnModel Model="SpinnerModel"/>
      </OnTimer>
    </Timer>
    <KeyPress Comment="spacebar" Keys="{" CharCode="3" RepeatDelay="0.2">
      <OnPressed>
        <ZExpression Expression="setVR(App.RenderPasses == 1 ? 1 : 0);"/>
      </OnPressed>
    </KeyPress>
  </OnUpdate>
  <OnBeginRenderPass>
    <ZExpression>
      <Expression>
<![CDATA[// Calculate & set the viewport ratio

int P = App.CurrentRenderPass;

// Only clear the screen on first pass

if (P) App.ClearScreenMode = 1;
else   App.ClearScreenMode = 0;

App.CameraPosition.X = -0.1;
App.CameraPosition.Y = 0;
App.CameraPosition.Z = 12;

if (App.RenderPasses > 1)
{
  if (App.CurrentRenderPass == 0) App.CameraPosition.X -= 0.1;
  if (App.CurrentRenderPass == 1) App.CameraPosition.X += 0.1;

  if (App.CurrentRenderPass == 2)
  {
    App.CameraPosition.X = 0;
    App.CameraPosition.Y = 1000;
    App.CameraPosition.Z = 0;
  }
}]]>
      </Expression>
    </ZExpression>
  </OnBeginRenderPass>
  <OnRender>
    <RenderTransformGroup Comment="Sky" Scale="150 100 150">
      <Children>
        <UseMaterial Material="SkyMaterial"/>
        <RenderMesh Mesh="SphereMesh"/>
      </Children>
    </RenderTransformGroup>
    <RenderTransformGroup Comment="Ground" Scale="100 100 1" Translate="0 -6 -50" Rotate="-0.25 0 0">
      <Children>
        <UseMaterial Material="GroundMaterial"/>
        <RenderSprite/>
      </Children>
    </RenderTransformGroup>
    <Condition Comment="UI over double screen" Expression="return(App.RenderPasses &gt; 1 &amp;&amp; App.CurrentRenderPass == 2);">
      <OnTrue>
        <RenderTransformGroup Scale="0.01 10 1" Translate="0 1000 -10">
          <Children>
            <UseMaterial Material="BlackMaterial"/>
            <RenderSprite/>
          </Children>
        </RenderTransformGroup>
      </OnTrue>
      <OnFalse>
        <UseMaterial Material="TextMaterial"/>
        <RenderText Text="FPS " X="0.55" Y="-0.92" Scale="0.4" Align="1">
          <TextExpression>
<![CDATA[//"Hello " + "World " + IntToStr(App.FpsCounter)
IntToStr(App.ScreenWidth) + " "+ IntToStr(App.ScreenHeight);]]>
          </TextExpression>
        </RenderText>
      </OnFalse>
    </Condition>
  </OnRender>
  <Content>
    <Group Name="SpinnerGroup">
      <Children>
        <Model Name="SpinnerModel" Scale="2 2 1.5" RotationVelocity="0 0.5 0">
          <Definitions>
            <Variable Name="SpinnerTime"/>
          </Definitions>
          <OnUpdate>
            <Condition Expression="return CurrentModel.Position.Y&gt;5.5;">
              <OnTrue>
                <RemoveModel/>
              </OnTrue>
            </Condition>
            <ZExpression>
              <Expression>
<![CDATA[SpinnerTime+=App.DeltaTime*0.75;

float Temp1;
Temp1=-4 + SpinnerTime;
Temp1+=noise2(CurrentModel.Personality,SpinnerTime*0.5) * 1.0;
Temp1+=noise2((SpinnerTime+App.Time)*0.25,0)*3;
CurrentModel.Position.Y=Temp1;

Temp1=sin(SpinnerTime*2)*4;
Temp1+=noise2((SpinnerTime+App.Time)*0.2,0)*8;
CurrentModel.Position.X=Temp1;

Temp1=cos(SpinnerTime*2)*4;
CurrentModel.Position.Z=Temp1;]]>
              </Expression>
            </ZExpression>
          </OnUpdate>
          <OnRender>
            <UseMaterial Material="SpinnerMaterial"/>
            <RenderMesh Mesh="SpinnerMesh"/>
          </OnRender>
        </Model>
        <Mesh Name="SpinnerMesh">
          <Producers>
            <MeshSphere Scale="0.25 0.25 0.25" ZSamples="8" RadialSamples="8"/>
          </Producers>
        </Mesh>
        <Material Name="SpinnerMaterial" Shading="1" Color="0.5 0 0 1"/>
      </Children>
    </Group> <!-- SpinnerGroup -->

    <Array Name="View" Type="1" Dimensions="1" SizeDim1="3" SizeDim2="4" Persistent="255">
      <Values>
<![CDATA[78DA6360800055260686F7501A990F03DE2C103E0049420392]]>
      </Values>
    </Array>
    <Model Name="System">
      <Comment>
<![CDATA[This is the easiest way to make sure glViewport is called before anything else.
Calling it in OnBeginRenderPass doesn't work since ZGE sets the viewport natively at the beginning of OnRender.]]>
      </Comment>
      <OnSpawn>
        <ZExpression>
          <Expression>
<![CDATA[screenW= App.ScreenWidth;
screenH = App.ScreenHeight;

setVR(1);]]>
          </Expression>
        </ZExpression>
      </OnSpawn>
      <OnRender>
        <ZExpression>
          <Expression>
<![CDATA[//

int P = App.CurrentRenderPass;

int X = View[P,0];
int Y = View[P,1];
int W = View[P,2];
int H = View[P,3];

//

glViewport(X,Y,W,H);]]>
          </Expression>
        </ZExpression>
      </OnRender>
    </Model>
    <Group Comment="meshes + materials">
      <Children>
        <Mesh Name="SphereMesh">
          <Producers>
            <MeshBox Scale="1 0.5 1" XCount="18" YCount="12" Grid2DOnly="255"/>
            <MeshExpression AutoNormals="0">
              <Expression>
<![CDATA[//

        float E, A, K, X, Y, Z;

        // Convert range to radians

        E = v.Y*PI; // Elevation
        A = v.X*PI; // Azimuth

        // Convert spherical coordinates into cartesian

        K = cos(E);

        X = sin(A)*K;
        Y = sin(E);
        Z = cos(A)*K;

        // Assign coordinates

        v.X = X;
        v.Y = Y;
        v.Z = Z;

        n.X = X;
        n.Y = Y;
        n.Z = Z;]]>
              </Expression>
            </MeshExpression>
          </Producers>
        </Mesh>
        <Bitmap Name="GroundBitmap" Width="16" Height="16">
          <Producers>
            <BitmapExpression UseBlankSource="1" Expression="pixel = x &gt; 0.98 || y &gt; 0.98 ? 1 : 0;"/>
          </Producers>
        </Bitmap>
        <Material Name="GroundMaterial" Shading="1" Color="0 1 0 0.5" Blend="2">
          <Textures>
            <MaterialTexture Texture="GroundBitmap" TextureScale="10 10 1" TextureWrapMode="1" TexCoords="1"/>
          </Textures>
        </Material>
        <Bitmap Name="SkyBitmap" Width="16" Height="128" Filter="2">
          <Producers>
            <BitmapFromFile Comment="Imported from sky.png" DataWidth="16" DataHeight="128">
              <BitmapFile>
<![CDATA[78DAB5D8F946C4511806E0F67D99F67ED7111189212211318C88884444242212119188881111912122464444222212119168DFF7BDAEE1F9239E0B98E59CEFBCDF1B54FD06A4DA54D6FC985A535187C2DFA6DE94377C994653D6849A3F4D8B298D7C98A82969456DEFA6DD1477BC994E53D485BA5F4D8F09F5BE983E53D88F069ECDA029187A32C3267F048D3E9A319337FE60264CEE249ABA37D326277667664CF62C9ABB35F3266BE1C6C44DE6225ABA36CB26237165564CFA2A5ABB34EB266DE3C26C9AD42DB47D6E764CCAEE99D933C9FBE8E0D41C9AA4A31373FCCFF0F3E8F7E5DF13FF2F3D0F7ADEF43CF37DC1FBA8F75DE789CE2B9E87386F759EEB7BA1EF11BF77F89EEA7BAD7940F306E719CC4B9AC734EF699EE4BC8A7958F3B6E679DD17781FC17D47F729DDD7741FE47D13F759DD97751FD77D9FFB04EC2BB40FD1BE45FB1CEE8BB08FD2BE4BFB34EDEBB80FC4BE51FB4CED4BB58FE5BE17FB64EDABB50FD7BE3DC03EFF0FB9DE52F5]]>
              </BitmapFile>
            </BitmapFromFile>
          </Producers>
        </Bitmap>
        <Material Name="SkyMaterial" WireframeWidth="0" Light="0" SpecularColor="0 0 0 1" EmissionColor="0 0 0 1" DrawBackFace="255">
          <Textures>
            <MaterialTexture Texture="SkyBitmap" TextureScale="1 1 0" TextureY="0.05" TexCoords="1" Origin="0 0 0"/>
          </Textures>
        </Material>
        <Material Name="BlackMaterial" Shading="1" Color="0 0 0 1" Light="0" ZBuffer="0"/>
      </Children>
    </Group>
    <Group Name="Font3Group">
      <Children>
        <Bitmap Name="FontBitmap3" Width="256" Height="32">
          <Producers>
            <BitmapFromFile Transparency="1" DataWidth="256" DataHeight="32">
              <BitmapFile>
<![CDATA[78DAED9BC19284300844E7FF7F7AF7B45596A3041A8831FBDE51CB4CC49634C4F97C0000000000000000000076E6E78FCB83478815A07F80EDF56F1C07D84FFCBC020000FFD3F99C523DFE1FD03FFA070000003879A726AF7579A1FF2AC3BCD95ECE73D5F0E78C012FA3710AE670F2978FC038E59F463E1A43CF3C3C6E8731A42859ABEBD41A7DA73C8F3BA97FCF2D0BD118EABF291A2565A3B0D99ACC724DD5AB9CFF3369CD8864F4B1CA031626A8687BE138DA5DA0AAD27526C93B63924C9B210FD09DD2F3FA37EE42D6BF16B1AAB4369C6DD4937C2BFC38A0712AB3F226FD6AF4A5084DC3693EE7B89A92E5A37CC05737A593B624B3FCE54F9578636D356CCDFF99D94E36F91D5583B6D4CEAC50A205F5CAE1D58ADC6EFFAFBD5C8227E9EBFF8466D2A77FFB54B48720F77FB4E655F27975EB7F3F6F0000008B94B1EBCCDC5885E7377B93CB6EA67915328A8600E4268FBF7E8956D9E8DF1FCFF2F25F1827F3D6084ACE6C06556DDE7D223BBCD1DD61D6A9E86B3BB3035CF55B9E2A2FB4E5A4B5A1B4E2DDAFE1D686EA7EF95FFB7BDA7BF53FECE408B76C6F2F66BC56288D8716AFA7FC8FF32304F4BF4D5E9A1CA8EF9D5C63264FE9BFF50BA2FC039AE9FF9F2A6AEEEC4A55D57037EC225B630FE699772D01ADFD9F05EBFA4CD72854FF6A1B85D16FD21E11FF2F2CB9AB28]]>
              </BitmapFile>
            </BitmapFromFile>
          </Producers>
        </Bitmap>
        <Font Name="Font3" Bitmap="FontBitmap3" FirstChar="33" CharPixelWidth="8" CharPixelHeight="8" BorderPixels="1"/>
        <Material Name="TextMaterial" Color="0.42 0.07 0.07 1" Light="0" Blend="1" ZBuffer="0" Font="Font3"/>
      </Children>
    </Group> <!-- Font3Group -->

  </Content>
</ZApplication>
Here's what's going on my phone:
Image

I'd like to avoid setting the screen manually as I don't know what hardware the game will run the game.
User avatar
VilleK
Site Admin
Posts: 2274
Joined: Mon Jan 15, 2007 4:50 pm
Location: Stockholm, Sweden
Contact:

Re: Cardboard VR

Post by VilleK »

Hi, I've been away on vacation. Did you find a workaround or do you still have problems with ScreenWidth issue?
User avatar
Ats
Posts: 603
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Cardboard VR

Post by Ats »

Hi Ville :)

Kjell managed to help me by calling that on onUpdate:

Code: Select all

glGetIntegerv(0x0BA2, Viewport);
screenW = Viewport[2];
screenH = Viewport[3];
Here's my current Cardboard VR test:

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="Cardboard VR" ClearScreenMode="1" RenderPasses="3" ClearColor="0.24 0.24 0.24 1" ScreenMode="0" CameraPosition="0 1000 0" ClipFar="200" FileVersion="2" AndroidPackageName="com.mydomain.ZGEVR" AndroidPortrait="2">
  <OnLoaded>
    <ZExternalLibrary ModuleName="opengl32">
      <BeforeInitExp>
<![CDATA[if(ANDROID) {
  this.ModuleName = "libGLESv1_CM.so";
}]]>
      </BeforeInitExp>
      <Source>
<![CDATA[//

void glViewport(int X, int Y, int Width, int Height){}

void glGetIntegerv(int pname, xptr params){}]]>
      </Source>
    </ZExternalLibrary>
    <ZLibrary HasInitializer="1">
      <Source>
<![CDATA[int screenW = App.screenWidth;//2560;
int screenH = App.screenHeight;//1440;

float eyeSpace = 0.6;

void setScreen()
{
  glGetIntegerv(0x0BA2, Viewport);
  screenW = Viewport[2];
  screenH = Viewport[3];
}

void setVR(int i)
{
  switch(i)
  {
    case 0:
    App.RenderPasses = 1;

    View[0,0] = 0;
    View[0,1] = 0;
    View[0,2] = screenW;
    View[0,3] = screenH;
    break;

    case 1:
    App.RenderPasses = 3;

    View[0,0] = 0;
    View[0,1] = 0;
    View[0,2] = screenW / 2;
    View[0,3] = screenH;

    View[1,0] = screenW / 2;
    View[1,1] = 0;
    View[1,2] = screenW / 2;
    View[1,3] = screenH;

    View[2,0] = 0;
    View[2,1] = 0;
    View[2,2] = screenW;
    View[2,3] = screenH;
    break;
  }
}]]>
      </Source>
    </ZLibrary>
    <SpawnModel Model="System" SpawnStyle="1"/>
  </OnLoaded>
  <OnUpdate>
    <Timer Interval="0.31">
      <OnTimer>
        <SpawnModel Model="SpinnerModel"/>
      </OnTimer>
    </Timer>
    <KeyPress Comment="left click" Keys="{" RepeatDelay="0.2">
      <OnPressed>
        <ZExpression Expression="setVR(App.RenderPasses == 1 ? 1 : 0);"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="spacebar" CharCode="32" RepeatDelay="0.2">
      <OnPressed>
        <ZExpression Expression="setVR(App.RenderPasses == 1 ? 1 : 0);"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="left" Keys="&lt;Z" RepeatDelay="0.2">
      <OnPressed>
        <ZExpression Expression="eyeSpace -= 0.01;"/>
      </OnPressed>
    </KeyPress>
    <KeyPress Comment="right" Keys="&gt;X" RepeatDelay="0.2">
      <OnPressed>
        <ZExpression Expression="eyeSpace += 0.01;"/>
      </OnPressed>
    </KeyPress>
  </OnUpdate>
  <OnBeginRenderPass>
    <ZExpression>
      <Expression>
<![CDATA[// Calculate & set the viewport ratio

int Pass = App.CurrentRenderPass;

// Only clear the screen on first pass

if (Pass) App.ClearScreenMode = 1;
else      App.ClearScreenMode = 0;

App.CameraPosition.X = 0;
App.CameraPosition.Y = 0;
App.CameraPosition.Z = 12;

if (App.RenderPasses > 1)
{
  switch (Pass)
  {
    case 0:
    App.CameraPosition.X -= eyeSpace;
    break;

    case 1:
    App.CameraPosition.X += eyeSpace;
    break;

    case 2:
    App.CameraPosition.X = 0;
    App.CameraPosition.Y = 1000;
    App.CameraPosition.Z = 0;
    break;
  }
}]]>
      </Expression>
    </ZExpression>
  </OnBeginRenderPass>
  <OnRender>
    <RenderTransformGroup Comment="Sky" Scale="150 100 150">
      <Children>
        <UseMaterial Material="SkyMaterial"/>
        <RenderMesh Mesh="SphereMesh"/>
      </Children>
    </RenderTransformGroup>
    <RenderTransformGroup Comment="Ground" Scale="100 100 1" Translate="0 -6 -50" Rotate="-0.25 0 0">
      <Children>
        <UseMaterial Material="GroundMaterial"/>
        <RenderSprite/>
      </Children>
    </RenderTransformGroup>
    <Condition Comment="UI over double screen" Expression="return(App.RenderPasses &gt; 1 &amp;&amp; App.CurrentRenderPass == 2);">
      <OnTrue>
        <RenderTransformGroup Scale="0.01 10 1" Translate="0 1000 -10">
          <Children>
            <UseMaterial Material="BlackMaterial"/>
            <RenderSprite/>
          </Children>
        </RenderTransformGroup>
      </OnTrue>
      <OnFalse>
        <UseMaterial Material="TextMaterial"/>
        <RenderText Y="-0.9" Scale="0.5" TextExpression="IntToStr(App.ScreenWidth) + &quot; &quot;+ IntToStr(App.ScreenHeight) + &quot; &quot; + IntToStr(eyeSpace*100);"/>
      </OnFalse>
    </Condition>
  </OnRender>
  <Content>
    <Group Name="SpinnerGroup">
      <Children>
        <Model Name="SpinnerModel" Scale="2 2 1.5" RotationVelocity="0 0.5 0">
          <Definitions>
            <Variable Name="SpinnerTime"/>
          </Definitions>
          <OnUpdate>
            <Condition Expression="return CurrentModel.Position.Y&gt;5.5;">
              <OnTrue>
                <RemoveModel/>
              </OnTrue>
            </Condition>
            <ZExpression>
              <Expression>
<![CDATA[SpinnerTime+=App.DeltaTime*0.75;

float Temp1;
Temp1=-4 + SpinnerTime;
Temp1+=noise2(CurrentModel.Personality,SpinnerTime*0.5) * 1.0;
Temp1+=noise2((SpinnerTime+App.Time)*0.25,0)*3;
CurrentModel.Position.Y=Temp1;

Temp1=sin(SpinnerTime*2)*4;
Temp1+=noise2((SpinnerTime+App.Time)*0.2,0)*8;
CurrentModel.Position.X=Temp1;

Temp1=cos(SpinnerTime*2)*4;
CurrentModel.Position.Z=Temp1;]]>
              </Expression>
            </ZExpression>
          </OnUpdate>
          <OnRender>
            <UseMaterial Material="SpinnerMaterial"/>
            <RenderMesh Mesh="SpinnerMesh"/>
          </OnRender>
        </Model>
        <Mesh Name="SpinnerMesh">
          <Producers>
            <MeshSphere Scale="0.25 0.25 0.25" ZSamples="8" RadialSamples="8"/>
          </Producers>
        </Mesh>
        <Material Name="SpinnerMaterial" Shading="1" Color="0.5 0 0 1"/>
      </Children>
    </Group> <!-- SpinnerGroup -->

    <Array Name="View" Type="1" Dimensions="1" SizeDim1="3" SizeDim2="4" Persistent="255">
      <Values>
<![CDATA[78DA6360800055260686F7501A990F03DE2C103E0049420392]]>
      </Values>
    </Array>
    <Model Name="System">
      <Comment>
<![CDATA[This is the easiest way to make sure glViewport is called before anything else.
Calling it in OnBeginRenderPass doesn't work since ZGE sets the viewport natively at the beginning of OnRender.]]>
      </Comment>
      <OnSpawn>
        <ZExpression>
          <Expression>
<![CDATA[setScreen();
setVR(1);]]>
          </Expression>
        </ZExpression>
      </OnSpawn>
      <OnRender>
        <ZExpression>
          <Expression>
<![CDATA[//
setScreen();

int P = App.CurrentRenderPass;

int X = View[P,0];
int Y = View[P,1];
int W = View[P,2];
int H = View[P,3];

//

glViewport(X,Y,W,H);]]>
          </Expression>
        </ZExpression>
      </OnRender>
    </Model>
    <Group Comment="meshes + materials">
      <Children>
        <Mesh Name="SphereMesh">
          <Producers>
            <MeshBox Scale="1 0.5 1" XCount="18" YCount="12" Grid2DOnly="255"/>
            <MeshExpression AutoNormals="0">
              <Expression>
<![CDATA[//

        float E, A, K, X, Y, Z;

        // Convert range to radians

        E = v.Y*PI; // Elevation
        A = v.X*PI; // Azimuth

        // Convert spherical coordinates into cartesian

        K = cos(E);

        X = sin(A)*K;
        Y = sin(E);
        Z = cos(A)*K;

        // Assign coordinates

        v.X = X;
        v.Y = Y;
        v.Z = Z;

        n.X = X;
        n.Y = Y;
        n.Z = Z;]]>
              </Expression>
            </MeshExpression>
          </Producers>
        </Mesh>
        <Bitmap Name="GroundBitmap" Width="16" Height="16">
          <Producers>
            <BitmapExpression UseBlankSource="1" Expression="pixel = x &gt; 0.98 || y &gt; 0.98 ? 1 : 0;"/>
          </Producers>
        </Bitmap>
        <Material Name="GroundMaterial" Shading="1" Color="0 1 0 0.5" Blend="2">
          <Textures>
            <MaterialTexture Texture="GroundBitmap" TextureScale="10 10 1" TextureWrapMode="1" TexCoords="1"/>
          </Textures>
        </Material>
        <Bitmap Name="SkyBitmap" Width="16" Height="128" Filter="2">
          <Producers>
            <BitmapFromFile Comment="Imported from sky.png" DataWidth="16" DataHeight="128">
              <BitmapFile>
<![CDATA[78DAB5D8F946C4511806E0F67D99F67ED7111189212211318C88884444242212119188881111912122464444222212119168DFF7BDAEE1F9239E0B98E59CEFBCDF1B54FD06A4DA54D6FC985A535187C2DFA6DE94377C994653D6849A3F4D8B298D7C98A82969456DEFA6DD1477BC994E53D485BA5F4D8F09F5BE983E53D88F069ECDA029187A32C3267F048D3E9A319337FE60264CEE249ABA37D326277667664CF62C9ABB35F3266BE1C6C44DE6225ABA36CB26237165564CFA2A5ABB34EB266DE3C26C9AD42DB47D6E764CCAEE99D933C9FBE8E0D41C9AA4A31373FCCFF0F3E8F7E5DF13FF2F3D0F7ADEF43CF37DC1FBA8F75DE789CE2B9E87386F759EEB7BA1EF11BF77F89EEA7BAD7940F306E719CC4B9AC734EF699EE4BC8A7958F3B6E679DD17781FC17D47F729DDD7741FE47D13F759DD97751FD77D9FFB04EC2BB40FD1BE45FB1CEE8BB08FD2BE4BFB34EDEBB80FC4BE51FB4CED4BB58FE5BE17FB64EDABB50FD7BE3DC03EFF0FB9DE52F5]]>
              </BitmapFile>
            </BitmapFromFile>
          </Producers>
        </Bitmap>
        <Material Name="SkyMaterial" WireframeWidth="0" Light="0" SpecularColor="0 0 0 1" EmissionColor="0 0 0 1" DrawBackFace="255">
          <Textures>
            <MaterialTexture Texture="SkyBitmap" TextureScale="1 1 0" TextureY="0.05" TexCoords="1" Origin="0 0 0"/>
          </Textures>
        </Material>
        <Material Name="BlackMaterial" Shading="1" Color="0 0 0 1" Light="0" ZBuffer="0"/>
      </Children>
    </Group>
    <Group Name="Font3Group">
      <Children>
        <Bitmap Name="FontBitmap3" Width="256" Height="32">
          <Producers>
            <BitmapFromFile Transparency="1" DataWidth="256" DataHeight="32">
              <BitmapFile>
<![CDATA[78DAED9BC19284300844E7FF7F7AF7B45596A3041A8831FBDE51CB4CC49634C4F97C0000000000000000000076E6E78FCB83478815A07F80EDF56F1C07D84FFCBC020000FFD3F99C523DFE1FD03FFA070000003879A726AF7579A1FF2AC3BCD95ECE73D5F0E78C012FA3710AE670F2978FC038E59F463E1A43CF3C3C6E8731A42859ABEBD41A7DA73C8F3BA97FCF2D0BD118EABF291A2565A3B0D99ACC724DD5AB9CFF3369CD8864F4B1CA031626A8687BE138DA5DA0AAD27526C93B63924C9B210FD09DD2F3FA37EE42D6BF16B1AAB4369C6DD4937C2BFC38A0712AB3F226FD6AF4A5084DC3693EE7B89A92E5A37CC05737A593B624B3FCE54F9578636D356CCDFF99D94E36F91D5583B6D4CEAC50A205F5CAE1D58ADC6EFFAFBD5C8227E9EBFF8466D2A77FFB54B48720F77FB4E655F27975EB7F3F6F0000008B94B1EBCCDC5885E7377B93CB6EA67915328A8600E4268FBF7E8956D9E8DF1FCFF2F25F1827F3D6084ACE6C06556DDE7D223BBCD1DD61D6A9E86B3BB3035CF55B9E2A2FB4E5A4B5A1B4E2DDAFE1D686EA7EF95FFB7BDA7BF53FECE408B76C6F2F66BC56288D8716AFA7FC8FF32304F4BF4D5E9A1CA8EF9D5C63264FE9BFF50BA2FC039AE9FF9F2A6AEEEC4A55D57037EC225B630FE699772D01ADFD9F05EBFA4CD72854FF6A1B85D16FD21E11FF2F2CB9AB28]]>
              </BitmapFile>
            </BitmapFromFile>
          </Producers>
        </Bitmap>
        <Font Name="Font3" Bitmap="FontBitmap3" FirstChar="33" CharPixelWidth="8" CharPixelHeight="8" BorderPixels="1"/>
        <Material Name="TextMaterial" Color="0.42 0.07 0.07 1" Light="0" Blend="1" ZBuffer="0" Font="Font3"/>
      </Children>
    </Group> <!-- Font3Group -->

    <Array Name="Viewport" Type="1" SizeDim1="4"/>
  </Content>
</ZApplication>

We can see the cushion deformation at the very bottom of the ground lines, but since the camera is not following the headset orientation, it does not bother me. I'm going to push that to Omeganaut today and see if I puke :D

Edit: I just discovered that my demo is only working for Android and preview mode in ZGE. Fullscreen mode on PC is broken...
Last edited by Ats on Thu Apr 25, 2019 1:28 am, edited 1 time in total.
User avatar
Ats
Posts: 603
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Cardboard VR

Post by Ats »

I managed to make it work in Omeganaut :P

On Android, I'm displaying a first AppState without touching a thing, in my case, the disclaimer. This initiates the Android viewport. Then I can simply call:

Code: Select all

screenW = App.screenWidth;
screenH = App.screenHeight;
instead of

Code: Select all

glGetIntegerv(0x0BA2, Viewport);
screenW = Viewport[2];
screenH = Viewport[3];
Now I have a little problem: when I divide the screen into two parts, it shrinks the width of the screen per two. So the image looks slim on the X axis. How can I manage that? Does that have to do with the App.CustomViewportRatio ?
User avatar
Kjell
Posts: 1876
Joined: Sat Feb 23, 2008 11:15 pm

Re: Cardboard VR

Post by Kjell »

Hi Ats,
Ats wrote: Wed Apr 24, 2019 7:25 pmNow I have a little problem: when I divide the screen into two parts, it shrinks the width of the screen per two. So the image looks slim on the X axis. How can I manage that? Does that have to do with the App.CustomViewportRatio?
Correct, you need to use a custom viewport ratio set to the ratio of the viewports you're using for each eye. Here's a simple example ..

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" ClearScreenMode="1" RenderPasses="2" ViewportRatio="1" RenderOrder="1" FileVersion="2">
  <OnLoaded>
    <ZExternalLibrary ModuleName="opengl32">
      <Source>
<![CDATA[//

void glClear(int mask){}
void glViewport(int x, int y, int width, int height){}]]>
      </Source>
    </ZExternalLibrary>
    <SpawnModel Model="Box"/>
    <ZExpression>
      <Expression>
<![CDATA[//

App.CustomViewportRatio = App.ScreenWidth * 0.5 / App.ScreenHeight;]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <OnRender>
    <ZExpression>
      <Expression>
<![CDATA[//

int w = App.ScreenWidth >> 1;

//

if(App.CurrentRenderPass)
{
  glViewport(w, 0, w, App.ScreenHeight);
}
else
{
  glClear(0x4100);
  glViewport(0, 0, w, App.ScreenHeight);
}]]>
      </Expression>
    </ZExpression>
  </OnRender>
  <Content>
    <Model Name="Box" RotationVelocity="0.2 0.3 0">
      <OnRender>
        <UseMaterial Material="BoxMaterial"/>
        <RenderMesh Mesh="BoxMesh"/>
      </OnRender>
    </Model>
    <Mesh Name="BoxMesh">
      <Producers>
        <MeshBox/>
      </Producers>
    </Mesh>
    <Material Name="BoxMaterial" Shading="1"/>
  </Content>
</ZApplication>
K
User avatar
Ats
Posts: 603
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Cardboard VR

Post by Ats »

Hi Kjell, I had some free time today to go back to VR and I'm having trouble with your example when I try to switch from one to two cameras. Here's the modified example to show the problem. This is working perfectly in the preview mode, but not with build and run... Just press space several times, you'll see:

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" ClearScreenMode="1" CustomViewportRatio="1.0664" RenderOrder="1" FileVersion="2">
  <OnLoaded>
    <ZExternalLibrary ModuleName="opengl32">
      <Source>
<![CDATA[//

void glClear(int mask){}
void glViewport(int x, int y, int width, int height){}]]>
      </Source>
    </ZExternalLibrary>
    <SpawnModel Model="Box"/>
    <ZLibrary>
      <Source>
<![CDATA[void switchVR(int i)
{
  switch (i)
  {
    case 0: // one camera
    VR = 0;
    App.ViewportRatio = 0; // full screen
    App.RenderPasses = 1;
    break;

    case 1: // two cameras
    VR = 1;
    App.ViewportRatio = 1; // custom
    App.CustomViewportRatio = App.ScreenWidth * 0.5 / App.ScreenHeight;
    App.RenderPasses = 2;
    break;
  }
}

switchVR(0);]]>
      </Source>
    </ZLibrary>
  </OnLoaded>
  <OnUpdate>
    <KeyPress Comment="SPACE" CharCode="32" RepeatDelay="0.12">
      <OnPressed>
        <ZExpression>
          <Expression>
<![CDATA[//
switchVR (VR == 0 ? 1 : 0);]]>
          </Expression>
        </ZExpression>
      </OnPressed>
    </KeyPress>
  </OnUpdate>
  <OnRender>
    <ZExpression>
      <Expression>
<![CDATA[//

int w = App.ScreenWidth >> 1;

switch (VR)
{
  case 0:
  glClear(0x4100);
  glViewport(0, 0, App.ScreenWidth, App.ScreenHeight);
  break;

  case 1:
  if(App.CurrentRenderPass)
  {
    glViewport(w, 0, w, App.ScreenHeight);
  }
  else
  {
    glClear(0x4100);
    glViewport(0, 0, w, App.ScreenHeight);
  }
}]]>
      </Expression>
    </ZExpression>
  </OnRender>
  <Content>
    <Model Name="Box" RotationVelocity="0.2 0.3 0">
      <OnRender>
        <UseMaterial Material="BoxMaterial"/>
        <RenderMesh Mesh="BoxMesh"/>
      </OnRender>
    </Model>
    <Mesh Name="BoxMesh">
      <Producers>
        <MeshBox/>
      </Producers>
    </Mesh>
    <Material Name="BoxMaterial" Shading="1"/>
    <Variable Name="VR" Type="1"/>
  </Content>
</ZApplication>
I also tried

Code: Select all

App.ViewportRatio = 1; // custom
App.CustomViewportRatio = App.ScreenWidth / App.ScreenHeight;
instead of

Code: Select all

App.ViewportRatio = 0; // full screen
without success.

I really can't see where is the problem... Thanks for your help :)
User avatar
Kjell
Posts: 1876
Joined: Sat Feb 23, 2008 11:15 pm

Re: Cardboard VR

Post by Kjell »

Hi Ats,

Seems like there's a bug when switching between certain ViewportRatio modes in standalone executables. The easiest way to go around this is to simply use a CustomViewportRatio for both VR and non-VR. Here's your example with a couple of small modifications ..

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" ClearScreenMode="1" RenderOrder="1" FileVersion="2">
  <OnLoaded>
    <ZExternalLibrary ModuleName="opengl32">
      <Source>
<![CDATA[//

void glClear(int mask){}
void glViewport(int x, int y, int width, int height){}]]>
      </Source>
    </ZExternalLibrary>
    <SpawnModel Model="Box"/>
    <ZLibrary>
      <Source>
<![CDATA[void switchVR(int i)
{
  switch (i)
  {
    case 0: // one camera
    VR = 0;
    App.CustomViewportRatio = App.ScreenWidth * 1.0 / App.ScreenHeight;
    App.RenderPasses = 1;
    break;

    case 1: // two cameras
    VR = 1;
    App.CustomViewportRatio = App.ScreenWidth * 0.5 / App.ScreenHeight;
    App.RenderPasses = 2;
    break;
  }
}

switchVR(0); // This doesn't do anything! Just delete it]]>
      </Source>
    </ZLibrary>
    <ZExpression>
      <Expression>
<![CDATA[//

App.ViewportRatio = 1;
switchVR(0);]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <OnUpdate>
    <KeyPress Comment="SPACE" CharCode="32" RepeatDelay="0.12">
      <OnPressed>
        <ZExpression>
          <Expression>
<![CDATA[//
switchVR (VR == 0 ? 1 : 0);]]>
          </Expression>
        </ZExpression>
      </OnPressed>
    </KeyPress>
  </OnUpdate>
  <OnRender>
    <ZExpression>
      <Expression>
<![CDATA[//

int w = App.ScreenWidth >> 1;

switch (VR)
{
  case 0:
  glClear(0x4100);
  glViewport(0, 0, App.ScreenWidth, App.ScreenHeight);
  break;

  case 1:
  if(App.CurrentRenderPass)
  {
    glViewport(w, 0, w, App.ScreenHeight);
  }
  else
  {
    glClear(0x4100);
    glViewport(0, 0, w, App.ScreenHeight);
  }
}]]>
      </Expression>
    </ZExpression>
  </OnRender>
  <OnClose>
    <ZExpression>
      <Expression>
<![CDATA[//

App.ViewportRatio = 0;]]>
      </Expression>
    </ZExpression>
  </OnClose>
  <Content>
    <Model Name="Box" RotationVelocity="0.2 0.3 0">
      <OnRender>
        <UseMaterial Material="BoxMaterial"/>
        <RenderMesh Mesh="BoxMesh"/>
      </OnRender>
    </Model>
    <Mesh Name="BoxMesh">
      <Producers>
        <MeshBox/>
      </Producers>
    </Mesh>
    <Material Name="BoxMaterial" Shading="1"/>
    <Variable Name="VR" Type="1"/>
  </Content>
</ZApplication>
And the reason why the following doesn't work ...

Code: Select all

App.CustomViewportRatio = App.ScreenWidth / App.ScreenHeight;
.. is because both ScreenWidth and ScreenHeight are integers, so the result is a integer as well ( you want a floating point value ).

+ You can't call functions from the global scope in a ZLibrary .. so that "switchVR(0);" call in your ZLibrary doesn't do anything.

K
User avatar
VilleK
Site Admin
Posts: 2274
Joined: Mon Jan 15, 2007 4:50 pm
Location: Stockholm, Sweden
Contact:

Re: Cardboard VR

Post by VilleK »

Kjell wrote: Tue May 07, 2019 7:04 pm .. is because both ScreenWidth and ScreenHeight are integers, so the result is a integer as well ( you want a floating point value ).
Quite unfortunate, this is a parser bug, ZGE should handle it better.
Kjell wrote: Tue May 07, 2019 7:04 pm+ You can't call functions from the global scope in a ZLibrary .. so that "switchVR(0);" call in your ZLibrary doesn't do anything.
Indeed, but if you put it in a global "{" block it will work since addition of "Library initialization" feature: viewtopic.php?f=2&t=1344
User avatar
Kjell
Posts: 1876
Joined: Sat Feb 23, 2008 11:15 pm

Re: Cardboard VR

Post by Kjell »

Hi guys,
VilleK wrote: Wed May 08, 2019 7:05 amQuite unfortunate, this is a parser bug, ZGE should handle it better.
I wouldn't call this a bug at all, it behaves the same as-in C & is by design, no?
VilleK wrote: Wed May 08, 2019 7:05 amIndeed, but if you put it in a global "{" block it will work since addition of "Library initialization" feature
Personally i wouldn't put any calls that don't actually initialize the library in the ZLibrary initialization* .. but you're right, that works too :-)

*The "switchVR(0)" call isn't necessary to initialize the library itself, it's to switch the project into that mode at startup.

K
User avatar
VilleK
Site Admin
Posts: 2274
Joined: Mon Jan 15, 2007 4:50 pm
Location: Stockholm, Sweden
Contact:

Re: Cardboard VR

Post by VilleK »

Kjell wrote: Wed May 08, 2019 10:28 am I wouldn't call this a bug at all, it behaves the same as-in C & is by design, no?
That is correct yes, but it is still unfortunate as it is obviously not what anyone writes that kind of code wants to happen.
User avatar
Ats
Posts: 603
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Cardboard VR

Post by Ats »

both ScreenWidth and ScreenHeight are integers, so the result is a integer as well ( you want a floating point value ).
Thanks :D
(I should've thought about that...)

And for the switchVR(0), I keep switching from one language to another this days...

Now everything regarding VR is working in Omeganaut. I can get back at adding new content!
User avatar
Ats
Posts: 603
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Cardboard VR

Post by Ats »

Hahaha :lol:
I have another problem regarding the mouse position when the App.CustomViewportRatio is changed...
What I'm trying to do is to get the X mouse coordinates from -1 to 1 on the left screen, and same on the right screen using:

Code: Select all

float MouseX = (VR ? (App.MousePosition.X * 2 + (App.MousePosition.X > 0 ? -1 : 1)) : App.MousePosition.X);
But in the previous example, if you trace on OnUpdate :

Code: Select all

trace(intToStr(100*App.MousePosition.X)+"  "+intToStr(100*App.MousePosition.Y));
You'll see that the MousePosition.X is divided by 2 on the X axis. It just follows the CustomViewportRatio.

Is there a solution to correct that or should I do something quite hacky such as:
  • set App.CustomViewportRatio = App.ScreenWidth * 1.0 / App.ScreenHeight;
  • get the mouse coordinates
  • set App.CustomViewportRatio = App.ScreenWidth * 0.5 / App.ScreenHeight;
  • use my mouse coordinates... ?
User avatar
Kjell
Posts: 1876
Joined: Sat Feb 23, 2008 11:15 pm

Re: Cardboard VR

Post by Kjell »

Hi Ats,

Ville informed me that the AspectRatio properties aren't meant to be updated in real-time / mid-frame, so it's better to not touch any of those values and to just modify the projection matrix instead ( might sound intimidating .. but it's super easy ). Here's a quick & dirty example.

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" ClearScreenMode="1" RenderPasses="2" CameraPosition="0 0 90" FOV="90" ClipNear="1" ClipFar="360" MouseVisible="255" RenderOrder="1" FileVersion="2">
  <OnLoaded>
    <ZExternalLibrary ModuleName="opengl32">
      <Source>
<![CDATA[//

void glViewport(int x, int y, int width, int height){}]]>
      </Source>
    </ZExternalLibrary>
    <DefineCollision Cat1="1" Cat2="2"/>
    <ZExpression>
      <Expression>
<![CDATA[// Spawn Logo

createModel(Logo);

// Spawn Start & Menu Buttons

Button.Position.Y = -48;

Button.Position.X = -32;
ButtonTile.TileIndex = 0;
createModel(Button);

Button.Position.X = 32;
ButtonTile.TileIndex = 1;
createModel(Button);

// Spawn Cursor

createModel(Cursor);]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <OnBeginRenderPass>
    <ZExpression>
      <Expression>
<![CDATA[// Only enable screen clearing first pass

if(App.CurrentRenderPass)
{
  App.ClearScreenMode = 1;
}
else
{
  App.ClearScreenMode = 0;
}]]>
      </Expression>
    </ZExpression>
  </OnBeginRenderPass>
  <OnRender>
    <ZExpression>
      <Expression>
<![CDATA[// Set left & right viewport depending on pass

int x = App.ScreenWidth >> 1;

if(App.CurrentRenderPass)
{
  glViewport(0, 0, x, App.ScreenHeight);
}
else
{
  glViewport(x, 0, x, App.ScreenHeight);
}

// Scale projection matrix for half-horizontal resolution

mat4 m;

getMatrix(1, m);
m[0,0] *= 2;
setMatrix(1, m);]]>
      </Expression>
    </ZExpression>
  </OnRender>
  <Content>
    <Model Name="Logo">
      <OnUpdate>
        <ZExpression>
          <Expression>
<![CDATA[// Ultra-fancy 3D orientation animation :-O

Logo.Rotation.X = cos(App.Time)/16;
Logo.Rotation.Y = sin(App.Time)/16;]]>
          </Expression>
        </ZExpression>
      </OnUpdate>
      <OnRender>
        <RenderTile TileSet="LogoTileSet" OriginX="48" OriginY="11"/>
      </OnRender>
    </Model>
    <Bitmap Name="LogoBitmap" Width="96" Height="22" Filter="1">
      <Producers>
        <BitmapFromFile Transparency="1" DataWidth="96" DataHeight="22">
          <BitmapFile>
<![CDATA[78DAD5983D0EC2300C857B2B8EC0CEC4E918B91437E80833129D9015C7F673ECFCB4CA129E9BDA5F9E5B946DCBB9F6EFAB3A222A8F3CEF759470BB5C8B410978D56AE4D9F9D01DE70470558A9C5257D0F9B490C77EFF0F0E04577964433E59A5459C5F8CA2AE6655E1E3CA27581A3530E27CA50BB80722AACE47CFC19C7A97AA3A9F2E4225A98A886A3A4DCAC77C4A70A975F8147BB73E9FAAF3FBF131F35C8A8FE476F3E9E3F978A7AEA5BC6F4E1E194935858F3707FD2F07C247D9DCA0B77BF0F1E6A07F08243E9FF7531A54C5235D5333B2E0A324802C45FD43D5E3976A70273EFC71929AC587ABAE292FA72B1F5CCDE213F130CE87372922CDE28327DFB0A1521B2A4D5AB4F0743ED24BC3DB1A29FD6596D96964F59799706F3E29E70FFAA1442E1FA45372F90C38857365DBEC4CCA27BD11469EE2226738A033BDF73637C2E0D3CE784AD2BD3FEE60A67E]]>
          </BitmapFile>
        </BitmapFromFile>
      </Producers>
    </Bitmap>
    <TileSet Name="LogoTileSet" Bitmap="LogoBitmap" TileWidth="96" TileHeight="22"/>
    <Model Name="Button" Position="32 -48 0" Category="2" CollisionBounds="40 14 0 0">
      <OnUpdate>
        <ZExpression>
          <Expression>
<![CDATA[// When cursor doesn't collide with button, use dimmed color

ButtonColor.Color = 0.5;]]>
          </Expression>
        </ZExpression>
      </OnUpdate>
      <OnCollision>
        <ZExpression>
          <Expression>
<![CDATA[// When cursor collides with button, use normal color

ButtonColor.Color = 1;]]>
          </Expression>
        </ZExpression>
      </OnCollision>
      <OnRender>
        <RenderSetColor Name="ButtonColor" Color="1 1 1 1"/>
        <RenderTile Name="ButtonTile" TileSet="ButtonTileSet" TileIndex="1" OriginX="20" OriginY="7"/>
      </OnRender>
    </Model>
    <Bitmap Name="ButtonBitmap" Width="80" Height="14" Filter="1">
      <Producers>
        <BitmapFromFile DataWidth="80" DataHeight="14">
          <BitmapFile>
<![CDATA[78DAE596CD0980300C850F8EE0C9315C44C40DDCD2715C44AB107C3C9A5AA4AD3F18C92149439F5F93A27D5BF5966CB067CBBA3C6CAFEBCE63F3987D5677EA6A31CE049DB37C32AF16E59709CA45142FEA620CB0152A25B99B64BC902BD3789D739EEF8E8757C5D97565298E7CF19C83147C29E22793C32BBB81114D644C8D9F33CF20D2BCEC9CE573780587A1B8BFE869415E4C296E0D3A189F67AE4CE6654CDD5F262DC5CB83EAF90875990EEFBEBF05794D7D8F7EAC6BEAFFCA946D2B5ABACC]]>
          </BitmapFile>
        </BitmapFromFile>
      </Producers>
    </Bitmap>
    <TileSet Name="ButtonTileSet" Bitmap="ButtonBitmap" TileWidth="40" TileHeight="14"/>
    <Model Name="Cursor" Category="1" CollisionBounds="1 1 0 0">
      <OnUpdate>
        <ZExpression>
          <Expression>
<![CDATA[// These values depend on your camera setup

float r = App.ScreenWidth*1f/App.ScreenHeight;
float s = App.CameraPosition.Z;

// If mouse if less than 0 you're picking the left eye, if not the right

Cursor.Position.X = App.MousePosition.X < 0 ? (App.MousePosition+0.5)*s*r
                                            : (App.MousePosition-0.5)*s*r;

Cursor.Position.Y = App.MousePosition.Y*s;]]>
          </Expression>
        </ZExpression>
      </OnUpdate>
    </Model>
  </Content>
</ZApplication>
If you have any questions, let me know :wink:

K
Post Reply