[ Home | Capitolo 1 | Capitolo 2 | Capitolo 3 | Capitolo 4 | Capitolo 5 ]

Guida alla Programmazione di Giochi con le DirectX di David Joffe

Capitolo 4: Un semplice esempio Direct3D in modalità Retained

4.1 Direct3D: una panoramica

Qui mostrerò alcune basi, come i sistemi di coordinate, i sistemi di coordinate globali e dell'oggetto, etc. Per ora presupporrò che abbiate almeno un po' familiarità con la programmazione 3D. Blah blah blah, differenze tra modalità immediate e retained, etc etc.

4.1.1 Devices

Direct3D si interfaccia con la superficie che sta renderizzando (es. screen memory, system memory) utilizzando un oggetto IDirect3DRMDevice. Può esistere più di un tipo di device di renderizzazione e un device di renderizzazione specifico deve essere scelto per una scena. Per esempio, c'è normalmente un device per il rendering RGB e un device per il rendering Mono (questi nomi si riferiscono al modello di illuminazione utilizzato per il rendering. Mono significa che nella scena possono esistere solo le luci bianche, mentre RGB supporta le luci colorate, perciò è più lento). Possono essere installati devices aggiuntivi che permettono l'uso dell'accelerazione hardware 3D. E' possibile reiterare tutti i devices D3D installati contandoli dal primo all'ultimo (EnumDevices). E' possibile avere due devices differenti che renderizzano la stessa superficie.

4.1.2 Viewports

L'oggetto IDirect3DRMViewport è usato per tenere traccia di come viene renderizzata nel device la nostra scena 3D. E' possibile avere viewports multipli per device, ed è anche possibile avere un viewport del rendering di più di un device. L'oggetto viewport object tiene traccia della telecamera, campi di clipping frontali e posteriori, campi visivi, etc.

4.1.3 Frames

Un frame in Direct3D è fondamentalmente usato per immagazzinare la posizione di un oggetto e le informazioni sul suo orientamento, relativa ad un dato frame di riferimento, che è da dove deriva il termine frame. I frames vengono posizionati in relazione ad altri frames, oppure alle coordinate globali. I frames vengono usati per immagazzinare le posizioni degli oggetti nella scena, così come altre cose come le luci. OK, così mi sto spiegando in maniera scorretta. E' tardi, sono stanco, lo correggerò presto. Per aggiungere un oggetto alla scena, dobbiamo collegare l'oggetto ad un frame. L'oggetto è chiamato visual in Direct3D, dal momento che rappresenta quello che l'utente vede. Quindi, un visual non ha di per sé informazioni significative sulla posizione o sull'orientamento, ma quando viene collegato ad un frame, viene trasformato quando renderizzato in accordo con le informazioni sulla trasformazione nel frame. Frames multipli possono essere usati nello stesso visual. Questo può far risparmiare un sacco di tempo e memoria in una situazione come, per esempio, una foresta o una piccola flotta di veicoli spaziali, dove abbiamo un gruppo di oggetti che hanno un aspetto uguale, ma si trovano tutti in posizioni e orientamenti differenti.

Qui c'è uno scadente diagramma ASCII di un singolo visual collegato a due frames che sono in posizioni differenti:

   _____
  /    /| <- Cube (visual)
 /    / |<==========================>[Frame1: (21, 3, 4)]
+----+  |
|    | /<===========================>[Frame2: (-12, 10, -6)]
|    |/
+----+

Se tutti e due i frames sono collegati al frame della scena, allora la nostra scena avrà 2 cubi; uno in (21, 3, 4) e l'altro in (-12, 10, -6).

4.1.4 Materiali

Prima o poi ci sarà.

4.2 l'esempio Direct3D RM

Per prima cosa, ecco uno screenshot del piccolo semplice esempio dell'applicazione che troverete insieme a questo tutorial.

[Screenshot 2]

4.3 Settaggio delle variabili globali

Prima di iniziare abbiamo bisogno di qualche variabile globale.

LPDIRECTDRAW pDD;                // A DirectDraw object
LPDIRECT3DRM pD3DRM;             // A Direct3D RM object
LPDIRECTDRAWSURFACE pDDSPrimary; // DirectDraw primary surface
LPDIRECTDRAWSURFACE pDDSBack;    // DirectDraw back surface
LPDIRECTDRAWPALETTE pDDPal;      // Palette for primary surface
LPDIRECTDRAWCLIPPER pClipper;    // Clipper for windowed mode
LPDIRECT3DRMDEVICE pD3DRMDevice; // A device
LPDIRECT3DRMVIEWPORT pViewport;  // A viewport
LPDIRECT3DRMFRAME pCamera;       // A camera
LPDIRECT3DRMFRAME pScene;        // The scene
LPDIRECT3DRMFRAME pCube;         // The one and only object in
                                 // our scene
BOOL bFullScreen;                // Are we in full-screen mode?
BOOL bAnimating;                 // Has our animating begun?
HWND ddWnd;                      // HWND of the DDraw window

