|
Načítání souborů 3DStudioMax (*.3ds)
|
|
|
Zdrojový kód načítání a vykreslování modelů 3DS vytvořil kolega
Jiří Studnička (Well),
inpirovaný tutorialem od DigiBena.
Já jsem jen popsal každý řádek kódu, trochu kód upravil a teď budu dělat chytrýho v tomto tutorialu.
Takže, na podrobný anglický popis fomátu (*.3ds) můžete mrknou
zde. Všechny hodnoty tohoto
formátu jsou uspořádány do bytového stromu. V každé větvi stromu jsou uloženy specifické
hodnoty. Tento strom vypadá takto:
|
|
///////////////////////////////////////////
// ///
// * STROM BYTOVYCH DAT SOUBORU 3DS * ///
// ///
///////////////////////////////////////////
//
//
// MAIN3DS (0x4D4D)
// |
// +--EDIT3DS (0x3D3D)
// | |
// | +--EDIT_MATERIAL (0xAFFF)
// | | |
// | | +--MAT_NAME01 (0xA000) (See mli Doc)
// | |
// | +--EDIT_CONFIG1 (0x0100)
// | +--EDIT_CONFIG2 (0x3E3D)
// | +--EDIT_VIEW_P1 (0x7012)
// | | |
// | | +--TOP (0x0001)
// | | +--BOTTOM (0x0002)
// | | +--LEFT (0x0003)
// | | +--RIGHT (0x0004)
// | | +--FRONT (0x0005)
// | | +--BACK (0x0006)
// | | +--USER (0x0007)
// | | +--CAMERA (0xFFFF)
// | | +--LIGHT (0x0009)
// | | +--DISABLED (0x0010)
// | | +--BOGUS (0x0011)
// | |
// | +--EDIT_VIEW_P2 (0x7011)
// | | |
// | | +--TOP (0x0001)
// | | +--BOTTOM (0x0002)
// | | +--LEFT (0x0003)
// | | +--RIGHT (0x0004)
// | | +--FRONT (0x0005)
// | | +--BACK (0x0006)
// | | +--USER (0x0007)
// | | +--CAMERA (0xFFFF)
// | | +--LIGHT (0x0009)
// | | +--DISABLED (0x0010)
// | | +--BOGUS (0x0011)
// | |
// | +--EDIT_VIEW_P3 (0x7020)
// | +--EDIT_VIEW1 (0x7001)
// | +--EDIT_BACKGR (0x1200)
// | +--EDIT_AMBIENT (0x2100)
// | +--EDIT_OBJECT (0x4000)
// | | |
// | | +--OBJ_TRIMESH (0x4100)
// | | | |
// | | | +--TRI_VERTEXL (0x4110)
// | | | +--TRI_VERTEXOPTIONS (0x4111)
// | | | +--TRI_MAPPINGCOORS (0x4140)
// | | | +--TRI_MAPPINGSTANDARD (0x4170)
// | | | +--TRI_FACEL1 (0x4120)
// | | | | |
// | | | | +--TRI_SMOOTH (0x4150)
// | | | | +--TRI_MATERIAL (0x4130)
// | | | |
// | | | +--TRI_LOCAL (0x4160)
// | | | +--TRI_VISIBLE (0x4165)
// | | |
// | | +--OBJ_LIGHT (0x4600)
// | | | |
// | | | +--LIT_OFF (0x4620)
// | | | +--LIT_SPOT (0x4610)
// | | | +--LIT_UNKNWN01 (0x465A)
// | | |
// | | +--OBJ_CAMERA (0x4700)
// | | | |
// | | | +--CAM_UNKNWN01 (0x4710)
// | | | +--CAM_UNKNWN02 (0x4720)
// | | |
// | | +--OBJ_UNKNWN01 (0x4710)
// | | +--OBJ_UNKNWN02 (0x4720)
// | |
// | +--EDIT_UNKNW01 (0x1100)
// | +--EDIT_UNKNW02 (0x1201)
// | +--EDIT_UNKNW03 (0x1300)
// | +--EDIT_UNKNW04 (0x1400)
// | +--EDIT_UNKNW05 (0x1420)
// | +--EDIT_UNKNW06 (0x1450)
// | +--EDIT_UNKNW07 (0x1500)
// | +--EDIT_UNKNW08 (0x2200)
// | +--EDIT_UNKNW09 (0x2201)
// | +--EDIT_UNKNW10 (0x2210)
// | +--EDIT_UNKNW11 (0x2300)
// | +--EDIT_UNKNW12 (0x2302)
// | +--EDIT_UNKNW13 (0x2000)
// | +--EDIT_UNKNW14 (0xAFFF)
// |
// +--KEYF3DS (0xB000)
// |
// +--KEYF_UNKNWN01 (0xB00A)
// +--............. (0x7001) ( viewport, same as editor )
// +--KEYF_FRAMES (0xB008)
// +--KEYF_UNKNWN02 (0xB009)
// +--KEYF_OBJDES (0xB002)
// |
// +--KEYF_OBJHIERARCH (0xB010)
// +--KEYF_OBJDUMMYNAME (0xB011)
// +--KEYF_OBJUNKNWN01 (0xB013)
// +--KEYF_OBJUNKNWN02 (0xB014)
// +--KEYF_OBJUNKNWN03 (0xB015)
// +--KEYF_OBJPIVOT (0xB020)
// +--KEYF_OBJUNKNWN04 (0xB021)
// +--KEYF_OBJUNKNWN05 (0xB022)
//
//
//
//
|
Podle tohoto bytového stromu si vytvoříme definice.
|
|
// Struktura souboru je tvořena z bytového stromu. Každá větev tohoto stromu obsahuje specifickou
// informaci o své délce v bytech.
// Definuj základní kmen ( Chunk ), bytového stromu souboru
#define PRIMARY 0x4D4D
// Definuj hlavní větev bytového stromu
#define OBJECTINFO 0x3D3D // Obsahuje informace o skupině objektů. Objekt se nazývá též "Mesh"
#define VERSION 0x0002 // Obsahuje informace o verzi *.3ds souboru
#define EDITKEYFRAME 0xB000 // Obsahuje infomace o animaci modelu
// Definuj větev informací o objektu
#define MATERIAL 0xAFFF // Obsahuje informace o texturách
#define OBJECT 0x4000 // Obsahuje informace o trojúhelnících, vrcholech, atd.
// Definuj větev informací o materiálu
#define MATNAME 0xA000 // Obsahuje informace o jménu materiálu
#define MATDIFFUSE 0xA020 // Obsahuje info o barvě RGB materiálu
#define MATMAP 0xA200 // Hlavička pro nový materiál
#define MATMAPFILE 0xA300 // Info o jménu textury
#define OBJECT_MESH 0x4100 // Info, zda se má vytvořit nová "Mesh", nový objekt
// Definuj novou větev "Mesh". nový objekt
#define OBJECT_VERTICES 0x4110 // Vrcholy objektu
#define OBJECT_TRIANGLES 0x4120 // Trojúhelníky objektu
#define OBJECT_MATERIAL 0x4130 // Info o materiálu, buď je tvořen z textury, nebo barvy
#define OBJECT_UV 0x4140 // UV texturová koordinace
|
A struktury
|
|
// Pomocná struktura pro definování 2D vektoru. Hodnota x = pf[0], y = pf[1]
struct TVector2d
{
float pf[2];
};
// Struktura informací o větvích ( chunk )
struct TChunk
{
unsigned short int ui_id; // Identifikace větví
unsigned int ui_length; // Délka větve
unsigned int ui_bytes_read; // Množství bytů potřebných pro čtení větve
};
// Struktura informací o trojúhelnících, ze kterých je každý objekt tvořen.
// Struktura uchovává informace o souřadnicích
// a texturových koordinacích vrcholů trojúhelníka.
struct TTriangle
{
int i_vert_index[3]; // Informace o souřadnicích vrcholů trojúhelníka
int i_coord_index[3]; // Informace o texturové koordinaci trojúhelníka
};
// Struktura informací o materialu. Obsahuje specifické jméno textury a pozici
// obrazového souboru, ze kterého se textura tvoří. Struktura dále obsahuje
// barvu objektu zadanou v R = uc_color[0], G = uc_color[1], B = uc_color[2].
// Další hodnoty se nepoužívaj, ale eventuelně se použít mohou. Jde o informaci
// o dlaždicovitém UV rozložení textury.
struct TMaterial
{
char c_tex_name[255]; // Jméno textury
char c_tex_file_name[255]; // Cesta k obrázku, ze kterého se vytváří textura
unsigned char uc_color[3]; // Barva objektu
int i_tex_id; // the texture ID
float f_u_tile_tex; // "u" poměr dlaždicovitého rozložení textury (nepoužívá se)
float f_v_tile_tex; // "v" poměr dlaždicovitého rozložení textury (nepoužívá se)
float f_u_offset_tex; // "u" odchylky dlaždicovitého rozložení textury (nepoužívá se)
float f_v_offset_tex; // "v" odchylky dlaždicovitého rozložení textury (nepoužívá se)
};
// Struktura informací o objektu.
struct TObject
{
int i_num_vert; // Počet vrcholů objektu
int i_num_triangle; // Počet trojúhelníků objektu
int i_num_tex_coord; // Počet texturových koordinací objektu
unsigned ui_material_id; // Identifikátor materiálu (textury) objektu
bool b_texture; // Určuje zda je zapnuto, nebo vypnuto texturování objektu
char c_name[255]; // Jméno objektu
K_CVector *pvec_vert; // Vrcholy objektů
K_CVector *pvec_normal; // Normálové vektory vrcholů objektů
TVector2d *ptvec2d_coord; // Texturová koordinace objektů
TTriangle *pttriangle; // Informace o trojúhelnících
};
// Struktura informací o modelu. Model je tvořen z jednoho, nebo několika objektů.
struct TModel
{
int i_num_objects; // Počet objektů modelu
int i_num_materials; // Počet materiálů modelu
vector<
TMaterial> vtmaterial; // Seznam informací o materiálech (textury a barvy)
vector<
TObject> vtobject; // Seznam informací o objektech modelu
};
|
Načítání souboru 3DS provádí funkce Load3ds(),
která volá další pomocné funkce.
|
|
// Načti soubor *.3ds
bool K_CLoad3ds::Load3ds( TModel *ptmodel, char *pc_filename )
{
char pc_msg[255] = {0}; // Pomocná proměnná která bude obsahovat text případné chybové hlášky
// Otevři soubor 3DS
pfileStream = fopen( pc_filename, "rb" );
// Jestliže se soubor nepodařilo otevřít, tak vyhoď chybovou hlášku
if( !pfileStream )
{
sprintf( pc_msg, "Unable to find the file: %s!", pc_filename );
MessageBox(NULL, pc_msg, "Error", MB_OK);
return false;
}
// Zkus číst první větev souboru, která obsahuje primární informace o identifikátoru souboru a jeho délce v bytech, jinak
if( !ReadChunk(ptchunkCurrent ))
{
return false; // Vyskoč z této funkce
}
// Jestliže id aktuální větve neodpovídá definované struktuře PRIMARY, tak vyhoď chybovou hlášku
if ( ptchunkCurrent->ui_id != PRIMARY )
{
sprintf( pc_msg, "Unable to load PRIMARY chuck from file: %s!", pc_filename );
MessageBox( NULL, pc_msg, "Error", MB_OK );
return false;
}
// Začni načítat všechny hodnoty objektů
ProcessNextChunk( ptmodel, ptchunkCurrent );
// Vypočítej normálové vektory trojúhelníků, pro správné stínování
ComputeNormals( ptmodel );
// Vymaž všechny pomocné hodnoty z paměti a zavři obraz načítaného souboru
CleanUp();
return 0;
}
|
Funkce Load3ds() volá pomocnou funkci
ProcessNextChunk(), která načte
všechny hodnoty.
|
|
// Začni načítat všechny hodnoty objektů. Tato funkce je volána rekurzivně, to znamená z vnějšku vlastní funkce
bool K_CLoad3ds::ProcessNextChunk( TModel *ptmodel, TChunk *ptchunk_prev )
{
TObject tobject_new = {0}; // Nová struktura objektu, která se používá se pro přídání seznamu objektů
TMaterial tmaterial_new = {0}; // Nová struktura materiálu, která se používá se pro přídání seznamu materiálů
unsigned short ui_version = 0; // Proměnná uchovává verzi souboru
int pi_buffer[50000] = {0}; // Proměnná uchovává načtená data
ptchunkCurrent = new TChunk; // Alokuj novou větev
// Cykluj pokud množství bytů potřebných pro čtení větve je menší než délka větve
while ( ptchunk_prev->ui_bytes_read <
ptchunk_prev->ui_length )
{
// Zkus číst další větev, jinak
if( !ReadChunk( ptchunkCurrent ))
{
return false; // Vyskoč z funkce
}
// Zkontroluj ID větve
switch ( ptchunkCurrent->ui_id )
{
case VERSION: // Informace o verzi souboru
// Čti verzi souboru
ptchunkCurrent->ui_bytes_read += fread( &ui_version, 1, ptchunkCurrent->ui_length - ptchunkCurrent->ui_bytes_read, pfileStream);
// Pokud je verze vyšší než 3, tak vyhoď chybovou hlášku
if ( ui_version > 0x03 )
MessageBox( NULL, "This 3DS file is over version 3 so it may load incorrectly", "Warning", MB_OK );
break; // Vyskoč z větve
case OBJECTINFO: // Obsahuje informace o skupině objektů. Objekt se nazývá též "Mesh"
// Zkus číst další větev, jinak
if( !ReadChunk( ptchunkTemp ))
{
return false; // Vyskoč z funkce
}
// Získej verzi objektu
ptchunkTemp->ui_bytes_read += fread( &ui_version, 1, ptchunkTemp->ui_length - ptchunkTemp->ui_bytes_read, pfileStream );
// Přidej k velikosti načítaných dat, velikost dat informace o skupině objektů
ptchunkCurrent->ui_bytes_read += ptchunkTemp->ui_bytes_read;
// Běž na další větev. Volej (rekurzivně) funkci, ve které se nacházíš
ProcessNextChunk( ptmodel, ptchunkCurrent );
break; // Vyskoč z větve
case MATERIAL: // Obsahuje informace o materiálu
// Přičti počet materiálů
ptmodel->i_num_materials++;
// Použij funkci STL "vector" třídy, která přidá novou strukturu materiálu do seznamu
ptmodel->vtmaterial.push_back( tmaterial_new );
// Načti hodnoty materiálu
ProcessNextMaterialChunk( ptmodel, ptchunkCurrent );
break; // Vyskoč z větve
case OBJECT: // Obsahuje informace o trojúhelnících, vrcholech, atd
// Přičti počet objektů
ptmodel->i_num_objects++;
// Použij funkci STL "vector" třídy, která přidá novou strukturu objektu do seznamu
ptmodel->vtobject.push_back( tobject_new );
// Vyplň druhým parametrem "0" obsah paměti, který je daný sizeof()
memset( &( ptmodel->vtobject[ptmodel->i_num_objects - 1] ), 0, sizeof( TObject ));
// Získej jméno objektu. Množství bytů jména objektu přidej k bytům aktuální větve.
// GetString() čte data (jméno objektu) a vráťí délku celého řetězce
ptchunkCurrent->ui_bytes_read += GetString( ptmodel->vtobject[ptmodel->i_num_objects - 1].c_name );
// Načti všechny hodnoty objektu
ProcessNextObjectChunk( ptmodel, &( ptmodel->vtobject[ptmodel->i_num_objects - 1] ), ptchunkCurrent );
break; // Vyskoč z větve
case EDITKEYFRAME: // Obsahuje infomace o animaci modelu
// Tyto data nebudem načítát. Jde o infomace o animaci objektu.
// Umíme již načítat animované souboru *.ms3d, takže to
// ani potřebovat nebudeme
// Přidej k množství bytů původní větve, množství bytů této větve
ptchunkCurrent->ui_bytes_read += fread( pi_buffer, 1, ptchunkCurrent->ui_length - ptchunkCurrent->ui_bytes_read, pfileStream );
break; // Vyskoč z větve
default: // Pokud žádná větev neodpovídá, provádí se tato základní
// Přidej k množství bytů původní větve, množství bytů ignorované, nebo neznámé větve
ptchunkCurrent->ui_bytes_read += fread( pi_buffer, 1, ptchunkCurrent->ui_length - ptchunkCurrent->ui_bytes_read, pfileStream );
break; // Vyskoč z větve
}
// Přidej k množství bytů původní větve, množství bytů větve aktuální
ptchunk_prev->ui_bytes_read += ptchunkCurrent->ui_bytes_read;
}
delete ptchunkCurrent; // Uvolni paměť aktuální větve
ptchunkCurrent = ptchunk_prev; // Nastav původní větev jako aktuální
return true; // Pokud jsi došel až sem, tak vrať funkci true
}
|
Funkce ProcessNextChunk()
opět volá další pomocné funkce, které finálně načítají hodnoty.
Pomocná funkce ReadChunk()
čte hodnoty identifikátorů (ID) aktuální větve a její délku.
|
|
// Čti větev souboru
bool K_CLoad3ds::ReadChunk( TChunk *ptchunk )
{
// Čti 2 bytovou informaci o ID souboru
ptchunk->ui_bytes_read = fread( &ptchunk->ui_id, 1, 2, pfileStream );
// Čti 4 bytovou informaci o délce souboru
ptchunk->ui_bytes_read += fread( &ptchunk->ui_length, 1, 4, pfileStream );
if( !ptchunk->ui_bytes_read ) // Jestliže je špatný formát *.3ds, tak
{
MessageBox( NULL, "Invalid file format *.3ds", "Error", MB_OK | MB_ICONERROR ); // Zobraz chybovou zprávu a
return false; // Ukonči funkci
}
return true; // Pokud vše proběhne OK, tak vrať funkci true
}
|
Pomocná funkce ProcessNextMaterialChunk()
čte hodnoty materiálu.
|
|
// Načti hodnoty materiálu. Tato funkce je volána rekurzivně, to znamená z vnějšku vlastní funkce
bool K_CLoad3ds::ProcessNextMaterialChunk( TModel *ptmodel, TChunk *ptchunk_prev )
{
int pi_buffer[50000] = {0}; // Proměnná uchovává načtená data
ptchunkCurrent = new TChunk; // Alokuj novou větev
// Cykluj pokud množství bytů potřebných pro čtení větve je menší než délka větve
while ( ptchunk_prev->ui_bytes_read <
ptchunk_prev->ui_length )
{
// Zkus číst další větev, jinak
if( !ReadChunk( ptchunkCurrent ))
{
return false; // Vyskoč z funkce
}
// Zkontroluj ID větve
switch ( ptchunkCurrent->ui_id )
{
case MATNAME: // Obsahuje informace o jménu materiálu
// Čti jméno textury
ptchunkCurrent->ui_bytes_read += fread( ptmodel->vtmaterial[ptmodel->i_num_materials - 1].c_tex_name, 1, ptchunkCurrent->ui_length - ptchunkCurrent->ui_bytes_read, pfileStream );
break; // Vyskoč z větve
case MATDIFFUSE: // Obsahuje info o barvě RGB materiálu
// Čti hodnoty barvy RGB materiálu
ReadColorChunk( &( ptmodel->vtmaterial[ptmodel->i_num_materials - 1] ), ptchunkCurrent );
break; // Vyskoč z větve
case MATMAP: // Hlavička pro nový materiál
// Načti další informace o novém materiálu. Volej funkci, ve které se nacházíš (rekurzivní volání)
ProcessNextMaterialChunk( ptmodel, ptchunkCurrent );
break; // Vyskoč z větve
case MATMAPFILE: // Info o jménu textury
// Načti jméno textury
ptchunkCurrent->ui_bytes_read += fread( ptmodel->vtmaterial[ptmodel->i_num_materials - 1].c_tex_file_name, 1, ptchunkCurrent->ui_length - ptchunkCurrent->ui_bytes_read, pfileStream );
break; // Vyskoč z větve
default: // Pokud žádná větev neodpovídá, provádí se tato základní
// Přidej k množství bytů původní větve, množství bytů ignorované, nebo neznámé větve
ptchunkCurrent->ui_bytes_read += fread( pi_buffer, 1, ptchunkCurrent->ui_length - ptchunkCurrent->ui_bytes_read, pfileStream );
break; // Vyskoč z větve
}
// Přidej k množství bytů původní větve, množství bytů větve aktuální
ptchunk_prev->ui_bytes_read += ptchunkCurrent->ui_bytes_read;
}
delete ptchunkCurrent; // Uvolni paměť aktuální větve
ptchunkCurrent = ptchunk_prev; // Nastav původní větev jako aktuální
return true; // Pokud jsi došel až sem, tak vrať funkci true
}
|
Pomocná funkce
ProcessNextObjectChunk()
čte hodnoty objektu.
|
|
// Načti hodnoty objektu. Tato funkce je volána rekurzivně, to znamená z vnějšku vlastní funkce
bool K_CLoad3ds::ProcessNextObjectChunk( TModel *ptmodel, TObject *ptobject, TChunk *ptchunk_prev )
{
int pi_buffer[50000] = {0}; // Proměnná uchovává načtená data
// Alokuj novou větev
ptchunkCurrent = new TChunk;
// Cykluj pokud množství bytů potřebných pro čtení větve je menší než délka větve
while ( ptchunk_prev->ui_bytes_read <
ptchunk_prev->ui_length )
{
// Zkus číst další větev, jinak
if( !ReadChunk( ptchunkCurrent ))
{
return false; // Vyskoč z funkce
}
// Zkontroluj ID větve
switch ( ptchunkCurrent->ui_id )
{
case OBJECT_MESH: // Info, zda se má vytvořit nová "Mesh", nový objekt
// Jestliže jsi našel nový objekt tak volej znovu vlastní funkci. Rekurzivní volání.
ProcessNextObjectChunk( ptmodel, ptobject, ptchunkCurrent );
break; // Vyskoč z větve
case OBJECT_VERTICES: // Vrcholy objektu
// Čti hodnoty vrcholů objektu
ReadVertices( ptobject, ptchunkCurrent );
break; // Vyskoč z větve
case OBJECT_TRIANGLES: // Trojúhelníky objektu
// Čti hodnoty trojúhelníků objektu
ReadTriangles( ptobject, ptchunkCurrent );
break; // Vyskoč z větve
case OBJECT_MATERIAL: // Info o materiálu, buď je tvořen z textury, nebo barvy
// Čti hodnoty materiálu
ReadObjectMaterial( ptmodel, ptobject, ptchunkCurrent );
break; // Vyskoč z větve
case OBJECT_UV: // UV texturová koordinace
// Čti UV texturové koordinace
ReadUVCoordinates( ptobject, ptchunkCurrent );
break; // Vyskoč z větve
default: // Pokud žádná větev neodpovídá, provádí se tato základní
// Přidej k množství bytů původní větve, množství bytů ignorované, nebo neznámé větve
ptchunkCurrent->ui_bytes_read += fread( pi_buffer, 1, ptchunkCurrent->ui_length - ptchunkCurrent->ui_bytes_read, pfileStream );
break; // Vyskoč z větve
}
// Přidej k množství bytů původní větve, množství bytů větve aktuální
ptchunk_prev->ui_bytes_read += ptchunkCurrent->ui_bytes_read;
}
delete ptchunkCurrent; // Uvolni paměť aktuální větve
ptchunkCurrent = ptchunk_prev; // Nastav původní větev jako aktuální
return true; // Pokud jsi došel až sem, tak vrať funkci true
}
|
Tato funkce opět volá další pomocné funkce.
Pomocná funkce
ReadVertices()
načítá hodnoty vrcholů objektu.
|
|
// Čti hodnoty vrcholů objektu
void K_CLoad3ds::ReadVertices( TObject *ptobject, TChunk *ptchunk_prev )
{
// Načti počet vrcholů
ptchunk_prev->ui_bytes_read += fread( &( ptobject->i_num_vert), 1, 2, pfileStream );
// Alokuj paměť pro všechny vrcholy třídy K_CVector
ptobject->pvec_vert = new K_CVector [ptobject->i_num_vert];
// Vyplň druhým parametrem "0" obsah paměti, který je daný sizeof()
memset( ptobject->pvec_vert, 0, sizeof( K_CVector ) * ptobject->i_num_vert );
// Čti hodnoty pole vrcholů
ptchunk_prev->ui_bytes_read += fread( ptobject->pvec_vert, 1, ptchunk_prev->ui_length - ptchunk_prev->ui_bytes_read, pfileStream );
}
|
Pomocná funkce
ReadTriangles()
načítá hodnoty trojúhelníků objektu.
|
|
// Čti hodnoty trojúhelníků objektu
void K_CLoad3ds::ReadTriangles( TObject *ptobject, TChunk *pchunk_prev )
{
unsigned short ui_index = 0; // Index trojúhelníka
// Čti počet trojúhelníků, které tvoří objekt
pchunk_prev->ui_bytes_read += fread( &ptobject->i_num_triangle, 1, 2, pfileStream );
// Alokuj paměť pro trojúhelníky struktury TTriangle
ptobject->pttriangle = new TTriangle[ptobject->i_num_triangle];
// Vyplň druhým parametrem "0" obsah paměti, který je daný sizeof()
memset( ptobject->pttriangle, 0, sizeof( TTriangle ) * ptobject->i_num_triangle );
// Cykluj dokud nenačteš všechny trojúhelníky
for( int i = 0; i <
ptobject->i_num_triangle; i++ )
{
// Cykluj a načítej 4 hodnoty.
// Potřebujeme však jen 3 hodnoty (trojúhelník ma 3 vrcholy, jak jinak), proto 4 hodnotu načítat nebudeme
for( int j = 0; j <
4; j++ )
{
// Čti index vrcholu trojúhelníka
pchunk_prev->ui_bytes_read += fread(&ui_index, 1, sizeof(ui_index), pfileStream);
// Čti jenom první 3 hodnoty vrcholů, 4 vynech
if(j <
3)
{
// Načti hodnoty vrcholů trojúhelníka
ptobject->pttriangle[i].i_vert_index[j] = ui_index;
}
}
}
}
|
Pomocná funkce
ReadObjectMaterial()
načítá hodnoty materiálů (textur) objektu.
|
|
// Čti hodnoty materiálu
void K_CLoad3ds::ReadObjectMaterial( TModel *ptmodel, TObject *ptobject, TChunk *ptchunk_prev )
{
char pc_tex_name[255] = {0}; // Proměnná uchovává jméno textury
int pi_buffer[50000] = {0}; // Proměnná uchovává načtená data
// Získej jméno textury.
// Množství bytů jména textury přidej k bytům původní větve.
// GetString() čte data (jméno textury) a vráťí délku celého řetězce
ptchunk_prev->ui_bytes_read += GetString( pc_tex_name );
// Cykluj dokud nenačteš všechny materiály (textury)
for( int i = 0; i <
ptmodel->i_num_materials; i++ )
{
// Jestliže půjde načíst jméno textury, tak
if( strcmp( pc_tex_name, ptmodel->vtmaterial[i].c_tex_name ) == 0 )
{
// Nastav ID materiálu (textury)
ptobject->ui_material_id = i;
// Jestliže se podařilo načíst jméno textury
if( strlen( ptmodel->vtmaterial[i].c_tex_file_name ) > 0 )
{
// Tak zapni texturování
ptobject->b_texture = true;
}
break; // Vyskoč z podmínky if
}
else // Jinak
{
// Nastav ID materiálu na -1
ptobject->ui_material_id = -1;
}
}
// Přidej k množství bytů původní větve, množství bytů větve aktuální
ptchunk_prev->ui_bytes_read += fread( pi_buffer, 1, ptchunk_prev->ui_length - ptchunk_prev->ui_bytes_read, pfileStream );
}
|
A máme tu další pomocnou funkci funkce
ReadObjectMaterial() a také funkce ProcessNextChunk().
Jde o funkci GetString(),
která čte data (jméno textury, nebo objektu) a vrací délku celého řetězce
|
|
// Čti data (jméno objektu, nebo textury) a vrať délku celého řetězce
int K_CLoad3ds::GetString( char *pc_buffer )
{
int i = 0; // Pomocná proměnná pro cyklování
// Čti první byt, první písmeno jména objektu, nebo textury
fread( pc_buffer, 1, 1, pfileStream );
// Cykluj dokud neprojedeš celý řetězec znaků jména objektu, nebo textury
while ( *( pc_buffer + i++ ) != 0)
{
// Ulož znak jména objektu, nebo textury do bufferu
fread( pc_buffer + i, 1, 1, pfileStream );
}
// Vrať funkci délku řetězce jména objektu, nebo textury
return strlen( pc_buffer ) + 1;
}
|
Vrátíme se zpět k funkci ProcessNextObjectChunk(), která má ještě jednu pomocnou funkci
ReadUVCoordinates(). Čte hodnoty texturové koordinace.
|
|
// Čti UV texturové koordinace
void K_CLoad3ds::ReadUVCoordinates( TObject *ptobject, TChunk *pchunk_prev )
{
// Načti počet texturových koordinací
pchunk_prev->ui_bytes_read += fread( &ptobject->i_num_tex_coord, 1, 2, pfileStream );
// Alokuj paměť pro všechny texturové koordinace struktury TVector2d
ptobject->ptvec2d_coord = new TVector2d [ptobject->i_num_tex_coord];
// Čti texturové koordinace (pole s hodnotami x, y)
pchunk_prev->ui_bytes_read += fread( ptobject->ptvec2d_coord, 1, pchunk_prev->ui_length - pchunk_prev->ui_bytes_read, pfileStream );
}
|
Teď se vrátíme k počáteční funkci
Load3ds(), která volá pomocnou
funkci
ComputeNormals(). Funkce vypočítá
normálové vektory trojúhelníků, pro správné stínování objektu.
|
|
// Vypočítej normálové vektory trojúhelníků, pro správné stínování
void K_CLoad3ds::ComputeNormals( TModel *ptmodel )
{
K_CVector vec_1, vec_2, vec_normal, vec_vert[3], vec_sum; // Pomocné vektory
int j, k; // Pomocné proměnné cyklů
// Pokud neexistuje žádný objet tak opusť funkci
if( ptmodel->i_num_objects <
= 0 )
return;
// Cykluj podle počtu objektů
for( int i = 0; i <
ptmodel->i_num_objects; i++ )
{
// Získej hodnoty aktuálního objektu
TObject *ptobject = &( ptmodel->vtobject[i] );
// Alokuj paměť pro nenormalizovaný normálový vektor všech trojúhelníků třídy K_CVector
K_CVector *pvec_unnormalize = new K_CVector [ptobject->i_num_triangle];
// Alokuj paměť normálový vektor všech vrcholů trojúhelníků třídy K_CVector
ptobject->pvec_normal = new K_CVector [ptobject->i_num_vert];
// Cykluj podle počtu trojúhelníků
for( j = 0; j <
ptobject->i_num_triangle; j++ )
{
// Získej pozici x, y, z každého vrcholu trojúhelníka
vec_vert[0] = ptobject->pvec_vert[ptobject->pttriangle[j].i_vert_index[0]]; // První vrchol
vec_vert[1] = ptobject->pvec_vert[ptobject->pttriangle[j].i_vert_index[1]]; // Druhý vrchol
vec_vert[2] = ptobject->pvec_vert[ptobject->pttriangle[j].i_vert_index[2]]; // Třetí vrchol
// Vypočítej vektor mezi prvním a třetím vrcholem
vec_1.VectorBetween( vec_vert[0], vec_vert[2] );
// Vypočítej vektor mezi třetí a druhým vrcholem
vec_2.VectorBetween( vec_vert[2], vec_vert[1] );
// Vypočítej kolmý vektor na vektory vec_1 a vec_2
vec_normal.Cross( vec_1, vec_2 );
// Výsledek ulož do nenormalizovaného vektoru
pvec_unnormalize[j] = vec_normal;
}
vec_sum.SetXYZ(0.0, 0.0, 0.0); // Resetuj vektor součtu všech vektorů
int i_divider = 0; // Dělitel nastav na nulu
// Cykluj podle počtu vrcholů
for ( j = 0; j <
ptobject->i_num_vert; j++ )
{
// Cykluj podle počtu trojúhelníků
for ( k = 0; k <
ptobject->i_num_triangle; k++ )
{
// Jestliže index vrcholu souhlasí s aktuální hodnotou počtu vrcholů, tak
if ( ptobject->pttriangle[k].i_vert_index[0] == j ||
ptobject->pttriangle[k].i_vert_index[1] == j ||
ptobject->pttriangle[k].i_vert_index[2] == j )
{
// Přidej nenormalizovaný normálový vektor k hodnotě vektoru součtu všech vektorů
vec_sum.AddVector(vec_sum, pvec_unnormalize[k]);
// Přidej hodnotu dělitele. Dělitel bude mít nakonec hodnotu počtu trojúhelníků
i_divider++;
}
}
// Získej normálový vektor dělením vektoru součtu všech vektorů dělitelem
ptobject->pvec_normal[j].DivideVector( vec_sum, -i_divider );
// Normalizuj výsledný vektor
ptobject->pvec_normal[j].Normalize();
vec_sum.SetXYZ(0.0, 0.0, 0.0); // Resetuj vektor součtu všech vektorů
i_divider = 0; // Dělitel nastav na nulu
}
// Uvolni paměť obsahující nenormalizovaný vektor
delete [] pvec_unnormalize;
}
}
|
Poslední pomocná funkce je
CleanUp()
která vymaže všechny pomocné hodnoty z paměti
a zavře obraz načítaného souboru.
|
|
// Vymaž všechny pomocné hodnoty z paměti a zavři obraz načítaného souboru
void K_CLoad3ds::CleanUp()
{
fclose( pfileStream ); // Zavři obraz načítaného soubor
delete ptchunkCurrent; // Uvolni data aktuální větve
delete ptchunkTemp; // Uvolni data dočasného skladu
}
|
Nejdříve konstruktor třídy
K_CModel3ds
načte hodnoty z 3DS souboru Load3ds()
a pak v cyklu generuje texturu pro všechny objekty modelu pomocí
funkce LoadGLTexture(). Na tuto funkci mrkneme později.
|
|
// Konstruktor
K_CModel3ds::K_CModel3ds( char *pc_filename )
{
int i; // Pomocná proměnná cyklů
char pc_path[270]; // Pomocný řetězec znaků cesty k souboru
char c_drive[_MAX_DRIVE]; // Informace na kterém disku je uložen načítaný soubor
char c_dir[_MAX_DIR]; // Informace ve kterém adresáři je uložen načítaný soubor
char c_fname[_MAX_FNAME]; // Informace o jménu načítaného souboru
char c_ext[_MAX_EXT]; // Informace o příponě načítaného souboru
_splitpath( pc_filename, c_drive, c_dir, c_fname, c_ext ); // Získej všechny tyto informace
// Cykluj, podle počtu textur, nastav všechny textury na 0
for( i = 0; i <
MAX_TEXTURES; i++)
{
puiTexture[i] = 0; // Nastav texturu na 0
}
tmodelInfo.i_num_materials = 0; // Nastav počet materiálů na 0
tmodelInfo.i_num_objects = 0; // Nastav počet objektů na 0
// Načti všechny hodnoty modelu ze souboru *.3ds
Load3ds( &tmodelInfo, pc_filename );
// Cykluj podle počtu materiálů a načítej textury
for( i = 0; i <
tmodelInfo.i_num_materials; i++ )
{
// Jestliže existuje jméno textury z načteného souboru, tak textury načti
if( strlen( tmodelInfo.vtmaterial[i].c_tex_file_name ) > 0 )
{
strcpy( pc_path, c_dir ); // Ulož do cesty jméno adresáře
strcat( pc_path, tmodelInfo.vtmaterial[i].c_tex_file_name ); // Přidej k adresáři jméno souboru
puiTexture[i] = texJpg.LoadGLTexture( pc_path ); // Načti texturu
}
// Nastav ID textury
tmodelInfo.vtmaterial[i].i_tex_id = i;
}
}
|
Nyní, když známe všechny potřebné hodnoty, tak je zobrazíme.
Funkce Render()
zobrazí model pomocí trojúhelníků
|
|
// Vykresli objekt pomocí trojúhelníků
void K_CModel3ds::Render()
{
TObject *ptobject; // Struktura objektu
BYTE *pby_color; // Barva materiálu
// Cykluj podle počtu objektů
for( int i = 0; i <
tmodelInfo.i_num_objects; i++ )
{
// Jestliže objekt neexistuje, tak vyskoč z cyklu
if( tmodelInfo.vtobject.size() <=
0 ) break;
// Získej aktuální strukturu objektu
ptobject = &tmodelInfo.vtobject[i];
// Jestliže je zapnuto texturování objektu
if( ptobject->b_texture )
{
// Zapni texturování
glEnable( GL_TEXTURE_2D );
// Nastav bílou barvu
glColor3ub( 255, 255, 255 );
// Aktivuj texturu
glBindTexture( GL_TEXTURE_2D, puiTexture[ptobject->ui_material_id] );
}
else // Jinak
{
// Vypni texturováni
glDisable( GL_TEXTURE_2D );
// Nastav bílou barvu
glColor3ub( 255, 255, 255 );
}
glBegin( GL_TRIANGLES ); // Začni vykreslovat trojúhelníky
// Projdi všechny trojúhelníky objektu
for( int j = 0; j <
ptobject->i_num_triangle; j++ )
{
// Projdi všechny tři vrcholy trojúhelníka
for( int k = 0; k <
3; k++ )
{
// Získej indexy všech vrcholů všech trojúhelníků
int index = ptobject->pttriangle[j].i_vert_index[k];
// Nastav normálový vektor pro každý vrchol. Prohoď Y a Z souřadnici.
glNormal3f( ptobject->pvec_normal[ index ][0], ptobject->pvec_normal[ index ][2], ptobject->pvec_normal[ index ][1]);
// Jestliže je zapnuto texturování objektu
if( ptobject->b_texture )
{
// Jestliže je existuje hodnota texturové koordinace, tak
if( ptobject->ptvec2d_coord )
{
// Nastav texturovou koordinaci
glTexCoord2f( ptobject->ptvec2d_coord[ index ].pf[0], ptobject->ptvec2d_coord[ index ].pf[1] );
}
}
else // Jinak nastav barvu objektu
{
// Jestliže existují hodnoty materiálu a ID materiálu, tak
if( tmodelInfo.vtmaterial.size() && ptobject->ui_material_id >= 0 )
{
// Získej barvu materiálu
pby_color = tmodelInfo.vtmaterial[ptobject->ui_material_id].uc_color;
// Nastav aktuální barvu objektu
glColor3ub(pby_color[0], pby_color[1], pby_color[2]);
}
}
// Vykresli vrcholy trojúhelníků. Prohoď Y a Z souřadnici
glVertex3f( ptobject->pvec_vert[ index ][0], ptobject->pvec_vert[ index ][2], ptobject->pvec_vert[ index ][1]);
}
}
glEnd(); // Ukonči vykreslování trojúhelníků
}
}
|
Funkce RenderLine() vykresluje model pomocí čar
|
|
// Vykresli objekt pomocí čar
void K_CModel3ds::RenderLine()
{
TObject *ptobject; // Struktura objektu
BYTE *pby_color; // Barva materiálu
// Cykluj podle počtu objektů
for( int i = 0; i <
tmodelInfo.i_num_objects; i++ )
{
// Jestliže objekt neexistuje, tak vyskoč z cyklu
if( tmodelInfo.vtobject.size() <=
0 ) break;
// Získej aktuální strukturu objektu
ptobject = &tmodelInfo.vtobject[i];
// Jestliže je zapnuto texturování objektu
if( ptobject->b_texture )
{
// Zapni texturování
glEnable( GL_TEXTURE_2D );
// Nastav bílou barvu
glColor3ub( 255, 255, 255 );
// Aktivuj texturu
glBindTexture( GL_TEXTURE_2D, puiTexture[ptobject->ui_material_id] );
}
else // Jinak
{
// Vypni texturování
glDisable( GL_TEXTURE_2D );
// Nastav bílou barvu
glColor3ub( 255, 255, 255 );
}
// Projdi všechny trojúhelníky objektu
for( int j = 0; j <
ptobject->i_num_triangle; j++ )
{
glBegin( GL_LINE_LOOP ); // Začni vykreslovat čáry
// Projdi všechny tři vrcholy trojúhelníka
for( int k = 0; k <
3; k++ )
{
// Získej indexy všech vrcholů všech trojúhelníků
int index = ptobject->pttriangle[j].i_vert_index[k];
// Nastav normálový vektor pro každý vrchol. Prohoď Y a Z souřadnici
glNormal3f( ptobject->pvec_normal[ index ][0], ptobject->pvec_normal[ index ][2], ptobject->pvec_normal[ index ][1]);
// Jestliže je zapnuto texturování objektu
if( ptobject->b_texture )
{
// Jestliže je existuje hodnota texturové koordinace, tak
if( ptobject->ptvec2d_coord )
{
// Nastav texturovou koordinaci
glTexCoord2f( ptobject->ptvec2d_coord[ index ].pf[0], ptobject->ptvec2d_coord[ index ].pf[1] );
}
}
else
{
// Jestliže existují hodnoty materiálu a ID materiálu, tak
if( tmodelInfo.vtmaterial.size() && ptobject->ui_material_id >= 0 )
{
// Získej barvu materiálu
pby_color = tmodelInfo.vtmaterial[ptobject->ui_material_id].uc_color;
// Nastav aktuální barvu objektu
glColor3ub(pby_color[0], pby_color[1], pby_color[2]);
}
}
// Vykresli vrcholy trojúhelníků. Prohoď Y a Z souřadnici
glVertex3f( ptobject->pvec_vert[ index ][0], ptobject->pvec_vert[ index ][2], ptobject->pvec_vert[ index ][1] );
}
glEnd(); // Ukonči vykreslování čar
}
}
}
|
Všimněte se, že při vykreslování normálových vektorů a vrcholů jsou
prohozeny souřadnice Y a Z. Proč? Protože OpenGL a 3DStudioMax mají rozdílný
souřadnicový systém. Osa Y v OpenGL, je v 3DStudiuMax osa Z.
|
|
// Nastav normálový vektor pro každý vrchol. Prohoď Y a Z souřadnici
glNormal3f( ptobject->pvec_normal[ index ][0], ptobject->pvec_normal[ index ][2], ptobject->pvec_normal[ index ][1]);
// Vykresli vrcholy trojúhelníků. Prohoď Y a Z souřadnici
glVertex3f( ptobject->pvec_vert[ index ][0], ptobject->pvec_vert[ index ][2], ptobject->pvec_vert[ index ][1] );
|
Funkce pro vykreslování 3DS modelů se volají
v nekonečné smyčce funkcí DrawScene()
třídy K_CScene
|
|
// Vykresli scénu
void K_CScene::DrawScene( void )
{
...
...
...
glPushMatrix(); // Začátek oddělené matice
glTranslatef(0.0f, -50.0f, 0.0f); // Posuň model o 50 dolu
pmodel3ds->Render(); // Vykresli 3DS model pomocí trojúhelníků
glTranslatef(50.0f, 0, 0); // Posuň model o 50 doprava
pmodel3ds->RenderLine(); // Vykresli 3DS model pomocí čar
glPopMatrix(); // Konec oddělené matice
...
...
...
}
|
Samozřejmě musíme 3DS model před vykreslováním vytvořit.
To provádí funkce InitScene() třídy K_CScene
|
|
K_CModel3ds *pmodel3ds; // 3D Studio Max model (*.3ds)
// Načti scénu
void K_CScene::InitScene( void )
{
...
...
...
pmodel3ds = new K_CModel3ds("models/teapot/teapot.3ds"); // Alokuj paměť pro 3DS model a zároveň ho načti
...
...
...
}
|
Načítání textur provádí funkce
LoadGLTexture() třídy K_CTexture.
Tato funkce zjistí, jaký má načítaný soubor příponu a podle přípony
pak volá potřebné funkce.
|
|
// Vytvoř textury načtením z obrazových souborů (*.tga, *.bmp, *.jpg)
GLuint K_CTexture::LoadGLTexture( char *pc_filename )
{
TTexture texture; // Lokální proměnná struktury TTexture
char c_drive[_MAX_DRIVE]; // Informace na kterém disku je uložen načítaný soubor
char c_dir[_MAX_DIR]; // Informace ve kterém adresáři je uložen načítaný soubor
char c_fname[_MAX_FNAME]; // Informace o jménu načítaného souboru
char c_ext[_MAX_EXT]; // Informace o příponě načítaného souboru
_splitpath( pc_filename, c_drive, c_dir, c_fname, c_ext ); // Získej všechny tyto informace
///////////////////////////////
// Načítání ze souboru *.tga //
///////////////////////////////
...
...
...
///////////////////////////////
// Načítání ze souboru *.bmp //
///////////////////////////////
...
...
...
///////////////////////////////
// Načítání ze souboru *.jpg //
///////////////////////////////
// Převeď řetězce na malá písmena a porovnej jestli má načítaný soubor příponu *.jpg
if ( !strcomp( c_ext, ".jpg" ))
{
tImageJPG *pimage; // Vytvoř obraz textury
pimage = NULL; // Obraz textury nastav na NULL
pimage = LoadJPG( pc_filename ); // Načti obraz textury ze souboru *.jpg
// Jestliže existuje obraz textury
if ( pimage != NULL && pimage->data != NULL )
{
// Vytvoř texturu
glGenTextures(1, &texture.i_tex_id );
// Aktivuj texturu
glBindTexture( GL_TEXTURE_2D, texture.i_tex_id );
// Lineárně rozlož (roztáhni) texturu do celého objektu, pokud je textura menší nez plocha objektu
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// Lineárně rozlož (zmenši) texturu, pokud je textura větší než plocha objektu
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Nastav parametry textury
glTexImage2D(GL_TEXTURE_2D, 0, 3, pimage->sizeX, pimage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, pimage->data);
}
else // Jinak
{
return 0; // Vyskoč z funkce
}
if ( pimage->data ) // Jestliže existuje obraz textury
{
// Vymaž obraz textury z paměti
free( pimage->data );
free( pimage );
}
return texture.i_tex_id; // Vrať funkci hodnotu identifikátoru texturu
}
return 0; // Pokud se dostaneš az sem, tak vyskoč z funkce
}
|
Načítání textur ze souboru (*.jpg) provádí pomocná funkce
LoadJPG()
|
|
// Načti JPG formát
tImageJPG *K_CTexture::LoadJPG( char *pc_filename )
{
struct jpeg_decompress_struct tjpg_info; // Struktura hodnot pro dekompresi jpg
tImageJPG *ptimage_data = NULL; // Ukazatel na strukturu JPG hodnot nastav na NULL
FILE *pfile_jpg; // Obraz souboru *.jpg
// Otevři soubor *.jpg
pfile_jpg = fopen( pc_filename, "rb" );
// Pokud se obraz souboru nepodařilo otevřít, tak
if( pfile_jpg == NULL )
{
K_CViewValue vie; // Aktivuj třídu zobrazení kontrolních hodnot
vie.ErrorMsgChar( "Could not open texture file", pc_filename ); // Zobraz chybovou hlášku
return NULL; // Vyskoč z funkce
}
// Vytvoř ovladač hodnot pro JPG chybové zprávy
jpeg_error_mgr jpg_error;
// Získej adresu ovladače JPG chybových zpráv
tjpg_info.err = jpeg_std_error( &jpg_error );
// Vytvoř dekomprimační objekt
jpeg_create_decompress( &tjpg_info );
// Načti hodnoty z obrazu souboru
jpeg_stdio_src( &tjpg_info, pfile_jpg );
// Alokuj paměť pro strukturu JPG formátu
ptimage_data = ( tImageJPG* )malloc( sizeof( tImageJPG ));
// Dekóduj JPG formát
DecodeJPG( &tjpg_info, ptimage_data );
// Uvolni pamět obsahující strukturu hodnot JPG formátu
jpeg_destroy_decompress( &tjpg_info );
// Zavři obraz souboru *.jpg
fclose( pfile_jpg );
// Otoč texturu aby nebyla vzhůru nohama
if( ptimage_data->data )
{
unsigned char* puc_turn; // Pomocná proměnná pro otočení
int i_x, i_y; // Pomocné proměnné pro x, y hodnoty pixelů textury
// Alokuj paměť pro hodnoty otočení
puc_turn = ( unsigned char* ) malloc( ptimage_data->rowSpan * ptimage_data->sizeY );
// Cykluj podle velikosti počtu řádků na Y souřadnici
for( i_y = 0; i_y <
ptimage_data->sizeY; i_y++ )
{
// Cykluj podle velikosti počtu na X souřadnici
for( i_x = 0; i_x <
ptimage_data->rowSpan; i_x++)
{
// Prohoď hodnoty, aby došlo k otočení textury
puc_turn[i_x + i_y * ptimage_data->rowSpan] = ptimage_data->data[i_x + ( ptimage_data->sizeY - 1 - i_y ) * ptimage_data->rowSpan];
}
}
free( ptimage_data->data ); // Uvolni paměť obsahující strukturu JPG hodnot
ptimage_data->data = puc_turn; // A do této paměti ulož nová data otočené textury
}
// Vrať funkci výsledná data vytvořené textury
return ptimage_data;
}
|
Protože je JPG formát komprimovaný, musíme formát dekomprimovat.
To provádí pomocná funkce DecodeJPG()
|
|
// Dekóduj JPG formát
void K_CTexture::DecodeJPG( jpeg_decompress_struct* ptjpg_info, tImageJPG *ptimage_data )
{
// Čti hlavičku souboru
jpeg_read_header( ptjpg_info, TRUE );
// Rozjeď dekomprimaci jpeg formátu
jpeg_start_decompress( ptjpg_info );
// Získej informace o souboru
ptimage_data->rowSpan = ptjpg_info->image_width * ptjpg_info->num_components; // Délka řádku obrázku
ptimage_data->sizeX = ptjpg_info->image_width; // Šířka obrázku
ptimage_data->sizeY = ptjpg_info->image_height; // Výška obrázku
// Alokuj paměť pro pixelový zásobník
ptimage_data->data = new unsigned char[ptimage_data->rowSpan * ptimage_data->sizeY];
// Vytvoř pole ukazatelů na řádek obrázku
unsigned char** ppuc_row = new unsigned char*[ptimage_data->sizeY];
// Cykluj podle velikosti řádku
for ( int i = 0; i <
ptimage_data->sizeY; i++ )
ppuc_row[i] = &( ptimage_data->data[i * ptimage_data->rowSpan] ); // Získej pixelová data řádku
int rows_read = 0; // Nastav počet načítaných řádků na 0
// Cykluj dokud neprojedeš všechny řádky
while ( ptjpg_info->output_scanline <
ptjpg_info->output_height )
{
// Čti hodnoty pixelů v aktuální řadce a nakonec přičti další řadek
rows_read += jpeg_read_scanlines( ptjpg_info, &ppuc_row[rows_read], ptjpg_info->output_height - rows_read );
}
// Vymaž pole ukazatelů na řádek obrázku
delete [] ppuc_row;
// Dokonči dekompresi jpeg fomátu
jpeg_finish_decompress( ptjpg_info );
}
|
A je hotovo.
Příště si něco řekneme o přehrávání muziky pomocí knihovny FMOD.
Kwan
|
|
Home