Stereoscopic 3D – ShiVa Engine

Stereoscopic 3D

ImperialPenguin: “Implementing HTC’s S3D SDK and LG’s Real3D SDK into Shiva may sound straightforward from the simple instructions that come with the tools, but there are a few gotchas in getting them to work with Shiva. It took a lot of trial and error for me to get it to work, and here is what worked for me. Hopefully this tutorial will save you some time. You’ll need to download and install the Real3D SDK from LG, and the OpenSense SDK from HTC in order to make this work.”
Supported Devices: HTC Evo 3D, LG Optimus 3D

ShiVa Steps

Your game will need to be in landscape for 3D to function on these devices, so you probably have a code block that looks like the one below somewhere in your initialization functions:

if ( system.getClientType ( ) ~= system.kClientTypeEditor ) then
    application.setOption ( application.kOptionViewportRotation, 3 )
end

This is the code that rotates your game into landscape mode. This is normally required, because Shiva is actually running on Android phones in portrait mode, and locks the phone to portrait mode in the Android Manifest. We need to change that and the rotation will no longer be necessary. So, replace the code above with the code below:

if ( system.getClientType ( ) ~= system.kClientTypeEditor ) then
   application.setOption ( application.kOptionStereoscopic3DMode, 2 )
end

Pay close attention to that second argument to setOption. It is a 2, which indicates over and under mode instead of a 1, which would indicate side by side mode. The reason why we are doing that is because Shiva is going to think it is still in portrait mode. Over and under in portrait mode will display as side by side in landscape mode. Of course, we don’t want our editor displaying in split screen mode like that because it will make testing much more difficult. So we keep the IF statement from the first code block, so the editor ignores it.
Now you are ready to export your project and run it through the Authoring Tool. We are going to need to edit the files that are created, so be sure to export it as a project and not an APK package. Also, make sure that the 32 bit color buffer and stencil buffer are both checked.

Eclipse Steps

Import the project into Eclipse and open up its manifest file. That will be the first thing that we edit. I’ve already told you what the first change will be. We will change the orientation from portrait to landscape. So change the line of code that says:

android:screenOrientation="portrait"

to:

android:screenOrientation="landscape"

Apparently, at some point there was a bug in the Android Market (Google Play) that would block apps which set their manifests to landscape from showing up phones which aren’t in landscape orientation by default. That leaves almost all phones out. I don’t now if that’s still the case, but to prevent that, add the following lines to the bottom of the manifest. (but within the tags)


The second line of the above code is required by LG in order to get Real3D working. There is one other line of code that must be added to the manifest for Real3D. Place the code below within the tags of your manifest.

Now we have to edit some Java files. First, open up YOURGAMENAME.java and search for the function called ‘onStopOverlayMovie’. Replace the line:

oThis.setRequestedOrientation ( ActivityInfo.SCREEN_ORIENTATION_PORTRAIT ) ; 

with:

//oThis.setRequestedOrientation ( ActivityInfo.SCREEN_ORIENTATION_PORTRAIT ) ; 

The two forward slashes comment out the code so that it does not run. This is necessary because although we’ve told the app to run in landscape, that code will put it back into portrait mode when it loses focus and is brought back to the front.
Now comes the bulk of our work. Open up S3DSurfaceView.java and let’s get to it. First, let’s add in the required imports. That is as simple as adding the following lines of code to the list of imports.

import com.htc.view.DisplaySetting;
import com.lge.real3d.Real3D;
import com.lge.real3d.Real3DInfo;

You will probably get an error that these references aren’t found. That is because we have to add the Real3D and HTCExtension libraries to the project. To do that, click on the root of your project in the left hand panel of Eclipse. Then right-click, and open its properties. From the dialog that opens up, select Java Build Path and open up the Libraries tab. Then click on External JARs. The libraries we need are located in their respective subdirectories of the add-ons directory of your Android SDK installation. Once you have added HTCExtension.jar and real3d.jar to the project, the errors should go away.
The HTC code needs to be ran from within the renderer, and it needs to know the surface holder of the 3D scene in order to work. So, we are going to add an argument to the constructor of the renderer to pass that information to it. The first line to change is in the constructor for S3DSurfaceView that creates an instance of S3dRenderer. Find the line below:

oRenderer       = new S3DRenderer ( context, sCacheDirPath, sHomeDirPath, sPackDirPath, forceDefaultOrientation ) ;

and replace it with:

oRenderer       = new S3DRenderer ( context, sCacheDirPath, sHomeDirPath, sPackDirPath, forceDefaultOrientation, getHolder() ) ;

This simply adds an argument passing the holder of the S3DSurfaceView to the renderer when it is created. Now let’s add the code to tell the renderer to expect this extra argument, and what to do with it. First, we’ll set up some variables that we are going to use. Search for the line that says:

class S3DRenderer implements GLSurfaceView.Renderer

Immediately after the open bracket that follows that line, we are going to declare two variables by entering the following code:

public SurfaceHolder holder;
public boolean is3Dsupported;