Notare che, per creare una applicazione Direct3D, ci serve sia un oggetto DirectDraw che un oggetto Direct3D. Questo succede perché Direct3D lavora in congiunzione con DirectDraw. Come prima, ci serve una superficie primaria e una superficie back per il nostro double-buffering, e un clipper per l'handle al window-clipping in modalità windowed. L'oggetto palette non viene (ancora) trattato in questo tutorial. Abbiamo oggetti per il device e il viewport, e abbiamo oggetti frame per tenere traccia della scena e della telecamera della scena. Inoltre, abbiamo un frame che viene usato per l'oggetto che avremo in questa scena.

Ecco una routine solo per appiattire inizialmente queste globali:

void InitDirectXGlobals()
{
    pDD = NULL;
    pD3DRM = NULL;
    pDDSPrimary = NULL;
    pDDSBack = NULL;
    pDDPal = NULL;
    pClipper = NULL;
    pD3DRMDevice = NULL;
    pViewport = NULL;
    pCamera = NULL;
    pScene = NULL;
    pCube = NULL;

    bFullScreen = FALSE;
    bAnimating = FALSE;
}

4.4 Da 'Inizializzazione del sistema DirectDraw' a 'Creazione del clipper'

Questo ripercorre tutto il procedimento esatamente come nell'esempio DirectDraw, con l'eccezione della funzione CreateSurface, dove la superficie back viene creata con DDSCAPS_3DDEVICE, poiché verrà usata per il rendering 3d:

UINT CreatePrimarySurface()
{
    .
    .
    .
    // Create an offscreen surface, specifying 3d device
    ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE;
    .
    .
    .
}

4.5 Creazione dell'oggetto Direct3D in modalità Retained

Ora abbiamo bisogno di creare un oggetto IDirect3DRM. Questo viene fatto, molto semplicemente, chiamando la funzione Direct3DRMCreate.

UINT CreateDirect3DRM()
{
    HRESULT hr;
    // Create the IDirect3DRM object.
    hr = Direct3DRMCreate(&pD3DRM);
    if (FAILED(hr)) {
        TRACE("Error creating Direct3d RM object\n");
        return 1;
    }
    return 0;
}

4.6 Creazione del device per il rendering

Creiamo l'oggetto devicedalla superficie back, poiché questa superficie è quella che andremo a renderizzare.

UINT CreateDevice()
{
    HRESULT hr;
    hr = pD3DRM->CreateDeviceFromSurface(
        NULL, pDD, pDDSBack, &pD3DRMDevice);
    if (FAILED(hr)) {
        TRACE("Error %d creating d3drm device\n", int(LOWORD(hr)));
        return 1;
    }
    // success
    return 0;
}

4.7 Creazione del viewport

Qui facciamo un po' di più che creare solo un viewport. Creiamo l'oggetto scena e l'oggetto telecamera, così come settiamo la luce ambientale per la scena e creiamo una luce direzionale.

UINT CreateViewport()
{
    HRESULT hr;

    // First create the scene frame
    hr = pD3DRM->CreateFrame(NULL, &pScene);
    if (FAILED(hr)) {
        TRACE("Error creating the scene frame\n");
        return 1;
    }

    // Next, create the camera as a child of the scene
    hr = pD3DRM->CreateFrame(pScene, &pCamera);
    if (FAILED(hr)) {
        TRACE("Error creating the scene frame\n");
        return 2;
    }
    // Set the camera to lie somewhere on the negative z-axis, and
    // point towards the origin
    pCamera->SetPosition(
        pScene, D3DVAL(0.0), D3DVAL(0.0), D3DVAL(-300.0));
    pCamera->SetOrientation(
        pScene,
        D3DVAL(0.0), D3DVAL(0.0), D3DVAL(1.0),
        D3DVAL(0.0), D3DVAL(1.0), D3DVAL(0.0));

    // create lights
    LPDIRECT3DRMLIGHT pLightAmbient = NULL;
    LPDIRECT3DRMLIGHT pLightDirectional = NULL;
    LPDIRECT3DRMFRAME pLights = NULL;

    // Create two lights and a frame to attach them to
    // I haven't quite figured out the CreateLight's second
    // parameter yet.
    pD3DRM->CreateFrame(pScene, &pLights);
    pD3DRM->CreateLight(D3DRMLIGHT_AMBIENT, pD3DRMCreateColorRGB(
        D3DVALUE(0.3), D3DVALUE(0.3), D3DVALUE(0.3)),
        &pLightAmbient);
    pD3DRM->CreateLight(D3DRMLIGHT_DIRECTIONAL, D3DRMCreateColorRGB(
        D3DVALUE(0.8), D3DVALUE(0.8), D3DVALUE(0.8)),
        &pLightDirectional);

    // Orient the directional light
    pLights->SetOrientation(pScene,
        D3DVALUE(30.0), D3DVALUE(-20.0), D3DVALUE(50.0),
        D3DVALUE(0.0), D3DVALUE(1.0), D3DVALUE(0.0));

    // Add ambient light to the scene, and the directional light
    // to the pLights frame
    pScene->AddLight(pLightAmbient);
    pLights->AddLight(pLightDirectional);

    // Create the viewport on the device
    hr = pD3DRM->CreateViewport(pD3DRMDevice,
        pCamera, 10, 10, 300, 220, &pViewport);
    if (FAILED(hr)) {
        TRACE("Error creating viewport\n");
        return 3;
    }
    // set the back clipping field
    hr = pViewport->SetBack(D3DVAL(5000.0));

    // Release the temporary lights created. It seems
    // they will have been copied for the scene during AddLight
    pLightAmbient->Release();
    pLightDirectional->Release();

    // success
    return 0;
}

