source: trunk/Packages/bgrabitmap/part3d.inc

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 16.0 KB
Line 
1type
2 { TBGRAPart3D }
3
4 TBGRAPart3D = class(TInterfacedObject,IBGRAPart3D)
5 private
6 FVertices: array of IBGRAVertex3D;
7 FVertexCount: integer;
8 FNormals: array of IBGRANormal3D;
9 FNormalCount: integer;
10 FMatrix: TMatrix3D;
11 FParts: array of IBGRAPart3D;
12 FPartCount: integer;
13 FContainer: IBGRAPart3D;
14 FCoordPool: TBGRACoordPool3D;
15 FNormalPool: TBGRANormalPool3D;
16 FObject3D: TBGRAObject3D;
17 public
18 constructor Create(AObject3D: TBGRAObject3D; AContainer: IBGRAPart3D);
19 destructor Destroy; override;
20 procedure Clear(ARecursive: boolean);
21 function Add(x,y,z: single): IBGRAVertex3D; overload;
22 function Add(pt: TPoint3D): IBGRAVertex3D; overload;
23 function Add(pt: TPoint3D; normal: TPoint3D): IBGRAVertex3D; overload;
24 function Add(pt: TPoint3D_128): IBGRAVertex3D; overload;
25 function Add(pt: TPoint3D_128; normal: TPoint3D_128): IBGRAVertex3D; overload;
26 function Add(const coords: array of single): arrayOfIBGRAVertex3D; overload;
27 function Add(const pts: array of TPoint3D): arrayOfIBGRAVertex3D; overload;
28 function Add(const pts: array of TPoint3D_128): arrayOfIBGRAVertex3D; overload;
29 procedure Add(const pts: array of IBGRAVertex3D); overload;
30 procedure Add(AVertex: IBGRAVertex3D); overload;
31 function AddNormal(x,y,z: single): IBGRANormal3D; overload;
32 function AddNormal(pt: TPoint3D): IBGRANormal3D; overload;
33 function AddNormal(pt: TPoint3D_128): IBGRANormal3D; overload;
34 procedure AddNormal(ANormal: IBGRANormal3D); overload;
35 procedure RemoveVertex(Index: integer);
36 procedure RemoveNormal(Index: integer);
37 function GetBoundingBox: TBox3D;
38 function GetRadius: single;
39 function GetMatrix: TMatrix3D;
40 function GetPart(AIndex: Integer): IBGRAPart3D;
41 function GetPartCount: integer;
42 function GetVertex(AIndex: Integer): IBGRAVertex3D;
43 function GetVertexCount: integer;
44 function GetNormal(AIndex: Integer): IBGRANormal3D;
45 function GetNormalCount: integer;
46 function GetTotalVertexCount: integer;
47 function GetTotalNormalCount: integer;
48 function GetContainer: IBGRAPart3D;
49 procedure SetVertex(AIndex: Integer; AValue: IBGRAVertex3D);
50 procedure SetNormal(AIndex: Integer; AValue: IBGRANormal3D);
51 procedure ResetTransform;
52 procedure Translate(x,y,z: single; Before: boolean = true); overload;
53 procedure Translate(ofs: TPoint3D; Before: boolean = true); overload;
54 procedure Scale(size: single; Before: boolean = true); overload;
55 procedure Scale(x,y,z: single; Before: boolean = true); overload;
56 procedure Scale(size: TPoint3D; Before: boolean = true); overload;
57 procedure RotateXDeg(angle: single; Before: boolean = true);
58 procedure RotateYDeg(angle: single; Before: boolean = true);
59 procedure RotateZDeg(angle: single; Before: boolean = true);
60 procedure RotateXRad(angle: single; Before: boolean = true);
61 procedure RotateYRad(angle: single; Before: boolean = true);
62 procedure RotateZRad(angle: single; Before: boolean = true);
63 procedure SetMatrix(const AValue: TMatrix3D);
64 procedure ComputeWithMatrix(const AMatrix: TMatrix3D; const AProjection: TProjection3D);
65 function ComputeCoordinate(var ASceneCoord: TPoint3D_128; const AProjection: TProjection3D): TPointF;
66 procedure NormalizeViewNormal;
67 function CreatePart: IBGRAPart3D;
68 procedure LookAt(ALookWhere,ATopDir: TPoint3D);
69 procedure RemoveUnusedVertices;
70 function IndexOf(AVertex: IBGRAVertex3D): integer;
71 procedure ForEachVertex(ACallback: TVertex3DCallback);
72 end;
73
74{ TBGRAPart3D }
75
76procedure TBGRAPart3D.LookAt(ALookWhere,ATopDir: TPoint3D);
77var ZDir, XDir, YDir: TPoint3D_128;
78 ViewPoint: TPoint3D_128;
79 CurPart: IBGRAPart3D;
80 ComposedMatrix: TMatrix3D;
81begin
82 YDir := -Point3D_128(ATopDir);
83 if IsPoint3D_128_Zero(YDir) then exit;
84 Normalize3D_128(YDir);
85
86 ComposedMatrix := FMatrix;
87 CurPart := self.FContainer;
88 while CurPart <> nil do
89 begin
90 ComposedMatrix := CurPart.Matrix*ComposedMatrix;
91 CurPart := CurPart.Container;
92 end;
93 ViewPoint := ComposedMatrix*Point3D_128_Zero;
94
95 ZDir := Point3D_128(ALookWhere)-ViewPoint;
96 if IsPoint3D_128_Zero(ZDir) then exit;
97 Normalize3D_128(ZDir);
98
99 VectProduct3D_128(YDir,ZDir,XDir);
100 VectProduct3D_128(ZDir,XDir,YDir); //correct Y dir
101
102 FMatrix := Matrix3D(XDir,YDir,ZDir,ViewPoint);
103 ComposedMatrix := MatrixIdentity3D;
104 CurPart := self.FContainer;
105 while CurPart <> nil do
106 begin
107 ComposedMatrix := CurPart.Matrix*ComposedMatrix;
108 CurPart := CurPart.Container;
109 end;
110 FMatrix := MatrixInverse3D(ComposedMatrix)*FMatrix;
111end;
112
113procedure TBGRAPart3D.RemoveUnusedVertices;
114var
115 i: Integer;
116begin
117 for i := FVertexCount-1 downto 0 do
118 if FVertices[i].Usage <= 2 then RemoveVertex(i);
119 for i := 0 to FPartCount-1 do
120 FParts[i].RemoveUnusedVertices;
121end;
122
123function TBGRAPart3D.IndexOf(AVertex: IBGRAVertex3D): integer;
124var i: integer;
125begin
126 for i := 0 to FVertexCount-1 do
127 if FVertices[i] = AVertex then
128 begin
129 result := i;
130 exit;
131 end;
132 result := -1;
133end;
134
135procedure TBGRAPart3D.ForEachVertex(ACallback: TVertex3DCallback);
136var i: integer;
137begin
138 for i := 0 to FVertexCount-1 do
139 ACallback(FVertices[i]);
140end;
141
142procedure TBGRAPart3D.Add(AVertex: IBGRAVertex3D);
143begin
144 if FVertexCount = length(FVertices) then
145 setlength(FVertices, FVertexCount*2+3);
146 FVertices[FVertexCount] := AVertex;
147 inc(FVertexCount);
148end;
149
150function TBGRAPart3D.AddNormal(x, y, z: single): IBGRANormal3D;
151begin
152 if not Assigned(FNormalPool) then FNormalPool := TBGRANormalPool3D.Create(4);
153 result := TBGRANormal3D.Create(FNormalPool,Point3D_128(x,y,z));
154 AddNormal(result);
155end;
156
157function TBGRAPart3D.AddNormal(pt: TPoint3D): IBGRANormal3D;
158begin
159 if not Assigned(FNormalPool) then FNormalPool := TBGRANormalPool3D.Create(4);
160 result := TBGRANormal3D.Create(FNormalPool,pt);
161 AddNormal(result);
162end;
163
164function TBGRAPart3D.AddNormal(pt: TPoint3D_128): IBGRANormal3D;
165begin
166 if not Assigned(FNormalPool) then FNormalPool := TBGRANormalPool3D.Create(4);
167 result := TBGRANormal3D.Create(FNormalPool,pt);
168 AddNormal(result);
169end;
170
171procedure TBGRAPart3D.AddNormal(ANormal: IBGRANormal3D);
172begin
173 if FNormalCount = length(FNormals) then
174 setlength(FNormals, FNormalCount*2+3);
175 FNormals[FNormalCount] := ANormal;
176 inc(FNormalCount);
177end;
178
179procedure TBGRAPart3D.RemoveVertex(Index: integer);
180var i: integer;
181begin
182 if (Index >= 0) and (Index < FVertexCount) then
183 begin
184 for i := Index to FVertexCount-2 do
185 FVertices[i] := FVertices[i+1];
186 FVertices[FVertexCount-1] := nil;
187 dec(FVertexCount);
188 end;
189end;
190
191procedure TBGRAPart3D.RemoveNormal(Index: integer);
192var i: integer;
193begin
194 if (Index >= 0) and (Index < FNormalCount) then
195 begin
196 for i := Index to FNormalCount-2 do
197 FNormals[i] := FNormals[i+1];
198 FNormals[FNormalCount-1] := nil;
199 dec(FNormalCount);
200 end;
201end;
202
203function TBGRAPart3D.GetRadius: single;
204var i: integer;
205 pt: TPoint3D_128;
206 d: single;
207begin
208 result := 0;
209 for i := 0 to GetVertexCount-1 do
210 begin
211 pt := GetVertex(i).SceneCoord_128;
212 d:= sqrt(DotProduct3D_128(pt,pt));
213 if d > result then result := d;
214 end;
215end;
216
217constructor TBGRAPart3D.Create(AObject3D: TBGRAObject3D; AContainer: IBGRAPart3D);
218begin
219 FObject3D := AObject3D;
220 FContainer := AContainer;
221 FMatrix := MatrixIdentity3D;
222 FCoordPool := TBGRACoordPool3D.Create(4);
223 FNormalPool := nil;
224 FNormalCount:= 0;
225 FVertexCount := 0;
226end;
227
228destructor TBGRAPart3D.Destroy;
229begin
230 FVertices := nil;
231 FVertexCount := 0;
232 if FCoordPool.UsedCapacity > 0 then
233 raise Exception.Create('Coordinate pool still used. Please set vertex references to nil before destroying the scene.');
234 FreeAndNil(FCoordPool);
235 if Assigned(FNormalPool) then
236 begin
237 if FNormalPool.UsedCapacity > 0 then
238 raise Exception.Create('Normal pool still used');
239 FreeAndNil(FNormalPool);
240 end;
241 inherited Destroy;
242end;
243
244procedure TBGRAPart3D.Clear(ARecursive: boolean);
245var i: integer;
246begin
247 FVertices := nil;
248 FVertexCount := 0;
249 FNormals := nil;
250 FNormalCount := 0;
251 if ARecursive then
252 begin
253 for i := 0 to FPartCount-1 do
254 FParts[i].Clear(ARecursive);
255 FParts := nil;
256 FPartCount := 0;
257 end;
258end;
259
260function TBGRAPart3D.Add(x, y, z: single): IBGRAVertex3D;
261begin
262 result := TBGRAVertex3D.Create(FObject3D,FCoordPool,Point3D(x,y,z));
263 Add(result);
264end;
265
266function TBGRAPart3D.Add(pt: TPoint3D): IBGRAVertex3D;
267begin
268 result := TBGRAVertex3D.Create(FObject3D,FCoordPool,pt);
269 Add(result);
270end;
271
272function TBGRAPart3D.Add(pt: TPoint3D; normal: TPoint3D): IBGRAVertex3D;
273begin
274 result := TBGRAVertex3D.Create(FObject3D,FCoordPool,pt);
275 result.CustomNormal := normal;
276 Add(result);
277end;
278
279function TBGRAPart3D.Add(pt: TPoint3D_128): IBGRAVertex3D;
280begin
281 result := TBGRAVertex3D.Create(FObject3D,FCoordPool,pt);
282 Add(result);
283end;
284
285function TBGRAPart3D.Add(pt: TPoint3D_128; normal: TPoint3D_128): IBGRAVertex3D;
286begin
287 result := TBGRAVertex3D.Create(FObject3D,FCoordPool,pt);
288 result.CustomNormal := Point3D(normal);
289 Add(result);
290end;
291
292function TBGRAPart3D.Add(const coords: array of single
293 ): arrayOfIBGRAVertex3D;
294var pts: array of TPoint3D;
295 CoordsIdx: integer;
296 i: Integer;
297begin
298 if length(coords) mod 3 <> 0 then
299 raise exception.Create('Array size must be a multiple of 3');
300 setlength(pts, length(coords) div 3);
301 coordsIdx := 0;
302 for i := 0 to high(pts) do
303 begin
304 pts[i] := Point3D(coords[CoordsIdx],coords[CoordsIdx+1],coords[CoordsIdx+2]);
305 inc(coordsIdx,3);
306 end;
307 result := Add(pts);
308end;
309
310function TBGRAPart3D.Add(const pts: array of TPoint3D): arrayOfIBGRAVertex3D;
311var
312 i: Integer;
313begin
314 setlength(result, length(pts));
315 for i := 0 to high(pts) do
316 result[i] := TBGRAVertex3D.Create(FObject3D,FCoordPool,pts[i]);
317 Add(result);
318end;
319
320function TBGRAPart3D.Add(const pts: array of TPoint3D_128
321 ): arrayOfIBGRAVertex3D;
322var
323 i: Integer;
324begin
325 setlength(result, length(pts));
326 for i := 0 to high(pts) do
327 result[i] := TBGRAVertex3D.Create(FObject3D,FCoordPool,pts[i]);
328 Add(result);
329end;
330
331procedure TBGRAPart3D.Add(const pts: array of IBGRAVertex3D);
332var
333 i: Integer;
334begin
335 if FVertexCount + length(pts) > length(FVertices) then
336 setlength(FVertices, (FVertexCount*2 + length(pts))+1);
337 for i := 0 to high(pts) do
338 begin
339 FVertices[FVertexCount] := pts[i];
340 inc(FVertexCount);
341 end;
342end;
343
344function TBGRAPart3D.GetBoundingBox: TBox3D;
345var i: integer;
346 pt: TPoint3D_128;
347begin
348 if GetVertexCount > 0 then
349 begin
350 result.min := GetVertex(0).SceneCoord;
351 result.max := result.min;
352 end else
353 begin
354 result.min := Point3D(0,0,0);
355 result.max := Point3D(0,0,0);
356 exit;
357 end;
358 for i := 1 to GetVertexCount-1 do
359 begin
360 pt := GetVertex(i).SceneCoord_128;
361 if pt.x < result.min.x then result.min.x := pt.x else
362 if pt.x > result.max.x then result.max.x := pt.x;
363 if pt.y < result.min.y then result.min.y := pt.y else
364 if pt.y > result.max.y then result.max.y := pt.y;
365 if pt.z < result.min.z then result.min.z := pt.z else
366 if pt.z > result.max.z then result.max.z := pt.z;
367 end;
368end;
369
370function TBGRAPart3D.GetMatrix: TMatrix3D;
371begin
372 result := FMatrix;
373end;
374
375function TBGRAPart3D.GetPart(AIndex: Integer): IBGRAPart3D;
376begin
377 if (AIndex < 0) or (AIndex >= FPartCount) then
378 raise ERangeError.Create('Index of out bounds');
379 result := FParts[AIndex];
380end;
381
382function TBGRAPart3D.GetPartCount: integer;
383begin
384 result := FPartCount;
385end;
386
387function TBGRAPart3D.GetVertex(AIndex: Integer): IBGRAVertex3D;
388begin
389 if (AIndex < 0) or (AIndex >= FVertexCount) then
390 raise ERangeError.Create('Index of out bounds');
391 result := FVertices[AIndex];
392end;
393
394function TBGRAPart3D.GetVertexCount: integer;
395begin
396 result := FVertexCount;
397end;
398
399function TBGRAPart3D.GetNormal(AIndex: Integer): IBGRANormal3D;
400begin
401 if (AIndex < 0) or (AIndex >= FNormalCount) then
402 raise ERangeError.Create('Index of out bounds');
403 result := FNormals[AIndex];
404end;
405
406function TBGRAPart3D.GetNormalCount: integer;
407begin
408 result := FNormalCount;
409end;
410
411function TBGRAPart3D.GetTotalVertexCount: integer;
412var i: integer;
413begin
414 result := GetVertexCount;
415 for i := 0 to GetPartCount-1 do
416 result += GetPart(i).GetTotalVertexCount;
417end;
418
419function TBGRAPart3D.GetTotalNormalCount: integer;
420var i: integer;
421begin
422 result := GetNormalCount;
423 for i := 0 to GetPartCount-1 do
424 result += GetPart(i).GetTotalNormalCount;
425end;
426
427procedure TBGRAPart3D.ResetTransform;
428begin
429 FMatrix := MatrixIdentity3D;
430end;
431
432procedure TBGRAPart3D.Scale(size: single; Before: boolean = true);
433begin
434 Scale(size,size,size,Before);
435end;
436
437procedure TBGRAPart3D.Scale(x, y, z: single; Before: boolean = true);
438begin
439 Scale(Point3D(x,y,z),Before);
440end;
441
442procedure TBGRAPart3D.Scale(size: TPoint3D; Before: boolean = true);
443begin
444 if Before then
445 FMatrix *= MatrixScale3D(size)
446 else
447 FMatrix := MatrixScale3D(size)*FMatrix;
448end;
449
450procedure TBGRAPart3D.RotateXDeg(angle: single; Before: boolean = true);
451begin
452 RotateXRad(-angle*Pi/180, Before);
453end;
454
455procedure TBGRAPart3D.RotateYDeg(angle: single; Before: boolean = true);
456begin
457 RotateYRad(-angle*Pi/180, Before);
458end;
459
460procedure TBGRAPart3D.RotateZDeg(angle: single; Before: boolean = true);
461begin
462 RotateZRad(-angle*Pi/180, Before);
463end;
464
465procedure TBGRAPart3D.RotateXRad(angle: single; Before: boolean = true);
466begin
467 if Before then
468 FMatrix *= MatrixRotateX(angle)
469 else
470 FMatrix := MatrixRotateX(angle) * FMatrix;
471end;
472
473procedure TBGRAPart3D.RotateYRad(angle: single; Before: boolean = true);
474begin
475 if Before then
476 FMatrix *= MatrixRotateY(angle)
477 else
478 FMatrix := MatrixRotateY(angle) * FMatrix;
479end;
480
481procedure TBGRAPart3D.RotateZRad(angle: single; Before: boolean = true);
482begin
483 if Before then
484 FMatrix *= MatrixRotateZ(angle)
485 else
486 FMatrix := MatrixRotateZ(angle) * FMatrix;
487end;
488
489procedure TBGRAPart3D.SetMatrix(const AValue: TMatrix3D);
490begin
491 FMatrix := AValue;
492end;
493
494{$PUSH}{$OPTIMIZATION OFF} //avoids Internal error 2012090607
495procedure TBGRAPart3D.ComputeWithMatrix(const AMatrix: TMatrix3D; const AProjection: TProjection3D);
496var
497 i: Integer;
498 Composed: TMatrix3D;
499begin
500 Composed := AMatrix* self.FMatrix;
501 FCoordPool.ComputeWithMatrix(Composed, AProjection);
502 if Assigned(FNormalPool) then FNormalPool.ComputeWithMatrix(Composed);
503 for i := 0 to FPartCount-1 do
504 FParts[i].ComputeWithMatrix(Composed,AProjection);
505end;
506{$POP}
507
508function TBGRAPart3D.ComputeCoordinate(var ASceneCoord: TPoint3D_128; const AProjection: TProjection3D): TPointF;
509var part: IBGRAPart3D;
510 newViewCoord: TPoint3D_128;
511 InvZ: single;
512begin
513 newViewCoord := FMatrix * ASceneCoord;
514 part := FContainer;
515 while part <> nil do
516 begin
517 newViewCoord := part.Matrix * newViewCoord;
518 part := part.Container;
519 end;
520 if NewViewCoord.z > 0 then
521 begin
522 InvZ := 1/NewViewCoord.z;
523 result := PointF(NewViewCoord.x*InvZ*AProjection.Zoom.x + AProjection.Center.x,
524 NewViewCoord.y*InvZ*AProjection.Zoom.Y + AProjection.Center.y);
525 end else
526 result := PointF(0,0);
527end;
528
529procedure TBGRAPart3D.NormalizeViewNormal;
530var
531 i: Integer;
532begin
533 for i := 0 to FVertexCount-1 do
534 FVertices[i].NormalizeViewNormal;
535 for i := 0 to FPartCount-1 do
536 FParts[i].NormalizeViewNormal;
537end;
538
539procedure TBGRAPart3D.Translate(x, y, z: single; Before: boolean = true);
540begin
541 Translate(Point3D(x,y,z),Before);
542end;
543
544procedure TBGRAPart3D.Translate(ofs: TPoint3D; Before: boolean = true);
545begin
546 if Before then
547 FMatrix *= MatrixTranslation3D(ofs)
548 else
549 FMatrix := MatrixTranslation3D(ofs)*FMatrix;
550end;
551
552function TBGRAPart3D.CreatePart: IBGRAPart3D;
553begin
554 if FPartCount = length(FParts) then
555 setlength(FParts, FPartCount*2+1);
556 result := TBGRAPart3D.Create(FObject3D,self);
557 FParts[FPartCount] := result;
558 inc(FPartCount);
559end;
560
561function TBGRAPart3D.GetContainer: IBGRAPart3D;
562begin
563 result := FContainer;
564end;
565
566procedure TBGRAPart3D.SetVertex(AIndex: Integer; AValue: IBGRAVertex3D);
567begin
568 if (AIndex < 0) or (AIndex >= FVertexCount) then
569 raise ERangeError.Create('Index of out bounds');
570 FVertices[AIndex] := AValue;
571end;
572
573procedure TBGRAPart3D.SetNormal(AIndex: Integer; AValue: IBGRANormal3D);
574begin
575 if (AIndex < 0) or (AIndex >= FNormalCount) then
576 raise ERangeError.Create('Index of out bounds');
577 FNormals[AIndex] := AValue;
578end;
579
580
Note: See TracBrowser for help on using the repository browser.