I’ve already told you what the variable ‘holder’ is for. The second variable won’t really be used for this basic tutorial, but it will be set to true if 3D is available and false if it isn’t. Such information would be needed by any plugin to determine whether or not to display controls for the 3D effect. We declare them both as public so we will be able to access them from outside the function if we need to.
Now it’s time to modify the constructor for the renderer to accept the extra argument that points to the surface holder. To do that, look directly under the variables you just entered and change the line that says:

public S3DRenderer ( Context context, String sCacheDirPath, String sHomeDirPath, String sPackDirPath, boolean bForceDefaultOrientation ) 

to:

public S3DRenderer ( Context context, String sCacheDirPath, String sHomeDirPath, String sPackDirPath, boolean bForceDefaultOrientation, SurfaceHolder hHolder ) 

Then, within that function, right after the line that says:

bPaused     = false ;

add the code:

holder = hHolder;
holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);

Now comes a big chunk of code. After the closed bracket that ends the constructor for S3DRenderer, insert this code block:

static public void enableS3D(boolean enable) {
    boolean isS3Dsupported, isReal3Dsupported;
    isS3Dsupported = isReal3Dsupported = true;
    boolean formatResult = true;
    try {
        Real3D mReal3D = new Real3D(holder);
        if (enable)
            formatResult = mReal3D.setReal3DInfo(new Real3DInfo(true, Real3D.REAL3D_TYPE_SS, Real3D.REAL3D_ORDER_LR));
        else
            formatResult = mReal3D.setReal3DInfo(new Real3DInfo(true, Real3D.REAL3D_TYPE_NONE, Real3D.REAL3D_ORDER_LR));
        } catch (NoClassDefFoundError e) {
        android.util.Log.i("S3DRenderer", "class not found - Real3D display not available");
        isReal3Dsupported = false;
        } catch (UnsatisfiedLinkError usle) {
        android.util.Log.i("S3DRenderer", "unsatisfied link - Real3D display not available");
        isReal3Dsupported = false;
    }
    if (isReal3Dsupported && formatResult) {
        android.util.Log.i("S3DRenderer", "Real3D display available");
        is3Dsupported = true;
        return;
    }
    formatResult = true;
    Surface surface = holder.getSurface();
    try {
        if (enable)
        formatResult = DisplaySetting.setStereoscopic3DFormat(surface, DisplaySetting.STEREOSCOPIC_3D_FORMAT_SIDE_BY_SIDE);
        else
        formatResult = DisplaySetting.setStereoscopic3DFormat(surface, DisplaySetting.STEREOSCOPIC_3D_FORMAT_OFF);
    } catch (NoClassDefFoundError e) {
        android.util.Log.i("S3DRenderer", "class not found - S3D display not available");
        isS3Dsupported = false;
    } catch (UnsatisfiedLinkError usle) {
        android.util.Log.i("S3DRenderer", "unsatisfied link - S3D display not available");
        isS3Dsupported = false;
    }
    if (isS3Dsupported && formatResult) {
        android.util.Log.i("S3DRenderer", "S3D display available");
        is3Dsupported = true;
        return;
    }
    is3Dsupported = false;
}

This is a fairly large chunk of code, but as you can see, it is basically divided up into two similar sections. One section is for LG’s Real3D format, and the other is for HTC’s S3D format. What we are doing is calling the code to enable (or disable, depending on which argument is passed to the function) the stereoscopic displays in a TRY-CATCH block. If the code executes, and the function to enable stereoscopy succeeds, then we set the variable is3Dsupported to true and return from the function. If the device does not contain the functions required to activate 3D, because it is not one of the models that supports 3D, then an error will be returned. That error is caught by the CATCH statement so the program simply notifies us that the display wasn’t found instead of crashing. If the attempts to create a stereoscopic display on LG and HTC both fail, then the variable is3Dsupported is set to false.
With that done, we only have to make two more changes to the code. The next change is in the function immediately following the one we just added (assuming you added it where I told you to.) That is the onPause function for the S3DRenderer. Change that function from:

public void onPause ( )
{
    enginePause ( true ) ;
    bPaused      = true   ;
}

to:

public void onPause ( )
{
    enableS3D ( false );
    enginePause ( true ) ;
    bPaused      = true   ;
}

In that code block, we are simply calling the function we just created and turning off the 3D display when the application is moved to the background. If the onPause statement does not look like the one above, then you are probably looking at the onPause for the S3DSurfaceView. Make sure you are looking at the right function before modifying it.
Finally, we need to add the code to enable the 3D display. We are going to do that in the onSurfaceChanged function of the S3DRenderer. Here is how that code looks before the change:

public void onSurfaceChanged ( GL10 gl, int w, int h )
{
    engineOnSurfaceChanged ( w, h ) ;
}

Change that to:

public void onSurfaceChanged ( GL10 gl, int w, int h )
{
    engineOnSurfaceChanged ( w, h ) ;
    enableS3D(true);
}

Building and Testing

That’s all for the coding! You can now build your game using ANT. The stereoscopic display on your device should be active when you run it. It’s important to note here that the emulator that comes with the LG SDK will run the game and automatically convert the game into red/cyan anaglyphs. It will run unusably slow, but it will let you confirm that your code works. HTC does not provide such a tool, so you’ll need a physical device to test it on.




Need more answers?

  • slackBanner