SilverLining for Android  1.0
Skies, 3D clouds, and weather for Android
Integrating SilverLining into OpenSceneGraph on Android

We also provide an integration example for SilverLining with OpenSceneGraph (OSG) for Android. To try it out, you must first have a working OpenSceneGraph built for Android and OpenGL ES 2.0 on your system. http://forum.openscenegraph.org/viewtopic.php?t=10076 provides a good tutorial for getting up and running with OSG for Android.

Specifically, our sample assumes that the ANDROID_NDK environment variable is set to point to your NDK directory, and ANDROID_SDK points to where your Android SDK's are installed (this is where the Android SDK manager installs individual SDK's to, NOT to where you installed the Android SDK itself.)

To try out the OSGAndroidSilverLiningDemo, you must first build its native code. First, you must edit the jni/Android.mk file in the sample to specify where your OSG for Android build is located:

### Main Install dir
OSG_ANDROID_DIR := /home/fkane/osgAndroidExampleGLES2

Then from the OSGAndroidSilverLiningDemo directory, run:

$ANDROID_NDK/ndk-build

Next, import this directory into Eclipse as existing Android code (Import / Android / Existing Android Code into Workspace under Eclipse Juno), right click the project, and "run as" an Android application. Your tethered OpenGL ES 2.0 capable device should then run OpenSceneGraph with SilverLining's 3D clouds and skybox in the background, and the OSG cow in the foreground:

osgandroid-med.jpg
SilverLining running under OpenSceneGraph on Android

Let's explore how it works, so you may integrate SilverLining into your own OSG application for Android.

First, you'll need to include the SilverLining static libraries for gnustl as part of your Android.mk file:

include $(CLEAR_VARS)

LOCAL_MODULE    := silverlining_prebuilt
LOCAL_SRC_FILES := ../../static_lib_gnustl/$(TARGET_ARCH_ABI)/libsilverlining_static.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../include

include $(PREBUILT_STATIC_LIBRARY)

You may then add silverlining_prebuilt to your activity's LOCAL_STATIC_LIBRARIES line. You'll also want to include the SkyDrawable.cpp and CloudsDrawable.cpp files from our OSG example and their associated headers as part of your own project, adding the cpp files to your LOCAL_SRC_FILES.

SkyDrawable and CloudsDrawable wrap SilverLining in OSG-friendly objects. The OsgMainApp.cpp file included with the sample provides a good example on how to use these classes in your OSG application. Its integrateSilverLining method is of particular interest:

void integrateSilverLining(osg::ref_ptr<osg::Node> sceneGraphRoot, osgViewer::Viewer& viewer,
                AAssetManager *assetManager)
{
        // No need for OSG to clear the color buffer, the sky will fill it for you.
        viewer.getCamera()->setClearMask(GL_DEPTH_BUFFER_BIT);

        // Make sure lighting mode is correct for setting sun direction in SilverLining's world space
        viewer.setLightingMode(osgViewer::View::SKY_LIGHT);

        // Instantiate an Atmosphere and associate it with this camera. If you have multiple cameras
        // in multiple contexts, be sure to instantiate seperate Atmosphere objects for each.
        SilverLining::Atmosphere *atm = new SilverLining::Atmosphere("Your user name", "Your license code");

        osg::notify(osg::ALWAYS)<<"Creating SilverLining resource loader..."<<std::endl;

        // Pass the Android asset manager to SilverLining so it can load its own resources
        AndroidResourceLoader *rl = SL_NEW AndroidResourceLoader(assetManager);
        atm->SetResourceLoader(rl);

        // Add the sky (calls Atmosphere::DrawSky and handles initialization once you're in
        // the rendering thread)
        osg::Geode *skyGeode = new osg::Geode;
        SkyDrawable *skyDrawable = new SkyDrawable(&viewer);

        skyGeode->addDrawable(skyDrawable);
        skyGeode->setCullingActive(false);

        /* If you let OSG auto-calculate the near and far clip planes; it'll exclude the
        sky box and clouds. One solution is to set the near and far clip planes explicitly
        like this:
    */
#ifdef FIXED_NEAR_FAR
        viewer.getCamera()->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
        double fovy, aspect, zNear, zFar;
        viewer.getCamera()->getProjectionMatrixAsPerspective(fovy, aspect, zNear, zFar);
        viewer.getCamera()->setProjectionMatrixAsPerspective(fovy, aspect, 10, 100000);
#endif
 /*
        or, you can use the included projection matrix callback to intercept how OSG computes
        the near and far clip planes and take SilverLining's objects into account, like this:
 */

#ifndef FIXED_NEAR_FAR
        SilverLiningProjectionMatrixCallback *cb = new SilverLiningProjectionMatrixCallback(
                atm, viewer.getCamera());
        viewer.getCamera()->setClampProjectionMatrixCallback(cb);
        cb->setSkyDrawable(skyDrawable);
#endif

        AtmosphereReference *ar = new AtmosphereReference;
        ar->atmosphere = atm;
        viewer.getCamera()->setUserData(ar);

        // Use a RenderBin to enforce that the scene gets drawn first, then the sky, then the clouds
        skyGeode->getOrCreateStateSet()->setRenderBinDetails(98, "RenderBin");
        skyGeode->getOrCreateStateSet()->setAttributeAndModes( new osg::Depth( osg::Depth::LEQUAL, 0.0, 1.0, false ) );

        // Add the models
        sceneGraphRoot.get()->getOrCreateStateSet()->setRenderBinDetails(1, "RenderBin");

        // Add the clouds (note, you need this even if you don't want clouds - it calls
        // Atmosphere::DrawObjects() )
        osg::Geode *cloudsGeode = new osg::Geode;
        CloudsDrawable * cloudsDrawable = new CloudsDrawable(&viewer);
        cloudsGeode->addDrawable(cloudsDrawable);
        cloudsGeode->getOrCreateStateSet()->setRenderBinDetails(99, "RenderBin");
        cloudsGeode->setCullingActive(false);

#ifndef FIXED_NEAR_FAR
        cb->setCloudsDrawable(cloudsDrawable);
#endif

        viewer.getSceneData()->asGroup()->addChild(skyGeode);
        viewer.getSceneData()->asGroup()->addChild(cloudsGeode);

        osg::notify(osg::ALWAYS)<<"SilverLining integration completed..."<<std::endl;
}