4.8 Creazione della vostra scena

Ci serve qualche oggetto per la scena. Creiamo un cubo! Wheee! Qui trovate il file cube.x, zippato. Unzipppatelo nella directory dalla quale lanciate la vostra applicazione.

SetRotation assegna valori in radianti ad un frame che deve rotare attorno al proprio asse ogni volta che il frame viene renderizzato usando Tick. Direct3D esegue questa rotazione automaticamente, ogni volta che viene chiamato Tick.

// Put stuff into the scene
UINT CreateDefaultScene()
{
    HRESULT hr;

    // Create the frame for the cube
    hr = pD3DRM->CreateFrame(pScene, &pCube);
    if (FAILED(hr)) {
        TRACE("Error creating frame pCube\n");
        return 1;
    }
    // Load the cube
    hr = pCube->Load("CUBE.X", NULL, D3DRMLOAD_FROMFILE, NULL, NULL);
    if (FAILED(hr)) {
        TRACE("Error [%d] loading cube.x\n", int(LOWORD(hr)));
        return 2;
    }
    // Set cube's position/orientation relative to scene
    pCube->SetPosition(pScene,
        D3DVALUE(0.0), D3DVALUE(0.0), D3DVALUE(0.0));
    pCube->SetRotation(pScene,
        D3DVALUE(1.0), D3DVALUE(1.0), D3DVALUE(0.0), D3DVALUE(0.1));

    // success
    return 0;
}

4.9 Posizionamento del tutto

Qui c'è una parte finale della funzione InitInstancedell'applicazione:

    InitDirectXGlobals();
    TRACE("Calling InitDDraw\n");
    InitDDraw();
    SetMode();
//    TRACE("Calling LoadJascPalette\n");
//    LoadJascPalette("inspect.pal", 10, 240);
    TRACE("Calling CreatePrimarySurface\n");
    CreatePrimarySurface();
    TRACE("Calling CreateClipper\n");
    CreateClipper();
//    TRACE("Calling AttachPalette\n");
//    AttachPalette(pDDPal);
    TRACE("Calling CreateDirect3DRM\n");
    CreateDirect3DRM();
    TRACE("Calling CreateDevice\n");
    CreateDevice();
    TRACE("Calling CreateViewport\n");
    CreateViewport();
    TRACE("Calling CreateDefaultScene\n");
    CreateDefaultScene();

    bAnimating = TRUE;
	
    return TRUE;
}

4.10 Ripristino delle superfici perse

Qualcosa di simile all'esempio DirectDraw:

BOOL CheckSurfaces()
{
    // Check the primary surface
    if (pDDSPrimary) {
        if (pDDSPrimary->IsLost() == DDERR_SURFACELOST) {
            pDDSPrimary->Restore();
            return FALSE;
        }
    }
    return TRUE;
}

4.11 Il loop del rendering

Qualcosa di simile all'esempio DirectDraw:

BOOL CD3dRmAppApp::OnIdle(LONG lCount) 
{
    CWinApp::OnIdle(lCount);
    if (bAnimating) {
        HeartBeat();
        Sleep(50);
    }
    return TRUE;
}

4.12 la funzione HeartBeat

BOOL CD3dRmAppApp::HeartBeat()
{
    HRESULT hr;
//    if (!CheckSurfaces) bForceUpdate = TRUE;
//    if (bForceUpdate) pViewport->ForceUpdate(10,10,300,220);
    hr = pD3DRM->Tick(D3DVALUE(1.0));
    if (FAILED(hr)) {
        TRACE("Tick error!\n");
        return FALSE;
    }

    // Call our routine for flipping the surfaces
    FlipSurfaces();

    // No major errors
    return TRUE;
}

4.13 Flipping delle superfici

Qualcosa di simile all'esempio DirectDraw.

4.14 Controllo degli errori Direct3D

A breve(?).

4.15 Pulizia

A brevissimo(?).

Articolo aggiornato il: 19 Giugno 1998


Articolo di David Joffe
http://www.geocities.com/SoHo/Lofts/2018/

---
Traduzione: Papero - IPG 1999
Eng?Ita! Team
http://www.ItaProGaming.com