1// SPDX-License-Identifier: LGPL-3.0-linking-exception
2{$ifdef PARAM_PHONGSSE}
3  {$asmmode intel}
4  //SSE rotate singles
5  const Shift231 = 1 + 8;
6        Shift312 = 2 + 16;
7{$endif}
8
9var
10  //Light source normal.
11  vL: TPoint3D_128;  {xmm0}
12  //Light source position.
13  vLS: TPoint3D_128; {xmm1}
14  //Vector H is the unit normal to the hypothetical surface oriented
15  //halfway between the light direction vector (L) and the viewing vector (V).
16  vH: TPoint3D_128;  {xmm2}
17
18  vN: TPoint3D_128;  {xmm3}        // surface normal
19  vP: TPoint3D_128;  {xmm4}        // position of lighted pixel
20  vV: TPoint3D_128;                // viewer direction
21{$ifdef PARAM_PHONGSSE}
22  LightDestFactor4: TPoint3D_128;  // for multiplication
23{$endif}
24
25  //Calculate LdotN and NnH
26  NH: Single;
27{$ifndef PARAM_PHONGSSE}
28  vD: TPoint3D_128;
29{$endif}
30
31  Iw, Ic: integer;    // Iw: specular intensity, Ic: ambient+diffuse intensity
32  sIw: single;        // floating point value for Iw
33
34  z, LdotN, NnH,
35  dist, distfactor, diffuseterm, specularterm: single;
36  eLight: TExpandedPixel;
37  mc,mcLeft,mcRight,mcTop,mcBottom: TBGRAPixel; ///map values
38
39{$ifdef PARAM_SIMPLECOLOR}
40  eColor: TExpandedPixel;
41{$else}
42  {$ifndef PARAM_SCANNER}
43    pcolormap: PBGRAPixel;
44  {$endif}
45{$endif}
46
47  {$hints off}
48  function ComputePixel(x,y: integer; DiffuseLight, SpecularLight: Word; Alpha: Byte): TBGRAPixel; inline;
49  var ec: TExpandedPixel;
50    {$ifndef PARAM_SIMPLECOLOR}
51      eColor: TExpandedPixel;
52    {$endif}
53  begin
54    {$ifndef PARAM_SIMPLECOLOR}
55      {$ifdef PARAM_SCANNER}
56        eColor := GammaExpansion(ColorScan.ScanNextPixel);
57      {$else}
58        eColor := GammaExpansion(pcolormap^);
59      {$endif}
60    {$endif}
61    Alpha := ApplyOpacity(Alpha, eColor.alpha shr 8);
62    ec.red := (eColor.Red*DiffuseLight+eLight.Red*SpecularLight+PhongLightPrecisionDiv2) shr PhongLightPrecisionSh;
63    ec.green := (eColor.Green*DiffuseLight+eLight.Green*SpecularLight+PhongLightPrecisionDiv2) shr PhongLightPrecisionSh;
64    ec.blue := (eColor.Blue*DiffuseLight+eLight.Blue*SpecularLight+PhongLightPrecisionDiv2) shr PhongLightPrecisionSh;
65    ec.alpha := Alpha shl 8+Alpha;
66    result := GammaCompression(ec);
67  end;
68  {$hints on}
69
70var
71  minx,miny,maxx,maxy: integer;
72  pmap: PBGRAPixel;
73  pdest: PBGRAPixel;
74  x,y : integer;      // Coordinates of point in height map.
75  vS1,vS2: TPoint3D_128; // surface vectors (plane)
76  deltaDown: Int32or64;
77  IsLineUp,IsLineDown: boolean;
78
79begin
80  if map = nil then exit;
81  {$ifndef PARAM_SIMPLECOLOR}
82    {$ifndef PARAM_SCANNER}
83      if (colorMap.Width < map.width) or (colorMap.Height < map.height) then
84        raise Exception.Create('Dimension mismatch');
85    {$endif}
86  {$endif}
87
88  if (map.width = 0) or (map.Height = 0) then exit;
89  if ofsX >= dest.ClipRect.Right then exit;
90  if ofsY >= dest.ClipRect.Bottom then exit;
91  if ofsX <= dest.ClipRect.Left-map.Width then exit;
92  if ofsY <= dest.ClipRect.Top-map.Height then exit;
93
94  minx := 0;
95  miny := 0;
96  maxx := map.Width-1;
97  maxy := map.Height-1;
98  if ofsX < dest.clipRect.Left then minx := dest.clipRect.Left-ofsX;
99  if ofsY < dest.clipRect.Top then miny := dest.clipRect.Top-ofsY;
100  if OfsX+maxx > dest.ClipRect.Right-1 then maxx := dest.ClipRect.Right-1-ofsX;
101  if OfsY+maxy > dest.ClipRect.Bottom-1 then maxy := dest.ClipRect.Bottom-1-ofsY;
102
103  eLight := GammaExpansion(LightColor);
104  {$ifdef PARAM_SIMPLECOLOR}
105    eColor := GammaExpansion(color);
106  {$endif}
107
108  //light origin
109  vLS := Point3D_128(FLightPosition3D.X-ofsX,
110                  FLightPosition3D.Y-ofsY,
111                  FLightPosition3D.Z);
112
113  //surface vectors
114  vS1 := Point3D_128(1,0,0);
115  vS2 := Point3D_128(0,1,0);
116
117  vV := Point3D_128(0,0,1);
118
119  dist := 0;
120  LdotN := 0;
121  NnH := 0;
122
123  {$ifdef PARAM_PHONGSSE}
124  LightDestFactor4 := Point3D_128(LightDestFactor,LightDestFactor,LightDestFactor,LightDestFactor);
125  {$endif}
126
127  if map.LineOrder = riloTopToBottom then
128    deltaDown := map.Width*sizeof(TBGRAPixel)
129  else
130    deltaDown := -map.Width*sizeof(TBGRAPixel);
131  for y := miny to maxy do
132  begin
133    //read map values
134    pmap := map.ScanLine[y]+minx;
135    mc := BGRAPixelTransparent;
136    mcRight := pmap^;
137    pdest := dest.ScanLine[y+ofsY]+ofsX+minx;
138    {$ifndef PARAM_SIMPLECOLOR}
139      {$ifdef PARAM_SCANNER}
140        ColorScan.ScanMoveTo(OfsX+minx,OfsY+Y);
141      {$else}
142        pcolormap := ColorMap.ScanLine[y];
143      {$endif}
144    {$endif}
145    IsLineUp := y > 0;
146    IsLineDown := y < map.Height-1;
147    mcTop := BGRAPixelTransparent;
148    mcBottom := BGRAPixelTransparent;
149    for x := minx to maxx do
150    begin
151      mcLeft := mc;
152      mc := mcRight;
153      if x < map.width-1 then
154        mcRight := (pmap+1)^ else
155        mcRight := BGRAPixelTransparent;
156      if mc.alpha = 0 then
157      begin
158        {$ifndef PARAM_SIMPLECOLOR}
159          {$ifdef PARAM_SCANNER}
160            ColorScan.ScanNextPixel;
161          {$else}
162            inc(pcolormap);
163          {$endif}
164        {$endif}
165        inc(pdest);
166        inc(pmap);
167        continue;
168      end;
169
170      //compute surface vectors
171      if IsLineUp then mcTop := pbgrapixel(pbyte(pmap)-deltaDown)^;
172      if IsLineDown then mcBottom := pbgrapixel(pbyte(pmap)+deltaDown)^;
173      inc(pmap);
174
175      z := MapHeight(mc)*mapAltitude;
176      if mcLeft.alpha = 0 then
177      begin
178        if mcRight.alpha = 0 then
179          vS1.z := 0
180        else
181          vS1.z := (MapHeight(mcRight)-MapHeight(mc))*mapAltitude*2;
182      end else
183      begin
184        if mcRight.alpha = 0 then
185          vS1.z := (MapHeight(mc)-MapHeight(mcLeft))*mapAltitude*2
186        else
187          vS1.z := (MapHeight(mcRight)-MapHeight(mcLeft))*mapAltitude;
188      end;
189      if mcTop.alpha = 0 then
190      begin
191        if mcBottom.alpha = 0 then
192          vS2.z := 0
193        else
194          vS2.z := (MapHeight(mcBottom)-MapHeight(mc))*mapAltitude*2;
195      end else
196      begin
197        if mcBottom.alpha = 0 then
198          vS2.z := (MapHeight(mc)-MapHeight(mcTop))*mapAltitude*2
199        else
200          vS2.z := (MapHeight(mcBottom)-MapHeight(mcTop))*mapAltitude;
201      end;
202
203      //position vector
204      vP := Point3D_128(x, y, z);
205      {$ifdef PARAM_PHONGSSE}
206      if UseSSE3 then
207      begin
208        {$DEFINE PARAM_USESSE3}
209        asm
210          movups xmm1, vLS
211        end;
212        {$i phongdrawsse.inc}
213        {$UNDEF PARAM_USESSE3}
214      end else
215      begin
216        asm
217          movups xmm1, vLS
218        end;
219        {$i phongdrawsse.inc}
220      end;
221      {$else}
222        vP := Point3D_128(x, y, z);
223        vL := vLS- vP*LightDestFactor;
224        Normalize3D_128(vL);
225
226        //compute bisector of angle between light and observer
227        vH := vL + vV;
228        Normalize3D_128(vH);
229
230        // compute normal vector to the surface
231        VectProduct3D_128(vS1,vS2,vN);
232        Normalize3D_128(vN);
233
234        //Calculate LdotN and NnH
235        LdotN := DotProduct3D_128(vN,vL);
236        vD := vLS-vP;
237        dist := sqrt(DotProduct3D_128(vD,vD));
238
239        NH := DotProduct3D_128(vH,vN);
240      {$endif}
241
242      if NH <= 0 then
243        NnH := 0
244      else
245        NnH := exp(SpecularIndex*ln(NH));  //to be optimized
246
247      distfactor := LightSourceIntensity / (dist*LightSourceDistanceFactor + LightSourceDistanceTerm);
248
249      if (LdotN <= 0) then //Point is not illuminated by light source.
250        //Use negative diffuse for contrast
251        diffuseterm := distfactor * NegativeDiffusionFactor * LdotN
252      else
253        diffuseterm := distfactor * DiffusionFactor * LdotN;
254      Ic := round((AmbientFactor + diffuseterm)*PhongLightPrecision);
255
256      //specular (reflection)
257      specularterm := distfactor * SpecularFactor * NnH;
258      sIw := specularterm*PhongLightPrecision;
259      if sIw > PhongLightPrecision then Iw := PhongLightPrecision else
260        Iw := round(sIw);
261
262      //intensity bounds (0..PhongLightPrecision)
263      If Ic < 0 then Ic := 0;
264      If Ic > PhongLightPrecision then
265      begin
266        If DiffuseSaturation then
267        begin
268          Iw := Iw+(Ic-PhongLightPrecision);
269          if Iw > PhongLightPrecision then Iw := PhongLightPrecision;
270        end;
271        Ic := PhongLightPrecision;
272      end;
273      Ic := Ic*(PhongLightPrecision-Iw) shr PhongLightPrecisionSh;
274
275      DrawPixelInlineWithAlphaCheck(pdest, ComputePixel(x,y,Ic,Iw,mc.alpha));
276      {$ifndef PARAM_SIMPLECOLOR}
277        {$ifndef PARAM_SCANNER}
278          inc(pcolormap);
279        {$endif}
280      {$endif}
281      inc(pdest); //go to next pixel
282    end;
283  end;
284end;
285
286{$undef PARAM_PHONGSSE}
287{$undef PARAM_SIMPLECOLOR}
288{$undef PARAM_SCANNER}
289
290