2022-03-08 12:25:51 -05:00
|
|
|
{
|
|
|
|
Apophysis Copyright (C) 2001-2004 Mark Townsend
|
|
|
|
Apophysis Copyright (C) 2005-2006 Ronald Hordijk, Piotr Borys, Peter Sdobnov
|
|
|
|
Apophysis Copyright (C) 2007-2008 Piotr Borys, Peter Sdobnov
|
|
|
|
|
|
|
|
Apophysis "3D hack" Copyright (C) 2007-2008 Peter Sdobnov
|
|
|
|
Apophysis "7X" Copyright (C) 2009-2010 Georg Kiehne
|
|
|
|
Apophysis AV "Phoenix Edition" Copyright (C) 2021 Alice V. Koryagina
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
}
|
|
|
|
unit RenderingImplementation;
|
|
|
|
|
2022-06-23 06:22:32 -04:00
|
|
|
{$ifndef CPUX64}
|
2022-03-08 12:25:51 -05:00
|
|
|
{$define _ASM_}
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
2022-06-23 06:22:32 -04:00
|
|
|
{$ifdef _ASM_}
|
2022-03-08 12:25:51 -05:00
|
|
|
AsmRandom,
|
|
|
|
{$endif}
|
|
|
|
Windows, Classes, Forms, Graphics, Global,
|
|
|
|
RenderingInterface, Xform, Math, Translation,
|
|
|
|
Binary, RenderingCommon, ControlPoint, Sysutils,
|
|
|
|
BucketFillerThread;
|
|
|
|
|
|
|
|
type
|
|
|
|
TBatchProc = procedure of object;
|
|
|
|
TRenderWorkerST = class(TBaseRenderer)
|
|
|
|
|
|
|
|
protected
|
|
|
|
PropTable: array[0..PROP_TABLE_SIZE] of TXform;
|
|
|
|
finalXform: TXform;
|
|
|
|
UseFinalXform: boolean;
|
|
|
|
|
|
|
|
procedure Prepare; override;
|
|
|
|
procedure SetPixels; override;
|
|
|
|
|
|
|
|
protected
|
|
|
|
procedure IterateBatch;
|
|
|
|
procedure IterateBatchAngle;
|
|
|
|
procedure IterateBatchFX;
|
|
|
|
procedure IterateBatchAngleFX;
|
|
|
|
end;
|
|
|
|
|
|
|
|
type
|
|
|
|
TRenderWorkerMT = class(TBaseRenderer)
|
|
|
|
|
|
|
|
protected
|
|
|
|
batchcounter: Integer;
|
|
|
|
WorkingThreads: array of TBucketFillerThread;
|
|
|
|
CriticalSection: TRTLCriticalSection;
|
|
|
|
|
|
|
|
function NewThread: TBucketFillerThread;
|
|
|
|
procedure Prepare; override;
|
|
|
|
procedure SetPixels; override;
|
|
|
|
|
|
|
|
protected
|
|
|
|
procedure AddPointsToBuckets(const points: TPointsArray);
|
|
|
|
procedure AddPointsToBucketsAngle(const points: TPointsArray);
|
|
|
|
|
|
|
|
public
|
|
|
|
procedure Stop; override;
|
|
|
|
procedure BreakRender; override;
|
|
|
|
|
|
|
|
procedure Pause; override;
|
|
|
|
procedure UnPause; override;
|
|
|
|
procedure SetThreadPriority(p: TThreadPriority); override; // AV
|
|
|
|
end;
|
|
|
|
|
|
|
|
type
|
|
|
|
TRenderWorkerST_MM = class(TRenderWorkerST)
|
|
|
|
protected
|
|
|
|
procedure CalcBufferSize; override;
|
|
|
|
public
|
|
|
|
procedure Render; override;
|
|
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
type
|
|
|
|
TRenderWorkerMT_MM = class(TRenderWorkerMT)
|
|
|
|
protected
|
|
|
|
procedure CalcBufferSize; override;
|
|
|
|
public
|
|
|
|
procedure Render; override;
|
|
|
|
end;
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// PREPARE
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
procedure TRenderWorkerST.Prepare;
|
|
|
|
var
|
|
|
|
i, n: Integer;
|
|
|
|
propsum: double;
|
|
|
|
LoopValue: double;
|
|
|
|
j: integer;
|
|
|
|
TotValue: double;
|
|
|
|
begin
|
|
|
|
totValue := 0;
|
|
|
|
n := fcp.NumXforms;
|
|
|
|
assert(n > 0);
|
|
|
|
|
|
|
|
finalXform := fcp.xform[n];
|
|
|
|
finalXform.Prepare;
|
|
|
|
useFinalXform := fcp.FinalXformEnabled and fcp.HasFinalXform;
|
|
|
|
|
|
|
|
fcp.Prepare;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TRenderWorkerMT.Prepare;
|
|
|
|
begin
|
|
|
|
fcp.Prepare;
|
|
|
|
end;
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// SETPIXELS
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
procedure TRenderWorkerST.SetPixels;
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
nsamples: int64;
|
|
|
|
IterateBatchProc: procedure of object;
|
|
|
|
begin
|
|
|
|
if FNumSlices > 1 then
|
|
|
|
TimeTrace(Format(TextByKey('common-trace-rendering-multipleslices'), [FSlice + 1, FNumSlices]))
|
|
|
|
else
|
|
|
|
TimeTrace(TextByKey('common-trace-rendering-oneslice'));
|
|
|
|
|
|
|
|
Randomize;
|
|
|
|
|
|
|
|
if FCP.FAngle = 0 then begin
|
|
|
|
if UseFinalXform then
|
|
|
|
IterateBatchProc := IterateBatchFX
|
|
|
|
else
|
|
|
|
IterateBatchProc := IterateBatch;
|
|
|
|
end
|
|
|
|
else begin
|
|
|
|
if UseFinalXform then
|
|
|
|
IterateBatchProc := IterateBatchAngleFX
|
|
|
|
else
|
|
|
|
IterateBatchProc := IterateBatchAngle;
|
|
|
|
end;
|
|
|
|
|
|
|
|
NSamples := Round(sample_density * NrSlices * bucketSize / (oversample * oversample));
|
|
|
|
FNumBatches := Round(nsamples / (fcp.nbatches * SUB_BATCH_SIZE));
|
|
|
|
if FNumBatches = 0 then FNumBatches := 1;
|
|
|
|
|
|
|
|
FMinBatches := Round(FNumBatches * FMinDensity / fcp.sample_density);
|
|
|
|
if FMinBatches = 0 then FMinBatches := 1;
|
|
|
|
|
|
|
|
for i := 0 to FNumBatches-1 do
|
|
|
|
begin
|
|
|
|
if FStop <> 0 then begin
|
|
|
|
{ AV: moved below to update running quality estimate for mid-render snapshots
|
|
|
|
fcp.actual_density := fcp.actual_density + fcp.sample_density * i / FNumBatches;
|
|
|
|
}
|
|
|
|
FNumBatches := i;
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if ((i and $1F) = 0) then Progress(i / FNumBatches);
|
|
|
|
|
|
|
|
IterateBatchProc;
|
|
|
|
// AV: update running quality estimate (for mid-render snapshots)
|
|
|
|
fcp.actual_density := fcp.actual_density + fcp.sample_density / FNumBatches; // AV
|
|
|
|
|
|
|
|
Inc(FBatch);
|
|
|
|
end;
|
|
|
|
|
|
|
|
fcp.actual_density := {fcp.actual_density +} fcp.sample_density; // AV
|
|
|
|
|
|
|
|
Progress(1);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TRenderWorkerMT.SetPixels;
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
nSamples: Int64;
|
|
|
|
bc : integer;
|
|
|
|
begin
|
|
|
|
if FNumSlices > 1 then
|
|
|
|
TimeTrace(Format(TextByKey('common-trace-rendering-multipleslices'), [FSlice + 1, FNumSlices]))
|
|
|
|
else
|
|
|
|
TimeTrace(TextByKey('common-trace-rendering-oneslice'));
|
|
|
|
|
|
|
|
nSamples := Round(sample_density * NrSlices * BucketSize / (oversample * oversample));
|
|
|
|
FNumBatches := Round(nSamples / (fcp.nbatches * SUB_BATCH_SIZE));
|
|
|
|
if FNumBatches = 0 then FNumBatches := 1;
|
|
|
|
FMinBatches := Round(FNumBatches * FMinDensity / fcp.sample_density);
|
|
|
|
if FMinBatches = 0 then FMinBatches := 1; // AV
|
|
|
|
|
|
|
|
batchcounter := 1;
|
|
|
|
Randomize;
|
|
|
|
|
|
|
|
InitializeCriticalSection(CriticalSection);
|
|
|
|
|
|
|
|
SetLength(WorkingThreads, NumThreads);
|
|
|
|
for i := 0 to NumThreads - 1 do
|
|
|
|
WorkingThreads[i] := NewThread;
|
|
|
|
|
|
|
|
for i := 0 to NumThreads - 1 do
|
|
|
|
WorkingThreads[i].Resume;
|
|
|
|
|
|
|
|
bc := 1;
|
|
|
|
while (FStop = 0) and (bc <= FNumBatches) do begin
|
|
|
|
sleep(250);
|
|
|
|
try
|
|
|
|
EnterCriticalSection(CriticalSection);
|
|
|
|
|
|
|
|
Progress(batchcounter / FNumBatches);
|
|
|
|
if (bc < batchcounter) then
|
|
|
|
begin
|
|
|
|
// AV: update running quality estimate for mid-render snapshots
|
|
|
|
fcp.actual_density := fcp.actual_density +
|
|
|
|
fcp.sample_density * (BatchCounter - bc) / FNumBatches; // actual quality of incomplete render
|
|
|
|
bc := batchcounter;
|
|
|
|
end;
|
|
|
|
finally
|
|
|
|
LeaveCriticalSection(CriticalSection);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
for i := 0 to High(WorkingThreads) do begin
|
|
|
|
WorkingThreads[i].Terminate;
|
|
|
|
WorkingThreads[i].WaitFor;
|
|
|
|
WorkingThreads[i].Free;
|
|
|
|
end;
|
|
|
|
SetLength(WorkingThreads, 0);
|
|
|
|
|
|
|
|
// AV: running quality estimate is updated above; tidy up remaining counts
|
|
|
|
fcp.actual_density := fcp.actual_density +
|
|
|
|
fcp.sample_density * (BatchCounter - bc + 1) / FNumBatches; // AV: actual quality of incomplete render
|
|
|
|
// fcp.sample_density * BatchCounter / FNumBatches; // actual quality of incomplete render
|
|
|
|
FNumBatches := BatchCounter;
|
|
|
|
|
|
|
|
DeleteCriticalSection(CriticalSection);
|
|
|
|
Progress(1);
|
|
|
|
end;
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// MM OVERRIDES
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
procedure TRenderWorkerST_MM.CalcBufferSize;
|
|
|
|
begin
|
|
|
|
CalcBufferSizeMM;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TRenderWorkerST_MM.Render;
|
|
|
|
begin
|
|
|
|
RenderMM;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TRenderWorkerMT_MM.CalcBufferSize;
|
|
|
|
begin
|
|
|
|
CalcBufferSizeMM;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TRenderWorkerMT_MM.Render;
|
|
|
|
begin
|
|
|
|
RenderMM;
|
|
|
|
end;
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// BATCH ITERATION
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
procedure TRenderWorkerST.IterateBatch;
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
px, py: double;
|
|
|
|
Bucket: PBucket;
|
|
|
|
// ZBufPos: PDouble;
|
|
|
|
MapColor: PColorMapColor;
|
|
|
|
|
|
|
|
ix, iy: integer;
|
|
|
|
BmpColor: TColor;
|
|
|
|
|
|
|
|
p, q: TCPPoint;
|
|
|
|
xf: TXForm;
|
|
|
|
begin
|
|
|
|
{$ifndef _ASM_}
|
|
|
|
p.x := 2 * random - 1;
|
|
|
|
p.y := 2 * random - 1;
|
|
|
|
// AV: fixed - Apo renderer crashes if z-value is not initialized
|
|
|
|
p.z := 0; // AV
|
|
|
|
p.c := random;
|
|
|
|
{$else}
|
|
|
|
asm
|
|
|
|
fld1
|
|
|
|
call AsmRandExt
|
|
|
|
fadd st, st
|
|
|
|
fsub st, st(1)
|
|
|
|
fstp qword ptr [p.x]
|
|
|
|
call AsmRandExt
|
|
|
|
fadd st, st
|
|
|
|
fsubrp st(1), st
|
|
|
|
fstp qword ptr [p.y]
|
|
|
|
call AsmRandExt
|
|
|
|
fstp qword ptr [p.c]
|
|
|
|
fldz // AV
|
|
|
|
fstp qword ptr [p.z] // AV
|
|
|
|
end;
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
try
|
|
|
|
xf := fcp.xform[0];
|
|
|
|
for i := 0 to FUSE do begin
|
|
|
|
xf := xf.PropTable[Random(PROP_TABLE_SIZE)];
|
|
|
|
xf.NextPoint(p);
|
|
|
|
end;
|
|
|
|
|
|
|
|
for i := 0 to SUB_BATCH_SIZE-1 do begin
|
|
|
|
xf := xf.PropTable[Random(PROP_TABLE_SIZE)];
|
|
|
|
xf.NextPoint(p);
|
|
|
|
|
|
|
|
// if random >= xf.transOpacity then continue;
|
|
|
|
if (xf.transOpacity = 0) then continue // AV
|
|
|
|
else if ((xf.transOpacity < 1) and (random > xf.transOpacity)) then continue;
|
|
|
|
|
|
|
|
q := p;
|
|
|
|
fcp.ProjectionFunc(@q); // 3d hack
|
|
|
|
|
|
|
|
px := q.x - camX0;
|
|
|
|
if (px < 0) or (px > camW) then continue;
|
|
|
|
py := q.y - camY0;
|
|
|
|
if (py < 0) or (py > camH) then continue;
|
|
|
|
|
|
|
|
Bucket := @buckets[Round(bhs * py)][Round(bws * px)];
|
|
|
|
MapColor := @ColorMap[Round(p.c * 255)];
|
|
|
|
|
|
|
|
{$ifdef ENABLEZBUF}
|
|
|
|
ZBufPos := @zbuffer[Round(bhs * py)][Round(bws * px)];
|
|
|
|
if (q.z < ZBufPos^) then
|
|
|
|
begin
|
|
|
|
ZBufPos^ := q.z;
|
|
|
|
Bucket.Red := Bucket.Red + MapColor.Red;
|
|
|
|
Bucket.Green := Bucket.Green + MapColor.Green;
|
|
|
|
Bucket.Blue := Bucket.Blue + MapColor.Blue;
|
|
|
|
Bucket.Count := Bucket.Count + 1;
|
|
|
|
end;
|
|
|
|
{$else}
|
|
|
|
with Bucket^ do begin
|
|
|
|
Red := Red + MapColor.Red;
|
|
|
|
Green := Green + MapColor.Green;
|
|
|
|
Blue := Blue + MapColor.Blue;
|
|
|
|
Count := Count + 1;
|
|
|
|
end;
|
|
|
|
{$endif}
|
|
|
|
end;
|
|
|
|
|
|
|
|
except
|
|
|
|
on EMathError do begin
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TRenderWorkerST.IterateBatchAngle;
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
px, py: double;
|
|
|
|
Bucket: PBucket;
|
|
|
|
MapColor: PColorMapColor;
|
|
|
|
// ZBufPos: PDouble;
|
|
|
|
ix, iy: integer;
|
|
|
|
BmpColor: TColor;
|
|
|
|
|
|
|
|
p, q: TCPPoint;
|
|
|
|
xf: TXForm;
|
|
|
|
begin
|
|
|
|
{$ifndef _ASM_}
|
|
|
|
p.x := 2 * random - 1;
|
|
|
|
p.y := 2 * random - 1;
|
|
|
|
p.z := 0; // AV: fixed - Apo renderer crashes if z-value is not initialized
|
|
|
|
p.c := random;
|
|
|
|
{$else}
|
|
|
|
asm
|
|
|
|
fld1
|
|
|
|
call AsmRandExt
|
|
|
|
fadd st, st
|
|
|
|
fsub st, st(1)
|
|
|
|
fstp qword ptr [p.x]
|
|
|
|
call AsmRandExt
|
|
|
|
fadd st, st
|
|
|
|
fsubrp st(1), st
|
|
|
|
fstp qword ptr [p.y]
|
|
|
|
call AsmRandExt
|
|
|
|
fstp qword ptr [p.c]
|
|
|
|
fldz // AV
|
|
|
|
fstp qword ptr [p.z] // AV
|
|
|
|
end;
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
try
|
|
|
|
xf := fcp.xform[0];
|
|
|
|
for i := 0 to FUSE do begin
|
|
|
|
xf := xf.PropTable[Random(PROP_TABLE_SIZE)];
|
|
|
|
xf.NextPoint(p);
|
|
|
|
end;
|
|
|
|
|
|
|
|
for i := 0 to SUB_BATCH_SIZE-1 do begin
|
|
|
|
xf := xf.PropTable[Random(PROP_TABLE_SIZE)];
|
|
|
|
xf.NextPoint(p);
|
|
|
|
|
|
|
|
// if random >= xf.transOpacity then continue;
|
|
|
|
if (xf.transOpacity = 0) then continue // AV
|
|
|
|
else if ((xf.transOpacity < 1) and (random > xf.transOpacity)) then continue;
|
|
|
|
|
|
|
|
q := p;
|
|
|
|
fcp.ProjectionFunc(@q);
|
|
|
|
|
|
|
|
px := q.x * cosa + q.y * sina + rcX;
|
|
|
|
if (px < 0) or (px > camW) then continue;
|
|
|
|
py := q.y * cosa - q.x * sina + rcY;
|
|
|
|
if (py < 0) or (py > camH) then continue;
|
|
|
|
|
|
|
|
Bucket := @buckets[Round(bhs * py)][Round(bws * px)];
|
|
|
|
MapColor := @ColorMap[Round(p.c * 255)];
|
|
|
|
|
|
|
|
{$ifdef ENABLEZBUF}
|
|
|
|
ZBufPos := @zbuffer[Round(bhs * py)][Round(bws * px)];
|
|
|
|
if (q.z < ZBufPos^) then
|
|
|
|
begin
|
|
|
|
ZBufPos^ := q.z;
|
|
|
|
Bucket.Red := Bucket.Red + MapColor.Red;
|
|
|
|
Bucket.Green := Bucket.Green + MapColor.Green;
|
|
|
|
Bucket.Blue := Bucket.Blue + MapColor.Blue;
|
|
|
|
Bucket.Count := Bucket.Count + 1;
|
|
|
|
end;
|
|
|
|
{$else}
|
|
|
|
with Bucket^ do begin
|
|
|
|
Red := Red + MapColor.Red;
|
|
|
|
Green := Green + MapColor.Green;
|
|
|
|
Blue := Blue + MapColor.Blue;
|
|
|
|
Count := Count + 1;
|
|
|
|
end;
|
|
|
|
{$endif}
|
|
|
|
end;
|
|
|
|
|
|
|
|
except
|
|
|
|
on EMathError do begin
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TRenderWorkerST.IterateBatchFX;
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
px, py: double;
|
|
|
|
Bucket: PBucket;
|
|
|
|
MapColor: PColorMapColor;
|
|
|
|
// ZbufPos: PDouble;
|
|
|
|
ix, iy: integer;
|
|
|
|
BmpColor: TColor;
|
|
|
|
|
|
|
|
p, q: TCPPoint;
|
|
|
|
xf: TXForm;
|
|
|
|
begin
|
|
|
|
{$ifndef _ASM_}
|
|
|
|
p.x := 2 * random - 1;
|
|
|
|
p.y := 2 * random - 1;
|
|
|
|
p.z := 0; // AV: fixed - Apo renderer crashes if z-value is not initialized
|
|
|
|
p.c := random;
|
|
|
|
{$else}
|
|
|
|
asm
|
|
|
|
fld1
|
|
|
|
call AsmRandExt
|
|
|
|
fadd st, st
|
|
|
|
fsub st, st(1)
|
|
|
|
fstp qword ptr [p.x]
|
|
|
|
call AsmRandExt
|
|
|
|
fadd st, st
|
|
|
|
fsubrp st(1), st
|
|
|
|
fstp qword ptr [p.y]
|
|
|
|
call AsmRandExt
|
|
|
|
fstp qword ptr [p.c]
|
|
|
|
fldz // AV
|
|
|
|
fstp qword ptr [p.z] // AV
|
|
|
|
end;
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
try
|
|
|
|
xf := fcp.xform[0];
|
|
|
|
for i := 0 to FUSE do begin
|
|
|
|
xf := xf.PropTable[Random(PROP_TABLE_SIZE)];
|
|
|
|
xf.NextPoint(p);
|
|
|
|
end;
|
|
|
|
|
|
|
|
for i := 0 to SUB_BATCH_SIZE-1 do begin
|
|
|
|
xf := xf.PropTable[Random(PROP_TABLE_SIZE)];
|
|
|
|
xf.NextPoint(p);
|
|
|
|
|
|
|
|
if (xf.transOpacity = 0) then continue // AV
|
|
|
|
else if ((xf.transOpacity < 1) and (random > xf.transOpacity)) then continue;
|
|
|
|
|
|
|
|
finalXform.NextPointTo(p, q);
|
|
|
|
fcp.ProjectionFunc(@q);
|
|
|
|
|
|
|
|
px := q.x - camX0;
|
|
|
|
if (px < 0) or (px > camW) then continue;
|
|
|
|
py := q.y - camY0;
|
|
|
|
if (py < 0) or (py > camH) then continue;
|
|
|
|
|
|
|
|
Bucket := @buckets[Round(bhs * py)][Round(bws * px)];
|
|
|
|
MapColor := @ColorMap[Round(q.c * 255)];
|
|
|
|
|
|
|
|
{$ifdef ENABLEZBUF}
|
|
|
|
ZBufPos := @zbuffer[Round(bhs * py)][Round(bws * px)];
|
|
|
|
if (q.z < ZBufPos^) then
|
|
|
|
begin
|
|
|
|
ZBufPos^ := q.z;
|
|
|
|
Bucket.Red := Bucket.Red + MapColor.Red;
|
|
|
|
Bucket.Green := Bucket.Green + MapColor.Green;
|
|
|
|
Bucket.Blue := Bucket.Blue + MapColor.Blue;
|
|
|
|
Bucket.Count := Bucket.Count + 1;
|
|
|
|
end;
|
|
|
|
{$else}
|
|
|
|
with Bucket^ do begin
|
|
|
|
Red := Red + MapColor.Red;
|
|
|
|
Green := Green + MapColor.Green;
|
|
|
|
Blue := Blue + MapColor.Blue;
|
|
|
|
Count := Count + 1;
|
|
|
|
end;
|
|
|
|
{$endif}
|
|
|
|
end;
|
|
|
|
|
|
|
|
except
|
|
|
|
on EMathError do begin
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TRenderWorkerST.IterateBatchAngleFX;
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
px, py: double;
|
|
|
|
Bucket: PBucket;
|
|
|
|
MapColor: PColorMapColor;
|
|
|
|
// ZBufPos: PDouble;
|
|
|
|
ix, iy: integer;
|
|
|
|
BmpColor: TColor;
|
|
|
|
|
|
|
|
p, q: TCPPoint;
|
|
|
|
xf: TXForm;
|
|
|
|
begin
|
|
|
|
{$ifndef _ASM_}
|
|
|
|
p.x := 2 * random - 1;
|
|
|
|
p.y := 2 * random - 1;
|
|
|
|
p.z := 0; // AV: fixed - Apo renderer crashes if z-value is not initialized
|
|
|
|
p.c := random;
|
|
|
|
{$else}
|
|
|
|
asm
|
|
|
|
fld1
|
|
|
|
call AsmRandExt
|
|
|
|
fadd st, st
|
|
|
|
fsub st, st(1)
|
|
|
|
fstp qword ptr [p.x]
|
|
|
|
call AsmRandExt
|
|
|
|
fadd st, st
|
|
|
|
fsubrp st(1), st
|
|
|
|
fstp qword ptr [p.y]
|
|
|
|
call AsmRandExt
|
|
|
|
fstp qword ptr [p.c]
|
|
|
|
fldz // AV
|
|
|
|
fstp qword ptr [p.z] // AV
|
|
|
|
end;
|
|
|
|
{$endif}
|
|
|
|
|
|
|
|
try
|
|
|
|
xf := fcp.xform[0];
|
|
|
|
for i := 0 to FUSE do begin
|
|
|
|
xf := xf.PropTable[Random(PROP_TABLE_SIZE)];
|
|
|
|
xf.NextPoint(p);
|
|
|
|
end;
|
|
|
|
|
|
|
|
for i := 0 to SUB_BATCH_SIZE-1 do begin
|
|
|
|
xf := xf.PropTable[Random(PROP_TABLE_SIZE)];
|
|
|
|
xf.NextPoint(p);
|
|
|
|
|
|
|
|
if (xf.transOpacity = 0) then continue // AV
|
|
|
|
else if ((xf.transOpacity < 1) and (random > xf.transOpacity)) then continue;
|
|
|
|
|
|
|
|
finalXform.NextPointTo(p, q);
|
|
|
|
fcp.ProjectionFunc(@q);
|
|
|
|
|
|
|
|
px := q.x * cosa + q.y * sina + rcX;
|
|
|
|
if (px < 0) or (px > camW) then continue;
|
|
|
|
py := q.y * cosa - q.x * sina + rcY;
|
|
|
|
if (py < 0) or (py > camH) then continue;
|
|
|
|
|
|
|
|
Bucket := @buckets[Round(bhs * py)][Round(bws * px)];
|
|
|
|
MapColor := @ColorMap[Round(q.c * 255)];
|
|
|
|
|
|
|
|
{$ifdef ENABLEZBUF}
|
|
|
|
ZBufPos := @zbuffer[Round(bhs * py)][Round(bws * px)];
|
|
|
|
if (q.z < ZBufPos^) then
|
|
|
|
begin
|
|
|
|
ZBufPos^ := q.z;
|
|
|
|
Bucket.Red := Bucket.Red + MapColor.Red;
|
|
|
|
Bucket.Green := Bucket.Green + MapColor.Green;
|
|
|
|
Bucket.Blue := Bucket.Blue + MapColor.Blue;
|
|
|
|
Bucket.Count := Bucket.Count + 1;
|
|
|
|
end;
|
|
|
|
{$else}
|
|
|
|
with Bucket^ do begin
|
|
|
|
Red := Red + MapColor.Red;
|
|
|
|
Green := Green + MapColor.Green;
|
|
|
|
Blue := Blue + MapColor.Blue;
|
|
|
|
Count := Count + 1;
|
|
|
|
end;
|
|
|
|
{$endif}
|
|
|
|
end;
|
|
|
|
|
|
|
|
except
|
|
|
|
on EMathError do begin
|
|
|
|
exit;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TRenderWorkerMT.AddPointsToBuckets(const points: TPointsArray);
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
px, py: double;
|
|
|
|
Bucket: PBucket;
|
|
|
|
// ZBufPos: PDouble;
|
|
|
|
MapColor: PColorMapColor;
|
|
|
|
begin
|
|
|
|
for i := SUB_BATCH_SIZE - 1 downto 0 do begin
|
|
|
|
|
|
|
|
px := points[i].x - camX0;
|
|
|
|
if (px < 0) or (px > camW) then continue;
|
|
|
|
py := points[i].y - camY0;
|
|
|
|
if (py < 0) or (py > camH) then continue;
|
|
|
|
|
|
|
|
Bucket := @buckets[Round(bhs * py)][Round(bws * px)];
|
|
|
|
MapColor := @ColorMap[Round(points[i].c * 255)];
|
|
|
|
|
|
|
|
{$ifdef ENABLEZBUF}
|
|
|
|
ZBufPos := @zbuffer[Round(bhs * py)][Round(bws * px)];
|
|
|
|
if (points[i].z < ZBufPos^) then
|
|
|
|
begin
|
|
|
|
ZBufPos^ := points[i].z;
|
|
|
|
Bucket.Red := Bucket.Red + MapColor.Red;
|
|
|
|
Bucket.Green := Bucket.Green + MapColor.Green;
|
|
|
|
Bucket.Blue := Bucket.Blue + MapColor.Blue;
|
|
|
|
Bucket.Count := Bucket.Count + 1;
|
|
|
|
end;
|
|
|
|
{$else}
|
|
|
|
with Bucket^ do begin
|
|
|
|
Red := Red + MapColor.Red;
|
|
|
|
Green := Green + MapColor.Green;
|
|
|
|
Blue := Blue + MapColor.Blue;
|
|
|
|
Count := Count + 1;
|
|
|
|
end;
|
|
|
|
{$endif}
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TRenderWorkerMT.AddPointsToBucketsAngle(const points: TPointsArray);
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
px, py: double;
|
|
|
|
Bucket: PBucket;
|
|
|
|
MapColor: PColorMapColor;
|
|
|
|
//ZBufPos: PDouble;
|
|
|
|
begin
|
|
|
|
for i := SUB_BATCH_SIZE - 1 downto 0 do begin
|
|
|
|
|
|
|
|
px := points[i].x * cosa + points[i].y * sina + rcX;
|
|
|
|
if (px < 0) or (px > camW) then continue;
|
|
|
|
py := points[i].y * cosa - points[i].x * sina + rcY;
|
|
|
|
if (py < 0) or (py > camH) then continue;
|
|
|
|
|
|
|
|
Bucket := @buckets[Round(bhs * py)][Round(bws * px)];
|
|
|
|
MapColor := @ColorMap[Round(points[i].c * 255)];
|
|
|
|
|
|
|
|
{$ifdef ENABLEZBUF}
|
|
|
|
ZBufPos := @zbuffer[Round(bhs * py)][Round(bws * px)];
|
|
|
|
if (points[i].z < ZBufPos^) then
|
|
|
|
begin
|
|
|
|
ZBufPos^ := points[i].z;
|
|
|
|
Bucket.Red := Bucket.Red + MapColor.Red;
|
|
|
|
Bucket.Green := Bucket.Green + MapColor.Green;
|
|
|
|
Bucket.Blue := Bucket.Blue + MapColor.Blue;
|
|
|
|
Bucket.Count := Bucket.Count + 1;
|
|
|
|
end;
|
|
|
|
{$else}
|
|
|
|
with Bucket^ do begin
|
|
|
|
Red := Red + MapColor.Red;
|
|
|
|
Green := Green + MapColor.Green;
|
|
|
|
Blue := Blue + MapColor.Blue;
|
|
|
|
Count := Count + 1;
|
|
|
|
end;
|
|
|
|
{$endif}
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// THREADING
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
procedure TRenderWorkerMT.Stop;
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
begin
|
|
|
|
for i := 0 to High(WorkingThreads) do
|
|
|
|
WorkingThreads[i].Terminate;
|
|
|
|
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TRenderWorkerMT.BreakRender;
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
|
|
|
|
for i := 0 to High(WorkingThreads) do
|
|
|
|
WorkingThreads[i].Terminate;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TRenderWorkerMT.Pause;
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
|
|
|
|
for i := 0 to High(WorkingThreads) do
|
|
|
|
WorkingThreads[i].Suspend;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TRenderWorkerMT.UnPause;
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
|
|
|
|
for i := 0 to High(WorkingThreads) do
|
|
|
|
WorkingThreads[i].Resume;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TRenderWorkerMT.SetThreadPriority(p: TThreadPriority); // AV
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
|
|
|
|
for i := 0 to High(WorkingThreads) do
|
|
|
|
WorkingThreads[i].Priority := p;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TRenderWorkerMT.NewThread: TBucketFillerThread;
|
|
|
|
begin
|
|
|
|
Result := TBucketFillerThread.Create(fcp);
|
|
|
|
assert(Result <> nil);
|
|
|
|
|
|
|
|
if FThreadPriority <> tpNormal then // AV
|
|
|
|
Result.Priority := {tpLower;} FThreadPriority;
|
|
|
|
|
|
|
|
if FCP.FAngle = 0 then
|
|
|
|
Result.AddPointsProc := self.AddPointsToBuckets
|
|
|
|
else
|
|
|
|
Result.AddPointsProc := self.AddPointsToBucketsAngle;
|
|
|
|
|
|
|
|
Result.CriticalSection := CriticalSection;
|
|
|
|
Result.Nrbatches := FNumBatches;
|
|
|
|
Result.batchcounter := @batchcounter;
|
|
|
|
end;
|
|
|
|
|
|
|
|
end.
|
|
|
|
|