Change Encyclo button name and macros icon
[ryzomcore.git] / nel / src / 3d / patch_noise.cpp
blobf0a49134694133f19d92b53cdac2fb0dfc779983
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "std3d.h"
20 #include "nel/3d/patch.h"
21 #include "nel/3d/tessellation.h"
22 #include "nel/3d/bezier_patch.h"
23 #include "nel/3d/zone.h"
24 #include "nel/3d/landscape.h"
25 #include "nel/misc/vector.h"
26 #include "nel/misc/common.h"
27 #include "nel/3d/tile_noise_map.h"
28 #include "nel/3d/patchuv_locator.h"
30 using namespace std;
31 using namespace NLMISC;
33 #ifdef DEBUG_NEW
34 #define new DEBUG_NEW
35 #endif
37 namespace NL3D
41 // ***************************************************************************
44 #if defined(NL_OS_WINDOWS) && !defined(NL_NO_ASM) && defined(NL_USE_FASTFLOOR)
46 /* This floor works only for floor with noise, because floor/ceil are only made on decimal coordinates:
47 sTile =1.25 .... NB: because of difference of mapping (rare case), we may have sometimes values with
48 precision < 1/4 (eg 1.125). Just use f*256 to compute the floor.
50 NB: using a fastFloor() (fistp changing the controlfp() is not very a good idea here, because
51 computeNoise() are not "packed", so change on controlFp() would bee too frequent...
52 And also because we need either floor() or ceil() here.
54 inline sint noiseFloor(float f)
56 // build a fixed 24:8.
57 sint a;
58 f*=256;
60 // fast ftol. work if no decimal.
61 _asm
63 fld f
64 fistp a
67 // floor.
68 a>>=8;
70 return a;
74 inline sint noiseCeil(float f)
76 // build a fixed 24:8.
77 sint a;
78 f*=256;
80 // fast ftol. work if no decimal.
81 _asm
83 fld f
84 fistp a
87 // ceil.
88 a+=255;
89 a>>=8;
91 return a;
95 inline float noiseFloorF(float f)
97 return (float)noiseFloor(f);
99 inline float noiseCeilF(float f)
101 return (float)noiseCeil(f);
105 #else
108 inline float noiseFloorF(float f)
110 return (float)floor(f);
112 inline float noiseCeilF(float f)
114 return (float)ceil(f);
117 inline sint noiseFloor(float f)
119 return (sint)floor(f);
121 inline sint noiseCeil(float f)
123 return (sint)ceil(f);
126 #endif
130 // ***************************************************************************
131 float CPatch::computeDisplaceRawInteger(sint ts, sint tt, sint ms, sint mt) const
133 // Choose the noiseMap.
134 // ===============================
135 clamp(ts, 0, OrderS-1);
136 clamp(tt, 0, OrderT-1);
138 uint tileId= tt*OrderS + ts;
139 // Get the tile for pass0. This is the principal tile, and this one tells what noise to take.
140 sint tileNumber= Tiles[tileId].Tile[0];
141 // Get the subNoise from tileElement.
142 uint tileSubNoise= Tiles[tileId].getTileSubNoise();
144 // retrieve the wanted noiseMap.
145 CTileNoiseMap *noiseMap;
146 noiseMap = getZone()->getLandscape()->TileBank.getTileNoiseMap (tileNumber, tileSubNoise);
148 if (noiseMap == NULL)
149 return 0.0f;
151 // Sample the noiseMap with (s,t).
152 // ===============================
154 // sample from map.
155 sint8 pix= noiseMap->Pixels[mt*NL3D_TILE_NOISE_MAP_SIZE + ms];
157 // normalize.
158 return (float)pix * (NL3D_NOISE_MAX / 127.f);
162 // ***************************************************************************
163 void CPatch::computeDisplaceRawCoordinates(float sTile, float tTile, float s, float t,
164 sint &ts, sint &tt, sint &ms, sint &mt) const
166 // Choose the noiseMap.
167 // ===============================
168 // Compute coordinate in the patch.
169 ts= noiseFloor(sTile);
170 tt= noiseFloor(tTile);
173 // Sample the noiseMap with (s,t).
174 // ===============================
176 // scale the map.
177 float u= s * NL3D_TILE_NOISE_MAP_TILE_FACTOR;
178 float v= t * NL3D_TILE_NOISE_MAP_TILE_FACTOR;
180 // Speed rotation.
181 CUV uv;
182 switch(NoiseRotation & 3)
184 case 0:
185 uv.U= u;
186 uv.V= v;
187 break;
188 case 1:
189 uv.U= NL3D_TILE_NOISE_MAP_SIZE-v;
190 uv.V= u;
191 break;
192 case 2:
193 uv.U= NL3D_TILE_NOISE_MAP_SIZE-u;
194 uv.V= NL3D_TILE_NOISE_MAP_SIZE-v;
195 break;
196 case 3:
197 uv.U= v;
198 uv.V= NL3D_TILE_NOISE_MAP_SIZE-u;
199 break;
202 // direct map (no bilinear, no round, the case where s,t < 1/4 of a tile is very rare).
203 ms= noiseFloor(uv.U);
204 mt= noiseFloor(uv.V);
206 // Manage Tiling (add NL3D_TILE_NOISE_MAP_SIZE*1 should be sufficient, but take margin).
207 ms= (ms + (NL3D_TILE_NOISE_MAP_SIZE*256)) & (NL3D_TILE_NOISE_MAP_SIZE-1);
208 mt= (mt + (NL3D_TILE_NOISE_MAP_SIZE*256)) & (NL3D_TILE_NOISE_MAP_SIZE-1);
213 // ***************************************************************************
214 float CPatch::computeDisplaceRaw(float sTile, float tTile, float s, float t) const
216 sint ts,tt,ms,mt;
217 computeDisplaceRawCoordinates(sTile, tTile, s, t, ts, tt, ms, mt);
218 return computeDisplaceRawInteger(ts, tt, ms, mt);
222 // ***************************************************************************
223 static inline void computeDisplaceBilinear(float sTile, float tTile,
224 float &sInc, float &tInc, float &sa, float &ta, float &sa1, float &ta1)
226 float sDecimal= sTile-noiseFloor(sTile);
227 float tDecimal= tTile-noiseFloor(tTile);
228 float sDist, tDist;
230 // Do a bilinear centered on 0.5, 0.5.
232 // Compute increment, according to position against center.
233 if(sDecimal>=0.5)
234 sInc= 1;
235 else
236 sInc= -1;
237 if(tDecimal>=0.5)
238 tInc= 1;
239 else
240 tInc= -1;
242 // Compute weight factor.
243 sDist= (float)fabs(0.5 - sDecimal); // s distance from center.
244 tDist= (float)fabs(0.5 - tDecimal); // t distance from center.
245 sa= 1-sDist;
246 ta= 1-tDist;
247 sa1= 1-sa;
248 ta1= 1-ta;
252 // ***************************************************************************
253 float CPatch::computeDisplaceInteriorSmooth(float s, float t) const
255 float sTile= s;
256 float tTile= t;
257 float ret;
259 // compute bi-linear weight factors.
260 float sInc, tInc, sa, ta, sa1, ta1;
261 computeDisplaceBilinear(sTile, tTile, sInc, tInc, sa, ta, sa1, ta1);
264 // NB: to have smooth transition, must keep the same (s,t), so we do a transition with the noise tile of
265 // our neigbhor, but under us.
267 // speed up, using just one computeDisplaceRawCoordinates(), and multiple computeDisplaceRawInteger().
268 sint ts,tt,ms,mt;
269 computeDisplaceRawCoordinates(sTile, tTile, s, t, ts, tt, ms, mt);
271 sint sIncInt= (sint) sInc;
272 sint tIncInt= (sint) tInc;
273 ret = computeDisplaceRawInteger(ts, tt, ms,mt) * sa * ta;
274 ret+= computeDisplaceRawInteger(ts+sIncInt, tt, ms,mt) * sa1 * ta;
275 ret+= computeDisplaceRawInteger(ts, tt+tIncInt, ms,mt) * sa * ta1;
276 ret+= computeDisplaceRawInteger(ts+sIncInt, tt+tIncInt, ms,mt) * sa1 * ta1;
278 return ret;
282 // ***************************************************************************
283 float CPatch::computeDisplaceEdgeSmooth(float s, float t, sint8 smoothBorderX, sint8 smoothBorderY) const
285 float sTile= s;
286 float tTile= t;
287 CBindInfo bindInfo;
288 uint edge=0;
290 // only one must be not null
291 nlassert( (smoothBorderX==0) != (smoothBorderY==0) );
294 // Get the edge against we must share displace.
295 if(smoothBorderX==-1) edge=0;
296 else if(smoothBorderY==1) edge=1;
297 else if(smoothBorderX==1) edge=2;
298 else if(smoothBorderY==-1) edge=3;
299 else nlstop;
301 // Build the bindInfo against this edge.
302 getBindNeighbor(edge, bindInfo);
304 // Fast reject: if no neighbor, just do a simple computeDisplaceInteriorSmooth.
305 if(!bindInfo.Zone)
306 return computeDisplaceInteriorSmooth(s, t);
307 // else, look for result in neighborhood.
308 else
310 float ret;
313 // compute bi-linear weight factors.
314 float sInc, tInc, sa, ta, sa1, ta1;
315 computeDisplaceBilinear(sTile, tTile, sInc, tInc, sa, ta, sa1, ta1);
316 // Manage limit case: if bilinear has not chosen the good direction (because of floor and orientation).
317 // eg on Right edge: This case arise if sDecimal==0, so if sa==sa1==0.5f. result is that
318 // smoothBorderX is != than sInc on right border. same reasoning with downBorder (smoothBorderY=1).
320 // NO NEED TO DO HERE, because sInc or tInc is not used if it is bad (smoothBorder? used instead).
321 // and no need to correct sa, sa1, because in this case they are both equal to 0.5.
324 // compute Neighboring info.
325 CPatchUVLocator uvLocator;
326 uvLocator.build(this, edge, bindInfo);
328 /* NB: there is floor problems with neighbors:
329 - difference of orientation => uv.v=1. This point to the 1th tile. But this is not the same, if
330 v goes to up, or goes to down.
331 - if multiple bind, problem at limit (eg a bind 1/2 on edge 0 with OrdertT=8, when uv.v= 4).
332 because, selection of the patch is dependent of orientation too.
333 To avoid them, just take center of (sTile, tTile) to remove ambiguity.
334 This works because computeDisplaceRaw() use sTile, tTile to get the noiseMap, so the decimal part is not
335 used.
337 Notice that we do this AFTER computeDisplaceBilinear() of course.
339 sTile= noiseFloor(sTile) + 0.5f;
340 tTile= noiseFloor(tTile) + 0.5f;
341 // If we were exactly on the superior edge, prec compute is false... so correct this here.
342 if(sTile>OrderS) sTile--;
343 if(tTile>OrderT) tTile--;
346 // Bilinear across an edge (enjoy!!).
347 CVector2f stTileIn, stIn;
348 CVector2f stTileOut, stOut;
349 CPatch *patchOut;
350 uint patchId;
353 // if vertical edge.
354 if(smoothBorderX!=0)
356 // compute contribution of our patch.
357 ret = computeDisplaceRaw(sTile,tTile, s,t) * sa * ta;
358 ret+= computeDisplaceRaw(sTile,tTile+tInc, s,t) * sa * ta1;
360 // compute contribution of next(s) patchs.
362 // contribution of next at tTile.
363 // Keep the same coordinate.
364 stIn.set(s, t);
365 // But look for the neighbor noise tile.
366 stTileIn.set(sTile+smoothBorderX, tTile);
367 // change basis: find the s,t on the neighbor patch.
368 patchId= uvLocator.selectPatch(stTileIn);
369 uvLocator.locateUV(stTileIn, patchId, patchOut, stTileOut);
370 uvLocator.locateUV(stIn, patchId, patchOut, stOut);
371 // Compute displace, and bi-linear on the neighbor patch.
372 ret+= patchOut->computeDisplaceRaw(stTileOut.x, stTileOut.y, stOut.x, stOut.y) * sa1 * ta;
374 // contribution of next at tTile+tInc (same reasoning).
375 stIn.set(s, t);
376 stTileIn.set(sTile+smoothBorderX, tTile+tInc);
377 patchId= uvLocator.selectPatch(stTileIn);
378 uvLocator.locateUV(stTileIn, patchId, patchOut, stTileOut);
379 uvLocator.locateUV(stIn, patchId, patchOut, stOut);
380 ret+= patchOut->computeDisplaceRaw(stTileOut.x, stTileOut.y, stOut.x, stOut.y) * sa1 * ta1;
383 // else if horizontal edge.
384 else
386 // same reasoning as above.
388 // compute contribution of our patch.
389 ret = computeDisplaceRaw(sTile, tTile, s,t) * sa * ta;
390 ret+= computeDisplaceRaw(sTile+sInc,tTile, s,t) * sa1 * ta;
392 // compute contribution of next(s) patchs.
393 // contribution of next at tTile.
394 stIn.set(s, t);
395 stTileIn.set(sTile, tTile+smoothBorderY);
396 patchId= uvLocator.selectPatch(stTileIn);
397 uvLocator.locateUV(stTileIn, patchId, patchOut, stTileOut);
398 uvLocator.locateUV(stIn, patchId, patchOut, stOut);
399 ret+= patchOut->computeDisplaceRaw(stTileOut.x, stTileOut.y, stOut.x, stOut.y) * sa * ta1;
401 // contribution of next at tTile+tInc (same reasoning).
402 stIn.set(s, t);
403 stTileIn.set(sTile+sInc, tTile+smoothBorderY);
404 patchId= uvLocator.selectPatch(stTileIn);
405 uvLocator.locateUV(stTileIn, patchId, patchOut, stTileOut);
406 uvLocator.locateUV(stIn, patchId, patchOut, stOut);
407 ret+= patchOut->computeDisplaceRaw(stTileOut.x, stTileOut.y, stOut.x, stOut.y) * sa1 * ta1;
410 return ret;
416 // ***************************************************************************
417 float CPatch::computeDisplaceRawOnNeighbor(float sTile, float tTile, float s, float t) const
419 sint edge= -1;
421 // look on what neighbor patch we must find the value (if any).
422 if(sTile<0) edge=0;
423 else if(tTile>OrderT) edge=1;
424 else if(sTile>OrderS) edge=2;
425 else if(tTile<0) edge=3;
427 // If the location is In the patch, just return normal value.
428 if(edge==-1)
429 return computeDisplaceRaw(sTile, tTile, s, t);
430 // else must find on neighbor.
431 else
433 CBindInfo bindInfo;
434 getBindNeighbor(edge, bindInfo);
436 // Fast reject: if no neighbor on the edge, just do a simple computeDisplaceRaw()
437 if(!bindInfo.Zone)
439 return computeDisplaceRaw(sTile, tTile, s, t);
441 // else must find on neighbor.
442 else
444 CPatchUVLocator uvLocator;
445 uvLocator.build(this, edge, bindInfo);
447 CVector2f stTileIn, stIn;
448 CVector2f stTileOut, stOut;
449 CPatch *patchOut;
450 uint patchId;
452 // look on neighbor. same reasoning as in computeDisplaceEdgeSmooth();
453 stIn.set(s, t);
454 stTileIn.set(sTile, tTile);
455 patchId= uvLocator.selectPatch(stTileIn);
456 uvLocator.locateUV(stTileIn, patchId, patchOut, stTileOut);
457 uvLocator.locateUV(stIn, patchId, patchOut, stOut);
458 return patchOut->computeDisplaceRaw(stTileOut.x, stTileOut.y, stOut.x, stOut.y);
465 // ***************************************************************************
466 float CPatch::computeDisplaceCornerSmooth(float s, float t, sint8 smoothBorderX, sint8 smoothBorderY) const
468 // compute the value across the corner (enjoy!!)
469 // NB: Only corners with Edges==4 and corners on a bind are correclty supported.
470 // ignore problems with corner which nbEdges!=4, because Blend of normals blend to 0 on corner (see computenoise()).
472 float sTile= s;
473 float tTile= t;
474 CBindInfo bindInfoX;
475 CBindInfo bindInfoY;
476 uint edgeX=0;
477 uint edgeY=0;
479 // both must be not null
480 nlassert( (smoothBorderX!=0) && (smoothBorderY!=0) );
483 // Get the edge against we must share displace.
484 if(smoothBorderX==-1) edgeX=0;
485 else if(smoothBorderX==1) edgeX=2;
486 else nlstop;
487 if(smoothBorderY==1) edgeY=1;
488 else if(smoothBorderY==-1) edgeY=3;
489 else nlstop;
491 // Build the bindInfo against those 2 edge.
492 getBindNeighbor(edgeX, bindInfoX);
493 getBindNeighbor(edgeY, bindInfoY);
495 // Fast reject: if no neighbor on one of the edge, just do a simple computeDisplaceInteriorSmooth.
496 if(!bindInfoX.Zone || !bindInfoY.Zone)
497 return computeDisplaceInteriorSmooth(s, t);
498 else
500 float ret;
503 // compute bi-linear weight factors.
504 float sInc, tInc, sa, ta, sa1, ta1;
505 computeDisplaceBilinear(sTile, tTile, sInc, tInc, sa, ta, sa1, ta1);
506 // Manage limit case: if bilinear has not chosen the good direction (because of floor and orientation).
507 // eg on Right edge: This case arise if sDecimal==0, so if sa==sa1==0.5f. result is that
508 // smoothBorderX is != than sInc on right border. same reasoning with downBorder (smoothBorderY=1).
510 // NO NEED TO DO HERE, because sInc or tInc are not used at all.
514 // compute Neighboring info.
515 CPatchUVLocator uvLocatorX;
516 CPatchUVLocator uvLocatorY;
517 uvLocatorX.build(this, edgeX, bindInfoX);
518 uvLocatorY.build(this, edgeY, bindInfoY);
521 /* NB: see floor problems note in computeDisplaceEdgeSmooth();
523 sTile= noiseFloor(sTile) + 0.5f;
524 tTile= noiseFloor(tTile) + 0.5f;
525 // If we were exactly on the superior edge, prec compute is false... so correct this here.
526 if(sTile>OrderS) sTile--;
527 if(tTile>OrderT) tTile--;
530 // Bilinear across a corner.
531 CVector2f stTileIn, stIn;
532 CVector2f stTileOut, stOut;
533 CPatch *patchOut;
534 uint patchId;
537 // compute contribution of our patch.
538 ret = computeDisplaceRaw(sTile,tTile, s,t) * sa * ta;
540 // compute contribution of the patch on the left/right side. same reasoning as in computeDisplaceEdgeSmooth();
541 stIn.set(s, t);
542 stTileIn.set(sTile+smoothBorderX, tTile);
543 patchId= uvLocatorX.selectPatch(stTileIn);
544 uvLocatorX.locateUV(stTileIn, patchId, patchOut, stTileOut);
545 uvLocatorX.locateUV(stIn, patchId, patchOut, stOut);
546 ret+= patchOut->computeDisplaceRaw(stTileOut.x, stTileOut.y, stOut.x, stOut.y) * sa1 * ta;
549 // compute contribution of the patch on the up/down side. same reasoning as in computeDisplaceEdgeSmooth();
550 stIn.set(s, t);
551 stTileIn.set(sTile, tTile+smoothBorderY);
552 patchId= uvLocatorY.selectPatch(stTileIn);
553 uvLocatorY.locateUV(stTileIn, patchId, patchOut, stTileOut);
554 uvLocatorY.locateUV(stIn, patchId, patchOut, stOut);
555 ret+= patchOut->computeDisplaceRaw(stTileOut.x, stTileOut.y, stOut.x, stOut.y) * sa * ta1;
558 /* compute contribution of the patch adjacent to me.
559 There is multiple case to consider here. Take example with corner=0 (ie smBdX=smBdY=-1):
560 - if we are a normal corner with 4 edges, take the result from the left patch of our top patch.
561 - if the corner is on a bind Edge, the top patch may be the bigger patch, so don't take the result
562 from his neighbor (of course).
563 - if we are a normal corner with N!=4 edges, just do same thing than if N==4. this is false but don't bother.
565 To solve smoothly cases 1 and 2, use computeDisplaceRawOnNeighbor().
566 This method, if nessecary, look on his neighbor to compute the value.
568 stIn.set(s, t);
569 stTileIn.set(sTile+smoothBorderX, tTile+smoothBorderY);
570 // look on our "top" patch (this is arbitrary).
571 patchId= uvLocatorY.selectPatch(stTileIn);
572 uvLocatorY.locateUV(stTileIn, patchId, patchOut, stTileOut);
573 uvLocatorY.locateUV(stIn, patchId, patchOut, stOut);
574 ret+= patchOut->computeDisplaceRawOnNeighbor(stTileOut.x, stTileOut.y, stOut.x, stOut.y) * sa1 * ta1;
577 return ret;
582 // ***************************************************************************
583 CVector CPatch::computeNormalEdgeSmooth(float s, float t, sint8 smoothBorderX, sint8 smoothBorderY) const
585 CBindInfo bindInfo;
586 uint edge=0;
587 CBezierPatch *bpatch;
588 bpatch= unpackIntoCache();
590 // only one must be not null
591 nlassert( (smoothBorderX==0) != (smoothBorderY==0) );
594 // Get the edge against we must share displace.
595 if(smoothBorderX==-1) edge=0;
596 else if(smoothBorderY==1) edge=1;
597 else if(smoothBorderX==1) edge=2;
598 else if(smoothBorderY==-1) edge=3;
599 else nlstop;
601 // If the edge is smoothed, blend with neighbor.
602 if(getSmoothFlag(edge))
604 // Build the bindInfo against this edge.
605 getBindNeighbor(edge, bindInfo);
607 // Fast reject: if no neighbor, just do a simple computeDisplaceInteriorSmooth.
608 if(!bindInfo.Zone)
609 return bpatch->evalNormal(s/getOrderS(), t/getOrderT());
610 else
612 CVector r0, r1;
614 // Compute our contribution.
615 r0= bpatch->evalNormal(s/getOrderS(), t/getOrderT());
617 // Compute the coordinate on the border of the edge, and the coef of the blend.
618 float se=s;
619 float te=t;
620 float coef=0.0;
621 if(smoothBorderX==-1) se= noiseFloorF(se), coef=s-se;
622 else if(smoothBorderX==1) se= noiseCeilF(se), coef=se-s;
623 else if(smoothBorderY==-1) te= noiseFloorF(te), coef=t-te;
624 else if(smoothBorderY==1) te= noiseCeilF(te), coef=te-t;
625 coef= 0.5f + coef*0.5f;
627 // Compute contribution of the normal on the neighbor, on the border of the edge.
628 CPatchUVLocator uvLocator;
629 CVector2f stIn, stOut;
630 CPatch *patchOut;
631 uint patchId;
633 uvLocator.build(this, edge, bindInfo);
634 stIn.set(se, te);
635 patchId= uvLocator.selectPatch(stIn);
636 uvLocator.locateUV(stIn, patchId, patchOut, stOut);
638 bpatch= patchOut->unpackIntoCache();
639 r1= bpatch->evalNormal(stOut.x/patchOut->getOrderS(), stOut.y/patchOut->getOrderT());
641 // NB: don't bother problems with bind 1/X and the choice of the patch, because bind are C1, so normal is C0.
643 // Blend 2 result. For speed optim, don't normalize.
644 return r0*coef + r1*(1-coef);
647 // else blend with vector Null.
648 else
650 // compute coef.
651 float se=s;
652 float te=t;
653 float coef=0.0;
654 if(smoothBorderX==-1) se= noiseFloorF(se), coef=s-se;
655 else if(smoothBorderX==1) se= noiseCeilF(se), coef=se-s;
656 else if(smoothBorderY==-1) te= noiseFloorF(te), coef=t-te;
657 else if(smoothBorderY==1) te= noiseCeilF(te), coef=te-t;
659 // Compute our contribution.
660 CVector r0;
661 r0= bpatch->evalNormal(s/getOrderS(), t/getOrderT());
663 // Blend with 0.
664 return r0*coef;
669 // ***************************************************************************
670 CVector CPatch::computeNormalOnNeighbor(float s, float t, uint edgeExclude) const
672 sint edge= -1;
674 // look on what neighbor patch we must find the value (if any).
675 if(s<1 && edgeExclude!=0) edge=0;
676 else if(t>OrderT-1 && edgeExclude!=1) edge=1;
677 else if(s>OrderS-1 && edgeExclude!=2) edge=2;
678 else if(t<1 && edgeExclude!=3) edge=3;
681 // If the location is In the patch, just return normal value. (case of a bind 1/X).
682 if(edge==-1)
684 CBezierPatch *bpatch= unpackIntoCache();
685 return bpatch->evalNormal(s/getOrderS(), t/getOrderT());
687 // else must find on neighbor.
688 else
690 CBindInfo bindInfo;
691 getBindNeighbor(edge, bindInfo);
693 // Fast reject: if no neighbor on the edge, just do a simple computeDisplaceRaw()
694 if(!bindInfo.Zone)
696 CBezierPatch *bpatch= unpackIntoCache();
697 return bpatch->evalNormal(s/getOrderS(), t/getOrderT());
699 // else must find on neighbor.
700 else
702 CPatchUVLocator uvLocator;
703 uvLocator.build(this, edge, bindInfo);
705 CVector2f stIn;
706 CVector2f stOut;
707 CPatch *patchOut;
708 uint patchId;
710 // look on neighbor. same reasoning as in computeDisplaceEdgeSmooth();
711 stIn.set(s, t);
712 patchId= uvLocator.selectPatch(stIn);
713 uvLocator.locateUV(stIn, patchId, patchOut, stOut);
714 CBezierPatch *bpatch= patchOut->unpackIntoCache();
715 return bpatch->evalNormal(stOut.x/patchOut->getOrderS(), stOut.y/patchOut->getOrderT());
722 // ***************************************************************************
723 CVector CPatch::computeNormalCornerSmooth(float s, float t, sint8 smoothBorderX, sint8 smoothBorderY) const
725 CBindInfo bindInfoX;
726 CBindInfo bindInfoY;
727 uint edgeX=0;
728 uint edgeY=0;
729 uint corner;
730 CBezierPatch *bpatch;
731 bpatch= unpackIntoCache();
733 // both must be not null
734 nlassert( (smoothBorderX!=0) && (smoothBorderY!=0) );
737 // Get the edge against we must share displace.
738 if(smoothBorderX==-1) edgeX=0;
739 else if(smoothBorderX==1) edgeX=2;
740 else nlstop;
741 if(smoothBorderY==1) edgeY=1;
742 else if(smoothBorderY==-1) edgeY=3;
743 else nlstop;
745 // Get the corner against we must share displace.
746 if(smoothBorderX==-1)
748 if(smoothBorderY==-1) corner=0;
749 else corner=1;
751 else
753 if(smoothBorderY==-1) corner=3;
754 else corner=2;
757 // If this corner is smoothed, blend with 4 neighbors patchs.
758 if(getCornerSmoothFlag(corner))
760 // Build the bindInfo against the 2 edge.
761 getBindNeighbor(edgeX, bindInfoX);
762 getBindNeighbor(edgeY, bindInfoY);
764 // Fast reject: if no neighbor, just do a simple computeDisplaceInteriorSmooth.
765 if(!bindInfoX.Zone || !bindInfoY.Zone)
766 return bpatch->evalNormal(s/getOrderS(), t/getOrderT());
767 else
769 CVector ret;
772 // Compute the coordinate on the border of the edge, and the coef of the blend.
773 float se=s;
774 float te=t;
775 float coefX;
776 float coefY;
777 if(smoothBorderX==-1) se= noiseFloorF(se), coefX=s-se;
778 else se= noiseCeilF(se), coefX=se-s;
779 if(smoothBorderY==-1) te= noiseFloorF(te), coefY=t-te;
780 else te= noiseCeilF(te), coefY=te-t;
781 coefX= 0.5f + coefX*0.5f;
782 coefY= 0.5f + coefY*0.5f;
785 // Compute our contribution.
786 ret= bpatch->evalNormal(s/getOrderS(), t/getOrderT()) *coefX*coefY;
789 // compute Neighboring info.
790 CPatchUVLocator uvLocatorX;
791 CPatchUVLocator uvLocatorY;
792 CVector2f stIn, stOut;
793 CPatch *patchOut;
794 uint patchId;
796 uvLocatorX.build(this, edgeX, bindInfoX);
797 uvLocatorY.build(this, edgeY, bindInfoY);
799 // Patch on our X side.
800 stIn.set(se, te);
801 patchId= uvLocatorX.selectPatch(stIn);
802 uvLocatorX.locateUV(stIn, patchId, patchOut, stOut);
803 bpatch= patchOut->unpackIntoCache();
804 ret+= bpatch->evalNormal(stOut.x/patchOut->getOrderS(), stOut.y/patchOut->getOrderT()) *(1-coefX)*coefY;
806 // Patch on our Y side.
807 stIn.set(se, te);
808 patchId= uvLocatorY.selectPatch(stIn);
809 uvLocatorY.locateUV(stIn, patchId, patchOut, stOut);
810 bpatch= patchOut->unpackIntoCache();
811 ret+= bpatch->evalNormal(stOut.x/patchOut->getOrderS(), stOut.y/patchOut->getOrderT()) *coefX*(1-coefY);
813 /* compute contribution of the patch adjacent to me.
814 Same reasoning as in computeDisplaceCornerSmooth().
816 stIn.set(se, te);
817 patchId= uvLocatorY.selectPatch(stIn);
818 uvLocatorY.locateUV(stIn, patchId, patchOut, stOut);
819 // Because we compute the normal exactly on the edge, we must inform this method not to take us as neighbor.
820 // ugly but simpler.
821 ret+= patchOut->computeNormalOnNeighbor(stOut.x, stOut.y, bindInfoY.Edge[patchId]) *(1-coefX)*(1-coefY);
823 return ret;
826 // else must blend with 0.
827 else
829 // compute coef.
830 float se=s;
831 float te=t;
832 float coefX;
833 float coefY;
834 if(smoothBorderX==-1) se= noiseFloorF(se), coefX=s-se;
835 else se= noiseCeilF(se), coefX=se-s;
836 if(smoothBorderY==-1) te= noiseFloorF(te), coefY=t-te;
837 else te= noiseCeilF(te), coefY=te-t;
840 // To have smooth continuities with smooth on edge (if any), we must do this.
841 CVector rx, ry;
842 // Compute a smooth with my X neighbor.
843 rx= computeNormalEdgeSmooth(s, t, smoothBorderX, 0);
844 // Compute a smooth with my Y neighbor.
845 ry= computeNormalEdgeSmooth(s, t, 0, smoothBorderY);
847 // Blend the 2 result.
848 if(coefY + coefX>0)
850 // This the weight used to blend to 0.
851 float maxCoef= max(coefY, coefX);
852 // This the weight used to blend beetween rx and ry.
853 float ooSum= 1.0f / (coefY + coefX);
854 float blendCoefX= coefX * ooSum;
855 float blendCoefY= coefY * ooSum;
857 return maxCoef* (rx*blendCoefY + ry*blendCoefX);
859 else
861 return CVector::Null;
868 // ***************************************************************************
869 void CPatch::computeNoise(float s, float t, CVector &displace) const
871 float so= s*OrderS;
872 float to= t*OrderT;
875 // Pre-Compute Border Smothing.
876 //=========================
877 // If we are on a border, flag it.
878 sint8 smoothNormalBorderX= 0;
879 sint8 smoothNormalBorderY= 0;
880 // NB: because OrderS and OrderT >=2, smoothNormalBorderX=-1 and smoothNormalBorderX=1 are exclusive (as smoothNormalBorderY).
881 if(so < 1) smoothNormalBorderX= -1;
882 else if(so > OrderS-1) smoothNormalBorderX= 1;
883 if(to < 1) smoothNormalBorderY= -1;
884 else if(to > OrderT-1) smoothNormalBorderY= 1;
886 bool smoothNormalEdge= (smoothNormalBorderX!=0) != (smoothNormalBorderY!=0);
887 bool smoothNormalCorner= (smoothNormalBorderX!=0) && (smoothNormalBorderY!=0);
890 // Do same thing, but to know if we must compute a displace on an interior, on an edge or on a corner.
891 sint8 smoothDisplaceBorderX= 0;
892 sint8 smoothDisplaceBorderY= 0;
893 // NB: because OrderS and OrderT >=2, smoothBorderX=-1 and smoothBorderX=1 are exclusive (as smoothBorderY).
894 if(so < 0.5) smoothDisplaceBorderX= -1;
895 else if(so > OrderS-0.5) smoothDisplaceBorderX= 1;
896 if(to < 0.5) smoothDisplaceBorderY= -1;
897 else if(to > OrderT-0.5) smoothDisplaceBorderY= 1;
899 bool smoothDisplaceEdge= (smoothDisplaceBorderX!=0) != (smoothDisplaceBorderY!=0);
900 bool smoothDisplaceCorner= (smoothDisplaceBorderX!=0) && (smoothDisplaceBorderY!=0);
903 // Compute Displace value.
904 //=========================
905 float displaceValue;
907 if(smoothDisplaceCorner)
908 displaceValue= computeDisplaceCornerSmooth(so, to, smoothDisplaceBorderX, smoothDisplaceBorderY);
909 else if(smoothDisplaceEdge)
910 displaceValue= computeDisplaceEdgeSmooth(so, to, smoothDisplaceBorderX, smoothDisplaceBorderY);
911 else
912 displaceValue= computeDisplaceInteriorSmooth(so, to);
916 // Compute Displace normal.
917 //=========================
919 // Evaluate the normal.
920 CVector displaceNormal;
923 // smooth on edges and on corners.
924 if(smoothNormalCorner)
925 displaceNormal= computeNormalCornerSmooth(so, to, smoothNormalBorderX, smoothNormalBorderY);
926 else if(smoothNormalEdge)
927 displaceNormal= computeNormalEdgeSmooth(so, to, smoothNormalBorderX, smoothNormalBorderY);
928 else
930 // unpack...
931 CBezierPatch *bpatch= unpackIntoCache();
932 // eval.
933 displaceNormal= bpatch->evalNormal(s, t);
938 // Final result.
939 //=========================
940 displace= displaceNormal * displaceValue;
945 // ***************************************************************************
946 void CPatch::setCornerSmoothFlag(uint corner, bool smooth)
948 nlassert(corner<=3);
949 uint mask= 1<<corner;
950 if(smooth)
951 _CornerSmoothFlag|= mask;
952 else
953 _CornerSmoothFlag&= ~mask;
956 // ***************************************************************************
957 bool CPatch::getCornerSmoothFlag(uint corner) const
959 nlassert(corner<=3);
960 uint mask= 1<<corner;
961 return (_CornerSmoothFlag& mask)!=0;
965 } // NL3D