/*==========================================================================
 *
 *  Copyright (C) 1995, 1996 Microsoft Corporation. All Rights Reserved.
 *
 *  File: globe.c
 *
 ***************************************************************************/

#include "rmdemo.h"

D3DRMRENDERQUALITY quality = D3DRMRENDER_GOURAUD;

LPDIRECT3DRMANIMATION anim;
int motion_points = 17;

D3DVECTOR motion[17] =
{{-D3DVAL(14), D3DVAL(4), D3DVAL(45)},
{D3DVAL(9), -D3DVAL(3), D3DVAL(36)},
{D3DVAL(5), D3DVAL(0), D3DVAL(5)},
{-D3DVAL(5), D3DVAL(8), D3DVAL(25)},
{D3DVAL(0), D3DVAL(3), D3DVAL(20)},
{-D3DVAL(3), -D3DVAL(4), D3DVAL(10)},
{D3DVAL(8), D3DVAL(10), D3DVAL(15)},
{D3DVAL(16), D3DVAL(0), D3DVAL(30)},
{D3DVAL(10), -D3DVAL(4), D3DVAL(42)},
{-D3DVAL(15), D3DVAL(0), D3DVAL(37)},
{-D3DVAL(5), -D3DVAL(7), D3DVAL(15)},
{D3DVAL(5), D3DVAL(5), D3DVAL(20)},
{D3DVAL(5), D3DVAL(10), D3DVAL(30)},
{D3DVAL(13), D3DVAL(8), D3DVAL(50)},
{D3DVAL(0), D3DVAL(8), D3DVAL(25)},
{D3DVAL(0), D3DVAL(0), D3DVAL(20)},
{-D3DVAL(14), D3DVAL(4), D3DVAL(45)}
};

static void __cdecl cleanupPath(LPDIRECT3DRMOBJECT obj, void* arg)
{
    anim->lpVtbl->Release(anim);
}

void __cdecl
moveCamera(LPDIRECT3DRMFRAME frame, void *arg, D3DVALUE delta)
{
    static D3DVALUE t = D3DVAL(0.0);
    LPDIRECT3DRMFRAME world_frame = (LPDIRECT3DRMFRAME) arg;
    LPDIRECT3DRMFRAME scene;

    frame->lpVtbl->GetScene(frame, &scene);

    t += D3DVAL(0.08);
    anim->lpVtbl->SetFrame(anim, frame);
    anim->lpVtbl->SetTime(anim, t);

    frame->lpVtbl->LookAt(frame, world_frame, scene, D3DRMCONSTRAIN_Z);

    scene->lpVtbl->Release(scene);
}

/*
 * world_scene
 */
