2
Ported to FPC by Nikolay Nikolov (nickysn@users.sourceforge.net)
6
Mojo demo for OpenPTC 1.0 C++ API
7
Coded by Alex Evans and adapted to OpenPTC 1.0 by Glenn Fiedler
9
nasty code by alex "statix" evans for ptc. (c) copyright alex evans 1998
10
time... 02.00 am on 13/1/98.
12
it's my take on some classic light mask effect
13
it's raytracing through properly modelled fog with occlusion, multiple
14
shadow rays cast back to the light for each pixel ray, and erm, its
15
s l o w... but it looks nice don't it?
17
oh and fresnel fall off... or something
21
define inv for interesting fx (not)
37
frandtab_seed : Uint16 = 54;
41
frandtab : Array[0..65535] Of Uint16;
46
False : (X, Y, Z : Single);
47
True : (R, G, B : Single);}
51
Constructor Init(_x, _y, _z : Single);
53
Function Magnitude : Single;
54
Function MagnitudeSq : Single;
58
Row : Array[0..2] Of FVector;
60
Constructor Init(a, b, c : FVector);
61
Function Column0 : FVector;
62
Function Column1 : FVector;
63
Function Column2 : FVector;
64
Procedure MakeXRot(theta : Single);
65
Procedure MakeYRot(theta : Single);
66
Procedure MakeZRot(theta : Single);
68
Function Transpose : FMatrix;
69
Procedure TransposeInPlace;
76
Constructor Init(Const p, d : FVector);
78
VLight = Class(TObject)
85
p, p2, _d : FVector; { temp space }
87
Constructor Create(Const col : FVector);
88
Procedure Move(Const q : FVector);
89
Procedure MoveT(Const q : FVector);
91
Function Light(Const ray : TRay) : FVector;
92
Function CalcLight(t : Single) : Single;
95
Constructor FVector.Init;
100
Constructor FVector.Init(_x, _y, _z : Single);
108
Function FVector.Magnitude : Single;
111
Magnitude := Sqrt(Sqr(X) + Sqr(Y) + Sqr(Z));
114
Function FVector.MagnitudeSq : Single;
117
MagnitudeSq := Sqr(X) + Sqr(Y) + Sqr(Z);
120
Procedure FVector.Normalise;
132
Operator * (a, b : FVector) res : Single;
135
res := a.X * b.X + a.Y * b.Y + a.Z * b.Z;
138
Operator * (a : FVector; b : Single) res : FVector;
146
Operator + (a, b : FVector) res : FVector;
154
Operator - (a, b : FVector) res : FVector;
162
Operator ** (a, b : FVector) res : FVector;
165
res.X := a.Y * b.Z - a.Z * b.Y;
166
res.Y := a.Z * b.X - a.X * b.Z;
167
res.Z := a.X * b.Y - a.Y * b.X;
170
Constructor FMatrix.Init;
175
Constructor FMatrix.Init(a, b, c : FVector);
183
Function FMatrix.Column0 : FVector;
189
res.Init(Row[0].X, Row[1].X, Row[2].X);
193
Function FMatrix.Column1 : FVector;
199
res.Init(Row[0].Y, Row[1].Y, Row[2].Y);
203
Function FMatrix.Column2 : FVector;
209
res.Init(Row[0].Z, Row[1].Z, Row[2].Z);
213
Procedure FMatrix.MakeXRot(theta : Single);
221
Row[1].Y := c; Row[1].Z := s; Row[1].X := 0;
222
Row[2].Y := -s; Row[2].Z := c; Row[2].X := 0;
223
Row[0].Y := 0; Row[0].Z := 0; Row[0].X := 1;
226
Procedure FMatrix.MakeYRot(theta : Single);
234
Row[2].Z := c; Row[2].X := s; Row[2].Y := 0;
235
Row[0].Z := -s; Row[0].X := c; Row[0].Y := 0;
236
Row[1].Z := 0; Row[1].X := 0; Row[1].Y := 1;
239
Procedure FMatrix.MakeZRot(theta : Single);
247
Row[0].X := c; Row[0].Y := s; Row[0].Z := 0;
248
Row[1].X := -s; Row[1].Y := c; Row[1].Z := 0;
249
Row[2].X := 0; Row[2].Y := 0; Row[2].Z := 1;
252
Procedure FMatrix.MakeID;
255
Row[0].Init(1, 0, 0);
256
Row[1].Init(0, 1, 0);
257
Row[2].Init(0, 0, 1);
260
Function FMatrix.Transpose : FMatrix;
266
res.Init(Column0, Column1, Column2);
270
Procedure FMatrix.TransposeInPlace;
273
Init(Column0, Column1, Column2);
276
Procedure FMatrix.Normalise;
280
Row[0] := Row[1]**Row[2];
282
Row[1] := Row[2]**Row[0];
286
Operator * (Const m : FMatrix; Const a : Single) res : FMatrix;
289
res.Init(m.Row[0]*a, m.Row[1]*a, m.Row[2]*a);
292
Operator * (Const m, a : FMatrix) res : FMatrix;
295
v1, v2, v3 : FVector;
298
v1.Init(m.Row[0].X*a.Row[0].X+m.Row[0].Y*a.Row[1].X+m.Row[0].Z*a.Row[2].X,
299
m.Row[0].X*a.Row[0].Y+m.Row[0].Y*a.Row[1].Y+m.Row[0].Z*a.Row[2].Y,
300
m.Row[0].X*a.Row[0].Z+m.Row[0].Y*a.Row[1].Z+m.Row[0].Z*a.Row[2].Z);
301
v2.Init(m.Row[1].X*a.Row[0].X+m.Row[1].Y*a.Row[1].X+m.Row[1].Z*a.Row[2].X,
302
m.Row[1].X*a.Row[0].Y+m.Row[1].Y*a.Row[1].Y+m.Row[1].Z*a.Row[2].Y,
303
m.Row[1].X*a.Row[0].Z+m.Row[1].Y*a.Row[1].Z+m.Row[1].Z*a.Row[2].Z);
304
v3.Init(m.Row[2].X*a.Row[0].X+m.Row[2].Y*a.Row[1].X+m.Row[2].Z*a.Row[2].X,
305
m.Row[2].X*a.Row[0].Y+m.Row[2].Y*a.Row[1].Y+m.Row[2].Z*a.Row[2].Y,
306
m.Row[2].X*a.Row[0].Z+m.Row[2].Y*a.Row[1].Z+m.Row[2].Z*a.Row[2].Z);
307
res.Init(v1, v2, v3);
310
Operator * (Const m : FMatrix; Const a : FVector) res : FVector;
313
res.Init(a*m.Row[0], a*m.Row[1], a*m.Row[2]);
316
Operator + (Const m, a : FMatrix) res : FMatrix;
319
res.Init(m.Row[0]+a.Row[0], m.Row[1]+a.Row[1], m.Row[2]+a.Row[2]);
322
Operator - (Const m, a : FMatrix) res : FMatrix;
325
res.Init(m.Row[0]+a.Row[0], m.Row[1]+a.Row[1], m.Row[2]+a.Row[2]);
328
Constructor TRay.Init(Const p, d : FVector);
336
Constructor VLight.Create(Const col : FVector);
341
mPosn.Init(0, 0, 20);
342
mTarget.Init(0, 0, 0.1);
347
Procedure VLight.Move(Const q : FVector);
354
Procedure VLight.MoveT(Const q : FVector);
361
Procedure VLight.Update;
364
mAxis.Row[2] := (mTarget - mPosn);
368
Function VLight.Light(Const ray : TRay) : FVector;
371
f, A, B, C, D, t1, t2, t3, fr, l1, l2, t, h : Single;
372
frc, x, y, q : Integer;
380
p := mAxis * (ray.mPosn - mPosn);
381
_d := mAxis * ray.mDir;
382
A := (_d.X*_d.X+_d.Y*_d.Y);
383
B := 2*(_d.X*p.X+_d.Y*p.Y)-mAng*(_d.Z);
384
C := (p.X*p.X+p.Y*p.Y)-mAng*(p.Z);
397
t3 := -ray.mPosn.Z/ray.mDir.Z;
408
{ clip to bitmap plane }
409
pp := ray.mPosn + ray.mDir*t3;
410
x := 160+Trunc(SC*pp.X);
412
If (x>=0) And (x<=319) Then
414
y := 100 + Trunc(SC*pp.Y);
415
If (y>=0) And (y<=199) Then
420
frc := MaskMap[y*320+x];
435
If (x >= 0) And (x <= 319) Then
437
y := 100 + Trunc(SC*pp.Y);
438
If (y >= 0) And (y <= 199) And (MaskMap[y*320 + x] < 128) Then
462
While (t<t3) And (q>0) And (t<t2) Do
481
While (q>0) And (t<t2) Do
504
Function VLight.CalcLight(t : Single) : Single;
511
{ trace line to bitmap from mPosn to p2 }
512
If Not ((mPosn.Z > 0) Xor (p2.Z > 0)) Then
514
{ fresnel fall off... }
515
CalcLight := p.Z / p.MagnitudeSq;
518
f := -(mPosn.Z)/(p2.Z - mPosn.Z);
519
x := 160 + Trunc(SC*((p2.X-mPosn.X)*f+mPosn.X));
521
If (x < 0) Or (x > 319) Then
523
CalcLight := p.Z / p.MagnitudeSq;
526
y := 100 + Trunc(SC*((p2.Y-mPosn.Y)*f+mPosn.Y));
527
If (y < 0) Or (y > 199) Then
529
CalcLight := p.Z / p.MagnitudeSq;
532
c := MaskMap[y * 320 + x];
534
If (x < 0) Or (x > 319) Then
539
y := 100 + Trunc(SC*((p2.Y-mPosn.Y)*f+mPosn.Y));
540
If (y < 0) Or (y > 199) Then
545
c := 255 - MaskMap[y * 320 + x];
552
CalcLight := (c*(1/255))*p.Z / p.MagnitudeSq;
555
Function CLIPC(f : Single) : Integer; {Inline;}
576
FillChar(frandtab, SizeOf(frandtab), 0);
578
For c1 := 1 To 65535 Do
580
frandtab[c1] := s And $FFFF;
581
s := (((s Shr 4) Xor (s Shr 13) Xor (s Shr 15)) And 1) + (s Shl 1);
585
Function frand : Integer; {Inline;}
588
frand := frandtab[frandtab_seed];
589
frandtab_seed := (frandtab_seed + 1) And $FFFF;
592
Procedure VLightPart(console : TPTCConsole; surface : TPTCSurface);
598
c1, c2, c3, ti, xx, yy, zz, i, a, x, y : Integer;
599
idx : Array[0..(200 Div 16) - 1, 0..(320 Div 16) - 1] Of Uint8;
600
order : Array[0..10*19 - 1, 0..1] Of Integer;
601
vlightt, t, cz, camf : Single;
605
time, delta : Single;
607
screenbuf, pd : PUint8;
614
tmp.Init(0.1, 0.4, 1);
615
vl := VLight.Create(tmp);
616
tmp.Init(1, 0.5, 0.2);
617
vl2 := VLight.Create(tmp);
623
camposn.Init(7, 0.5, -10);
627
camaxis.Row[2] := tmp - camposn;
631
MaskMap := GetMem(320 * 200);
632
FillChar(MaskMap^, 320 * 200, 0);
635
ASSign(F, 'mojo.raw');
637
BlockRead(F, MaskMap^, 320*200);
640
{ build the order of the squares }
641
For c1 := 0 To 10*19 - 1 Do
643
order[c1, 0] := c1 Mod 19;
644
order[c1, 1] := (c1 Div 19) + 1;
648
For c1 := 0 To 9999 Do
652
ti := order[c2, 0]; order[c2, 0] := order[c3, 0]; order[c3, 0] := ti;
653
ti := order[c2, 1]; order[c2, 1] := order[c3, 1]; order[c3, 1] := ti;
658
delta := 0.01; { this controls the speed of the effect }
661
While Not console.KeyPressed Do
664
pitch := surface.pitch;
666
{ light time (makes the effect loop) }
667
vlightt := 320 * Abs(Sin(time/5));
669
t := 13 - 0.1822 * vlightt;
670
cz := 1 - 0.01 * vlightt;
671
{tmp.Init(Sin(t)*5, Cos(t*-0.675+4543)*5, 15);
677
tmp.Init(-t, -7, 28);
680
camposn.Init(cz*4+9, cz, -t/7-13);
682
camaxis.Row[2] := tmp - camposn;
685
FillChar(idx, SizeOf(idx), 25);
692
ti := order[c2, 0]; order[c2, 0] := order[c3, 0]; order[c3, 0] := ti;
693
ti := order[c2, 1]; order[c2, 1] := order[c3, 1]; order[c3, 1] := ti;
695
For zz := 0 To 189 Do
702
screenbuf := surface.lock;
704
c2 := idx[yy, xx] Shr 1;
705
For c1 := 0 To c2 - 1 Do
708
x := xx * 16 + (a And 15) + 6 + 4;
709
y := yy * 16 + (a Shr 4) + 6;
712
ray.Init(camposn, camaxis.Row[2]*camf+camaxis.Row[0]*(x-160)+camaxis.Row[1]*(y-100));
713
col += vl.Light(ray);
714
col += vl2.Light(ray);
716
c := (CLIPC(col.X) Shl 16) + (CLIPC(col.Y) Shl 8) + (CLIPC(col.Z));
717
pd := screenbuf + x*4 + y*pitch;
718
Inc(i, Abs(Integer(c And 255)-Integer(pd[321] And 255)) + Abs(Integer(c Shr 16)-Integer(pd[321] Shr 16)));
720
Inc(i, Abs(Integer(c And 255)-Integer(oc And 255)) + Abs(Integer(c Shr 16)-Integer(oc Shr 16)));
723
c2_ := (c Shr 1) And $7F7F7F;
724
PUint32(pd)[1] := ((PUint32(pd)[1]) Shr 1) And $7F7F7F+ c2_;
725
PUint32(pd)[2] := ((PUint32(pd)[2]) Shr 1) And $7F7F7F+ c2_;
727
PUint32(pd)[0] := ((PUint32(pd)[0]) Shr 1) And $7F7F7F+ c2_;
730
PUint32(pd)[3] := ((PUint32(pd)[3]) Shr 1) And $7F7F7F+ c2_;
732
PUint32(pd)[0] := ((PUint32(pd)[0]) Shr 1) And $7F7F7F+ c2_;
735
PUint32(pd)[3] := ((PUint32(pd)[3]) Shr 1) And $7F7F7F+ c2_;
737
PUint32(pd)[1] := ((PUint32(pd)[1]) Shr 1) And $7F7F7F+ c2_;
738
PUint32(pd)[2] := ((PUint32(pd)[2]) Shr 1) And $7F7F7F+ c2_;
741
i := i Div (3*idx[yy, xx]);
752
If (zz Mod 95) = 0 Then
754
{ copy surface to console }
755
surface.copy(console);
771
console : TPTCConsole;
772
surface : TPTCSurface;
781
format := TPTCFormat.Create(32, $00FF0000, $0000FF00, $000000FF);
784
console := TPTCConsole.Create;
787
console.open('mojo by statix', 320, 200, format);
789
{ create main drawing surface }
790
surface := TPTCSurface.Create(320, 200, format);
792
{ do the light effect }
793
VLightPart(console, surface);
803
{ print message to stdout }
804
Writeln('mojo by alex "statix" evans');
805
Writeln('to be used as an example of bad coding and good ptc');
806
Writeln('no responsibility taken for this!');
807
Writeln('enjoy ptc! it''s great');
809
Writeln('-statix 13/1/98');
811
On error : TPTCError Do