1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013-2019 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "nel/3d/zone_lighter.h"
23 #include "nel/3d/landscape.h"
24 #include "nel/3d/patchuv_locator.h"
25 #include "nel/3d/shape.h"
26 #include "nel/3d/mesh.h"
27 #include "nel/3d/mesh_multi_lod.h"
28 #include "nel/3d/mesh_mrm.h"
29 #include "nel/3d/transform_shape.h"
30 #include "nel/3d/water_shape.h"
31 #include "nel/3d/texture_file.h"
33 #include "nel/misc/common.h"
34 #include "nel/misc/thread.h"
35 #include "nel/misc/path.h"
36 #include "nel/misc/file.h"
37 #include "nel/misc/aabbox.h"
38 #include "nel/misc/algo.h"
42 # define WIN32_LEAN_AND_MEAN
43 # ifndef NL_COMP_MINGW
48 #endif // NL_OS_WINDOWS
50 using namespace NLMISC
;
54 // Define this to render the zbuffers into a bitmap zbuffer and save it into a jpeg
55 // #define SAVE_ZBUFFER "c:/temp"
61 #define DEFAULT_JITTER 0.4f
62 #define DEFAULT_ZBUFFER_LANDSCAPE_SIZE 32768
63 #define DEFAULT_ZBUFFER_OBJECT_SIZE (32768*3)
64 #define DEFAULT_SUN_DISTANCE 50000
65 #define DEFAULT_SUN_FOV (NLMISC::Pi/6)
66 #define DEFAULT_SUN_CENTER (CVector (0, 0, 0))
67 #define DEFAULT_SUN_RADIUS 5000
68 #define DEFAULT_SUN_SRQT_SAMPLES 4
70 // Bad coded: don't set too big else it allocates too much memory.
71 #define NL3D_ZONE_LIGHTER_CUBE_GRID_SIZE 16
73 // ***************************************************************************
79 To light a zone, you must first adding shadow caster triangles using the addTriangle() methods.
80 Triangles can come from landscape zones or IG meshes. Then call the lighting process with ligth().
83 - Add landscape triangles to shadow caster triangle list
84 - Tesselate the landscape to shadow accuracy (2 meters)
85 - Add others triangles to shadow caster triangle list (trees, building)
86 - AlphaTest textures can be used here
89 The lighting process uses a software zbuffers render to compute shadow attenuation of the zone.
90 CRenderZBuffer () (multithread)
91 - Render shadow caster triangles into the light zbuffers for shadows. Each z value is tested and
92 written in the pixel and in the 8 neighbor pixels.
93 - There is a zbuffer per landscape softshadow sample and an additionnal zbuffer for objects. So
94 landscape triangles cast softshadows and object (trees and building) cast antialiased shadows.
96 - Render shadow caster triangles into a heightfield used for radiosity
98 buildZoneInformation ()
100 - Tesselate the landscape to shadow accuracy (2 meters)
102 - Compute lumel positions.
103 Lumel position is the average of lumel triangles center overlapping
104 the lumel but using the shadow accuracy triangle position because
105 we need the same triangles than shadow caster polygons.
106 Border lumel position are extended to fit the patch border.
108 - Tesselate to lumel accuracy (0.5 meter)
110 - Compute lumel normal
111 - Lumel normal is the average of lumel triangles normals. The normals
112 comes from the lumel accuracy to get more precise lighting. So normals
113 are interpolated from the center of the lumel but they will be rendered
114 on the patch border. Unlike the position, we can extand the normal to the
115 border without loosing normal precision because normal interpolation is
116 aligned with the tesselation. So we need some border normal smoothing.
118 - Border normal smoothing
119 - Normals on the border of the patches are smoothed with neighbor normals.
121 CLightRunnable () (multithread)
123 - For each patches and for each lumels
126 - Compute shadow attenuation
127 - Get an antialised attenuation value in each lansdcape softshadow zbuffer.
128 Average it with jitter.
129 - Get an antialised attenuation value from the object zbuffer.
130 - Return the smaller value of the both.
132 - Compute sun lighting (dot product)
134 getSkyContribution ()
135 - Compute sky lighting (radiosity)
137 - Get the lumel position in the heightfield
138 - Lookup in the 8 2d directions for max height
139 - Compute an approximation of the sky surface visible from
141 - Store final lumel luminosity
145 // ***************************************************************************
147 inline float easineasout(float x
)
150 // cubic tq f(0)=0, f'(0)=0, f(1)=1, f'(1)=0.
157 // ***************************************************************************
159 inline void transformVectorToZBuffer (const CZoneLighter::CZBuffer
& zbuffer
, const CVector
&world
, CVector
&projected
)
161 projected
= zbuffer
.WorldToZBuffer
* world
;
162 float temp
= projected
.z
;
163 projected
.z
= projected
.y
;
165 projected
= zbuffer
.WorldToZBufferFrustum
.project (projected
);
167 // Scale to zbuffer size
168 projected
.x
*= zbuffer
.ZBufferPixelSize
;
169 projected
.y
*= zbuffer
.ZBufferPixelSize
;
173 // ***********************************************************
175 static const sint DeltaZ
[9][2]=
188 // ***************************************************************************
190 inline float testZPercentageCloserFilter (float x
, float y
, float z
, CZoneLighter::CZBuffer
&zbuffer
, const CZoneLighter::CLightDesc
&description
, bool &zBufferOverflowFlag
)
192 // See "Rendering Antialiased Shadows With Depth Maps" Reeves, Salesint, Cook, ACM 1987
194 // Bilinear filtering
196 float biliValues
[2][2];
198 float ix
= (float)floor (x
-0.5f
);
199 float factorX
= x
- (ix
+0.5f
);
200 nlassert (factorX
>=0);
201 nlassert (factorX
<=1);
203 float iy
= (float)floor (y
-0.5f
);
204 float factorY
= y
- (iy
+0.5f
);
205 nlassert (factorY
>=0);
206 nlassert (factorY
<=1);
209 for (dy
=0; dy
<2; dy
++)
210 for (dx
=0; dx
<2; dx
++)
212 const sint fx
= dx
+ (sint
)ix
;
213 const sint fy
= dy
+ (sint
)iy
;
214 if ((fx
>= 0) && (fx
< zbuffer
.LocalZBufferWidth
) && (fy
>= 0) && (fy
< zbuffer
.LocalZBufferHeight
))
216 const float zRed
= zbuffer
.Pixels
[fx
+ (zbuffer
.LocalZBufferHeight
- 1 - fy
) * zbuffer
.LocalZBufferWidth
];
218 biliValues
[dx
][dy
] = (zRed
< (-z
)) ? 0.f
: 1.f
;
222 biliValues
[dx
][dy
] = 1;
223 zBufferOverflowFlag
= true;
228 return (biliValues
[0][0] * (1 - factorX
) + biliValues
[1][0] * factorX
) * (1 - factorY
) +
229 (biliValues
[0][1] * (1 - factorX
) + biliValues
[1][1] * factorX
) * factorY
;
232 // ***************************************************************************
234 CZoneLighter::CZoneLighter () : _PatchComputed ("PatchComputed")
238 // ***************************************************************************
240 void CZoneLighter::init ()
242 // Precalc some values
243 for (uint i
=0; i
<8; i
++)
245 // Precalc sinP and cosP
246 float sinP
=(float)(sin((Pi
/4)*(i
+0.5))-sin((Pi
/4)*(i
-0.5)));
247 float cosP
=(float)(cos((Pi
/4)*(i
-0.5))-cos((Pi
/4)*(i
+0.5)));
249 for (uint phi
=0; phi
<256; phi
++)
252 float fPhi
=(float)((Pi
/2)*phi
/256.0);
255 float tmp0
=(float)(fPhi
-sin(2*fPhi
)/2);
256 float tmp1
=(float)sin(fPhi
);
259 _K
[phi
][i
].set (tmp0
*sinP
, tmp0
*cosP
, (float)((Pi
/4)*tmp1
*tmp1
));
263 // Init some containers
264 _ZBufferOverflow
= false;
268 // ***************************************************************************
270 // N - NW - W - SW - S - SE - E - NE
271 static const sint deltaDirection
[8][2]=
283 // ***************************************************************************
285 float CZoneLighter::calcSkyContribution (sint s
, sint t
, float height
, float skyIntensity
, const CVector
& normal
) const
288 float skyContribution
;
293 // For the height direction
294 for (uint i
=0; i
<8; i
++)
296 // Get phi for this point
297 uint8 phi
=getMaxPhi (s
, t
, deltaDirection
[i
][0], deltaDirection
[i
][1], height
);
303 // Finalize sky contribution
304 skyContribution
=(float)(skyIntensity
*(normal
*k
)/(2*Pi
));
305 skyContribution
=(float)(skyIntensity
*(normal
*k
)/(2*Pi
));
306 clamp (skyContribution
, 0.f
, 1.f
);
307 return skyContribution
;
310 // ***************************************************************************
312 void NEL3DCalcBase (CVector
&direction
, CMatrix
& matrix
)
314 direction
.normalize();
315 CVector K
=-direction
;
316 CVector I
=CVector::K
^K
;
322 matrix
.setRot(I
,J
,K
, true);
325 // ***************************************************************************
327 void setCPUMask (IThread
*thread
, uint process
)
329 // Set the processor mask
330 uint64 mask
= IProcess::getCurrentProcess()->getCPUMask ();
332 // Mask must not be NULL
333 nlassert (mask
!= 0);
341 if (mask
& (UINT64_CONSTANT(1)<<i
))
343 if (count
== process
)
353 // thread->setCPUMask (1<<i);
357 // ***************************************************************************
359 class NL3D::CLightRunnable
: public IRunnable
363 CZoneLighter
*_ZoneLighter
;
364 const CZoneLighter::CLightDesc
*_Description
;
371 CLightRunnable (uint process
, CZoneLighter
*zoneLighter
, const CZoneLighter::CLightDesc
*description
)
373 _ZoneLighter
= zoneLighter
;
375 _Description
= description
;
382 // setCPUMask (Thread, _Process);
384 _ZoneLighter
->processCalc (_Process
, *_Description
);
385 _ZoneLighter
->_ProcessExitedMutex
.enter();
386 _ZoneLighter
->_ProcessExited
++;
387 _ZoneLighter
->_ProcessExitedMutex
.leave();
392 // ***************************************************************************
394 class NL3D::CRenderZBuffer
: public IRunnable
398 CZoneLighter
*_ZoneLighter
;
400 // The lighting decription
401 const CZoneLighter::CLightDesc
*_Description
;
403 // Triangles to render
406 const vector
<CZoneLighter::CTriangle
> *_Triangles
;
413 CRenderZBuffer (uint process
, CZoneLighter
*zoneLighter
, const CZoneLighter::CLightDesc
*description
, uint firstTriangle
, uint numTriangle
, const vector
<CZoneLighter::CTriangle
> *triangles
)
415 _ZoneLighter
= zoneLighter
;
416 _Description
= description
;
418 _FirstTriangle
= firstTriangle
;
419 _NumTriangle
= numTriangle
;
420 _Triangles
= triangles
;
427 // ***************************************************************************
429 #define CLIPPED_TOP 1
430 #define CLIPPED_BOTTOM 2
431 #define CLIPPED_RIGHT 3
432 #define CLIPPED_LEFT 4
433 #define CLIPPED_ALL (CLIPPED_TOP|CLIPPED_BOTTOM|CLIPPED_LEFT|CLIPPED_RIGHT)
435 void RenderTriangle (const CZoneLighter::CTriangle
&triangle
, const CZoneLighter::CLightDesc
&description
, CPolygon2D::TRasterVect
&borders
,
436 CFastMutex
&mutex
, CZoneLighter::CZBuffer
&zbuffer
, uint radius
)
438 // *** Transform it in the zbuffer basis
440 // 2d polygon used for rasteriation
442 zBasis
.Vertices
.resize (3);
444 // 3d polygon used for the gradient
445 NLMISC::CTriangle gradientTriangle
;
454 for (uint j
=0; j
<3; j
++)
456 // Pointer on the vector
457 const CVector
*pt
= (&triangle
.Triangle
.V0
)+j
;
458 CVector
*ptDest
= (&gradientTriangle
.V0
)+j
;
460 // Transform it in the zbuffer basis
461 transformVectorToZBuffer (zbuffer
, *pt
, *ptDest
);
464 if (ptDest
->x
>= zbuffer
.LocalZBufferXMin
)
466 if (ptDest
->x
<= zbuffer
.LocalZBufferXMax
)
468 if (ptDest
->y
>= zbuffer
.LocalZBufferYMin
)
470 if (ptDest
->y
<= zbuffer
.LocalZBufferYMax
)
471 in
|= CLIPPED_BOTTOM
;
474 zBasis
.Vertices
[j
].x
= ptDest
->x
- (float)zbuffer
.LocalZBufferXMin
;
475 zBasis
.Vertices
[j
].y
= ptDest
->y
- (float)zbuffer
.LocalZBufferYMin
;
476 ooz
[j
] = 1.f
/ ptDest
->z
;
483 if (in
== CLIPPED_ALL
)
488 zBasis
.computeBorders (borders
, minimumY
);
490 // Compute the gradient for one over z
492 gradientTriangle
.computeGradient (ooz
[0], ooz
[1], ooz
[2], ozzGradient
);
495 bool needUV
= triangle
.Texture
!= NULL
;
497 // Compute the gradient for uv
502 gradientTriangle
.computeGradient (triangle
.U
[0], triangle
.U
[1], triangle
.U
[2], uGradient
);
503 gradientTriangle
.computeGradient (triangle
.V
[0], triangle
.V
[1], triangle
.V
[2], vGradient
);
506 // Texture information
509 const CObjectVector
<uint8
> *pixels
= 0;
513 pixels
= &triangle
.Texture
->getPixels ();
515 // Get width and height
516 width
= triangle
.Texture
->getWidth ();
517 height
= triangle
.Texture
->getHeight ();
520 // For each scanlines
521 sint y
= std::max (minimumY
, 0);
522 sint yMax
= std::min ((sint
)(minimumY
+borders
.size ()), zbuffer
.LocalZBufferHeight
);
526 const CPolygon2D::TRaster
&raster
= borders
[y
-minimumY
];
528 // Gradient y for ooz, u and v
529 const float deltaY
= (float)y
- zBasis
.Vertices
[0].y
;
530 const float oozGradientY
= deltaY
* ozzGradient
.y
;
531 float uGradientY
=0.0f
;
532 float vGradientY
=0.0f
;
535 uGradientY
= deltaY
* uGradient
.y
;
536 vGradientY
= deltaY
* vGradient
.y
;
540 sint x
= std::max (raster
.first
, 0);
541 sint xMax
= std::min (raster
.second
+1, zbuffer
.LocalZBufferWidth
);
544 // Gradient x for ooz, u and v
545 const float deltaX
= (float)x
- zBasis
.Vertices
[0].x
;
546 const float oozGradientX
= deltaX
* ozzGradient
.x
;
547 float uGradientX
=0.0f
;
548 float vGradientX
=0.0f
;
551 uGradientX
= deltaX
* uGradient
.x
;
552 vGradientX
= deltaX
* vGradient
.x
;
556 float z
= - 1.f
/ (ooz
[0] + oozGradientX
+ oozGradientY
);
561 bool alphaTest
= true;
565 u
= triangle
.U
[0] + uGradientX
+ uGradientY
;
566 v
= triangle
.V
[0] + vGradientX
+ vGradientY
;
569 if (triangle
.Flags
& CZoneLighter::CTriangle::ClampU
)
572 u
-= (float)floor (u
);
573 if (triangle
.Flags
& CZoneLighter::CTriangle::ClampV
)
576 v
-= (float)floor (v
);
578 // Lookup in the texture
581 clamp (u
, 0, width
-1);
582 clamp (v
, 0, height
-1);
583 uint8 alpha
= ((const CRGBA
*)&((*pixels
)[(((uint
)u
)+((uint
)v
)*width
)*sizeof (CRGBA
)]))->A
;
586 alphaTest
= alpha
>= triangle
.AlphaTestThreshold
;
597 for (d
=0; d
<radius
; d
++)
599 // Ref in the zbuffer
600 sint fx
= x
+ DeltaZ
[d
][0];
601 sint fy
= y
+ DeltaZ
[d
][1];
602 if ( (fx
>= 0) && (fx
< zbuffer
.LocalZBufferWidth
) && (fy
>= 0) && (fy
< zbuffer
.LocalZBufferHeight
) )
604 float &zValue
= zbuffer
.Pixels
[fx
+(zbuffer
.LocalZBufferHeight
-fy
-1)*zbuffer
.LocalZBufferWidth
];
609 // Render z in zbuffer
624 void NL3D::CRenderZBuffer::run()
627 // setCPUMask (Thread, _Process);
630 CPolygon2D::TRasterVect borders
;
632 // For each triangles
634 for (i
=_FirstTriangle
; i
<_FirstTriangle
+_NumTriangle
; i
++)
636 // Triangle reference
637 const CZoneLighter::CTriangle
&triangle
= (*_Triangles
)[i
];
639 // Keep backface and doublesided polygons
640 if ((triangle
.Flags
& CZoneLighter::CTriangle::DoubleSided
) || ((triangle
.getPlane ().getNormal() * _ZoneLighter
->_SunDirection
) > 0))
642 // Landscape triangle ?
643 if (triangle
.Flags
& CZoneLighter::CTriangle::Landscape
)
645 // For each landscape zbuffer
647 const uint samples
= _Description
->SoftShadowSamplesSqrt
*_Description
->SoftShadowSamplesSqrt
;
648 for (sample
=0; sample
<samples
; sample
++)
650 RenderTriangle (triangle
, *_Description
, borders
, _ZoneLighter
->_Mutex
, _ZoneLighter
->_ZBufferLandscape
[sample
], 9);
655 // Render in a high resolution zbuffer
656 RenderTriangle (triangle
, *_Description
, borders
, _ZoneLighter
->_Mutex
, _ZoneLighter
->_ZBufferObject
, 1);
659 _ZoneLighter
->_NumberOfPatchComputed
++;
663 _ZoneLighter
->_ProcessExitedMutex
.enter();
664 _ZoneLighter
->_ProcessExited
++;
665 _ZoneLighter
->_ProcessExitedMutex
.leave();
668 // ***************************************************************************
670 class NL3D::CCalcLightableShapeRunnable
: public IRunnable
673 CCalcLightableShapeRunnable(uint process
,
674 CZoneLighter
*zoneLighter
,
675 const CZoneLighter::CLightDesc
*description
,
676 CZoneLighter::TShapeVect
*shapeToLit
,
681 _ZoneLighter(zoneLighter
),
682 _Description(description
),
683 _ShapesToLit(shapeToLit
),
684 _FirstShape(firstShape
),
685 _LastShape(lastShape
),
691 _ZoneLighter
->processLightableShapeCalc(_Process
, _ShapesToLit
, _FirstShape
, _LastShape
, *_Description
);
692 _ZoneLighter
->_ProcessExitedMutex
.enter();
693 _ZoneLighter
->_ProcessExited
++;
694 _ZoneLighter
->_ProcessExitedMutex
.leave();
697 CZoneLighter
*_ZoneLighter
;
698 const CZoneLighter::CLightDesc
*_Description
;
699 CZoneLighter::TShapeVect
*_ShapesToLit
;
700 uint _FirstShape
, _LastShape
;
705 // ***************************************************************************
707 void draw2dLine (CBitmap
&bitmap
, float x0
, float y0
, float x1
, float y1
, const CRGBA
&color
)
709 static vector
< std::pair
<sint
, sint
> > lines
;
710 drawFullLine (x0
, y0
, x1
, y1
, lines
);
713 CRGBA
*pixels
= (CRGBA
*)&(bitmap
.getPixels ()[0]);
716 sint width
= (sint
)bitmap
.getWidth ();
717 sint height
= (sint
)bitmap
.getHeight ();
721 for (i
=0; i
<lines
.size (); i
++)
723 sint x
= lines
[i
].first
;
724 sint y
= lines
[i
].second
;
727 if ( (x
>= 0) && (x
< width
) && (y
>= 0) && (y
< height
) )
729 pixels
[x
+(height
-y
-1)*width
] = color
;
734 // ***************************************************************************
736 void InitZBuffer (CZoneLighter::CZBuffer
&zbuffer
, const CVector
&SunPosition
, const CMatrix
&rayBasis
, const CAABBoxExt
&zoneBB
, uint zBufferPixelSize
, const CZoneLighter::CLightDesc
& description
)
738 // Clac the zbuffer world size
739 const float zBufferWorldSize
= (float)(tan (description
.SunFOV
/2)*description
.SunDistance
*2);
741 // ** Compute the zbuffer basis
742 zbuffer
.WorldToZBuffer
.identity ();
744 zbuffer
.WorldToZBuffer
= rayBasis
;
745 zbuffer
.WorldToZBuffer
.setPos (SunPosition
);
746 zbuffer
.WorldToZBuffer
.invert ();
747 zbuffer
.WorldToZBufferFrustum
.init ((float)zBufferWorldSize
, (float)zBufferWorldSize
, description
.SunDistance
, description
.SunDistance
*2);
750 zbuffer
.ZBufferPixelSize
= zBufferPixelSize
;
752 // Evaluate the size of the local zbuffer
754 // The zone bounding box
755 CVector bMin
= zoneBB
.getMin ();
756 CVector bMax
= zoneBB
.getMax ();
757 transformVectorToZBuffer (zbuffer
, CVector (bMin
.x
, bMax
.y
, bMin
.z
), zbuffer
.BoundingBoxVectors
[0]);
758 transformVectorToZBuffer (zbuffer
, CVector (bMin
.x
, bMin
.y
, bMin
.z
), zbuffer
.BoundingBoxVectors
[1]);
759 transformVectorToZBuffer (zbuffer
, CVector (bMax
.x
, bMin
.y
, bMin
.z
), zbuffer
.BoundingBoxVectors
[2]);
760 transformVectorToZBuffer (zbuffer
, CVector (bMax
.x
, bMax
.y
, bMin
.z
), zbuffer
.BoundingBoxVectors
[3]);
761 transformVectorToZBuffer (zbuffer
, CVector (bMin
.x
, bMax
.y
, bMax
.z
), zbuffer
.BoundingBoxVectors
[4]);
762 transformVectorToZBuffer (zbuffer
, CVector (bMin
.x
, bMin
.y
, bMax
.z
), zbuffer
.BoundingBoxVectors
[5]);
763 transformVectorToZBuffer (zbuffer
, CVector (bMax
.x
, bMin
.y
, bMax
.z
), zbuffer
.BoundingBoxVectors
[6]);
764 transformVectorToZBuffer (zbuffer
, CVector (bMax
.x
, bMax
.y
, bMax
.z
), zbuffer
.BoundingBoxVectors
[7]);
766 // Get the min and max
767 zbuffer
.LocalZBufferXMin
= 0x7fffffff;
768 zbuffer
.LocalZBufferYMin
= 0x7fffffff;
769 zbuffer
.LocalZBufferXMax
= 0x80000000;
770 zbuffer
.LocalZBufferYMax
= 0x80000000;
771 zbuffer
.LocalZBufferZMin
= FLT_MAX
;
772 zbuffer
.LocalZBufferZMax
= -FLT_MAX
;
776 sint minX
= (sint
)floor (zbuffer
.BoundingBoxVectors
[j
].x
);
777 sint maxX
= (sint
)ceil (zbuffer
.BoundingBoxVectors
[j
].x
);
778 sint minY
= (sint
)floor (zbuffer
.BoundingBoxVectors
[j
].y
);
779 sint maxY
= (sint
)ceil (zbuffer
.BoundingBoxVectors
[j
].y
);
780 if (minX
<zbuffer
.LocalZBufferXMin
)
781 zbuffer
.LocalZBufferXMin
= minX
;
782 if (maxX
>zbuffer
.LocalZBufferXMax
)
783 zbuffer
.LocalZBufferXMax
= maxX
;
784 if (minY
<zbuffer
.LocalZBufferYMin
)
785 zbuffer
.LocalZBufferYMin
= minY
;
786 if (maxY
>zbuffer
.LocalZBufferYMax
)
787 zbuffer
.LocalZBufferYMax
= maxY
;
788 if ((-zbuffer
.BoundingBoxVectors
[j
].z
)<zbuffer
.LocalZBufferZMin
)
789 zbuffer
.LocalZBufferZMin
= -zbuffer
.BoundingBoxVectors
[j
].z
;
790 if ((-zbuffer
.BoundingBoxVectors
[j
].z
)>zbuffer
.LocalZBufferZMax
)
791 zbuffer
.LocalZBufferZMax
= -zbuffer
.BoundingBoxVectors
[j
].z
;
794 // Expand the zbuffer
795 zbuffer
.LocalZBufferXMax
++;
796 zbuffer
.LocalZBufferXMin
--;
797 zbuffer
.LocalZBufferYMax
++;
798 zbuffer
.LocalZBufferYMin
--;
800 zbuffer
.LocalZBufferWidth
= zbuffer
.LocalZBufferXMax
-zbuffer
.LocalZBufferXMin
;
801 zbuffer
.LocalZBufferHeight
= zbuffer
.LocalZBufferYMax
-zbuffer
.LocalZBufferYMin
;
803 // Resize and clear the zbuffer
804 zbuffer
.Pixels
.resize (0);
805 zbuffer
.Pixels
.resize (zbuffer
.LocalZBufferWidth
*zbuffer
.LocalZBufferHeight
, FLT_MAX
);
808 // ***************************************************************************
811 void SaveZBuffer (CZoneLighter::CZBuffer
&zbuffer
, const char *filename
)
815 bitmap
.resize (zbuffer
.LocalZBufferWidth
, zbuffer
.LocalZBufferHeight
, CBitmap::Luminance
);
818 CObjectVector
<uint8
> &pixels
= bitmap
.getPixels ();
821 uint samples
= zbuffer
.LocalZBufferWidth
*zbuffer
.LocalZBufferHeight
;
822 for (uint i
=0; i
<samples
; i
++)
825 float value
= (zbuffer
.Pixels
[i
] - zbuffer
.LocalZBufferZMin
) * 255 / (zbuffer
.LocalZBufferZMax
- zbuffer
.LocalZBufferZMin
);
826 clamp (value
, 0, 255);
827 pixels
[i
] = (uint8
)value
;
831 bitmap
.convertToType (CBitmap::RGBA
);
833 // Draw some red lines
834 draw2dLine (bitmap
, zbuffer
.BoundingBoxVectors
[0].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[0].y
-(float)zbuffer
.LocalZBufferYMin
, zbuffer
.BoundingBoxVectors
[1].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[1].y
-(float)zbuffer
.LocalZBufferYMin
, CRGBA::Red
);
835 draw2dLine (bitmap
, zbuffer
.BoundingBoxVectors
[0].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[0].y
-(float)zbuffer
.LocalZBufferYMin
, zbuffer
.BoundingBoxVectors
[3].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[3].y
-(float)zbuffer
.LocalZBufferYMin
, CRGBA::Red
);
836 draw2dLine (bitmap
, zbuffer
.BoundingBoxVectors
[2].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[2].y
-(float)zbuffer
.LocalZBufferYMin
, zbuffer
.BoundingBoxVectors
[1].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[1].y
-(float)zbuffer
.LocalZBufferYMin
, CRGBA::Red
);
837 draw2dLine (bitmap
, zbuffer
.BoundingBoxVectors
[2].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[2].y
-(float)zbuffer
.LocalZBufferYMin
, zbuffer
.BoundingBoxVectors
[3].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[3].y
-(float)zbuffer
.LocalZBufferYMin
, CRGBA::Red
);
838 draw2dLine (bitmap
, zbuffer
.BoundingBoxVectors
[4].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[4].y
-(float)zbuffer
.LocalZBufferYMin
, zbuffer
.BoundingBoxVectors
[5].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[5].y
-(float)zbuffer
.LocalZBufferYMin
, CRGBA::Red
);
839 draw2dLine (bitmap
, zbuffer
.BoundingBoxVectors
[4].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[4].y
-(float)zbuffer
.LocalZBufferYMin
, zbuffer
.BoundingBoxVectors
[7].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[7].y
-(float)zbuffer
.LocalZBufferYMin
, CRGBA::Red
);
840 draw2dLine (bitmap
, zbuffer
.BoundingBoxVectors
[6].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[6].y
-(float)zbuffer
.LocalZBufferYMin
, zbuffer
.BoundingBoxVectors
[5].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[5].y
-(float)zbuffer
.LocalZBufferYMin
, CRGBA::Red
);
841 draw2dLine (bitmap
, zbuffer
.BoundingBoxVectors
[6].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[6].y
-(float)zbuffer
.LocalZBufferYMin
, zbuffer
.BoundingBoxVectors
[7].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[7].y
-(float)zbuffer
.LocalZBufferYMin
, CRGBA::Red
);
842 draw2dLine (bitmap
, zbuffer
.BoundingBoxVectors
[0].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[0].y
-(float)zbuffer
.LocalZBufferYMin
, zbuffer
.BoundingBoxVectors
[4].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[4].y
-(float)zbuffer
.LocalZBufferYMin
, CRGBA::Red
);
843 draw2dLine (bitmap
, zbuffer
.BoundingBoxVectors
[1].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[1].y
-(float)zbuffer
.LocalZBufferYMin
, zbuffer
.BoundingBoxVectors
[5].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[5].y
-(float)zbuffer
.LocalZBufferYMin
, CRGBA::Red
);
844 draw2dLine (bitmap
, zbuffer
.BoundingBoxVectors
[2].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[2].y
-(float)zbuffer
.LocalZBufferYMin
, zbuffer
.BoundingBoxVectors
[6].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[6].y
-(float)zbuffer
.LocalZBufferYMin
, CRGBA::Red
);
845 draw2dLine (bitmap
, zbuffer
.BoundingBoxVectors
[3].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[3].y
-(float)zbuffer
.LocalZBufferYMin
, zbuffer
.BoundingBoxVectors
[7].x
-(float)zbuffer
.LocalZBufferXMin
, zbuffer
.BoundingBoxVectors
[7].y
-(float)zbuffer
.LocalZBufferYMin
, CRGBA::Red
);
851 if (outputZFile
.open (filename
))
857 bitmap
.convertToType (CBitmap::RGBA
);
860 bitmap
.writeJPG (outputZFile
, 128);
862 catch (const Exception
& except
)
865 nlwarning ("ERROR writing %s: %s\n", filename
, except
.what());
870 // Error can't open the file
871 nlwarning ("ERROR Can't open %s for writing\n", filename
);
874 #endif // SAVE_ZBUFFER
876 // ***************************************************************************
878 void FilterZBuffer (CZoneLighter::CZBuffer
&zbuffer
, uint filterRadius
)
880 // Resize the temp buffer
881 static std::vector
<float> tempPixels
;
882 tempPixels
= zbuffer
.Pixels
;
885 for (y
=0; y
<zbuffer
.LocalZBufferHeight
; y
++)
886 for (x
=0; x
<zbuffer
.LocalZBufferWidth
; x
++)
889 float &newValue
= tempPixels
[x
+y
*zbuffer
.LocalZBufferWidth
];
892 for (n
=1; n
<filterRadius
; n
++)
894 const sint fx
= x
+ DeltaZ
[n
][0];
895 const sint fy
= y
+ DeltaZ
[n
][1];
898 if ( (fx
>=0) && (fx
< zbuffer
.LocalZBufferWidth
) && (fy
>=0) && (fy
< zbuffer
.LocalZBufferHeight
) )
900 const float &testValue
= zbuffer
.Pixels
[fx
+fy
*zbuffer
.LocalZBufferWidth
];
901 if (testValue
< newValue
)
902 newValue
= testValue
;
908 // Copy the new zbuffer
909 zbuffer
.Pixels
= tempPixels
;
912 // ***************************************************************************
914 void CZoneLighter::light (CLandscape
&landscape
, CZone
& output
, uint zoneToLight
, const CLightDesc
& description
, std::vector
<CTriangle
>& obstacles
, vector
<uint
> &listZone
)
920 * - Create a quad grid to store shadow casting triangles
921 * - Create a heightfield used for global illumination. Cells are initialized with -FLT_MAX
922 * - Insert each shadow casting triangles in the quad grid and fill the heightfield's cells overlapped by the bounding box of the triangle with
923 * the max height of the triangle if its height is > than the current height in the heightfield's cell.
927 // Backup thread mask
928 IThread
*currentThread
= IThread::getCurrentThread ();
929 uint64 threadMask
= currentThread
->getCPUMask();
930 // currentThread->setCPUMask (1);
932 // Calc the ray basis
933 _SunDirection
=description
.SunDirection
;
934 NEL3DCalcBase (_SunDirection
, _RayBasis
);
937 _ZoneToLight
=zoneToLight
;
940 _Landscape
=&landscape
;
943 _ProcessCount
=description
.NumCPU
;
944 if (_ProcessCount
==0)
946 // Create a doomy thread
947 IProcess
*pProcess
=IProcess::getCurrentProcess ();
948 _CPUMask
= pProcess
->getCPUMask();
953 if (_CPUMask
&((uint64
)1<<i
))
957 if (_ProcessCount
>MAX_CPU_PROCESS
)
958 _ProcessCount
=MAX_CPU_PROCESS
;
960 // Number of obstacle polygones
961 nlinfo ("Obstacle polygones : %u", (uint
)obstacles
.size ());
963 // Number of CPUS used
964 nlinfo ("Number of CPU used: %u", _ProcessCount
);
967 CZone
*pZone
=landscape
.getZone (_ZoneToLight
);
970 // *** Compute center of the object
972 // Get the zone bounding box
973 const CAABBoxExt
&zoneBB
=pZone
->getZoneBB();
976 CVector center
= zoneBB
.getCenter ();
978 // *** Compute planes
979 const uint size
=(uint
)obstacles
.size();
981 for (triangleId
=0; triangleId
<size
; triangleId
++)
984 CZoneLighter::CTriangle
& triangle
=obstacles
[triangleId
];
987 triangle
._Plane
.make (triangle
.Triangle
.V0
, triangle
.Triangle
.V1
, triangle
.Triangle
.V2
);
990 // Create landscape zbuffers
991 _ZBufferLandscape
.resize (description
.SoftShadowSamplesSqrt
*description
.SoftShadowSamplesSqrt
);
995 for (sampleY
=0; sampleY
<description
.SoftShadowSamplesSqrt
; sampleY
++)
996 for (sampleX
=0; sampleX
<description
.SoftShadowSamplesSqrt
; sampleX
++)
998 // *** Render the light zbuffer
999 CZBuffer
&zbuffer
= _ZBufferLandscape
[sampleX
+ sampleY
*description
.SoftShadowSamplesSqrt
];
1001 // Delta pos for area light
1002 float deltaX
= ( (float)sampleX
+ 0.5f
- (float)description
.SoftShadowSamplesSqrt
/ 2.f
) / (float)description
.SoftShadowSamplesSqrt
;
1003 float deltaY
= ( (float)sampleY
+ 0.5f
- (float)description
.SoftShadowSamplesSqrt
/ 2.f
) / (float)description
.SoftShadowSamplesSqrt
;
1004 CVector lightPos
= _RayBasis
.getI () * ((float)description
.SunRadius
* deltaX
) + _RayBasis
.getJ () * ((float)description
.SunRadius
* deltaY
);
1005 lightPos
= description
.SunCenter
- (description
.SunDirection
* description
.SunDistance
) + lightPos
;
1007 InitZBuffer (zbuffer
, lightPos
, _RayBasis
, zoneBB
, description
.ZBufferLandscapeSize
, description
);
1008 nlinfo ("Zbuffer %d size : %d x %d", sampleX
+sampleY
*description
.SoftShadowSamplesSqrt
, zbuffer
.LocalZBufferWidth
, zbuffer
.LocalZBufferHeight
);
1012 // *** Init the zbuffer for the vegetation
1013 CVector lightPos
= description
.SunCenter
- (description
.SunDirection
* description
.SunDistance
);
1014 InitZBuffer (_ZBufferObject
, lightPos
, _RayBasis
, zoneBB
, description
.ZBufferObjectSize
, description
);
1015 nlinfo ("Zbuffer object size : %d x %d", _ZBufferObject
.LocalZBufferWidth
, _ZBufferObject
.LocalZBufferHeight
);
1018 // Compute the zbuffer in multi thread
1021 // Number of triangle to render per thread
1022 uint numTriangle
= ((uint
)obstacles
.size () / _ProcessCount
) + 1;
1024 // First triangle for the thread
1025 uint firstTriangle
= 0;
1028 _NumberOfPatchComputed
= 0;
1030 for (uint process
=0; process
<_ProcessCount
; process
++)
1032 // Get list of triangles to render
1033 uint lastTriangle
=firstTriangle
+numTriangle
;
1034 if (lastTriangle
>obstacles
.size ())
1035 lastTriangle
=(uint
)obstacles
.size ();
1038 CRenderZBuffer
*runnable
= new CRenderZBuffer (process
, this, &description
, firstTriangle
, lastTriangle
- firstTriangle
, &obstacles
);
1039 IThread
*pThread
=IThread::create (runnable
);
1040 runnable
->Thread
= pThread
;
1043 firstTriangle
= lastTriangle
;
1049 // Wait for others processes
1050 while (_ProcessExited
!=_ProcessCount
)
1054 // Call the progress callback
1055 progress ("Render triangles", (float)_NumberOfPatchComputed
/(float)obstacles
.size());
1058 // * Save the zbuffer
1060 const uint samples
= description
.SoftShadowSamplesSqrt
*description
.SoftShadowSamplesSqrt
;
1062 for (sample
=0; sample
<samples
; sample
++)
1065 CZBuffer
&zbuffer
= _ZBufferLandscape
[sample
];
1067 string zbufferFilename
= SAVE_ZBUFFER
"/zbuffer_landscape_" + toString (sample
) + ".jpg";
1069 SaveZBuffer (zbuffer
, zbufferFilename
.c_str ());
1072 // Save the object zbuffer
1073 SaveZBuffer (_ZBufferObject
, SAVE_ZBUFFER
"/zbuffer_object.jpg");
1074 #endif // SAVE_ZBUFFER
1076 // *** Filter the zbuffer
1077 for (sample
=0; sample
<samples
; sample
++)
1079 // For landscape zbuffer, expand the z to neighbor
1080 FilterZBuffer (_ZBufferLandscape
[sample
], 5);
1083 // Change the quadGrid basis
1084 CMatrix invRayBasis
=_RayBasis
;
1085 invRayBasis
.invert ();
1087 // Init the heightfield
1088 _HeightfieldCellSize
=description
.HeightfieldCellSize
;
1089 _HeightFieldCellCount
=(sint
)(description
.HeightfieldSize
/_HeightfieldCellSize
);
1090 nlassert (_HeightFieldCellCount
!=0);
1091 _OrigineHeightField
=zoneBB
.getCenter ()-CVector (description
.HeightfieldSize
/2, description
.HeightfieldSize
/2, 0);
1092 _HeightField
.resize (_HeightFieldCellCount
*_HeightFieldCellCount
, -FLT_MAX
);
1094 // Fill the quadGrid and the heightField
1095 for (triangleId
=0; triangleId
<size
; triangleId
++)
1098 if ( (triangleId
&0xff) == 0)
1099 progress ("Build quadtree and heightfield", (float)triangleId
/(float)size
);
1102 CZoneLighter::CTriangle
& triangle
=obstacles
[triangleId
];
1104 // Look for the min coordinate, in World Basis
1106 minv
.minof (triangle
.Triangle
.V0
, triangle
.Triangle
.V1
);
1107 minv
.minof (minv
, triangle
.Triangle
.V2
);
1109 // Look for the max coordinate, in World Basis
1111 maxv
.maxof (triangle
.Triangle
.V0
, triangle
.Triangle
.V1
);
1112 maxv
.maxof (maxv
, triangle
.Triangle
.V2
);
1115 if (triangle
.Flags
& CTriangle::Landscape
)
1117 // Fill the heightfield
1118 sint minX
=std::max (0, (sint
)floor (0.5f
+(minv
.x
-_OrigineHeightField
.x
)/_HeightfieldCellSize
));
1119 sint maxX
=std::min (_HeightFieldCellCount
, (sint
)floor (0.5f
+(maxv
.x
-_OrigineHeightField
.x
)/_HeightfieldCellSize
));
1120 sint minY
=std::max (0, (sint
)floor (0.5f
+(minv
.y
-_OrigineHeightField
.y
)/_HeightfieldCellSize
));
1121 sint maxY
=std::min (_HeightFieldCellCount
, (sint
)floor (0.5f
+(maxv
.y
-_OrigineHeightField
.y
)/_HeightfieldCellSize
));
1123 // Calc position in the heightfield
1124 for (sint y
=minY
; y
<maxY
; y
++)
1125 for (sint x
=minX
; x
<maxX
; x
++)
1127 // Valid position, try to insert it
1128 if (maxv
.z
>_HeightField
[x
+y
*_HeightFieldCellCount
])
1130 // New height in this cell
1131 _HeightField
[x
+y
*_HeightFieldCellCount
]=maxv
.z
;
1137 // Retrieve the zone to fill its shaded value
1138 pZone
->retrieve (_PatchInfo
, _BorderVertices
);
1141 uint patchCount
=(uint
)_PatchInfo
.size();
1143 // Bit array to know if the lumel is shadowed
1144 if (description
.Shadow
)
1145 _ShadowArray
.resize (patchCount
);
1147 // A lumel vector by patch
1148 vector
<vector
<CLumelDescriptor
> > lumels
;
1149 lumels
.resize (patchCount
);
1151 // Build zone information
1152 buildZoneInformation (landscape
,
1159 uint patchCount
=(uint
)_PatchInfo
.size();
1161 // Reset patch count
1163 CSynchronized
<std::vector
<bool> >::CAccessor
access (&_PatchComputed
);
1164 access
.value().resize (0);
1165 access
.value().resize (patchCount
, false);
1169 uint patchCountByThread
= patchCount
/_ProcessCount
;
1170 patchCountByThread
++;
1172 // Patch to allocate
1174 _NumberOfPatchComputed
= 0;
1176 // Reset exited process
1179 // Set the thread state
1180 _LastPatchComputed
.resize (_ProcessCount
);
1186 for (process
= 0; process
< _ProcessCount
; process
++)
1189 uint lastPatch
= firstPatch
+ patchCountByThread
;
1190 lastPatch
%= patchCount
;
1192 // Last patch computed
1193 _LastPatchComputed
[process
] = firstPatch
;
1196 CLightRunnable
*runnable
= new CLightRunnable(process
, this, &description
);
1197 IThread
*pThread
= IThread::create(runnable
);
1198 runnable
->Thread
= pThread
;
1201 firstPatch
= lastPatch
;
1207 // Wait for others processes
1208 while (_ProcessExited
!= _ProcessCount
)
1212 // Call the progress callback
1213 progress("Lighting patches", (float)_NumberOfPatchComputed
/ (float)_PatchInfo
.size());
1218 nlwarning("Empty zone");
1221 // Reset old thread mask
1222 // currentThread->setCPUMask (threadMask);
1225 if (_ZBufferOverflow
)
1226 nlwarning ("Error : zbuffer overflow");
1229 progress ("Compute Influences of PointLights", 0.f
);
1231 // Compute PointLight influences on zone.
1233 compilePointLightRT(description
.GridSize
, description
.GridCellSize
, obstacles
, description
.Shadow
);
1234 // Influence patchs and get light list of interest
1235 std::vector
<CPointLightNamed
> listPointLight
;
1236 processZonePointLightRT(listPointLight
);
1242 progress ("Compress the lightmap", 0.6f
);
1244 // Build, with list of lights.
1246 zinfo
.ZoneId
= _ZoneToLight
;
1247 zinfo
.Patchs
= _PatchInfo
;
1248 zinfo
.BorderVertices
= _BorderVertices
;
1249 zinfo
.PointLights
= listPointLight
;
1250 output
.build (zinfo
);
1252 /// copy the tiles flags from the zone to light to the output zone
1253 copyTileFlags(output
, *(landscape
.getZone(zoneToLight
)));
1255 /// Perform lightning of some ig's of the current zone (if any)
1256 lightShapes(zoneToLight
, description
);
1260 // *************************************************************************************
1261 void CZoneLighter::copyTileFlags(CZone
&destZone
, const CZone
&srcZone
)
1263 nlassert(destZone
.getZoneId() == srcZone
.getZoneId());
1264 nlassert(destZone
.getNumPatchs() == srcZone
.getNumPatchs());
1265 for (sint k
= 0; k
< srcZone
.getNumPatchs(); ++k
)
1267 destZone
.copyTilesFlags(k
, srcZone
.getPatch(k
));
1271 // ***************************************************************************
1272 float CZoneLighter::getSkyContribution(const CVector
&pos
, const CVector
&normal
, float skyIntensity
) const
1274 float s
=(pos
.x
-_OrigineHeightField
.x
)/_HeightfieldCellSize
;
1275 float t
=(pos
.y
-_OrigineHeightField
.y
)/_HeightfieldCellSize
;
1276 sint sInt
=(sint
)(floor (s
+0.5f
));
1277 sint tInt
=(sint
)(floor (t
+0.5f
));
1280 float skyContributionTab
[2][2];
1281 skyContributionTab
[0][0] = calcSkyContribution (sInt
-1, tInt
-1, pos
.z
, skyIntensity
, normal
);
1282 skyContributionTab
[1][0] = calcSkyContribution (sInt
, tInt
-1, pos
.z
, skyIntensity
, normal
);
1283 skyContributionTab
[1][1] = calcSkyContribution (sInt
, tInt
, pos
.z
, skyIntensity
, normal
);
1284 skyContributionTab
[0][1] = calcSkyContribution (sInt
-1, tInt
, pos
.z
, skyIntensity
, normal
);
1286 float sFact
=s
+0.5f
-sInt
;
1287 float tFact
=t
+0.5f
-tInt
;
1288 return (skyContributionTab
[0][0]*(1.f
-sFact
) + skyContributionTab
[1][0]*sFact
)*(1.f
-tFact
) +
1289 (skyContributionTab
[0][1]*(1.f
-sFact
) + skyContributionTab
[1][1]*sFact
)*tFact
;
1293 // ***************************************************************************
1294 void CZoneLighter::processCalc (uint process
, const CLightDesc
& description
)
1296 // *** Raytrace each patches
1299 uint patch
= getAPatch (process
);
1300 while (patch
!= 0xffffffff)
1303 if (description
.Shadow
)
1306 std::vector
<CLumelDescriptor
> &lumels
=_Lumels
[patch
];
1309 uint lumelCount
=(uint
)lumels
.size();
1310 CPatchInfo
&patchInfo
=_PatchInfo
[patch
];
1311 nlassert (patchInfo
.Lumels
.size()==lumelCount
);
1313 // Resize shadow array
1314 _ShadowArray
[patch
].resize (lumelCount
);
1317 for (uint lumel
=0; lumel
<lumelCount
; lumel
++)
1320 factor
= attenuation (lumels
[lumel
].Position
, description
);
1321 patchInfo
.Lumels
[lumel
]=(uint
)(factor
*255);
1327 std::vector
<CLumelDescriptor
> &lumels
=_Lumels
[patch
];
1330 uint lumelCount
=(uint
)lumels
.size();
1331 CPatchInfo
&patchInfo
=_PatchInfo
[patch
];
1332 nlassert (patchInfo
.Lumels
.size()==lumelCount
);
1335 for (uint lumel
=0; lumel
<lumelCount
; lumel
++)
1338 patchInfo
.Lumels
[lumel
]=255;
1344 // Get the patch info
1345 CPatchInfo
&patchInfo
=_PatchInfo
[patch
];
1347 // ** Pointer on arries
1348 std::vector
<CLumelDescriptor
> &lumels
=_Lumels
[patch
];
1350 // Go for light each lumel
1351 for (uint lumel
=0; lumel
<lumels
.size(); lumel
++)
1354 float skyContribution
;
1356 if (description
.SkyContribution
)
1358 skyContribution
= getSkyContribution(lumels
[lumel
].Position
, lumels
[lumel
].Normal
, description
.SkyIntensity
);
1362 skyContribution
= 0.f
;
1366 float sunContribution
;
1367 if (description
.SunContribution
)
1369 sunContribution
=(-lumels
[lumel
].Normal
*_SunDirection
)-skyContribution
;
1370 clamp (sunContribution
, 0.f
, 1.f
);
1376 sint finalLighting
=(sint
)(255.f
*(((float)patchInfo
.Lumels
[lumel
])*sunContribution
/255.f
+skyContribution
));
1377 clamp (finalLighting
, 0, 255);
1378 patchInfo
.Lumels
[lumel
]=finalLighting
;
1382 patch
= getAPatch (process
);
1386 // ***************************************************************************
1388 uint8
CZoneLighter::getMaxPhi (sint s
, sint t
, sint deltaS
, sint deltaT
, float heightPos
) const
1394 // Distance increment
1395 float stepDistance
=CVector (deltaS
*_HeightfieldCellSize
, deltaT
*_HeightfieldCellSize
,0).norm ();
1398 float distance
=stepDistance
;
1405 while ((s
<_HeightFieldCellCount
)&&(t
<_HeightFieldCellCount
)&&(s
>=0)&&(t
>=0))
1408 float height
=_HeightField
[s
+t
*_HeightFieldCellCount
];
1412 if (height
>maxHeight
)
1415 float tanTeta
=height
/distance
;
1416 nlassert (tanTeta
>=0);
1419 if (tanTeta
>maxTanTeta
)
1428 distance
+=stepDistance
;
1432 float teta
=(float)atan (maxTanTeta
);
1434 nlassert (teta
<=Pi
/2);
1435 clamp (teta
, 0.f
, (float)Pi
/2);
1436 sint res
=(sint
)((Pi
/2-teta
)*256/(Pi
/2));
1437 clamp (res
, 0, 255);
1441 // ***************************************************************************
1447 // ***************************************************************************
1449 bool CZoneLighter::isLumelOnEdgeMustBeOversample (uint patch
, uint edge
, sint s
, sint t
, const vector
<bool> &binded
,
1450 const vector
<bool> &oversampleEdges
, vector
<CPatchUVLocator
> &locator
,
1451 uint8 shadowed
, vector
<vector
<uint8
> >& shadowBuffer
)
1453 // Must force oversampling of this edge ?
1454 if (oversampleEdges
[edge
])
1462 CVector2f
lumelCoord (((float)(s
+_GetNormalDeltaS
[edge
])+0.5f
)/4.f
, ((float)(t
+_GetNormalDeltaT
[edge
])+0.5f
)/4.f
);
1463 uint otherPatch
=locator
[edge
].selectPatch(lumelCoord
);
1466 CVector2f neighborUV
;
1468 locator
[edge
].locateUV (lumelCoord
, otherPatch
, patchOut
, neighborUV
);
1470 // Is the same shadowed flag ?
1471 sint ss
=(sint
)(neighborUV
.x
*4.f
);
1472 sint tt
=(sint
)(neighborUV
.y
*4.f
);
1473 return (shadowBuffer
[patchOut
->getPatchId()][ss
+(patchOut
->getOrderS()<<2)*tt
]!=shadowed
);
1477 // Not oversample if not binded
1483 // ***************************************************************************
1485 float easineasoutC2(float x
)
1488 // 5-nome tq f(0)=0, f'(0)=0, f''(0)=0, f(1)=1, f'(1)=0, f''(1)=0.
1492 y
= 6*x5
-15*x4
+10*x3
;
1496 // ***************************************************************************
1499 sint16
CZoneLighter::_GetNormalDeltaS
[4]={ -1, 0, 1, 0 };
1500 sint16
CZoneLighter::_GetNormalDeltaT
[4]={ 0, 1, 0, -1 };
1502 // ***************************************************************************
1504 void CZoneLighter::getNormal (const CPatch
*pPatch
, sint16 lumelS
, sint16 lumelT
, vector
<CPatchUVLocator
> &locator
,
1505 const vector
<CPatch::CBindInfo
> &bindInfo
, const vector
<bool> &binded
, set
<uint64
>& visited
,
1506 float deltaS
, float deltaT
, uint rotation
, const CBezierPatch
&bezierPatch
, uint lastEdge
)
1508 // Build a desc srructure
1509 uint64 id
=(uint64
)lumelS
|(((uint64
)lumelT
)<<16)|(((uint64
)pPatch
->getPatchId())<<32)|(((uint64
)pPatch
->getZone()->getZoneId())<<48);
1512 if (visited
.insert (id
).second
)
1515 float sqDist
=deltaS
*deltaS
+deltaT
*deltaT
;
1520 sint orderSx4
=pPatch
->getOrderS()<<2;
1521 sint orderTx4
=pPatch
->getOrderT()<<2;
1523 sint16 _GetNormalBorderS
[4]={ 0, -10, 1, -10 };
1524 sint16 _GetNormalBorderT
[4]={ -10, 1, -10, 0 };
1525 _GetNormalBorderS
[2]=orderSx4
-1;
1526 _GetNormalBorderT
[1]=orderTx4
-1;
1529 _GetNormalNormal
+=bezierPatch
.evalNormal ( ((float)lumelS
+0.5f
)/(float)orderSx4
, ((float)lumelT
+0.5f
)/(float)orderTx4
);
1531 // For the four neighbors
1532 for (uint edge
=0; edge
<4; edge
++)
1538 uint globalDirection
=(edge
+(4-rotation
))&0x3;
1541 if ( (lumelS
==_GetNormalBorderS
[edge
]) || (lumelT
==_GetNormalBorderT
[edge
]) )
1544 bool bind
=binded
[edge
];
1545 bool smooth
=pPatch
->getSmoothFlag (edge
);
1549 CVector2f
lumelCoord ( ((float)(lumelS
+_GetNormalDeltaS
[edge
])+0.5f
)/4,
1550 ((float)(lumelT
+_GetNormalDeltaT
[edge
])+0.5f
)/4 );
1552 // Get neighbor pixel
1553 uint otherPatch
=locator
[edge
].selectPatch(lumelCoord
);
1556 CVector2f neighborUV
;
1558 locator
[edge
].locateUV (lumelCoord
, otherPatch
, patchOut
, neighborUV
);
1561 sint16 newLumelS
=(sint16
)(4.f
*neighborUV
.x
);
1562 sint16 newLumelT
=(sint16
)(4.f
*neighborUV
.y
);
1565 uint16 patchId
=patchOut
->getPatchId();
1566 uint16 zoneId
=_ZoneId
[patchOut
->getZone()->getZoneId ()];
1571 for (i
=0; i
<=(uint
)bindInfo
[edge
].NPatchs
; i
++)
1574 if (bindInfo
[edge
].Next
[i
]==patchOut
)
1577 newEdge
=bindInfo
[edge
].Edge
[i
];
1583 uint newRotation
=(2-edge
+rotation
+newEdge
)&0x3;
1586 nlassert (i
!=(uint
)bindInfo
[edge
].NPatchs
);
1588 // Get the bezier patch
1589 CBezierPatch
&NewBezierPatch
=_BezierPatch
[zoneId
][patchId
];
1592 getNormal (patchOut
, newLumelS
, newLumelT
, _Locator
[zoneId
][patchId
], _BindInfo
[zoneId
][patchId
],
1593 _Binded
[zoneId
][patchId
], visited
, deltaS
+_GetNormalDeltaS
[globalDirection
],
1594 deltaT
+_GetNormalDeltaT
[globalDirection
], newRotation
, NewBezierPatch
, newEdge
);
1600 getNormal (pPatch
, lumelS
+_GetNormalDeltaS
[edge
], lumelT
+_GetNormalDeltaT
[edge
], locator
, bindInfo
, binded
, visited
,
1601 deltaS
+_GetNormalDeltaS
[globalDirection
], deltaT
+_GetNormalDeltaT
[globalDirection
], rotation
, bezierPatch
, (edge
+2)&0x3);
1609 // ***************************************************************************
1611 void CZoneLighter::addTriangles (CLandscape
&landscape
, vector
<uint
> &listZone
, uint order
, std::vector
<CTriangle
>& triangleArray
)
1613 // Set all to refine
1614 excludeAllPatchFromRefineAll (landscape
, listZone
, false);
1616 // Setup the landscape
1617 landscape
.setThreshold (0);
1618 landscape
.setTileMaxSubdivision (order
);
1621 landscape
.refineAll (CVector (0, 0, 0));
1623 // Dump tesselated triangles
1624 std::vector
<const CTessFace
*> leaves
;
1625 landscape
.getTessellationLeaves(leaves
);
1628 uint leavesCount
=(uint
)leaves
.size();
1630 // Reserve the array
1631 triangleArray
.reserve (triangleArray
.size()+leavesCount
);
1634 for (uint leave
=0; leave
<leavesCount
; leave
++)
1637 const CTessFace
*face
=leaves
[leave
];
1640 triangleArray
.push_back (CTriangle (NLMISC::CTriangle (face
->VBase
->EndPos
, face
->VLeft
->EndPos
, face
->VRight
->EndPos
)));
1643 // Setup the landscape
1644 landscape
.setThreshold (1000);
1645 landscape
.setTileMaxSubdivision (0);
1647 // Remove all triangles
1648 landscape
.refineAll (CVector (0, 0, 0));
1649 landscape
.refineAll (CVector (0, 0, 0));
1650 landscape
.refineAll (CVector (0, 0, 0));
1651 landscape
.refineAll (CVector (0, 0, 0));
1652 landscape
.refineAll (CVector (0, 0, 0));
1653 landscape
.refineAll (CVector (0, 0, 0));
1654 landscape
.refineAll (CVector (0, 0, 0));
1655 landscape
.refineAll (CVector (0, 0, 0));
1656 landscape
.refineAll (CVector (0, 0, 0));
1657 landscape
.refineAll (CVector (0, 0, 0));
1660 // ***************************************************************************
1662 void CZoneLighter::addTriangles (const IShape
&shape
, const CMatrix
& modelMT
, std::vector
<CTriangle
>& triangleArray
)
1665 const CMesh
*mesh
=dynamic_cast<const CMesh
*>(&shape
);
1667 // Cast to CMeshMultiLod
1668 const CMeshMultiLod
*meshMulti
=dynamic_cast<const CMeshMultiLod
*>(&shape
);
1670 // Cast to CMeshMultiLod
1671 const CMeshMRM
*meshMRM
=dynamic_cast<const CMeshMRM
*>(&shape
);
1676 // Add its triangles
1677 addTriangles (*mesh
, mesh
->getMeshGeom (), modelMT
, triangleArray
);
1679 // It is a CMeshMultiLod ?
1682 // Get the first geommesh
1683 const IMeshGeom
*meshGeom
=&meshMulti
->getMeshGeom (0);
1686 const CMeshGeom
*geomMesh
=dynamic_cast<const CMeshGeom
*>(meshGeom
);
1689 addTriangles (*meshMulti
, *geomMesh
, modelMT
, triangleArray
);
1693 const CMeshMRMGeom
*mrmGeomMesh
=dynamic_cast<const CMeshMRMGeom
*>(meshGeom
);
1696 addTriangles (*meshMulti
, *mrmGeomMesh
, modelMT
, triangleArray
);
1699 // It is a CMeshMultiLod ?
1702 // Get the first lod mesh geom
1703 addTriangles (*meshMRM
, meshMRM
->getMeshGeom (), modelMT
, triangleArray
);
1707 // ***************************************************************************
1709 void CZoneLighter::addTriangles (const CMeshBase
&meshBase
, const CMeshGeom
&meshGeom
, const CMatrix
& modelMT
, std::vector
<CTriangle
>& triangleArray
)
1711 // Get the vertex buffer
1712 const CVertexBuffer
&vb
=meshGeom
.getVertexBuffer();
1713 CVertexBufferRead vba
;
1716 // For each matrix block
1717 uint numBlock
=meshGeom
.getNbMatrixBlock();
1718 for (uint block
=0; block
<numBlock
; block
++)
1720 // For each render pass
1721 uint numRenderPass
=meshGeom
.getNbRdrPass(block
);
1722 for (uint pass
=0; pass
<numRenderPass
; pass
++)
1724 // Get the primitive block
1725 const CIndexBuffer
&primitive
=meshGeom
.getRdrPassPrimitiveBlock ( block
, pass
);
1728 const CMaterial
&material
= meshBase
.getMaterial (meshGeom
.getRdrPassMaterial ( block
, pass
));
1730 // ** Get the bitmap
1732 // Texture information, not NULL only if texture is used for alpha test
1736 uint8 alphaTestThreshold
;
1738 if (getTexture (material
, texture
, clampU
, clampV
, alphaTestThreshold
, doubleSided
))
1741 CIndexBufferRead iba
;
1742 primitive
.lock (iba
);
1743 if (iba
.getFormat() == CIndexBuffer::Indices32
)
1745 const uint32
* triIndex
= (const uint32
*) iba
.getPtr ();
1746 uint numTri
=primitive
.getNumIndexes ()/3;
1748 for (tri
=0; tri
<numTri
; tri
++)
1751 CVector v0
=modelMT
*(*vba
.getVertexCoordPointer (triIndex
[tri
*3]));
1752 CVector v1
=modelMT
*(*vba
.getVertexCoordPointer (triIndex
[tri
*3+1]));
1753 CVector v2
=modelMT
*(*vba
.getVertexCoordPointer (triIndex
[tri
*3+2]));
1756 float u
[3] = { 0.f
};
1757 float v
[3] = { 0.f
};
1758 for (uint i
=0; i
<3; i
++)
1760 // Get UV coordinates
1761 const float *uv
= (const float*)vba
.getTexCoordPointer (triIndex
[tri
*3+i
], 0);
1771 triangleArray
.push_back (CTriangle (NLMISC::CTriangle (v0
, v1
, v2
), doubleSided
, texture
, clampU
, clampV
, u
, v
,
1772 alphaTestThreshold
));
1775 else if (iba
.getFormat() == CIndexBuffer::Indices16
)
1777 const uint16
* triIndex
=(const uint16
*)iba
.getPtr ();
1778 uint numTri
=primitive
.getNumIndexes ()/3;
1780 for (tri
=0; tri
<numTri
; tri
++)
1783 CVector v0
=modelMT
*(*vba
.getVertexCoordPointer (triIndex
[tri
*3]));
1784 CVector v1
=modelMT
*(*vba
.getVertexCoordPointer (triIndex
[tri
*3+1]));
1785 CVector v2
=modelMT
*(*vba
.getVertexCoordPointer (triIndex
[tri
*3+2]));
1788 float u
[3] = { 0.f
};
1789 float v
[3] = { 0.f
};
1790 for (uint i
=0; i
<3; i
++)
1792 // Get UV coordinates
1793 const float *uv
= (const float*)vba
.getTexCoordPointer (triIndex
[tri
*3+i
], 0);
1803 triangleArray
.push_back (CTriangle (NLMISC::CTriangle (v0
, v1
, v2
), doubleSided
, texture
, clampU
, clampV
, u
, v
,
1804 alphaTestThreshold
));
1809 nlerror("Invalid index buffer format");
1816 // ***************************************************************************
1818 bool CZoneLighter::getTexture (const CMaterial
&material
, CBitmap
*&result
, bool &clampU
, bool &clampV
, uint8
&alphaTestThreshold
, bool &doubleSided
)
1820 // Texture information, not NULL only if texture is used for alpha test
1825 // Alpha test threashold
1826 float alphaTestThresholdF
= material
.getAlphaTestThreshold () * 255;
1827 clamp (alphaTestThresholdF
, 0.f
, 255.f
);
1828 alphaTestThreshold
= (uint8
)alphaTestThresholdF
;
1830 // Drawable material ?
1831 if (material
.getBlend ())
1835 if (material
.getAlphaTest ())
1838 ITexture
*texture
= material
.getTexture (0);
1840 // Is texture shared ?
1841 if (texture
&& texture
->supportSharing ())
1844 string name
= texture
->getShareName();
1847 std::map
<string
, NLMISC::CBitmap
>::iterator ite
= _Bitmaps
.find (name
);
1848 if (ite
!= _Bitmaps
.end ())
1851 result
= &(ite
->second
);
1856 ite
= _Bitmaps
.insert (std::map
<string
, NLMISC::CBitmap
>::value_type (name
, CBitmap())).first
;
1857 result
= &(ite
->second
);
1859 // Generate the texture
1860 texture
->generate ();
1863 texture
->convertToType (CBitmap::RGBA
);
1868 // Release the texture
1869 texture
->release ();
1873 clampU
= texture
->getWrapS () == ITexture::Clamp
;
1874 clampV
= texture
->getWrapT () == ITexture::Clamp
;
1878 // Get double sided flag
1879 doubleSided
= material
.getDoubleSided ();
1883 // ***************************************************************************
1885 void CZoneLighter::addTriangles (const CMeshBase
&meshBase
, const CMeshMRMGeom
&meshGeom
, const CMatrix
& modelMT
, std::vector
<CTriangle
>& triangleArray
)
1887 // Get the vertex buffer
1888 const CVertexBuffer
&vb
=meshGeom
.getVertexBuffer();
1889 CVertexBufferRead vba
;
1892 // For each render pass
1893 uint numRenderPass
=meshGeom
.getNbRdrPass(0);
1894 for (uint pass
=0; pass
<numRenderPass
; pass
++)
1896 // Get the primitive block
1897 const CIndexBuffer
&primitive
=meshGeom
.getRdrPassPrimitiveBlock ( 0, pass
);
1900 const CMaterial
&material
= meshBase
.getMaterial (meshGeom
.getRdrPassMaterial (0, pass
));
1902 // ** Get the bitmap
1904 // Texture information, not NULL only if texture is used for alpha test
1908 uint8 alphaTestThreshold
;
1910 if (getTexture (material
, texture
, clampU
, clampV
, alphaTestThreshold
, doubleSided
))
1913 CIndexBufferRead iba
;
1914 primitive
.lock (iba
);
1915 if (iba
.getFormat() == CIndexBuffer::Indices32
)
1917 const uint32
*triIndex
= (const uint32
*)iba
.getPtr();
1918 uint numTri
= primitive
.getNumIndexes() / 3;
1920 for (tri
= 0; tri
< numTri
; tri
++)
1923 CVector v0
= modelMT
* (*vba
.getVertexCoordPointer(triIndex
[tri
* 3]));
1924 CVector v1
= modelMT
* (*vba
.getVertexCoordPointer(triIndex
[tri
* 3 + 1]));
1925 CVector v2
= modelMT
* (*vba
.getVertexCoordPointer(triIndex
[tri
* 3 + 2]));
1928 float u
[3] = { 0.f
};
1929 float v
[3] = { 0.f
};
1930 for (uint i
= 0; i
< 3; i
++)
1932 // Get UV coordinates
1933 float *uv
= (float *)vba
.getTexCoordPointer(triIndex
[tri
* 3 + i
], 0);
1943 triangleArray
.push_back(CTriangle(NLMISC::CTriangle(v0
, v1
, v2
), doubleSided
, texture
, clampU
, clampV
, u
, v
,
1944 alphaTestThreshold
));
1947 else if (iba
.getFormat() == CIndexBuffer::Indices16
)
1949 const uint16
*triIndex
= (const uint16
*)iba
.getPtr();
1950 uint numTri
= primitive
.getNumIndexes() / 3;
1952 for (tri
= 0; tri
< numTri
; tri
++)
1955 CVector v0
= modelMT
* (*vba
.getVertexCoordPointer(triIndex
[tri
* 3]));
1956 CVector v1
= modelMT
* (*vba
.getVertexCoordPointer(triIndex
[tri
* 3 + 1]));
1957 CVector v2
= modelMT
* (*vba
.getVertexCoordPointer(triIndex
[tri
* 3 + 2]));
1960 float u
[3] = { 0.f
};
1961 float v
[3] = { 0.f
};
1962 for (uint i
= 0; i
< 3; i
++)
1964 // Get UV coordinates
1965 float *uv
= (float *)vba
.getTexCoordPointer(triIndex
[tri
* 3 + i
], 0);
1975 triangleArray
.push_back(CTriangle(NLMISC::CTriangle(v0
, v1
, v2
), doubleSided
, texture
, clampU
, clampV
, u
, v
,
1976 alphaTestThreshold
));
1981 nlerror("Invalid index buffer format");
1987 // ***************************************************************************
1989 void CZoneLighter::excludeAllPatchFromRefineAll (CLandscape
&landscape
, vector
<uint
> &listZone
, bool exclude
)
1992 for (uint zone
=0; zone
<listZone
.size(); zone
++)
1995 uint patchCount
=landscape
.getZone(listZone
[zone
])->getNumPatchs();
1998 for (uint patch
=0; patch
<patchCount
; patch
++)
2000 // Exclude all the patches from refine all
2001 landscape
.excludePatchFromRefineAll (listZone
[zone
], patch
, exclude
);
2006 // ***************************************************************************
2008 const sint8
CZoneLighter::TriangleIndexes
[10][2][3] =
2010 {{0, 11, 6}, {0, 6, 9}},
2011 {{9, 6, 4}, {4, 6, 14}},
2012 {{4, 14, 8}, {4, 8, 10}},
2013 {{10, 8, 1}, {-1, -1, -1}},
2014 {{11, 5, 6}, {5, 13, 6}},
2015 {{6, 13, 3}, {6, 3, 14}},
2016 {{3, 8, 14}, {-1, -1, -1}},
2017 {{5, 12, 7}, {5, 7, 13}},
2018 {{7, 3, 13}, {-1, -1, -1}},
2019 {{12, 2, 7}, {-1, -1, -1}}
2022 // ***************************************************************************
2024 // lumel vertex ID, tesselation edge ID (0 for base<->right, 1 for base<->left), tesselation edge vertex 0, tesselation edge vertex 1
2025 const sint8
CZoneLighter::VertexThanCanBeSnappedOnABorder
[8][4] =
2037 // ***************************************************************************
2039 // lumel vertex ID, tesselation corner vertex
2040 const sint8
CZoneLighter::VertexThanCanBeSnappedOnACorner
[3][2] =
2047 // ***************************************************************************
2049 void CZoneLighter::buildZoneInformation (CLandscape
&landscape
, const vector
<uint
> &listZone
, const CLightDesc
&lightDesc
)
2052 vector
<vector
<uint
> > visited
;
2055 uint zoneCount
=(uint
)listZone
.size();
2058 _Locator
.resize (zoneCount
);
2059 _Binded
.resize (zoneCount
);
2060 _BindInfo
.resize (zoneCount
);
2061 _BezierPatch
.resize (zoneCount
);
2064 for (uint zone
=0; zone
<zoneCount
; zone
++)
2067 uint patchCount
=landscape
.getZone(listZone
[zone
])->getNumPatchs();
2070 _ZoneId
.insert (map
<uint
, uint
>::value_type (listZone
[zone
], zone
));
2072 // This is the zone to light ?
2073 if (listZone
[zone
]==_ZoneToLight
)
2075 // Resize the arraies
2076 _Lumels
.resize(patchCount
);
2077 // _LumelCorners.resize(patchCount);
2078 // _BezierPatch.resize(patchCount);
2079 _OversampleEdges
.resize(patchCount
);
2080 visited
.resize(patchCount
);
2084 _Locator
[zone
].resize(patchCount
);
2085 _Binded
[zone
].resize(patchCount
);
2086 _BindInfo
[zone
].resize(patchCount
);
2087 _BezierPatch
[zone
].resize(patchCount
);
2091 for (patch
=0; patch
<patchCount
; patch
++)
2093 // Get a patch pointer
2094 const CPatch
* pPatch
=(const_cast<const CZone
*>(landscape
.getZone(listZone
[zone
])))->getPatch (patch
);
2097 progress ("Scan all patches", (float)patch
/(float)patchCount
);
2099 // Get pointer on arries
2100 vector
<bool> &binded
=_Binded
[zone
][patch
];
2101 vector
<CPatch::CBindInfo
> &bindInfo
=_BindInfo
[zone
][patch
];
2102 vector
<CPatchUVLocator
> &locator
=_Locator
[zone
][patch
];
2103 CBezierPatch
&bezierPatch
=_BezierPatch
[zone
][patch
];
2104 binded
.resize (4, false);
2105 bindInfo
.resize (4);
2108 // Contruct the patch
2109 bezierPatch
=*pPatch
->unpackIntoCache();
2112 if (listZone
[zone
]==_ZoneToLight
)
2114 // oversample this edge
2115 _OversampleEdges
[patch
].resize (4, false);
2118 // *** Build bind info
2120 // *** Build neighboorhood information
2122 for (edge
=0; edge
<4; edge
++)
2125 pPatch
->getBindNeighbor (edge
, bindInfo
[edge
]);
2128 if (bindInfo
[edge
].NPatchs
>0)
2130 // This edeg is binded
2134 if ((listZone
[zone
]==_ZoneToLight
)&&(bindInfo
[edge
].Zone
->getZoneId()!=_ZoneToLight
))
2136 // oversample this edge
2137 _OversampleEdges
[patch
][edge
]=true;
2139 locator
[edge
].build (pPatch
, edge
, bindInfo
[edge
]);
2143 if (listZone
[zone
]==_ZoneToLight
)
2145 // oversample this edge
2146 _OversampleEdges
[patch
][edge
]=true;
2151 // This is the zone to light ?
2152 if (listZone
[zone
]==_ZoneToLight
)
2154 // *** Resize lumel array for this patch
2157 uint orderS
=pPatch
->getOrderS();
2158 uint orderT
=pPatch
->getOrderT();
2161 uint lumelCount
= orderS
*orderT
*16;
2163 // Resize the lumel descriptor
2164 CLumelDescriptor descriptor
;
2165 descriptor
.Normal
.set (0,0,0);
2166 descriptor
.Position
.set (0,0,0);
2169 _Lumels
[patch
].resize (lumelCount
, descriptor
);
2170 visited
[patch
].resize (lumelCount
, 0);
2171 // _LumelCorners[patch].resize (lumelCornerCount);
2174 // *** Unexclude this patch
2176 // Exclude all the patches from refine all
2177 landscape
.excludePatchFromRefineAll (listZone
[zone
], patch
, false);
2181 // Exclude all the patches from refine all
2182 landscape
.excludePatchFromRefineAll (listZone
[zone
], patch
, true);
2187 // *** Now tesselate this zone to shadow casters accuracy
2189 // Setup the landscape
2190 landscape
.setThreshold (0);
2191 landscape
.setTileMaxSubdivision (0);
2194 progress ("Refine landscape to shadow accuracy", 0.5f
);
2195 landscape
.refineAll (CVector (0, 0, 0));
2197 // Get tesselated faces
2198 std::vector
<const CTessFace
*> leaves
;
2199 landscape
.getTessellationLeaves(leaves
);
2204 if (_WaterShapes
.size() != 0) // any water shape in this zone ?
2206 /// make a quad grid of each water shape
2207 makeQuadGridFromWaterShapes(landscape
.getZone(_ZoneToLight
)->getZoneBB().getAABBox());
2209 /// check for each tile if it is above / below water
2210 computeTileFlagsForPositionTowardWater(lightDesc
, leaves
);
2214 setTileFlagsToDefault(leaves
);
2218 // Id of this zone in the array
2219 uint zoneNumber
=_ZoneId
[_ZoneToLight
];
2222 uint leavesCount
=(uint
)leaves
.size();
2224 for (leave
=0; leave
<leavesCount
; leave
++)
2227 if ( (leave
&0xff) == 0)
2228 progress ("Precompute lumel position", (float)leave
/(float)leavesCount
);
2231 const CTessFace
*face
=leaves
[leave
];
2234 if (face
->Patch
->getZone()->getZoneId()==_ZoneToLight
)
2236 // Get a patch pointer
2237 const CPatch
* pPatch
=face
->Patch
;
2240 uint orderS
=pPatch
->getOrderS();
2241 uint orderT
=pPatch
->getOrderT();
2243 // *** Base Coordinates
2246 pos
[0]=face
->VBase
->EndPos
; // p0
2247 pos
[1]=face
->VRight
->EndPos
;
2248 pos
[2]=face
->VLeft
->EndPos
; // p2
2249 pos
[3]=(pos
[1]+pos
[2])/2;
2250 pos
[4]=(pos
[0]+pos
[1])/2; // p4
2251 pos
[5]=(pos
[0]+pos
[2])/2;
2252 pos
[6]=(pos
[0]+pos
[3])/2; // p6
2253 pos
[7]=(pos
[2]+pos
[3])/2;
2254 pos
[8]=(pos
[1]+pos
[3])/2; // p8
2255 pos
[9]=(pos
[0]+pos
[4])/2;
2256 pos
[10]=(pos
[1]+pos
[4])/2; // p10
2257 pos
[11]=(pos
[0]+pos
[5])/2;
2258 pos
[12]=(pos
[2]+pos
[5])/2; // p12
2259 pos
[13]=(pos
[3]+pos
[5])/2;
2260 pos
[14]=(pos
[3]+pos
[4])/2; // p14
2262 float s0
=face
->PVBase
.getS();
2263 float s1
=face
->PVRight
.getS();
2264 float s2
=face
->PVLeft
.getS();
2272 float t0
=face
->PVBase
.getT();
2273 float t1
=face
->PVRight
.getT();
2274 float t2
=face
->PVLeft
.getT();
2282 // *** Interpolated value
2283 CVector interpolatedP
[10]=
2297 // Does the border are snapped ?
2298 uint sBase
= (uint
)floor ((float)orderS
* face
->PVBase
.getS() + 0.5);
2299 uint tBase
= (uint
)floor ((float)orderT
* face
->PVBase
.getT() + 0.5);
2300 uint sLeft
= (uint
)floor ((float)orderS
* face
->PVLeft
.getS() + 0.5);
2301 uint tLeft
= (uint
)floor ((float)orderT
* face
->PVLeft
.getT() + 0.5);
2302 uint sRight
= (uint
)floor ((float)orderS
* face
->PVRight
.getS() + 0.5);
2303 uint tRight
= (uint
)floor ((float)orderT
* face
->PVRight
.getT() + 0.5);
2306 (sBase
== 0) && (sRight
== 0),
2307 (sBase
== 0) && (sLeft
== 0),
2309 bool snapedRight
[2]=
2311 (sBase
== orderS
) && (sRight
== orderS
),
2312 (sBase
== orderS
) && (sLeft
== orderS
),
2316 (tBase
== 0) && (tRight
== 0),
2317 (tBase
== 0) && (tLeft
== 0),
2319 bool snapedBottom
[2]=
2321 (tBase
== orderT
) && (tRight
== orderT
),
2322 (tBase
== orderT
) && (tLeft
== orderT
),
2324 bool snapedBorder
[2]=
2326 snapedLeft
[0]||snapedRight
[0]||snapedTop
[0]||snapedBottom
[0],
2327 snapedLeft
[1]||snapedRight
[1]||snapedTop
[1]||snapedBottom
[1],
2330 bool snapedCorner
[3]=
2332 ((sBase
== 0) && ((tBase
== 0) || (tBase
== orderT
))) ||
2333 ((sBase
== orderS
) && ((tBase
== 0) || (tBase
== orderT
))),
2334 ((sRight
== 0) && ((tRight
== 0) || (tRight
== orderT
))) ||
2335 ((sRight
== orderS
) && ((tRight
== 0) || (tRight
== orderT
))),
2336 ((sLeft
== 0) && ((tLeft
== 0) || (tLeft
== orderT
))) ||
2337 ((sLeft
== orderS
) && ((tLeft
== 0) || (tLeft
== orderT
))),
2340 // Snap on the border
2345 if (snapedBorder
[VertexThanCanBeSnappedOnABorder
[i
][1]])
2347 // Compute the border vertex
2348 interpolatedP
[VertexThanCanBeSnappedOnABorder
[i
][0]] = (pos
[VertexThanCanBeSnappedOnABorder
[i
][2]]
2349 + pos
[VertexThanCanBeSnappedOnABorder
[i
][3]])/2;
2353 // Snap on the corner
2356 // Snaped on a corner ?
2357 uint tesselCornerIndex
= VertexThanCanBeSnappedOnACorner
[i
][1];
2358 if ( snapedCorner
[tesselCornerIndex
] )
2360 // Compute the border vertex
2361 interpolatedP
[VertexThanCanBeSnappedOnACorner
[i
][0]] = pos
[tesselCornerIndex
];
2365 float interpolatedS
[10]=
2379 float interpolatedT
[10]=
2393 for (i
=0; i
<10; i
++)
2395 sint s
=(sint
)((float)orderS
*4*interpolatedS
[i
]);
2396 sint t
=(sint
)((float)orderT
*4*interpolatedT
[i
]);
2398 if ((s
>=0)&&(s
<(sint
)orderS
*4)&&(t
>=0)&&(t
<(sint
)orderT
*4))
2401 uint index
=s
+t
*orderS
*4;
2404 uint patchId
=pPatch
->getPatchId();
2407 vector
<CLumelDescriptor
> &lumels
=_Lumels
[patchId
];
2410 visited
[patchId
][index
]++;
2413 lumels
[index
].Position
+=interpolatedP
[i
];
2419 // *** Now, finalise patch information for shadow source positions
2422 uint patchCount
=landscape
.getZone(_ZoneToLight
)->getNumPatchs();
2424 for (patch
=0; patch
<patchCount
; patch
++)
2427 progress ("Finalize lumel positions", (float)patch
/(float)patchCount
);
2429 // *** Resize lumel array for this patch
2431 // Get a patch pointer
2432 const CPatch
* pPatch
=(const_cast<const CZone
*>(landscape
.getZone(_ZoneToLight
)))->getPatch (patch
);
2433 uint orderS
=pPatch
->getOrderS();
2434 uint orderT
=pPatch
->getOrderT();
2437 vector
<CLumelDescriptor
> &lumels
=_Lumels
[patch
];
2439 // *** Average position
2442 nlassert (isPowerOf2 (orderS
));
2443 nlassert (isPowerOf2 (orderT
));
2444 uint lumelS
=4<<getPowerOf2 (orderS
);
2445 uint lumelT
=4<<getPowerOf2 (orderT
);
2447 for (uint t
=0; t
<lumelT
; t
++)
2448 for (uint s
=0; s
<lumelS
; s
++)
2451 uint lumelIndex
=s
+t
*lumelS
;
2453 // *** Number of visit
2454 uint visitedCount
=visited
[patch
][lumelIndex
];
2456 // If visited, renormalise other values
2459 // Normalise position
2460 lumels
[lumelIndex
].Position
/=(float)visitedCount
;
2463 // Not visited for next pass
2464 visited
[patch
][lumelIndex
]=false;
2468 // *** Now tesselate this zone to shadow receivers accuracy
2470 // Setup the landscape
2471 landscape
.setThreshold (0);
2472 landscape
.setTileMaxSubdivision (4);
2475 progress ("Refine landscape to lumels", 0.5f
);
2476 landscape
.refineAll (CVector (0, 0, 0));
2478 // Get tesselated faces
2480 landscape
.getTessellationLeaves(leaves
);
2483 leavesCount
=(uint
)leaves
.size();
2484 for (leave
=0; leave
<leavesCount
; leave
++)
2487 if ( (leave
&0xff) == 0)
2488 progress ("Precompute tesselation", (float)leave
/(float)leavesCount
);
2491 const CTessFace
*face
=leaves
[leave
];
2494 if (face
->Patch
->getZone()->getZoneId()==_ZoneToLight
)
2496 // Get a patch pointer
2497 const CPatch
* pPatch
=face
->Patch
;
2500 uint orderS
=pPatch
->getOrderS();
2501 uint orderT
=pPatch
->getOrderT();
2504 float fS
=(face
->PVBase
.getS()+face
->PVLeft
.getS()+face
->PVRight
.getS())/3.f
;
2505 float fT
=(face
->PVBase
.getT()+face
->PVLeft
.getT()+face
->PVRight
.getT())/3.f
;
2506 uint s
=(uint
)((float)orderS
*4*fS
);
2507 uint t
=(uint
)((float)orderT
*4*fT
);
2509 nlassert (s
<orderS
*4);
2511 nlassert (t
<orderT
*4);
2514 uint index
=s
+t
*orderS
*4;
2517 uint patchId
=pPatch
->getPatchId();
2520 vector
<CLumelDescriptor
> &lumels
=_Lumels
[patchId
];
2523 visited
[patchId
][index
]++;
2526 lumels
[index
].S
+=fS
;
2527 lumels
[index
].T
+=fT
;
2531 plane
.make (face
->VBase
->EndPos
, face
->VLeft
->EndPos
, face
->VRight
->EndPos
);
2532 lumels
[index
].Normal
+=plane
.getNormal();
2536 // *** Now, finalise patch information
2539 patchCount
=landscape
.getZone(_ZoneToLight
)->getNumPatchs();
2540 for (patch
=0; patch
<patchCount
; patch
++)
2543 progress ("Finalize patches", (float)patch
/(float)patchCount
);
2545 // *** Resize lumel array for this patch
2547 // Get a patch pointer
2548 const CPatch
* pPatch
=(const_cast<const CZone
*>(landscape
.getZone(_ZoneToLight
)))->getPatch (patch
);
2549 uint orderS
=pPatch
->getOrderS();
2550 uint orderT
=pPatch
->getOrderT();
2553 vector
<CLumelDescriptor
> &lumels
=_Lumels
[patch
];
2555 // *** Compute an interpolated normal
2557 // Get pointer on arries
2558 vector
<bool> &binded
=_Binded
[zoneNumber
][patch
];
2559 vector
<CPatchUVLocator
> &locator
=_Locator
[zoneNumber
][patch
];
2560 vector
<CPatch::CBindInfo
> &bindInfo
=_BindInfo
[zoneNumber
][patch
];
2561 CBezierPatch
&bezierPatch
=_BezierPatch
[zoneNumber
][patch
];
2564 nlassert (isPowerOf2 (orderS
));
2565 nlassert (isPowerOf2 (orderT
));
2566 uint powerS
=getPowerOf2 (orderS
);
2567 uint powerT
=getPowerOf2 (orderT
);
2568 uint lumelS
=4<<powerS
;
2569 uint lumelT
=4<<powerT
;
2571 // Sample edge normal
2572 CVector normals
[NL_MAX_TILES_BY_PATCH_EDGE
*NL_LUMEL_BY_TILE
+1][4];
2573 uint sFixed
[4] = { 0, 0xffffffff, lumelS
-1, 0xffffffff };
2574 uint tFixed
[4] = { 0xffffffff, lumelT
-1, 0xffffffff, 0 };
2575 float sOri
[4] = { 0, -1, (float)lumelS
, -1 };
2576 float tOri
[4] = { -1, (float)lumelT
, -1, 0 };
2577 for (uint edge
=0; edge
<4; edge
++)
2580 uint count
=(edge
&1)?lumelS
:lumelT
;
2581 for (uint lumel
=0; lumel
<=count
; lumel
++)
2583 // Start coordinates
2594 startT
=tFixed
[edge
];
2595 origineS
=(float)lumel
;
2596 origineT
=tOri
[edge
];
2604 startS
=sFixed
[edge
];
2605 origineT
=(float)lumel
;
2606 origineS
=sOri
[edge
];
2608 _GetNormalNormal
=CVector::Null
;
2609 set
<uint64
> visitedLumels
;
2610 getNormal (pPatch
, startS
, startT
, locator
, bindInfo
, binded
, visitedLumels
,
2611 startS
+0.5f
-origineS
, startT
+0.5f
-origineT
, 0, bezierPatch
);
2612 _GetNormalNormal
.normalize ();
2613 normals
[lumel
][edge
]=_GetNormalNormal
;
2616 // Smooth the corners
2618 for (uint i
=1; i
<BLUR_SIZE
; i
++)
2620 float value
=(float)i
/BLUR_SIZE
;
2621 value
=easineasout(value
);
2622 normals
[i
][edge
]=normals
[0][edge
]*(1-value
)+normals
[i
][edge
]*value
;
2623 normals
[i
][edge
].normalize();
2624 normals
[count
-i
][edge
]=normals
[count
][edge
]*(1-value
)+normals
[count
-i
][edge
]*value
;
2625 normals
[count
-i
][edge
].normalize();
2629 for (uint t
=0; t
<lumelT
; t
++)
2630 for (uint s
=0; s
<lumelS
; s
++)
2633 uint lumelIndex
=s
+t
*lumelS
;
2635 // *** Calc the smoothed normal
2638 CVector normalS
=bezierPatch
.evalNormal (((float)s
+0.5f
)/(float)lumelS
, ((float)t
+0.5f
)/(float)lumelT
);
2640 CVector normalT
=normalS
;
2642 bool sGood
=false, tGood
=false;
2646 // Average the two normals
2647 CVector average
=normals
[t
][0];
2648 average
+=normals
[t
+1][0];
2653 sFactor
=BLUR_SIZE
-value
;
2655 value
=easineasout(value
);
2656 normalS
=(normalS
*value
+average
*(1-value
));
2657 normalS
.normalize();
2659 if (s
>=lumelS
-BLUR_SIZE
)
2662 // Average the two normals
2663 CVector average
=normals
[t
][2];
2664 average
+=normals
[t
+1][2];
2669 sFactor
=BLUR_SIZE
-(lumelS
-value
);
2670 value
=(lumelS
-value
)/BLUR_SIZE
;
2671 value
=easineasout(value
);
2672 normalS
=(normalS
*value
+average
*(1-value
));
2673 normalS
.normalize();
2678 // Average the two normals
2679 CVector average
=normals
[s
][3];
2680 average
+=normals
[s
+1][3];
2685 tFactor
=BLUR_SIZE
-value
;
2687 value
=easineasout(value
);
2688 normalT
=(normalT
*value
+average
*(1-value
));
2689 normalT
.normalize();
2691 if (t
>=lumelT
-BLUR_SIZE
)
2694 // Average the two normals
2695 CVector average
=normals
[s
][1];
2696 average
+=normals
[s
+1][1];
2701 tFactor
=BLUR_SIZE
-(lumelT
-value
);
2702 value
=((lumelT
)-value
)/BLUR_SIZE
;
2703 value
=easineasout(value
);
2704 normalT
=(normalT
*value
+average
*(1-value
));
2705 normalT
.normalize();
2708 // The smooth normal
2709 CVector smoothNormal
;
2711 if ((sGood
)&&(tGood
))
2713 if ((sFactor
!=BLUR_SIZE
)||(tFactor
!=BLUR_SIZE
))
2714 smoothNormal
=normalS
*(BLUR_SIZE
-tFactor
)+normalT
*(BLUR_SIZE
-sFactor
);
2716 smoothNormal
=normalS
+normalT
;
2719 smoothNormal
=normalS
;
2721 smoothNormal
=normalT
;
2724 smoothNormal
.normalize();
2727 CVector purNormal
=bezierPatch
.evalNormal (((float)s
+0.5f
)/(float)lumelS
, ((float)t
+0.5f
)/(float)lumelT
);
2729 // Normalize the noisy normal
2730 lumels
[lumelIndex
].Normal
.normalize();
2733 lumels
[lumelIndex
].Normal
=lumels
[lumelIndex
].Normal
-purNormal
+smoothNormal
;
2734 lumels
[lumelIndex
].Normal
.normalize ();
2736 // *** Number of visit
2737 uint visitedCount
=visited
[patch
][lumelIndex
];
2739 // Some lumel have not been found in tesselation
2740 //nlassert (visitedCount==2);
2742 // If visited, renormalise other values
2745 // Normalise position
2746 lumels
[lumelIndex
].S
/=(float)visitedCount
;
2747 lumels
[lumelIndex
].T
/=(float)visitedCount
;
2753 // ***************************************************************************
2754 void CZoneLighter::computeTileFlagsOnly (CLandscape
&landscape
, CZone
& output
, uint zoneToLight
, const CLightDesc
& description
,
2755 std::vector
<uint
> &listZone
)
2758 _ZoneToLight
=zoneToLight
;
2761 _Landscape
=&landscape
;
2765 uint zoneCount
=(uint
)listZone
.size();
2768 for (uint zone
=0; zone
<zoneCount
; zone
++)
2771 uint patchCount
=landscape
.getZone(listZone
[zone
])->getNumPatchs();
2774 _ZoneId
.insert (map
<uint
, uint
>::value_type (listZone
[zone
], zone
));
2778 for (patch
=0; patch
<patchCount
; patch
++)
2781 progress ("Scan all patches", (float)patch
/(float)patchCount
);
2783 // This is the zone to light ?
2784 if (listZone
[zone
]==_ZoneToLight
)
2786 // unExclude all the patches from refine all
2787 landscape
.excludePatchFromRefineAll (listZone
[zone
], patch
, false);
2791 // Exclude all the patches from refine all
2792 landscape
.excludePatchFromRefineAll (listZone
[zone
], patch
, true);
2797 // *** Now tesselate this zone to max accuracy
2799 // Setup the landscape
2800 landscape
.setThreshold (0);
2801 landscape
.setTileMaxSubdivision (0);
2804 progress ("Refine landscape to maximum", 0.5f
);
2805 landscape
.refineAll (CVector (0, 0, 0));
2807 // Get tesselated faces
2808 std::vector
<const CTessFace
*> leaves
;
2809 landscape
.getTessellationLeaves(leaves
);
2812 // compute only the water states
2813 if (_WaterShapes
.size() != 0) // any water shape in this zone ?
2815 /// make a quad grid of each water shape
2816 makeQuadGridFromWaterShapes(landscape
.getZone(_ZoneToLight
)->getZoneBB().getAABBox());
2818 /// check for each tile if it is above / below water
2819 computeTileFlagsForPositionTowardWater(description
, leaves
);
2823 setTileFlagsToDefault(leaves
);
2826 /// verify that the zonew and the zonel (output) are compatible
2828 CZone
&zonew
= *(landscape
.getZone(zoneToLight
));
2829 if(zonew
.getNumPatchs() == output
.getNumPatchs())
2831 // verify for each patch that the tile array are same
2832 for(uint i
=0;i
<(uint
)zonew
.getNumPatchs();i
++)
2834 const CPatch
&p0
= *const_cast<const CZone
&>(zonew
).getPatch(i
);
2835 const CPatch
&p1
= *const_cast<const CZone
&>(output
).getPatch(i
);
2836 if( p0
.getOrderS()!=p1
.getOrderS() || p0
.getOrderT()!=p1
.getOrderT() )
2846 // can't copy tile flags
2848 throw Exception("The input zonew, and output zonel are too different: not same patchs!!");
2850 /// copy the tiles flags from the zone to light to the output zone
2851 copyTileFlags(output
, zonew
);
2854 // ***************************************************************************
2856 CZoneLighter::CLightDesc::CLightDesc ()
2858 SunDirection
.set (1, 1, -1);
2861 HeightfieldSize
=200;
2862 HeightfieldCellSize
=20;
2863 SkyContribution
=true;
2866 ZBufferLandscapeSize
= DEFAULT_ZBUFFER_LANDSCAPE_SIZE
;
2867 ZBufferObjectSize
= DEFAULT_ZBUFFER_OBJECT_SIZE
;
2868 SoftShadowJitter
= DEFAULT_JITTER
;
2869 SunDistance
= DEFAULT_SUN_DISTANCE
;
2870 SunFOV
= (float)DEFAULT_SUN_FOV
;
2871 SunCenter
= DEFAULT_SUN_CENTER
;
2872 SunRadius
= DEFAULT_SUN_RADIUS
;
2873 SoftShadowSamplesSqrt
= DEFAULT_SUN_SRQT_SAMPLES
;
2876 // ***************************************************************************
2877 void CZoneLighter::addLightableShape(IShape
*shape
, const NLMISC::CMatrix
& MT
)
2882 _LightableShapes
.push_back(lsi
);
2886 // ***************************************************************************
2887 bool CZoneLighter::isLightableShape(IShape
&shape
)
2889 /// for now, the only shape that we lit are water shapes
2890 if (dynamic_cast<CWaterShape
*>(&shape
) != NULL
)
2892 // check that this water surface has a diffuse map that is a CTextureFile (we must be able to save it !)
2893 CWaterShape
*ws
= static_cast<CWaterShape
*>(&shape
);
2894 const ITexture
*tex
= ws
->getColorMap();
2895 if (dynamic_cast<const CTextureFile
*>(tex
) != NULL
)
2897 return ws
->isLightMappingEnabled();
2903 // ***************************************************************************
2904 void CZoneLighter::lightShapes(uint zoneID
, const CLightDesc
& description
)
2906 /// compute light for the lightable shapes in the given zone
2907 if (_LightableShapes
.empty()) return;
2909 uint numShapePerThread
= 1 + ((uint
)_LightableShapes
.size() / _ProcessCount
);
2910 uint currShapeIndex
= 0;
2914 _NumLightableShapesProcessed
= 0;
2917 progress("Processing lightable shapes", 0);
2919 for (uint k
= 0; k
< _LightableShapes
.size(); ++k
, ++process
)
2921 uint lastShapeIndex
= currShapeIndex
+ numShapePerThread
;
2922 lastShapeIndex
= std::min((uint
)_LightableShapes
.size(), lastShapeIndex
);
2923 IThread
*pThread
= IThread::create (new CCalcLightableShapeRunnable(process
, this, &description
, &_LightableShapes
, currShapeIndex
, lastShapeIndex
));
2925 currShapeIndex
= lastShapeIndex
;
2928 /// wait for other process
2929 while (_ProcessExited
!= _ProcessCount
)
2938 // ***************************************************************************
2940 void CZoneLighter::processLightableShapeCalc (uint process
,
2941 TShapeVect
*shapesToLit
,
2944 const CLightDesc
& description
)
2946 // for each lightable shape
2947 for (uint k
= firstShape
; k
< lastShape
; ++k
)
2949 nlassert(isLightableShape(* (*shapesToLit
)[k
].Shape
)); // make sure it is a lightable shape
2950 lightSingleShape((*shapesToLit
)[k
], description
, process
);
2955 // ***************************************************************************
2956 void CZoneLighter::lightSingleShape(CShapeInfo
&si
, const CLightDesc
& description
, uint cpu
)
2958 /// we compute the lighting for one single shape
2959 if (dynamic_cast<CWaterShape
*>(si
.Shape
))
2961 lightWater(* static_cast<CWaterShape
*>(si
.Shape
), si
.MT
, description
, cpu
);
2963 ++_NumLightableShapesProcessed
;
2964 progress("Processing lightable shapes", (float) _NumLightableShapesProcessed
/ _LightableShapes
.size());
2970 // ***************************************************************************
2971 // utility function to get the directory of a fileName
2972 static std::string
getDir (const std::string
& path
)
2975 strcpy (tmpPath
, path
.c_str());
2976 char* slash
=strrchr (tmpPath
, '/');
2979 slash
=strrchr (tmpPath
, '\\');
2991 // ***************************************************************************
2992 // utility function to get a file name fdrom a path
2993 static std::string
getName (const std::string
& path
)
2995 std::string dir
=getDir (path
);
2998 strcpy (tmpPath
, path
.c_str());
3001 nlassert (dir
.length()<=strlen(tmpPath
));
3004 char* point
=strrchr (name
, '.');
3012 // ***************************************************************************
3013 // utility function to get the extension of a fileName
3014 static std::string
getExt (const std::string
& path
)
3016 std::string dir
= getDir (path
);
3017 std::string name
= getName (path
);
3020 strcpy (tmpPath
, path
.c_str());
3023 nlassert (dir
.length()+name
.length()<=strlen(tmpPath
));
3024 ext
+=dir
.length()+name
.length();
3030 // ***************************************************************************
3031 void CZoneLighter::lightWater(CWaterShape
&ws
, const CMatrix
&MT
, const CLightDesc
& description
, uint cpu
)
3035 /// get the diffuse map
3036 CTextureFile
*diffuseTex
= NLMISC::safe_cast
<CTextureFile
*>(ws
.getColorMap());
3037 std::string texFileName
= CPath::lookup(diffuseTex
->getFileName());
3038 diffuseTex
->generate();
3039 const uint width
= diffuseTex
->getWidth();
3040 const uint height
= diffuseTex
->getHeight();
3042 /// build a matrix to convert from water space to uv space
3043 NLMISC::CMatrix worldSpaceToUVs
;
3044 NLMISC::CVector2f col0
, col1
, pos
;
3045 ws
.getColorMapMat(col0
, col1
, pos
);
3046 worldSpaceToUVs
.setRot(NLMISC::CVector(col0
.x
* width
, col0
.y
* height
, 0),
3047 NLMISC::CVector(col1
.x
* width
, col1
.y
* height
, 0),
3048 NLMISC::CVector::K
);
3049 worldSpaceToUVs
.setPos(NLMISC::CVector(pos
.x
* width
, pos
.y
* height
, 0));
3051 /// get min and max uvs
3053 ws
.getShapeInWorldSpace(p
);
3058 NLMISC::CVector uvs
= worldSpaceToUVs
* p
.Vertices
[0];
3059 minU
= maxU
= uvs
.x
;
3060 minV
= maxV
= uvs
.y
;
3063 for (uint k
= 1; k
< (uint
) p
.getNumVertices(); ++k
)
3065 uvs
= worldSpaceToUVs
* p
.Vertices
[k
];
3066 minU
= std::min(uvs
.x
, minU
);
3067 minV
= std::min(uvs
.y
, minV
);
3068 maxU
= std::max(uvs
.x
, maxU
);
3069 maxV
= std::max(uvs
.y
, maxV
);
3075 sint iMinU
= (sint
) minU
;
3076 sint iMaxU
= (sint
) maxU
;
3077 sint iMinV
= (sint
) minV
;
3078 sint iMaxV
= (sint
) maxV
;
3080 NLMISC::clamp(iMinU
, 0, (sint
) width
);
3081 NLMISC::clamp(iMaxU
, 0, (sint
) width
);
3082 NLMISC::clamp(iMinV
, 0, (sint
) height
);
3083 NLMISC::clamp(iMaxV
, 0, (sint
) height
);
3085 // matrix to go from uv space to worldspace
3086 NLMISC::CMatrix UVSpaceToWorldSpace
= worldSpaceToUVs
.inverted();
3088 CObjectVector
<uint8
> &pixs8
= diffuseTex
->getPixels();
3089 NLMISC::CRGBA
*rgbPixs
= (NLMISC::CRGBA
*) &pixs8
[0];
3092 /// raytrace each texel
3093 for (sint x
= iMinU
; x
< iMaxU
; ++x
)
3095 for (sint y
= iMinV
; y
< iMaxV
; ++y
)
3098 NLMISC::CVector pos
= UVSpaceToWorldSpace
* NLMISC::CVector( x
+ 0.5f
, y
+ 0.5f
, 0 )
3099 + description
.WaterShadowBias
* NLMISC::CVector::K
;
3100 if (description
.Shadow
)
3102 factor
= attenuation (pos
, description
);
3106 factor
= - NLMISC::CVector::K
* description
.SunDirection
;
3108 clamp(factor
, 0.f
, 1.f
);
3109 factor
= factor
* description
.WaterDiffuse
+ description
.WaterAmbient
;
3110 if (description
.SkyContributionForWater
)
3112 factor
+= getSkyContribution(pos
, NLMISC::CVector::K
, description
.SkyIntensity
);
3114 clamp(factor
, 0.f
, 1.f
);
3115 uint intensity
= (uint8
) (255 * factor
);
3116 NLMISC::CRGBA
srcCol(intensity
,
3121 if (!description
.ModulateWaterColor
)
3123 rgbPixs
[x
+ y
* width
] = srcCol
;
3127 NLMISC::CRGBA
&col
= rgbPixs
[x
+ y
* width
];
3128 col
.modulateFromColor(col
, srcCol
);
3133 /// now, save the result
3134 if (getExt(texFileName
) != ".tga")
3136 nlwarning("Zone lighter : error when lighting a water surface : input bitmap is not a tga file");
3143 of
.open(texFileName
);
3144 diffuseTex
->writeTGA(of
, 24);
3147 catch (const NLMISC::Exception
&)
3149 nlwarning("Zone lighter : while lighting a water shape, writing %s failed! ", texFileName
.c_str());
3153 catch(const NLMISC::Exception
&e
)
3155 nlwarning("Water shape lighting failed !");
3156 nlwarning(e
.what());
3160 // ***********************************************************
3161 void CZoneLighter::addWaterShape(CWaterShape
*shape
, const NLMISC::CMatrix
&MT
)
3163 /// make sure it hasn't been inserted twice
3167 _WaterShapes
.push_back(ci
);
3170 // ***********************************************************
3171 void CZoneLighter::makeQuadGridFromWaterShapes(NLMISC::CAABBox zoneBBox
)
3173 if (_WaterShapes
.empty()) return;
3175 NLMISC::CAABBox tmpBox
;
3177 /// the number of cells we want in the quad grid
3178 const uint numCells
= 16;
3180 /// get the dimension
3181 float width
= zoneBBox
.getMax().x
- zoneBBox
.getMin().x
;
3182 float height
= zoneBBox
.getMax().y
- zoneBBox
.getMin().y
;
3184 float dim
= std::max(width
, height
);
3187 /// init the quad grid
3188 _WaterShapeQuadGrid
.create(numCells
, dim
/ numCells
);
3191 uint count
= 0, totalCount
= (uint
)_WaterShapes
.size();
3193 /// now, insert all water shapes
3194 for (TShapeVect::iterator it
= _WaterShapes
.begin(); it
!= _WaterShapes
.end(); ++it
, ++count
)
3196 /// get the current shape bbox in the world
3197 it
->Shape
->getAABBox(tmpBox
);
3198 NLMISC::CAABBox currBB
= NLMISC::CAABBox::transformAABBox(it
->MT
, tmpBox
);
3200 /// test if it intesect the zone bbox
3201 if (zoneBBox
.intersect(currBB
))
3203 _WaterShapeQuadGrid
.insert(currBB
.getMin(), currBB
.getMax(), *it
);
3205 progress("Building quadtree from water surfaces", (float) count
/ totalCount
);
3208 /// free the vector of water shapes
3209 NLMISC::contReset(_WaterShapes
);
3213 //==================================================================
3215 /// a struct that helps us to know which tile we've processed
3221 CTileOfPatch(uint8 tileId
, CPatch
*patch
) : TileId(tileId
), Patch(patch
)
3228 // ***************************************************************************
3229 // ***************************************************************************
3230 // Static point lights.
3231 // ***************************************************************************
3232 // ***************************************************************************
3235 // ***************************************************************************
3236 CZoneLighter::CPointLightRT::CPointLightRT()
3242 // ***************************************************************************
3243 bool CZoneLighter::CPointLightRT::testRaytrace(const CVector
&v
)
3247 if(!BSphere
.include(v
))
3250 // If Ambient light, just skip
3251 if(PointLight
.getType()== CPointLight::AmbientLight
)
3254 // If SpotLight verify in angle radius.
3255 if(PointLight
.getType()== CPointLight::SpotLight
)
3257 float att
= PointLight
.computeLinearAttenuation(v
);
3262 // Select in the cubeGrid
3263 FaceCubeGrid
.select(v
);
3264 // For all faces selected
3265 while(!FaceCubeGrid
.isEndSel())
3267 const CTriangle
*tri
= FaceCubeGrid
.getSel();
3269 // If intersect, the point is occluded.
3270 if( tri
->Triangle
.intersect(BSphere
.Center
, v
, dummy
, tri
->getPlane()) )
3274 FaceCubeGrid
.nextSel();
3277 // Ok the point is visilbe from the light
3282 // ***************************************************************************
3283 void CZoneLighter::addStaticPointLight(const CPointLightNamed
&pln
)
3287 plRT
.PointLight
= pln
;
3288 // compute plRT.OODeltaAttenuation
3289 plRT
.OODeltaAttenuation
= pln
.getAttenuationEnd() - pln
.getAttenuationBegin();
3290 if(plRT
.OODeltaAttenuation
<=0 )
3291 plRT
.OODeltaAttenuation
= 0;
3293 plRT
.OODeltaAttenuation
= 1.0f
/ plRT
.OODeltaAttenuation
;
3294 // compute plRT.BSphere
3295 plRT
.BSphere
.Center
= pln
.getPosition();
3296 plRT
.BSphere
.Radius
= pln
.getAttenuationEnd();
3297 // NB: FaceCubeGrid will be computed during light()
3300 _StaticPointLights
.push_back(plRT
);
3305 // ***************************************************************************
3306 void CZoneLighter::compilePointLightRT(uint gridSize
, float gridCellSize
, std::vector
<CTriangle
>& obstacles
, bool doShadow
)
3310 // Fill the quadGrid of Lights.
3312 _StaticPointLightQuadGrid
.create(gridSize
, gridCellSize
);
3313 for(i
=0; i
<_StaticPointLights
.size();i
++)
3315 CPointLightRT
&plRT
= _StaticPointLights
[i
];
3317 // Compute the bbox of the light
3319 bbox
.setCenter(plRT
.BSphere
.Center
);
3320 float hl
= plRT
.BSphere
.Radius
;
3321 bbox
.setHalfSize(CVector(hl
,hl
,hl
));
3323 // Insert the pointLight in the quadGrid.
3324 _StaticPointLightQuadGrid
.insert(bbox
.getMin(), bbox
.getMax(), &plRT
);
3328 // Append triangles to cubeGrid ??
3332 if (!_StaticPointLights
.empty ())
3334 // For all obstacles, Fill a quadGrid.
3336 CQuadGrid
<CTriangle
*> obstacleGrid
;
3337 obstacleGrid
.create(gridSize
, gridCellSize
);
3338 uint size
= (uint
)obstacles
.size();
3339 for(i
=0; i
<size
; i
++)
3343 bbox
.setCenter(obstacles
[i
].Triangle
.V0
);
3344 bbox
.extend(obstacles
[i
].Triangle
.V1
);
3345 bbox
.extend(obstacles
[i
].Triangle
.V2
);
3346 // insert triangle in quadGrid.
3347 obstacleGrid
.insert(bbox
.getMin(), bbox
.getMax(), &obstacles
[i
]);
3351 // For all PointLights, fill his CubeGrid
3353 for(i
=0; i
<_StaticPointLights
.size();i
++)
3356 progress ("Compute Influences of PointLights", 0.5f
*i
/ (float)(_StaticPointLights
.size()-1));
3358 CPointLightRT
&plRT
= _StaticPointLights
[i
];
3359 // Create the cubeGrid
3360 plRT
.FaceCubeGrid
.create(plRT
.PointLight
.getPosition(), NL3D_ZONE_LIGHTER_CUBE_GRID_SIZE
);
3362 // AmbiantLIghts: do nothing.
3363 if(plRT
.PointLight
.getType()!=CPointLight::AmbientLight
)
3365 // Select only obstacle Faces around the light. Other are not useful
3367 bbox
.setCenter(plRT
.PointLight
.getPosition());
3368 float hl
= plRT
.PointLight
.getAttenuationEnd();
3369 bbox
.setHalfSize(CVector(hl
,hl
,hl
));
3370 obstacleGrid
.select(bbox
.getMin(), bbox
.getMax());
3372 // For all faces, fill the cubeGrid.
3373 CQuadGrid
<CTriangle
*>::CIterator itObstacle
;
3374 itObstacle
= obstacleGrid
.begin();
3375 while( itObstacle
!=obstacleGrid
.end() )
3377 CTriangle
&tri
= *(*itObstacle
);
3379 // Triangle bounding box
3381 triBbox
.setCenter (tri
.Triangle
.V0
);
3382 triBbox
.extend (tri
.Triangle
.V1
);
3383 triBbox
.extend (tri
.Triangle
.V2
);
3385 // Triangle in the light
3386 if (triBbox
.intersect (bbox
))
3388 // Test BackFace culling. Only faces which are BackFace the point light are inserted.
3389 // This is to avoid AutoOccluding problems
3390 if( tri
.getPlane() * plRT
.BSphere
.Center
< 0)
3392 // Insert the triangle in the CubeGrid
3393 plRT
.FaceCubeGrid
.insert( tri
.Triangle
, &tri
);
3401 // Compile the CubeGrid.
3402 plRT
.FaceCubeGrid
.compile();
3404 // And Reset RefCount.
3409 // else, just build empty grid
3412 for(i
=0; i
<_StaticPointLights
.size();i
++)
3415 progress ("Compute Influences of PointLights", 0.5f
*i
/ (float)(_StaticPointLights
.size()-1));
3417 CPointLightRT
&plRT
= _StaticPointLights
[i
];
3418 // Create a dummy empty cubeGrid => no rayTrace :)
3419 plRT
.FaceCubeGrid
.create(plRT
.PointLight
.getPosition(), 4);
3421 // Compile the CubeGrid.
3422 plRT
.FaceCubeGrid
.compile();
3424 // And Reset RefCount.
3432 // ***************************************************************************
3433 bool CZoneLighter::CPredPointLightToPoint::operator() (CPointLightRT
*pla
, CPointLightRT
*plb
) const
3435 float ra
= (pla
->BSphere
.Center
- Point
).norm();
3436 float rb
= (plb
->BSphere
.Center
- Point
).norm();
3437 float infA
= (pla
->PointLight
.getAttenuationEnd() - ra
) * pla
->OODeltaAttenuation
;
3438 float infB
= (plb
->PointLight
.getAttenuationEnd() - rb
) * plb
->OODeltaAttenuation
;
3439 // return which light impact the most.
3445 // return better impact
3449 // ***************************************************************************
3450 void CZoneLighter::processZonePointLightRT(vector
<CPointLightNamed
> &listPointLight
)
3453 vector
<CPointLightRT
*> lightInfs
;
3454 lightInfs
.reserve(1024);
3456 // clear result list
3457 listPointLight
.clear();
3460 CZone
*zoneToLight
= _Landscape
->getZone(_ZoneToLight
);
3464 // Build patchForPLs
3466 vector
<CPatchForPL
> patchForPLs
;
3467 patchForPLs
.resize(_PatchInfo
.size());
3468 for(i
=0; i
<patchForPLs
.size(); i
++)
3470 // Get OrderS/OrderT
3471 patchForPLs
[i
].OrderS
= _PatchInfo
[i
].OrderS
;
3472 patchForPLs
[i
].OrderT
= _PatchInfo
[i
].OrderT
;
3473 // resize TileLightInfluences
3474 uint w
= patchForPLs
[i
].WidthTLI
= patchForPLs
[i
].OrderS
/2 +1 ;
3475 uint h
= patchForPLs
[i
].HeightTLI
= patchForPLs
[i
].OrderT
/2 +1;
3476 patchForPLs
[i
].TileLightInfluences
.resize(w
*h
);
3480 // compute each TileLightInfluence
3482 for(i
=0; i
<patchForPLs
.size(); i
++)
3485 progress ("Compute Influences of PointLights", 0.5f
+ 0.5f
*i
/ (float)patchForPLs
.size());
3487 CPatchForPL
&pfpl
= patchForPLs
[i
];
3488 const CPatch
*patch
= const_cast<const CZone
*>(zoneToLight
)->getPatch(i
);
3491 for(y
= 0; y
<pfpl
.HeightTLI
; y
++)
3493 for(x
= 0; x
<pfpl
.WidthTLI
; x
++)
3495 // compute the point and normal (normalized) where the TLI lies.
3497 CVector pos
, normal
;
3499 s
= (float)x
/ (pfpl
.WidthTLI
-1);
3500 t
= (float)y
/ (pfpl
.HeightTLI
-1);
3501 // Compute the Vertex, with Noise information (important for accurate raytracing).
3502 pos
= patch
->computeVertex(s
, t
);
3503 // Use UnNoised normal from BezierPatch, because the lighting does not need to be so precise.
3504 CBezierPatch
*bp
= patch
->unpackIntoCache();
3505 normal
= bp
->evalNormal(s
, t
);
3508 // Compute Which light influences him.
3511 // Search possible lights around the position.
3512 _StaticPointLightQuadGrid
.select(pos
, pos
);
3513 // For all of them, get the ones which touch this point.
3514 CQuadGrid
<CPointLightRT
*>::CIterator it
= _StaticPointLightQuadGrid
.begin();
3515 while(it
!= _StaticPointLightQuadGrid
.end())
3517 CPointLightRT
*pl
= *it
;
3519 // a light influence a TLI only if this one is FrontFaced to the light !!
3520 if( ( pl
->BSphere
.Center
- pos
) * normal
> 0)
3522 // Add 5cm else it fails in some case where ( pl->BSphere.Center - pos ) * normal is
3523 // nearly 0 and the point should be occluded.
3524 const float deltaY
= 0.05f
;
3525 CVector posToRT
= pos
+ normal
* deltaY
;
3526 // Test if really in the radius of the light, if no occlusion, and if in SpotAngle
3527 if( pl
->testRaytrace(posToRT
) )
3529 // Ok, add the light to the lights which influence the TLI
3530 lightInfs
.push_back(pl
);
3538 // Choose the Best ones.
3540 CPredPointLightToPoint predPLTP
;
3541 predPLTP
.Point
= pos
;
3543 sort(lightInfs
.begin(), lightInfs
.end(), predPLTP
);
3545 lightInfs
.resize( min((uint
)lightInfs
.size(), (uint
)CTileLightInfluence::NumLightPerCorner
) );
3548 // For each of them, fill TLI
3550 CTileLightInfUnpack tli
;
3552 for(lightInfId
=0; lightInfId
<lightInfs
.size(); lightInfId
++)
3554 CPointLightRT
*pl
= lightInfs
[lightInfId
];
3557 tli
.Light
[lightInfId
]= pl
;
3558 // Compute light Diffuse factor.
3559 CVector dir
= pl
->BSphere
.Center
- pos
;
3561 tli
.LightFactor
[lightInfId
]= dir
* normal
;
3562 clamp(tli
.LightFactor
[lightInfId
], 0.f
, 1.f
);
3563 // modulate by light attenuation.
3564 tli
.LightFactor
[lightInfId
]*= pl
->PointLight
.computeLinearAttenuation(pos
);
3566 // Inc RefCount of the light.
3569 // Reset any empty slot to NULL.
3570 for(; lightInfId
<CTileLightInfluence::NumLightPerCorner
; lightInfId
++)
3572 tli
.Light
[lightInfId
]= NULL
;
3576 // Set TLI in patch.
3578 pfpl
.TileLightInfluences
[y
*pfpl
.WidthTLI
+ x
]= tli
;
3584 // compress and setup _PatchInfo with compressed data.
3587 // Process each pointLights
3588 for(i
=0; i
<_StaticPointLights
.size(); i
++)
3590 CPointLightRT
&plRT
= _StaticPointLights
[i
];
3591 // If this light is used.
3592 if(plRT
.RefCount
> 0)
3594 // Must Copy it into Zone.
3595 listPointLight
.push_back(plRT
.PointLight
);
3597 // If index >= 255, too many lights (NB: => because 255 is a NULL code).
3600 throw Exception("Too many Static Point Lights influence the zone!!");
3605 // For each patch, compress TLI in PatchInfo.
3606 for(i
=0; i
<patchForPLs
.size(); i
++)
3608 CPatchForPL
&pfpl
= patchForPLs
[i
];
3609 CPatchInfo
&pInfo
= _PatchInfo
[i
];
3611 uint w
= pfpl
.WidthTLI
;
3612 uint h
= pfpl
.HeightTLI
;
3614 // Fill pInfo.TileLightInfluences
3615 pInfo
.TileLightInfluences
.resize(w
*h
);
3621 uint tliId
= y
*w
+ x
;
3622 // For all light slot
3623 for(uint lightId
= 0; lightId
<CTileLightInfluence::NumLightPerCorner
; lightId
++)
3625 CTileLightInfUnpack
&tliSrc
= pfpl
.TileLightInfluences
[tliId
];
3626 CTileLightInfluence
&tliDst
= pInfo
.TileLightInfluences
[tliId
];
3627 if(tliSrc
.Light
[lightId
] == NULL
)
3630 tliDst
.Light
[lightId
]= 0xFF;
3635 tliDst
.Light
[lightId
]= tliSrc
.Light
[lightId
]->DstId
;
3636 // Get Diffuse Factor.
3637 tliDst
.setDiffuseLightFactor(lightId
, (uint8
)(tliSrc
.LightFactor
[lightId
]*255));
3647 // ***********************************************************
3648 // ***********************************************************
3649 // TileFlagsForPositionTowardWater
3650 // ***********************************************************
3651 // ***********************************************************
3654 //==================================================================
3655 /// for map insertion of CTileOfPatch structs
3656 static inline bool operator < (const CTileOfPatch
&lhs
, const CTileOfPatch
&rhs
)
3658 return lhs
.Patch
== rhs
.Patch
?
3659 lhs
.TileId
< rhs
.TileId
:
3660 lhs
.Patch
< rhs
.Patch
;
3663 /// A set of tiles from patch and their bbox
3664 typedef std::map
<CTileOfPatch
, NLMISC::CAABBox
> TTileOfPatchMap
;
3666 // ***********************************************************
3667 void CZoneLighter::computeTileFlagsForPositionTowardWater(const CLightDesc
&lightDesc
,
3668 std::vector
<const CTessFace
*> &tessFaces
3671 uint numTileAbove
= 0;
3672 uint numTileBelow
= 0;
3673 uint numTileIntersect
= 0;
3675 /// the tiles that we have setupped so far...
3676 TTileOfPatchMap tiles
;
3678 ///////////////////////////////////////////
3679 // First, build the bbox for all tiles //
3680 ///////////////////////////////////////////
3682 uint triCount
= 0, totalTriCount
= (uint
)tessFaces
.size();
3684 nlinfo("Dealing with %d tessFaces", tessFaces
.size());
3685 for (std::vector
<const CTessFace
*>::iterator it
= tessFaces
.begin(); it
!= tessFaces
.end(); ++it
, ++triCount
)
3687 /// does the face belong to the zone to light ?
3688 if ((*it
)->Patch
->getZone()->getZoneId() != _ZoneToLight
) continue;
3689 /// if the tile flags say that micro vegetation is disabled, just skip that
3690 if ((*it
)->Patch
->Tiles
[(*it
)->TileId
].getVegetableState() == CTileElement::VegetableDisabled
)
3693 CTileOfPatch
top((*it
)->TileId
, (*it
)->Patch
);
3694 TTileOfPatchMap::iterator tileIt
= tiles
.find(top
);
3696 /// test whether we've seen face(s) from this tile before
3697 if (tileIt
== tiles
.end()) // first time ?
3699 /// build a bbox for this face
3701 b
.setMinMax((*it
)->VBase
->EndPos
, (*it
)->VLeft
->EndPos
);
3702 b
.extend((*it
)->VRight
->EndPos
);
3703 b
.extend(b
.getMax() + lightDesc
.VegetableHeight
* NLMISC::CVector::K
); // adds vegetable height
3706 else // extends the bbox with the given face
3708 NLMISC::CAABBox
&b
= tileIt
->second
;
3709 b
.extend((*it
)->VBase
->EndPos
);
3710 b
.extend((*it
)->VRight
->EndPos
);
3711 b
.extend((*it
)->VLeft
->EndPos
);
3714 if ((triCount
% 100) == 0)
3716 progress("Building bbox from tiles", (float) triCount
/ totalTriCount
);
3720 progress("Building bbox from tiles", 1.f
);
3724 ////////////////////////////////////////////////////
3725 // Now, check each tile bbox against water shapes //
3726 ////////////////////////////////////////////////////
3727 NLMISC::CPolygon waterPoly
;
3728 NLMISC::CPolygon2D tilePoly
;
3729 tilePoly
.Vertices
.resize(4);
3731 uint tileCount
= 0, totalTileCount
= (uint
)tiles
.size();
3733 for (TTileOfPatchMap::iterator tileIt
= tiles
.begin(); tileIt
!= tiles
.end(); ++tileIt
, ++tileCount
)
3735 const NLMISC::CVector v0
= tileIt
->second
.getMin();
3736 const NLMISC::CVector v1
= tileIt
->second
.getMax();
3738 /// build a top view from the bbox
3739 tilePoly
.Vertices
[0].set(v0
.x
, v0
.y
);
3740 tilePoly
.Vertices
[1].set(v1
.x
, v0
.y
);
3741 tilePoly
.Vertices
[2].set(v1
.x
, v1
.y
);
3742 tilePoly
.Vertices
[3].set(v0
.x
, v1
.y
);
3744 /// Select the candidate water shape from the quad grid
3745 _WaterShapeQuadGrid
.clearSelection();
3746 _WaterShapeQuadGrid
.select(tileIt
->second
.getMin(), tileIt
->second
.getMax());
3748 CTileElement
&te
= tileIt
->first
.Patch
->Tiles
[tileIt
->first
.TileId
]; // alias to the current tile element
3750 /// test more accurate intersection for each water shape
3751 TWaterShapeQuadGrid::CIterator qgIt
;
3752 for (qgIt
= _WaterShapeQuadGrid
.begin(); qgIt
!= _WaterShapeQuadGrid
.end(); ++qgIt
)
3754 CWaterShape
*waterShape
= safe_cast
<CWaterShape
*>((*qgIt
).Shape
);
3755 waterShape
->getShapeInWorldSpace(waterPoly
, (*qgIt
).MT
);
3756 NLMISC::CPolygon2D
poly(waterPoly
);
3757 if (poly
.intersect(tilePoly
)) // above or below a water surface ?
3760 float waterHeight
= waterPoly
.Vertices
[0].z
;
3762 if (v1
.z
< waterHeight
)
3765 te
.setVegetableState(CTileElement::UnderWater
);
3766 //nlassert(te.getVegetableState() == CTileElement::UnderWater);
3769 else if (v0
. z
> waterHeight
)
3772 te
.setVegetableState(CTileElement::AboveWater
);
3773 //nlassert(te.getVegetableState() == CTileElement::AboveWater);
3779 te
.setVegetableState(CTileElement::IntersectWater
);
3780 //nlassert(te.getVegetableState() == CTileElement::IntersectWater);
3781 ++ numTileIntersect
;
3787 if (qgIt
== _WaterShapeQuadGrid
.end()) // no intersection found ? if yes it's above water
3789 te
.setVegetableState(CTileElement::AboveWater
);
3790 //nlassert(te.getVegetableState() == CTileElement::AboveWater);
3794 if ((tileCount
% 50) == 0)
3796 progress("Computing tile position towards water", (float) tileCount
/ totalTileCount
);
3800 progress("Computing tile position towards water", 1.f
);
3802 nlinfo(" %d tiles are above water.", numTileAbove
);
3803 nlinfo(" %d tiles are below water.", numTileBelow
);
3804 nlinfo(" %d tiles intersect water.", numTileIntersect
);
3808 /// delete the quadgrid now
3809 NLMISC::contReset(_WaterShapeQuadGrid
);
3812 // ***********************************************************
3814 void CZoneLighter::setTileFlagsToDefault(std::vector
<const CTessFace
*> &tessFaces
)
3816 /// We may setup a tile several time, but this doesn't matter here...
3817 for (std::vector
<const CTessFace
*>::iterator it
= tessFaces
.begin(); it
!= tessFaces
.end(); ++it
)
3819 if ((*it
)->Patch
->getZone()->getZoneId() != _ZoneToLight
) continue;
3820 CTileElement
&te
= (*it
)->Patch
->Tiles
[(*it
)->TileId
];
3821 if (te
.getVegetableState() != CTileElement::VegetableDisabled
)
3823 te
.setVegetableState(CTileElement::AboveWater
);
3828 // ***********************************************************
3830 uint
CZoneLighter::getAPatch (uint process
)
3833 CSynchronized
<std::vector
<bool> >::CAccessor
access (&_PatchComputed
);
3836 uint index
= _LastPatchComputed
[process
];
3837 uint firstIndex
= index
;
3839 nlassert(index
< _PatchInfo
.size());
3841 if (access
.value().empty())
3845 while (access
.value()[index
])
3851 if (index
== _PatchInfo
.size())
3855 if (firstIndex
== index
)
3861 access
.value()[index
] = true;
3864 _LastPatchComputed
[process
] = index
;
3865 _NumberOfPatchComputed
++;
3871 // ***********************************************************
3873 float CZoneLighter::attenuation (const CVector
&pos
, const CZoneLighter::CLightDesc
&description
)
3877 // *** Landscape attenuation
3880 float averageAttenuation
= 0;
3881 float randomSum
= 0;
3885 const uint samples
= description
.SoftShadowSamplesSqrt
*description
.SoftShadowSamplesSqrt
;
3886 for (sample
=0; sample
<samples
; sample
++)
3889 CZBuffer
&zbuffer
= _ZBufferLandscape
[sample
];
3891 // Get position in z buffer
3893 transformVectorToZBuffer (zbuffer
, pos
, zPos
);
3896 float random
= (float)_Random
.rand () * description
.SoftShadowJitter
+ _Random
.RandMax
* (1.f
- description
.SoftShadowJitter
);
3897 averageAttenuation
+= random
* testZPercentageCloserFilter (zPos
.x
-(float)zbuffer
.LocalZBufferXMin
, zPos
.y
-(float)zbuffer
.LocalZBufferYMin
, zPos
.z
, zbuffer
, description
, _ZBufferOverflow
);
3898 randomSum
+= random
;
3901 // Average landscape attenuation
3902 averageAttenuation
/= randomSum
;
3906 // *** Attenuation in the object zbuffer
3908 // Get position in z buffer
3910 transformVectorToZBuffer (_ZBufferObject
, pos
, zPos
);
3912 const float objectAttenuation
= testZPercentageCloserFilter (zPos
.x
-(float)_ZBufferObject
.LocalZBufferXMin
, zPos
.y
-(float)_ZBufferObject
.LocalZBufferYMin
, zPos
.z
, _ZBufferObject
, description
, _ZBufferOverflow
);
3915 // *** Return the min of the both
3916 return std::min (objectAttenuation
, averageAttenuation
);
3919 // ***********************************************************