Kan du ikke komme med et konkret eksempel? Ellers forstår jeg det simpelthen ikke....
Hej,
Nu sad jeg og blev lidt interesseret i hvordan det kunne gøres...
Jeg har lavet en klasse som kan holde vektorer og faces samt gemme oh hente sig selv fra en fil (Jeg har taget udganspunkt fra det som Søren skrev):
HUSK at gemme koden i en pas fil med navnet 3dObject.pas
unit 3dObject;
interface
uses
SysUtils, Classes;
type
PVector = ^TVector;
TVector = record
X, Y, Z : Single;
end;
PFace = ^TFace;
TFace = record
V1, V2, V3 : Integer;
end;
PPFaceArray = ^TPFaceArray;
TPFaceArray = array[0..MaxInt div SizeOf(PFace)-1] of PFace;
PPVectorArray = ^TPVectorArray;
TPVectorArray = array[0..MaxInt div SizeOf(PVector)-1] of PVector;
T3dObject = class(TObject)
private
FFaces : TList;
FVectors : TList;
FName : String;
protected
function AllocVector(AX, AY, AZ : Single) : PVector;
function AllocFace(AVector1, AVector2, AVector3 : Integer) : PFace;
function GetFaces : PPFaceArray;
function GetVectors : PPVectorArray;
public
constructor Create;
destructor Destroy; override;
procedure Load(AFileName : String);
procedure Save(AFileName : String);
procedure Clear;
function AddVector(AX, AY, AZ : Single) : Integer; overload;
function AddVector(AVector : TVector) : Integer; overload;
function AddFace(AVector1, AVector2, AVector3 : Integer) : Integer; overload;
function AddFace(AFace : TFace) : Integer; overload;
function NumOfFaces : Integer;
function NumOfVectors : Integer;
property Faces : PPFaceArray read GetFaces;
property Vectors : PPVectorArray read GetVectors;
property Name : String read FName write FName;
end;
implementation
constructor T3dObject.Create;
begin
inherited Create;
FFaces := TList.Create;
FVectors := TList.Create;
FName := 'Unused.obj';
end;
destructor T3dObject.Destroy;
begin
Clear;
FFaces.Free;
FFaces := nil;
FVectors.Free;
FVectors := nil;
inherited Destroy;
end;
function T3dObject.AllocVector(AX, AY, AZ : Single) : PVector;
begin
Result := AllocMem(SizeOf(TVector));
if Result <> nil then
begin
Result^.X := AX;
Result^.Y := AY;
Result^.Z := AZ;
end;
end;
function T3dObject.AllocFace(AVector1, AVector2, AVector3 : Integer) : PFace;
begin
Result := AllocMem(SizeOf(TFace));
if Result <> nil then
begin
Result^.V1 := AVector1;
Result^.V2 := AVector2;
Result^.V3 := AVector3;
end;
end;
function T3dObject.GetFaces : PPFaceArray;
begin
Result := PPFaceArray(FFaces.List);
end;
function T3dObject.GetVectors : PPVectorArray;
begin
Result := PPVectorArray(FVectors.List);
end;
function T3dObject.NumOfFaces : Integer;
begin
Result := FFaces.Count;
end;
function T3dObject.NumOfVectors : Integer;
begin
Result := FVectors.Count;
end;
procedure T3dObject.Load(AFileName : String);
function FindTag(ATag : String; AList : TStringList; AStartIndex : Integer) : Integer;
var
I : Integer;
begin
Result := -1;
for I := AStartIndex to AList.Count-1 do
begin
if Pos(ATag, AList.Strings[I]) > 0 then
begin
Result := I;
break;
end;
end;
end;
function ReadVector(ASeperator : Char; AStr : String) : TVector;
var
F : TStringList;
begin
F := TStringList.Create;
F.Delimiter := ASeperator;
F.DelimitedText := AStr;
try
case F.Count of
1:
begin
Result.X := StrToFloat(F.Strings[0]);
Result.Y := 0;
Result.Z := 0;
end;
2:
begin
Result.X := StrToFloat(F.Strings[0]);
Result.X := StrToFloat(F.Strings[1]);
Result.Z := 0;
end;
else
begin
Result.X := StrToFloat(F.Strings[0]);
Result.Y := StrToFloat(F.Strings[1]);
Result.Z := StrToFloat(F.Strings[2]);
end;
end;
finally
F.Free;
end;
end;
function ReadFace(ASeperator : Char; AStr : String) : TFace;
var
F : TStringList;
begin
F := TStringList.Create;
F.Delimiter := ASeperator;
F.DelimitedText := AStr;
try
case F.Count of
1:
begin
Result.V1 := StrToInt(F.Strings[0]);
Result.V2 := 0;
Result.V3 := 0;
end;
2:
begin
Result.V1 := StrToInt(F.Strings[0]);
Result.V2 := StrToInt(F.Strings[1]);
Result.V3 := 0;
end;
else
begin
Result.V1 := StrToInt(F.Strings[0]);
Result.V2 := StrToInt(F.Strings[1]);
Result.V3 := StrToInt(F.Strings[2]);
end;
end;
finally
F.Free;
end;
end;
var
F : TStringList;
I : Integer;
Start : Integer;
Slut : Integer;
begin
Clear;
F := TStringList.Create;
try
F.LoadFromFile(AFileName);
I := FindTag('OBJECT ', F, 0);
if I <> -1 then
begin // okay vi fandt starten på vores objekt
FName := Copy(F.Strings[I], Pos('OBJECT ', F.Strings[I])+7, Length(F.Strings[I]));
// Læs vectors
I := FindTag('VECTORS', F, I);
if I <> -1 then
begin // okay vi fandt starten på vores vektorer
Inc(I);
Start := I;
I := FindTag('END', F, I);
if I <> -1 then
begin // okay vi fandt slutningen på vores vektorer
Slut := I;
for I := Start to Slut-1 do
begin // Læs vector data
AddVector(ReadVector(',', F.Strings[I]));
end;
// Læs faces
I := FindTag('FACES', F, Slut+1);
if I <> -1 then
begin // okay vi fandt starten på vores faces
Inc(I);
Start := I;
I := FindTag('END', F, I);
if I <> -1 then
begin // okay vi fandt slutningen på vores faces
Slut := I;
for I := Start to Slut-1 do
begin // Læs vector data
AddFace(ReadFace(',', F.Strings[I]));
end;
end;
end;
end;
end;
end;
finally
F.Free;
end;
end;
procedure T3dObject.Save(AFileName : String);
var
F : TStringList;
I : Integer;
Face : PFace;
Vec : PVector;
begin
F := TStringList.Create;
try
F.Add('OBJECT '+FName);
F.Add(' VECTORS');
for I := 0 to FVectors.Count-1 do
begin
Vec := FVectors.Items[I];
if Vec <> nil then
F.Add(' '+FloatToStr(Vec^.X)+','+FloatToStr(Vec^.Y)+','+FloatToStr(Vec^.Z))
end;
F.Add(' END');
F.Add(' FACES');
for I := 0 to FFaces.Count-1 do
begin
Face := FFaces.Items[I];
if Face <> nil then
F.Add(' '+IntToStr(Face^.V1)+','+IntToStr(Face^.V2)+','+IntToStr(Face^.V3))
end;
F.Add(' END');
F.Add('END');
F.SaveToFile(AFileName);
finally
F.Free;
end;
end;
procedure T3dObject.Clear;
var
I : Integer;
P : Pointer;
begin
for I := 0 to FFaces.Count-1 do
begin
P := FFaces.Items[I];
if P <> nil then
FreeMem(P);
end;
FFaces.Clear;
for I := 0 to FVectors.Count-1 do
begin
P := FVectors.Items[I];
if P <> nil then
FreeMem(P);
end;
FVectors.Clear;
FName := 'Unused.obj';
end;
function T3dObject.AddVector(AVector : TVector) : Integer;
begin
Result := FVectors.Add(AllocVector(AVector.X, AVector.Y, AVector.Z));
end;
function T3dObject.AddVector(AX, AY, AZ : Single) : Integer;
begin
Result := FVectors.Add(AllocVector(AX, AY, AZ));
end;
function T3dObject.AddFace(AFace : TFace) : Integer;
begin
Result := FFaces.Add(AllocFace(AFace.V1, AFace.V2, AFace.V3));
end;
function T3dObject.AddFace(AVector1, AVector2, AVector3 : Integer) : Integer;
begin
Result := FFaces.Add(AllocFace(AVector1, AVector2, AVector3));
end;
end.
Når du skal bruge klassen 3dObject så kan du gøre det på følgende måder:
procedure OpretNytObjekt;
var
Obj : T3dObject;
begin
Obj := T3dObject.Create;
Obj.AddVector(1.0, 1.0, 1.0);
Obj.AddVector(1.0, 2.0, 1.0);
Obj.AddVector(2.0, 2.0, 1.0);
Obj.AddVector(2.0, 1.0, 1.0);
Obj.AddFace(1,2,3);
Obj.AddFace(1,3,4);
Obj.Name := 'Wall_1';
Obj.Save('wall.obj');
Obj.Free;
end;
procedure Render(AObject : T3dObject);
var
I : Integer;
begin
glBegin(gl_Triangles);
for I := 0 to AObject.NumOfFaces-1 do
begin
glVertex3fv(Pointer(AObject.Vectors^[AObject.Faces^[I].V1]));
glVertex3fv(Pointer(AObject.Vectors^[AObject.Faces^[I].V2]));
glVertex3fv(Pointer(AObject.Vectors^[AObject.Faces^[I].V3]));
end;
glEnd;
end;
procedure HentObjektOgRenderIt;
var
Obj : T3dObject;
begin
Obj := T3dObject.Create;
Obj.Load('wall.obj');
Render(Obj);
Obj.Free;
end;
Grunden til at det ser lidt tricky ud når man skal render (her mener jeg ved glVertex3fv funktionskaldene) er det fordi min version af OpenGl.pas har defineret dens parameter som PGLfloat i stedet for en pointer til et array af single (hvilket det jo egentligt er da PGLfloat bare peger på et array med 1 element). Hvis det var defineret som jeg havde beskrevet så var vi ikke nødtil at typecaste PVector til en Pointer......Nå jeg skulle bare lige af med lidt luft ;-)
Du kan jo evt. udbygge 3dObject.pas med en ny klasse som kan indeholde/gemme/hente flere objekter...
Nå men jeg håber det hjælper dig lidt på vej..
PS. har du spørgsmål til koden så sig til.
/Michael.