BOOL
run_world_scene(LPDIRECT3DRMFRAME scene, LPDIRECT3DRMFRAME camera)
{
    LPDIRECT3DRMFRAME light1 = NULL;
    LPDIRECT3DRMFRAME world_frame = NULL;
    LPDIRECT3DRMLIGHT l1 = NULL;
    LPDIRECT3DRMLIGHT l2 = NULL;
    LPDIRECT3DRMTEXTURE tex = NULL;
    LPDIRECT3DRMWRAP wrap = NULL;
    LPDIRECT3DRMMESHBUILDER sphere3_builder = NULL;
    LPDIRECT3DRMMATERIAL mat = NULL;
    HRESULT rval;
    D3DRMBOX box;
    D3DVALUE miny, maxy, height;

    if (FAILED(lpD3DRM->lpVtbl->CreateFrame(lpD3DRM, scene, &light1)))
        goto generic_error;
    if (FAILED(light1->lpVtbl->SetPosition(light1, scene, D3DVAL(2), D3DVAL(0.0), D3DVAL(22))))
        goto generic_error;
    if (FAILED(lpD3DRM->lpVtbl->CreateLightRGB(lpD3DRM, D3DRMLIGHT_DIRECTIONAL, D3DVAL(0.9),
                                  D3DVAL(0.9), D3DVAL(0.9), &l1)))
                                  goto generic_error;
    if (FAILED(light1->lpVtbl->AddLight(light1, l1)))
        goto generic_error;
    if (FAILED(lpD3DRM->lpVtbl->CreateLightRGB(lpD3DRM, D3DRMLIGHT_AMBIENT, D3DVAL(0.1),
                                  D3DVAL(0.1), D3DVAL(0.1), &l2)))
                                  goto generic_error;
    if (FAILED(scene->lpVtbl->AddLight(scene, l2)))
        goto generic_error;
    if (FAILED(camera->lpVtbl->SetPosition(camera, scene, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0))))
        goto generic_error;
    if (FAILED(camera->lpVtbl->SetOrientation(camera, scene, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1), D3DVAL(0.0),
                                                D3DVAL(1), D3DVAL(0.0))))
                                                goto generic_error;

    if (FAILED(lpD3DRM->lpVtbl->CreateFrame(lpD3DRM, scene, &world_frame)))
        goto generic_error;
    if (FAILED(world_frame->lpVtbl->SetPosition(world_frame, scene, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(30))))
        goto generic_error;
    if (FAILED(world_frame->lpVtbl->SetOrientation(world_frame, scene, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1),
                                        D3DVAL(0.0), D3DVAL(1), D3DVAL(0.0))))
                                        goto generic_error;
    if (FAILED(world_frame->lpVtbl->SetRotation(world_frame, scene, D3DVAL(0.03), 
                                     D3DVAL(0.1), D3DVAL(0.0), D3DVAL(0.1))))
                                     goto generic_error;

    if (FAILED(lpD3DRM->lpVtbl->CreateMeshBuilder(lpD3DRM, &sphere3_builder)))
        goto generic_error;
    rval = sphere3_builder->lpVtbl->Load(sphere3_builder, "sphere3.x",
                                        NULL, D3DRMLOAD_FROMFILE, NULL, NULL);
    if (rval != D3DRM_OK) {
        Msg("Failed to load sphere3_builder.\n%s", D3DRMErrorToString(rval));
        goto ret_with_error;
    }
    if (FAILED(lpD3DRM->lpVtbl->CreateMaterial(lpD3DRM, D3DVAL(20.0), &mat)))
        goto generic_error;
    if (FAILED(sphere3_builder->lpVtbl->SetMaterial(sphere3_builder, mat)))
        goto generic_error;
    mat->lpVtbl->Release(mat);
    if (FAILED(sphere3_builder->lpVtbl->Scale(sphere3_builder,
                                   D3DVAL(2), D3DVAL(2), D3DVAL(2))))
                                   goto generic_error;

    if (FAILED(sphere3_builder->lpVtbl->SetColorRGB(sphere3_builder, D3DVAL(1), D3DVAL(1), D3DVAL(1))))
        goto generic_error;
    if (FAILED(sphere3_builder->lpVtbl->GetBox(sphere3_builder, &box)))
        goto generic_error;
    maxy = box.max.y;
    miny = box.min.y;
    height = maxy - miny;

    if (FAILED(lpD3DRM->lpVtbl->CreateWrap
        (lpD3DRM, D3DRMWRAP_CYLINDER, NULL,
         D3DVAL(0.0), D3DVAL(0.0), D3DVAL(0.0),
         D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0),
         D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1.0),
         D3DVAL(0.0), D3DDivide(miny, height),
         D3DVAL(1.0), D3DDivide(-D3DVAL(1.0), height),
         &wrap
        )))
        goto generic_error;
    if (FAILED(wrap->lpVtbl->Apply(wrap, (LPDIRECT3DRMOBJECT) sphere3_builder)))
        goto generic_error;

    rval = lpD3DRM->lpVtbl->LoadTexture(lpD3DRM, "tex1.ppm", &tex);
    if (rval != D3DRM_OK) {
        Msg("Failed to load rllogo.ppm\n%s", D3DRMErrorToString(rval));
        goto ret_with_error;
    }
    if (FAILED(tex->lpVtbl->SetShades(tex, 32)))
        goto generic_error;
    if (FAILED(sphere3_builder->lpVtbl->SetTexture(sphere3_builder, tex)))
        goto generic_error;

    if (FAILED(world_frame->lpVtbl->AddVisual(world_frame, (LPDIRECT3DRMVISUAL) sphere3_builder)))
        goto generic_error;

    if (FAILED(camera->lpVtbl->AddMoveCallback(camera, moveCamera, 
                                    (void *) world_frame)))
                                    goto generic_error;
    if (FAILED(camera->lpVtbl->AddDestroyCallback(camera, cleanupPath, NULL)))
        goto generic_error;

    RELEASE(light1);
    RELEASE(world_frame);
    RELEASE(sphere3_builder);
    RELEASE(l1);
    RELEASE(l2);
    RELEASE(tex);
    RELEASE(wrap);
    return TRUE;
generic_error:
    Msg("An error occured while building the scene.\n");
ret_with_error:
    RELEASE(light1);
    RELEASE(world_frame);
    RELEASE(l1);
    RELEASE(l2);
    RELEASE(tex);
    RELEASE(wrap);
    RELEASE(sphere3_builder);
    RELEASE(mat);
    return FALSE;
}

BOOL
BuildScene(LPDIRECT3DRMDEVICE dev, LPDIRECT3DRMVIEWPORT view,
           LPDIRECT3DRMFRAME scene, LPDIRECT3DRMFRAME camera)
{
    int i;

    view = view;                /* not used */

    /*
     * This example flys a camera around a globe. The path taken is
     * defined by a spline curve.
     */


    if (FAILED(dev->lpVtbl->SetQuality(dev, quality)))
        goto generic_error;

    if (FAILED(lpD3DRM->lpVtbl->CreateAnimation(lpD3DRM, &anim)))
        goto generic_error;
    if (FAILED(anim->lpVtbl->SetOptions(anim, D3DRMANIMATION_CLOSED | D3DRMANIMATION_LINEARPOSITION | D3DRMANIMATION_POSITION)))
        goto generic_error;
    for (i = 0; i < motion_points; i++) {
        if (FAILED(anim->lpVtbl->AddPositionKey(anim, D3DVAL(i), motion[i].x, motion[i].y,
                                 motion[i].z/2)))
                                 goto generic_error;
    }
    if (!run_world_scene(scene, camera))
        goto ret_with_error;
    return TRUE;
generic_error:
    Msg("A failure occured while building the scene.\n");
ret_with_error:
    return FALSE;
}

void
OverrideDefaults(Defaults* defaults)
{
    lstrcpy(defaults->Name, "Globe Direct3DRM Example");
}