To modify the conditions being simulated, you may modify the SkyDrawable::initializeSilverLining method to meet your own needs:

void SkyDrawable::initializeSilverLining(AtmosphereReference *ar) const
{
    if (ar && !ar->atmosphereInitialized)
    {
        ar->atmosphereInitialized = true; // only try once.
                SilverLining::Atmosphere *atmosphere = ar->atmosphere;

        if (atmosphere)
        {
            srand(1234); // constant random seed to ensure consistent clouds across windows

#ifdef ANDROID
            std::string resPath("Resources/");
            int ret = atmosphere->Initialize(SilverLining::Atmosphere::OPENGLES2, resPath.c_str(),
                                             true, 0);

#else
            // Update the path below to where you installed SilverLining's resources folder.
            const char *slPath = getenv("SILVERLINING_PATH");
            if (!slPath)
            {
                printf("Can't find SilverLining; set the SILVERLINING_PATH environment variable ");
                printf("to point to the directory containing the SDK.\n");
                exit(0);
            }

            std::string resPath(slPath);
            resPath += "\\Resources\\";

            int ret = atmosphere->Initialize(SilverLining::Atmosphere::OPENGL, resPath.c_str(),
                                             true, 0);
#endif
            if (ret != SilverLining::Atmosphere::E_NOERROR)
            {
                printf("SilverLining failed to initialize; error code %d.\n", ret);
                printf("Check that the path to the SilverLining installation directory is set properly ");
                printf("in SkyDrawable.cpp (in SkyDrawable::initializeSilverLining)\n");
                exit(0);
            }

            // Let SilverLining know which way is up. OSG usually has Z going up.
            atmosphere->SetUpVector(0, 0, 1);
            atmosphere->SetRightVector(1, 0, 0);

            // Set our location (change this to your own latitude and longitude)
            SilverLining::Location loc;
            loc.SetAltitude(0);
            loc.SetLatitude(45);
            loc.SetLongitude(-122);
            atmosphere->GetConditions()->SetLocation(loc);

            // Set the time to noon in PST
            SilverLining::LocalTime t;
            t.SetFromSystemTime();
                        t.SetHour(12);
                        t.SetTimeZone(PST);
            atmosphere->GetConditions()->SetTime(t);

            // Center the clouds around the camera's initial position
            osg::Vec3d pos = _view->getCameraManipulator()->getMatrix().getTrans();

            SilverLining::CloudLayer *cumulusCongestusLayer;
            cumulusCongestusLayer = SilverLining::CloudLayerFactory::Create(CUMULUS_CONGESTUS);
            cumulusCongestusLayer->SetIsInfinite(true);
            cumulusCongestusLayer->SetBaseAltitude(4000);
            cumulusCongestusLayer->SetThickness(500);
            cumulusCongestusLayer->SetBaseLength(40000);
            cumulusCongestusLayer->SetBaseWidth(40000);
            cumulusCongestusLayer->SetDensity(0.3);

            // Note, we pass in X and -Y since this accepts "east" and "south" coordinates.
            cumulusCongestusLayer->SetLayerPosition(pos.x(), -pos.y());
            cumulusCongestusLayer->SeedClouds(*atmosphere);
            cumulusCongestusLayer->GenerateShadowMaps(false);

            atmosphere->GetConditions()->AddCloudLayer(cumulusCongestusLayer);

        }
    }
}

The complete SilverLining C++ API is available to you, as documented in Integrating SilverLining into a Native Activity and at http://www.sundog-soft.com/docs/html/index.html