unit Model;
interface
uses
OpenGL,
Matrix,
Windows,
Textures,
SysUtils;
const
MS_MAX_NAME = 32;
MS_MAX_PATH = 256;
type
clsVec = class
public
x,y,z : single;
w : single;
u,v : single;
bone : integer;
procedure transform( m : clsMatrix );
procedure transform3( m : clsMatrix );
end;
type pclsVec = ^clsVec;
type
clsTri = class
public
v : array [0..2] of integer;
n : array [0..2] of integer;
end;
type pclsTri = ^clsTri;
type
clsNormal = class
public
x,y,z : single;
end;
type pclsNormal = ^clsNormal;
type
clsShape = class
public
num_vertices : integer;
vertices : array of clsVec;
num_triangles : integer;
triangles : array of clsTri;
num_normals : integer;
normals : array of clsNormal;
constructor create();
function loadFromFile( filename : string ) : boolean;
function loadFromMs3dAsciiSegment(var ifile : TextFile) : boolean;
function saveToFile( filename : string ) : boolean;
procedure render();
end;
type pclsShape = ^clsShape;
type
clsMaterial = class
public
constructor create();
function loadFromMs3dAsciiSegment(var ifile : TextFile ) : boolean;
procedure activate();
procedure reloadTexture();
private
Name : string;
Ambient : array [0..3] of single;
Diffuse : array [0..3] of single;
Specular : array [0..3] of single;
Emissive : array [0..3] of single;
Shininess : single;
Transparency : single;
DiffuseTexture : string;
AlphaTexture : string;
textexture : GLuint;
end;
type pclsMaterial = ^clsMaterial;
type
clsKeyFrame = class
public
Time : single;
Value : array [0..2] of single;
end;
type pclsKeyFrame = ^clsKeyFrame;
type
clsBone = class
public
Name : string;
ParentName : string;
Parent : ^clsBone;
startPosition : array [0..2] of single;
startRotation : array [0..2] of single;
m_relative : clsMatrix;
m_final : clsMatrix;
NumPositionKeys : integer;
PositionKeyFrames : array of clsKeyFrame;
NumRotationKeys : integer;
RotationKeyFrames : array of clsKeyFrame;
constructor create();
function loadFromMs3dAsciiSegment(var ifile : TextFile ) : boolean;
procedure render();
procedure fixup();
procedure advanceTo( CurrentTime : single );
procedure initialize();
end;
type pclsBone = ^clsBone;
type
clsModel = class
public
MaxTime : single;
CurrentTime : single;
num_shapes : integer;
shapes : array of clsShape;
material_indices : array of integer;
num_materials : integer;
materials : array of clsMaterial;
num_bones : integer;
bones : array of clsBone;
constructor create();
function loadFromMs3dAsciiFile( filename : string ) : boolean;
procedure reloadTextures();
procedure render();
procedure renderBones();
function linkBones() : boolean;
procedure initializeBones();
procedure advanceAnimation( deltaTime : single );
procedure attachSkin();
end;
implementation
procedure glBindTexture(target: GLenum; texture: GLuint); stdcall; external opengl32;
procedure clsVec.transform(m: clsMatrix);
var matrix : array [0..15] of single;
vector : array [0..3] of single;
begin
m.getMatrix(matrix);
vector[0] := x*matrix[0]+y*matrix[4]+z*matrix[8]+matrix[12];
vector[1] := x*matrix[1]+y*matrix[5]+z*matrix[9]+matrix[13];
vector[2] := x*matrix[2]+y*matrix[6]+z*matrix[10]+matrix[14];
vector[3] := x*matrix[3]+y*matrix[7]+z*matrix[11]+matrix[15];
x := vector[0];
y := vector[1];
z := vector[2];
w := vector[3];
end;
procedure clsVec.transform3(m: clsMatrix);
var matrix : array [0..15] of single;
vec : array [0..2] of single;
begin
m.getMatrix(matrix);
vec[0] := x*matrix[0]+y*matrix[4]+z*matrix[8];
vec[1] := x*matrix[1]+y*matrix[5]+z*matrix[9];
vec[2] := x*matrix[2]+y*matrix[6]+z*matrix[10];
x := vec[0];
y := vec[1];
z := vec[2];
end;
constructor clsShape.create;
begin
num_vertices := 0;
vertices := Nil;
num_triangles := 0;
triangles := Nil;
num_normals := 0;
normals := Nil;
end;
procedure clsShape.render;
var i,j : integer;
tri : clsTri;
vec : clsVec;
N : clsNormal;
begin
glBegin(GL_TRIANGLES);
for i := 0 to num_triangles - 1 do
begin
tri := triangles[i];
for j := 0 to 2 do
begin
N := normals[tri.n[j]];
glNormal3f(N.x, N.y, N.z);
vec := vertices[tri.v[j]];
glTexCoord2f (vec.u, vec.v);
glVertex3f( vec.x, vec.y, vec.z );
end;
end;
glEnd();
end;
function clsShape.saveToFile(filename: string): boolean;
begin
end;
function clsShape.loadFromFile(filename: string): boolean;
begin
end;
function clsShape.loadFromMs3dAsciiSegment(var ifile: TextFile): boolean;
var nFlags, nIndex, j : integer;
begin
Readln(ifile,num_vertices);
if eof(ifile) then
begin
result := false;
exit;
end;
SetLength(vertices,num_vertices);
if (num_vertices > 0)then
begin
for j := 0 to num_vertices - 1 do
begin
vertices[j] := clsVec.Create;
Readln(ifile,nFlags,vertices[j].x, vertices[j].y, vertices[j].z,vertices[j].u, vertices[j].v,vertices[j].bone);
if eof(ifile) then
begin
result := false;
exit;
end;
vertices[j].v := 1.0 - vertices[j].v;
end;
end;
Readln(ifile,num_normals);
if eof(ifile) then
begin
result := false;
exit;
end;
SetLength(normals,num_normals);
if num_normals > 0 then
begin
for j := 0 to num_normals - 1 do
begin
normals[j] := clsNormal.Create;
Readln(ifile,normals[j].x, normals[j].y, normals[j].z);
if eof(ifile) then
begin
result := false;
exit;
end;
end;
end;
Readln(ifile,num_triangles);
if eof(ifile) then
begin
result := false;
exit;
end;
SetLength (triangles,num_triangles);
for j := 0 to num_triangles - 1 do
begin
triangles[j] := clsTri.Create;
ReadLn(ifile,nFlags,triangles[j].v[0], triangles[j].v[1], triangles[j].v[2],triangles[j].n[0], triangles[j].n[1], triangles[j].n[2],nIndex);
if eof(ifile) then
begin
result := false;
exit;
end;
assert(triangles[j].v[0] >= 0);
assert(triangles[j].v[0] < num_vertices);
assert(triangles[j].v[1] >= 0);
assert(triangles[j].v[1] < num_vertices);
assert(triangles[j].v[2] >= 0);
assert(triangles[j].v[2] < num_vertices);
end;
result := true;
end;
procedure clsMaterial.activate;
begin
glMaterialfv( GL_FRONT, GL_AMBIENT, @Ambient );
glMaterialfv( GL_FRONT, GL_DIFFUSE, @Diffuse );
glMaterialfv( GL_FRONT, GL_SPECULAR, @Specular );
glMaterialfv( GL_FRONT, GL_EMISSION, @Emissive );
glMaterialf( GL_FRONT, GL_SHININESS, Shininess );
if ( textexture > 0 ) then
begin
glBindTexture( GL_TEXTURE_2D, textexture );
glEnable( GL_TEXTURE_2D );
end
else
glDisable( GL_TEXTURE_2D );
end;
constructor clsMaterial.create;
begin
end;
function clsMaterial.loadFromMs3dAsciiSegment(var ifile: TextFile): boolean;
var szLine : string;
begin
if (eof(ifile)) then
begin
result := false;
exit;
end;
Readln(ifile,szLine);
Name := StringReplace(szLine,'"','',[rfReplaceAll]);
if (eof(ifile)) then
begin
result := false;
exit;
end;
Readln(ifile,Ambient[0], Ambient[1], Ambient[2], Ambient[3]);
if (eof(ifile)) then
begin
result := false;
exit;
end;
Readln(ifile,Diffuse[0], Diffuse[1], Diffuse[2], Diffuse[3]);
if (eof(ifile)) then
begin
result := false;
exit;
end;
Readln(ifile,Specular[0], Specular[1], Specular[2], Specular[3]);
if (eof(ifile)) then
begin
result := false;
exit;
end;
Readln(ifile,Emissive[0], Emissive[1], Emissive[2], Emissive[3]);
if (eof(ifile)) then
begin
result := false;
exit;
end;
Readln(ifile,Shininess);
if (eof(ifile)) then
begin
result := false;
exit;
end;
Readln(ifile,Transparency);
if (eof(ifile)) then
begin
result := false;
exit;
end;
DiffuseTexture := '';
Readln(ifile,DiffuseTexture);
DiffuseTexture := StringReplace(DiffuseTexture,'"','',[rfReplaceAll]);
if (eof(ifile)) then
begin
result := false;
exit;
end;
AlphaTexture := '';
Readln(ifile,AlphaTexture);
AlphaTexture := StringReplace(AlphaTexture,'"','',[rfReplaceAll]);
reloadTexture();
result := true;
end;
procedure clsMaterial.reloadTexture;
begin
if( length(DiffuseTexture) > 0 ) then
LoadTexture(DiffuseTexture, textexture,false,GL_LINEAR,GL_LINEAR_MIPMAP_NEAREST,0)
else
textexture := 0;
end;
constructor clsBone.create;
begin
m_relative := clsMatrix.create;
m_final := clsMatrix.create;
end;
procedure clsBone.advanceTo(CurrentTime: single);
var i : integer;
deltaTime : single;
fraction : single;
Position : array [0..2] of single;
Rotation : array [0..2] of single;
m_rel , m_frame : clsMatrix;
tempm : array [0..15] of single;
begin
i := 0;
while ( (i < NumPositionKeys-1) and (PositionKeyFrames[i].Time < CurrentTime) ) do
i := i + 1;
assert(i < NumPositionKeys);
if( i > 0 ) then
begin
deltaTime := PositionKeyFrames[i].Time - PositionKeyFrames[i-1].Time;
assert( deltaTime > 0 );
fraction := (CurrentTime - PositionKeyFrames[i-1].Time) / deltaTime;
assert( fraction > 0 );
assert( fraction < 1.0 );
Position[0] := PositionKeyFrames[i-1].Value[0] + fraction * (PositionKeyFrames[i].Value[0] - PositionKeyFrames[i-1].Value[0]);
Position[1] := PositionKeyFrames[i-1].Value[1] + fraction * (PositionKeyFrames[i].Value[1] - PositionKeyFrames[i-1].Value[1]);
Position[2] := PositionKeyFrames[i-1].Value[2] + fraction * (PositionKeyFrames[i].Value[2] - PositionKeyFrames[i-1].Value[2]);
end
else
begin
Position[0] := PositionKeyFrames[i].Value[0];
Position[1] := PositionKeyFrames[i].Value[1];
Position[2] := PositionKeyFrames[i].Value[2];
end;
i := 0;
while( (i < NumRotationKeys-1) and (RotationKeyFrames[i].Time < CurrentTime) ) do
i := i + 1;
assert(i < NumRotationKeys);
if( i > 0 ) then
begin
deltaTime := RotationKeyFrames[i].Time - RotationKeyFrames[i-1].Time;
assert( deltaTime > 0 );
fraction := (CurrentTime - RotationKeyFrames[i-1].Time) / deltaTime;
assert( fraction > 0 );
assert( fraction < 1.0 );
Rotation[0] := RotationKeyFrames[i-1].Value[0] + fraction * (RotationKeyFrames[i].Value[0] - RotationKeyFrames[i-1].Value[0]);
Rotation[1] := RotationKeyFrames[i-1].Value[1] + fraction * (RotationKeyFrames[i].Value[1] - RotationKeyFrames[i-1].Value[1]);
Rotation[2] := RotationKeyFrames[i-1].Value[2] + fraction * (RotationKeyFrames[i].Value[2] - RotationKeyFrames[i-1].Value[2]);
end
else
begin
Rotation[0] := RotationKeyFrames[i].Value[0];
Rotation[1] := RotationKeyFrames[i].Value[1];
Rotation[2] := RotationKeyFrames[i].Value[2];
end;
m_rel := clsMatrix.create;
m_frame := clsMatrix.create;
m_rel.setRotationRadians( startRotation );
m_rel.setTranslation( startPosition );
m_frame.setRotationRadians( Rotation );
m_frame.setTranslation( Position );
m_rel.postMultiply( m_frame );
if ( Parent = nil ) then
begin
m_rel.getMatrix(tempm);
m_final.setMatrixValues(tempm);
end
else
begin
Parent.m_final.getMatrix(tempm);
m_final.setMatrixValues(tempm);
m_final.postMultiply( m_rel );
end;
end;
procedure clsBone.fixup;
begin
end;
procedure clsBone.initialize;
var m_rel : clsMatrix;
tempm : array [0..15] of single;
begin
m_rel := clsMatrix.create;
m_rel.setRotationRadians( startRotation );
m_rel.setTranslation( startPosition );
if ( Parent = nil ) then
begin
m_rel.getMatrix(tempm);
m_final.setMatrixValues(tempm);
end
else
begin
Parent.m_final.getMatrix(tempm);
m_final.setMatrixValues(tempm);
m_final.postMultiply( m_rel );
end;
end;
procedure clsBone.render;
var vector , parentvector : clsVec;
begin
vector := clsVec.Create;
parentvector := clsVec.Create;
vector.x := 0;
vector.y := 0;
vector.z := 0;
vector.w := 1;
vector.transform( m_final );
if( Parent <> nil ) then
begin
parentvector.x := 0;
parentvector.y := 0;
parentvector.z := 0;
parentvector.w := 1;
parentvector.transform( Parent.m_final );
end;
glDisable( GL_TEXTURE_2D );
glLineWidth(1.0);
glColor3f(1.0, 0, 0);
glBegin(GL_LINES);
glVertex3f( vector.x, vector.y, vector.z );
if( Parent <> nil ) then
glVertex3f( parentvector.x, parentvector.y, parentvector.z )
else
glVertex3f( vector.x, vector.y, vector.z );
glEnd();
glPointSize(2.0);
glColor3f(1.0, 0, 1.0);
glBegin(GL_POINTS);
glVertex3f( vector.x, vector.y, vector.z );
if( Parent <> nil ) then
glVertex3f( parentvector.x, parentvector.y, parentvector.z );
glEnd();
glColor3f(1.0, 1.0, 1.0);
end;
function clsBone.loadFromMs3dAsciiSegment(var ifile: TextFile): boolean;
var szLine : string;
j ,nFlags : integer;
begin
if (eof(ifile)) then
begin
result := false;
exit;
end;
Readln(ifile,Name);
Name := StringReplace(Name,'"','',[rfReplaceAll]) ;
if (eof(ifile)) then
begin
result := false;
exit;
end;
ParentName := '';
Readln(ifile,ParentName);
ParentName := StringReplace(ParentName,'"','',[rfReplaceAll]) ;
if (eof(ifile)) then
begin
result := false;
exit;
end;
ReadLn(ifile,nFlags,
startPosition[0], startPosition[1], startPosition[2],
startRotation[0], startRotation[1], startRotation[2]);
if (eof(ifile)) then
begin
result := false;
exit;
end;
Readln(ifile,NumPositionKeys);
SetLength(PositionKeyFrames,NumPositionKeys);
for j := 0 to NumPositionKeys - 1 do
begin
PositionKeyFrames[j] := clsKeyFrame.Create;
if (eof(ifile)) then
begin
result := false;
exit;
end;
Readln(ifile , PositionKeyFrames[j].Time,
PositionKeyFrames[j].Value[0],
PositionKeyFrames[j].Value[1],
PositionKeyFrames[j].Value[2] );
end;
if (eof(ifile)) then
begin
result := false;
exit;
end;
Readln(ifile,NumRotationKeys);
SetLength(RotationKeyFrames,NumRotationKeys);
for j := 0 to NumRotationKeys -1 do
begin
RotationKeyFrames[j] := clsKeyFrame.Create;
if (eof(ifile)) then
begin
result := false;
exit;
end;
Readln(ifile , RotationKeyFrames[j].Time,
RotationKeyFrames[j].Value[0],
RotationKeyFrames[j].Value[1],
RotationKeyFrames[j].Value[2] );
end;
result := true;
end;
procedure clsModel.advanceAnimation(deltaTime: single);
var i : integer;
begin
CurrentTime := CurrentTime + deltaTime;
if( CurrentTime > MaxTime ) then
CurrentTime := 1.0;
for i := 0 to num_bones -1 do
bones[i].advanceTo( CurrentTime );
end;
procedure clsModel.attachSkin;
var i, j : integer;
bone : integer;
matrix : clsMatrix;
v : array [0..2] of single;
begin
for i := 0 to num_shapes - 1 do
begin
for j := 0 to shapes[i].num_vertices - 1 do
begin
bone := shapes[i].vertices[j].bone;
if( bone <> -1 ) then
begin
matrix := bones[bone].m_final;
v[0] := shapes[i].vertices[j].x;
v[1] := shapes[i].vertices[j].y;
v[2] := shapes[i].vertices[j].z;
matrix.inverseTranslateVect( v );
matrix.inverseRotateVect( v );
shapes[i].vertices[j].x := v[0];
shapes[i].vertices[j].y := v[1];
shapes[i].vertices[j].z := v[2];
end;
end;
end;
end;
constructor clsModel.create;
begin
num_shapes := 0;
shapes := nil;
num_materials := 0;
materials := nil;
end;
procedure clsModel.initializeBones;
var i : integer;
begin
for i := 0 to num_bones-1 do
bones[i].initialize;
end;
function clsModel.linkBones: boolean;
var i, j : integer;
begin
for i := 0 to num_bones-1 do
begin
bones[i].Parent := nil;
if( length(bones[i].ParentName) > 0 ) then
begin
for j := 0 to num_bones -1 do
begin
if( bones[j].Name = bones[i].ParentName) then
begin
bones[i].Parent := @bones[j];
break;
end;
end;
if ( bones[i].Parent = nil) then
begin
result := false;
exit;
end;
end;
end;
result := true;
end;
function clsModel.loadFromMs3dAsciiFile(filename: string): boolean;
var StartTime : single;
bError : bool;
szLine , szName , strTemp : string;
nFrame, nFlags, nIndex, i : integer;
fModel : TextFile;
begin
bError := false;
AssignFile(fModel,filename);
Reset(fModel);
CurrentTime := 0;
while ((not eof(fModel)) and (not bError)) do
begin
Readln(fmodel,szLine);
if ( copy(szLine,0,2) = '//') then
continue;
if szLine = '' then
continue;
if (pos('Frames:',szLine) = 1) then
begin
nFrame := StrToInt(copy(szLine,8,length(szLine)));
MaxTime := 1.0 * nFrame;
continue;
end;
if (pos('Frame:',szLine) = 1) then
begin
nFrame := StrToInt(copy(szLine,7,length(szLine)));
StartTime := 1.0 * nFrame;
continue;
end;
if (pos('Meshes: ',szLine) = 1) then
begin
num_shapes := StrToInt(StringReplace(szLine,'Meshes: ','',[rfReplaceAll]));
SetLength(shapes,num_shapes);
SetLength(material_indices, num_shapes);
for i := 0 to num_shapes - 1 do
begin
shapes[i] := clsShape.create;
material_indices[num_shapes-1] := 0;
end;
for i := 0 to num_shapes - 1 do
begin
Readln(fmodel,szName);
if (eof(fmodel)) then
begin
bError := true;
break;
end;
szName := StringReplace(szName,'"','',[rfReplaceAll]);
strTemp := szName;
strTemp := copy(strTemp,pos(' ',strTemp)+1,length(strTemp));
nFlags := StrToInt(copy(strTemp,0,pos(' ',strTemp)-1));
nIndex := StrToInt(copy(strTemp,pos(' ',strTemp)+1,length(strTemp)));
material_indices[i] := nIndex;
if( not shapes[i].loadFromMs3dAsciiSegment(fModel) ) then
begin
bError := true;
break;
end;
end;
continue;
end;
if (pos('Materials: ',szLine) = 1) then
begin
num_materials := StrToInt(StringReplace(szLine,'Materials: ','',[rfReplaceAll]));
SetLength(materials,num_materials);
for i := 0 to num_materials -1 do
materials[i] := clsMaterial.create;
for i := 0 to num_materials -1 do
begin
if( not materials[i].loadFromMs3dAsciiSegment(fModel) ) then
begin
bError := true;
break;
end;
end;
continue;
end;
if (pos('Bones: ',szLine) = 1) then
begin
num_bones := StrToInt(StringReplace(szLine,'Bones: ','',[rfReplaceAll]));
SetLength(bones,num_bones);
for i := 0 to num_bones -1 do
bones[i] := clsBone.create;
for i := 0 to num_bones -1 do
begin
if( not bones[i].loadFromMs3dAsciiSegment(fModel) ) then
begin
bError := true;
break;
end;
end;
continue;
end;
end;
CloseFile(fModel);
if( not linkBones() ) then
begin
result := false;
exit;
end;
initializeBones();
attachSkin();
advanceAnimation( StartTime );
result := true;
end;
procedure clsModel.reloadTextures;
var i : integer;
begin
for i := 0 to num_materials - 1 do // for each shape
materials[i].reloadTexture();
end;
procedure clsModel.render;
var k,i,j,materialIndex : integer;
tri : clsTri;
N : clsNormal;
vec : clsVec;
bone : clsBone;
v : array [0..2] of single;
matrix : clsMatrix;
begin
for k := 0 to num_shapes - 1 do
begin
materialIndex := material_indices[k];
if ( materialIndex >= 0 ) then
materials[materialIndex].activate()
else
begin
// Material properties?
glDisable( GL_TEXTURE_2D );
end;
glBegin(GL_TRIANGLES);
for i := 0 to shapes[k].num_triangles - 1 do
begin
tri := shapes[k].triangles[i];
for j := 0 to 2 do
begin
N := shapes[k].normals[tri.n[j]];
glNormal3f(N.x, N.y, N.z);
vec := shapes[k].vertices[tri.v[j]];
glTexCoord2f (vec.u, vec.v);
if( vec.bone = -1 ) then
begin
glVertex3f( vec.x, vec.y, vec.z );
end
else
begin
bone := bones[vec.bone];
matrix := bone.m_final;
v[0] := vec.x;
v[1] := vec.y;
v[2] := vec.z;
matrix.rotateVect( v );
matrix.translateVect( v );
glVertex3fv( @v );
end;
end;
end;
glEnd();
end;
end;
procedure clsModel.renderBones;
var i : integer;
begin
for i := 0 to num_bones - 1 do
bones[i].render();
end;
end.
kadir göksu 0 537 714 57 25
0 537 714 57 25