You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ryzom-core/code/nel/include/nel/3d/patch.h

1239 lines
42 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef NL_PATCH_H
#define NL_PATCH_H
#include "nel/misc/types_nl.h"
#include "nel/misc/vector.h"
#include "nel/misc/vector_2f.h"
#include "nel/misc/rgba.h"
#include "nel/3d/tessellation.h"
#include "nel/misc/aabbox.h"
#include "nel/misc/bsphere.h"
#include "nel/misc/triangle.h"
#include "nel/misc/geom_ext.h"
#include "nel/misc/object_vector.h"
#include "nel/3d/tile_element.h"
#include "nel/3d/tile_color.h"
#include "nel/3d/tess_block.h"
#include "nel/3d/tile_light_influence.h"
#include "nel/3d/point_light_influence.h"
namespace NL3D {
#define NL_MAX_TILES_BY_PATCH_EDGE_SHIFT 4 // max 16x16 tiles by patch (shift version)
#define NL_MAX_TILES_BY_PATCH_EDGE (1<<NL_MAX_TILES_BY_PATCH_EDGE_SHIFT) // max 16x16 tiles by patch
#define NL_PATCH_FAR0_ROTATED 0x1 // Flags far0 rotated
#define NL_PATCH_FAR1_ROTATED 0x2 // Flags far1 rotated
#define NL_PATCH_SMOOTH_FLAG_SHIFT 0x2 // Smooth flags shift
#define NL_PATCH_SMOOTH_FLAG_MASK 0x3c // Smooth flags mask
#define NL_LUMEL_BY_TILE_SHIFT 2 // 4 lumels by tile
#define NL_LUMEL_BY_TILE (1<<NL_LUMEL_BY_TILE_SHIFT) // 4 lumels by tile
#define NL_BLOCK_LUMEL_COMPRESSED_SIZE 8 // Compressed block size 8 bytes
#define NL_PATCH_BLOCK_MAX_QUAD 4 // Max quad per CPatchQuadBlock.
#define NL_PATCH_BLOCK_MAX_VERTEX (NL_PATCH_BLOCK_MAX_QUAD+1) // Max vertex per CPatchQuadBlock.
using NLMISC::CVector;
using NLMISC::CPlane;
using NLMISC::CAABBox;
using NLMISC::CBSphere;
using NLMISC::CRGBA;
class CLandscape;
class CZone;
class CBezierPatch;
class ITexture;
class CVegetableClipBlock;
class CVegetableManager;
class CVegetableInstanceGroup;
class CLandscapeVegetableBlock;
class CLandscapeVegetableBlockCreateContext;
class CPatchDLMContext;
class CPatchDLMPointLight;
// ***************************************************************************
#define NL3D_NOISE_MAX 1
// ***************************************************************************
/*
NL3D_PATCH_VEGETABLE_NUM_TESSBLOCK_PER_CLIPBLOCK == 2 means that
clipBlocks enclose 2*2 tessBlocks (hence 4*4 tiles).
*/
#define NL3D_PATCH_VEGETABLE_NUM_TESSBLOCK_PER_CLIPBLOCK_SHIFT 1
#define NL3D_PATCH_VEGETABLE_NUM_TESSBLOCK_PER_CLIPBLOCK (1<<NL3D_PATCH_VEGETABLE_NUM_TESSBLOCK_PER_CLIPBLOCK_SHIFT)
// ***************************************************************************
class CVector3s
{
public:
sint16 x,y,z;
public:
void pack(const CVector &v, const CVector &bias, float scale)
{
float xr,yr,zr;
xr= (v.x - bias.x)/scale;
yr= (v.y - bias.y)/scale;
zr= (v.z - bias.z)/scale;
NLMISC::clamp(xr, -32768, 32767);
NLMISC::clamp(yr, -32768, 32767);
NLMISC::clamp(zr, -32768, 32767);
x= (sint16)xr;
y= (sint16)yr;
z= (sint16)zr;
}
void unpack(CVector &v, const CVector &bias, float scale) const
{
v.x= x*scale + bias.x;
v.y= y*scale + bias.y;
v.z= z*scale + bias.z;
}
void serial(NLMISC::IStream &f)
{
f.serial(x,y,z);
}
};
// ***************************************************************************
/**
* A landscape patch identifier (zone/patch).
*
* \author Lionel Berenguier
* \author Nevrax France
* \date 2000
*/
struct CPatchIdent
{
sint32 ZoneId; // From which zone this patch come from...
uint16 PatchId; // Id of this patch.
// default ctor
CPatchIdent() {}
//
CPatchIdent(sint32 zoneId, uint16 patchId) : ZoneId(zoneId), PatchId(patchId) {}
public:
bool operator<(const CPatchIdent &p) const
{
if(ZoneId!=p.ZoneId) return ZoneId<p.ZoneId;
return PatchId<p.PatchId;
}
bool operator==(const CPatchIdent &p) const
{
return ZoneId==p.ZoneId && PatchId==p.PatchId;
}
bool operator!=(const CPatchIdent &p) const
{
return !(*this==p);
}
};
// ***************************************************************************
/**
* A triangle from a patch identifier.
*
* \author Lionel Berenguier
* \author Nevrax France
* \date 2000
*/
struct CTrianglePatch : public NLMISC::CTriangleUV
{
/// from which patch this triangle comes from.
CPatchIdent PatchId;
};
// ***************************************************************************
/**
* An descriptor of a group of tiles in a patch.
*
* \author Lionel Berenguier
* \author Nevrax France
* \date 2000
*/
class CPatchBlockIdent
{
public:
/// the patch.
CPatchIdent PatchId;
/// size of the patch, in tile coordinates.
uint8 OrderS,OrderT;
/// coordinate of the SubPart of the patch, in tile coordinates.
uint8 S0,S1,T0,T1;
public:
/// \name Operators.
// @{
bool operator==(const CPatchBlockIdent &pb) const
{
return PatchId==pb.PatchId &&
S0==pb.S0 && S1==pb.S1 &&
T0==pb.T0 && T1==pb.T1;
}
bool operator!=(const CPatchBlockIdent &pb) const
{
return !(*this==pb);
}
bool operator<(const CPatchBlockIdent &pb) const
{
if(PatchId!=pb.PatchId)
return PatchId<pb.PatchId;
if(S0!=pb.S0) return S0<pb.S0;
if(S1!=pb.S1) return S1<pb.S1;
if(T0!=pb.T0) return T0<pb.T0;
return T1<pb.T1;
}
bool operator<=(const CPatchBlockIdent &pb) const
{
return (*this<pb) || (*this==pb);
}
bool operator>(const CPatchBlockIdent &pb) const
{
return !(*this<=pb);
}
bool operator>=(const CPatchBlockIdent &pb) const
{
return !(*this<pb);
}
// @}
};
// ***************************************************************************
/**
* A group of tiles faces in a patch. For speed/mem, max size of a CPatchQuadBlock is fixed.
*
* \author Lionel Berenguier
* \author Nevrax France
* \date 2000
*/
class CPatchQuadBlock
{
public:
/// from what subPart of the patch those data comes from.
CPatchBlockIdent PatchBlockId;
/// evaluated Vertices of this patch.
CVector Vertices[NL_PATCH_BLOCK_MAX_VERTEX*NL_PATCH_BLOCK_MAX_VERTEX];
public:
/** build the 2 triangles from a quad of the CPatchQuadBlock.
* quadId is the number of the quad, relatively to the PatchQuadBlock.
*/
void buildTileTriangles(uint8 quadId, CTrianglePatch triangles[2]) const;
};
// ***************************************************************************
/**
* A landscape patch.
* QuadPatch layout (same notations as 3ds Max SDK).
*
* A---> ad ---- da <---D
* | |
* | |
* v v
* ab ia id dc
*
* | |
* | |
*
* ba ib ic cd
* ^ ^
* | |
* | |
* B---> bc ---- cb <---C
*
* NB: Patch 1x1 or 1xX are illegal: lot of problem: rectangular geomoprh, Son0 and Son1 must be setup as tile at beginning ...
*
* NB: Edges number are:
* - 0: AB.
* - 1: BC.
* - 2: CD.
* - 3: DA.
*
* \author Lionel Berenguier
* \author Nevrax France
* \date 2000
*/
class CPatch
{
public:
struct CBindInfo
{
// The zone on this edge. NULL if not loaded (or if none).
CZone *Zone;
// The number of patchs on this edge. 0,1, 2 or 4. if MultipleBindNum>1, NPatchs==1.
sint NPatchs;
// Special case: on this edge, we are a small patch connected to a bigger: this is the X of 1/X (1,2 or 4).
// 0 if this is not the case.
uint8 MultipleBindNum;
// valid only if MultipleBindNum>1. this tells our place in this MultipleBind: 0<=MultipleBindId<MultipleBindNum.
uint8 MultipleBindId;
CPatch *Next[4]; // The neighbor patch i.
sint Edge[4]; // On which edge of Nexti we are binded.
};
public:
/// The patch coordinates (see CBezierPatch).
CVector3s Vertices[4];
CVector3s Tangents[8];
CVector3s Interiors[4];
// Lumel array compressed.
std::vector<uint8> CompressedLumels;
// There is OrderS*OrderT tiles. CZone build it at build() time.
std::vector<CTileElement> Tiles;
// There is OrderS*OrderT tiles color. CZone build it at build() time.
std::vector<CTileColor> TileColors;
// There is OrderS/2+1 * OrderT/2+1 tiles light influence. CZone build it at build() time.
std::vector<CTileLightInfluence> TileLightInfluences;
/// Noise Data.
// @{
/// The orientation of the NoiseMap. 0,1,2,3. This represent a CCW rotation of the NoiseMap.
uint8 NoiseRotation;
/** setup Smooth flags for Noise on corner: used for Noise geometry and for lighting.
* NB: convention: corner0==A, corner1==B ...
*/
void setCornerSmoothFlag(uint corner, bool smooth);
bool getCornerSmoothFlag(uint corner) const;
private:
/// Put here for packing with NoiseRotation.
uint8 _CornerSmoothFlag;
public:
// @}
public:
/// Constructor
CPatch();
/// dtor
~CPatch();
/** compile a valid patch. (init)
* Call this method before any other. Zone and Control points must be valid before calling compile(). \n
* This is an \b ERROR to call compile() two times. \n
* \param z zone where the patch must be binded.
* \param orderS the Tile order in S direction: 2,4,8,16.
* \param orderT the Tile order in T direction: 2,4,8,16.
* \param errorSize if 0, setup() compute himself the errormetric of the patch. May be setup to surface of patch,
* modulated by tangents and displacement map.
*/
void compile(CZone *z, uint patchId, uint8 orderS, uint8 orderT, CTessVertex *baseVertices[4], float errorSize=0);
/// Un-compile a patch. Tesselation is deleted. if patch is not compiled, no - op.
void release();
/// Get the landscape in which is placed this patch.
CLandscape *getLandscape () const;
CZone *getZone() const {return Zone;}
uint8 getOrderS() const {return OrderS;}
uint8 getOrderT() const {return OrderT;}
uint8 getOrderForEdge(sint8 edge) const;
float getErrorSize() const {return ErrorSize;}
sint getFar0() const {return Far0;}
sint getFar1() const {return Far1;}
uint16 getPatchId () const {return PatchId;}
/// return neighborhood information.
void getBindNeighbor(uint edge, CBindInfo &neighborEdge) const;
/// Build the bbox of the patch, according to ctrl points, and displacement map max value.
CAABBox buildBBox() const;
/** Compute a vertex.
* Compute a vertex according to:
* - s,t
* - patch control points uncompressed with zone Bias/Scale.
* - Patch UV geometric correction.
* - Patch noise (and noise of Patch neighbor).
*/
CVector computeVertex(float s, float t) const;
/** Same as computeVertex, but special accurate version for CVisualCollisionEntity.
* If on a corner of the patch (0,0), (0,1) ...., take directly the BaseVertices[] result
* if on a border of a patch (ie s==0, s==1, t==0 or t==1), also
* compute the vertex of the neighbor patch (if any), and then average the result.
* This ensure that tesselation generated is perfectly continous, even across patchs of same or different zones.
* This feature is very important for CVisualCollisionEntity::snapToGround()
*/
CVector computeContinousVertex(float s, float t) const;
/** unbind the patch from All neighbors. neighbors patchs links are modified too. The tesselation is forcemerged.
*/
void unbind();
/** bind the patch to 4 neighbors, given in this patch edge order (0,1,2,3). Tesselation is reseted (patch unbound first).
* NB: this patch and his neighborood must be compiled...
* NB: neighbor patchs must not be NULL (but according to NPatchs).
*/
void bind(CBindInfo Edges[4], bool rebind);
/// For changing TileMaxSubdivision. force tesselation to be under tile.
void forceMergeAtTileLevel();
/** This is especially for Pacs. see CLandscape desc.
*/
void averageTesselationVertices();
/// Refine this patch. Even if clipped. Refine all nodes.
void refineAll();
/// \name Render
//@{
/// preRender this patch. Build Max faces / pass etc...
void preRender(const NLMISC::CBSphere &patchSphere);
// like preRender(), but update TextureFar only. (you should call preRender() in this case).
void updateTextureFarOnly(const NLMISC::CBSphere &patchSphere);
/// Render this patch, if not clipped. Call PatchCurrentDriver->renderSimpleTriangles().
// Global Setup setup it in CLandscape::render().
void renderFar0();
void renderFar1();
// NB: renderTile() is now in CTileMaterial.
// For All Far0/Far1/Tile etc..., compute Geomorph and Alpha in software (no VertexShader).
void computeSoftwareGeomorphAndAlpha();
// update VB according to new clip state.
void updateClipPatchVB(bool renderClipped);
// get the next patch to render in the same RenderPass for Far0.
CPatch *getNextFar0ToRdr() const {return _NextRdrFar0;}
// get the next patch to render in the same RenderPass for Far1.
CPatch *getNextFar1ToRdr() const {return _NextRdrFar1;}
//@}
// release Far render pass/reset Tile/Far render.
void resetRenderFar();
// For CZone changePatchTexture only.
void deleteTileUvs();
void recreateTileUvs();
// For CZone::refreshTesselationGeometry() only.
void refreshTesselationGeometry();
// Serial just the un-compiled part.
void serial(NLMISC::IStream &f);
// unpack the patch into a floating point one.
void unpack(CBezierPatch &p) const;
/// \name Lumels methods
/**
* Unpack the lumels of the patches. Lumels are classes that describe the lighting environnement at a given texel
* of the lightmap. It is used to compute the shadow map of the patch, compress it and uncompress it.
* This method uncompress the lumels stored in its Lumels member.
*
* \param pShadow is a pointer on the destination lumel buffer. Size must be ((OrderS*4/ratio)+1)*((OrderT*4/ratio)+1).
* \param ratio is the one over the ratio of the texture destination. Must be 1 or 2.
* \see packShadowMap(), resetCompressedLumels()
*/
void unpackShadowMap (uint8 *pShadow);
/**
* Pack the lumels of the patches. Lumels are classes that describe the lighting environnement at a given texel
* of the lightmap. It is used to compute the shadow map of the patch, compress it and uncompress it.
* This method compress the lumels passed in parameter and stored them in its Lumels member.
*
* \param pShadow is a pointer on the destination lumel buffer. Size must be (OrderS*4+1)*(OrderS*4+1).
* \see unpackShadowMap(), resetCompressedLumels()
*/
void packShadowMap (const uint8 *pLumel);
/**
* Rebuild the packed lumels without shadow. Only the interpolated color will be used.
*
* \see packShadowMap(), unpackShadowMap()
*/
void resetCompressedLumels ();
/** Debug purpose only : setup the colors of this patch so that it shows which tiles
* have vegetable disabled, or are above, below water.
* User provides a table with 4 colors for each state :
* color 0 = above water
* color 1 = underwater
* color 2 = intersect water
* color 3 = vegetable disabled
*/
void setupColorsFromTileFlags(const NLMISC::CRGBA colors[4]);
/** Set this patch flags from another one.
* The patchs must match
*/
void copyTileFlagsFromPatch(const CPatch *src);
// Count the number of tri needed to draw the patch
uint32 countNumTriFar0() const;
uint32 countNumTriFar1() const;
private:
// Methods used internaly to compute shadowmaps
/**
* Pack a 4x4 lumel block
*
* \see packShadowMap(), unpackShadowMap()
*/
void packLumelBlock (uint8 *dest, const uint8 *source, uint8 alpha0, uint8 alpha1);
/**
* Eval an uncompressed 4x4 block against the original
*
* \see packShadowMap(), unpackShadowMap()
*/
uint evalLumelBlock (const uint8 *original, const uint8 *unCompressed, uint width, uint height);
/**
* Unpack a 4x4 lumel block
*
* \see packShadowMap(), unpackShadowMap()
*/
void unpackLumelBlock (uint8 *dest, const uint8 *src);
public:
/// \name Smooth flags methods
/**
* Set the smooth flag for the n-th edge. flag is false if this edge must by smoothed, true else.
*/
void setSmoothFlag (uint edge, bool flag)
{
// Erase it
Flags&=~(1<<(edge+NL_PATCH_SMOOTH_FLAG_SHIFT));
// Set it
Flags|=(((uint)flag)<<(edge+NL_PATCH_SMOOTH_FLAG_SHIFT));
}
/**
* Get the smooth flag for the n-th edge. Return true if this edge must by smoothed, false else.
*/
bool getSmoothFlag (uint edge) const
{
// Test it
return ((Flags&(1<<(edge+NL_PATCH_SMOOTH_FLAG_SHIFT)))!=0);
}
/// \name Subdivision / ForCollision.
// @{
/** Add triangles to triangles array which intersect the bbox.
* NB: this method use a convex hull subdivion to search in O(logn) what part of the patch to insert.
* \param patchId the id of this patch, used to fill triangles.
* \param bbox the bbox to test against.
* \param triangles array to be filled (no clear performed, elements added).
* \param tileTessLevel 0,1 or 2 size of the triangles (2*2m, 1*1m or 0.5*0.5m). Level of subdivision of a tile.
*/
void addTrianglesInBBox(CPatchIdent paId, const CAABBox &bbox, std::vector<CTrianglePatch> &triangles, uint8 tileTessLevel) const;
/** Fill a CPatchQuadBlock, from its required PatchId.
* nlassert(PatchId size is less than NL_PATCH_BLOCK_MAX_QUAD)
*/
void fillPatchQuadBlock(CPatchQuadBlock &quadBlock) const;
/** Add CPatchBlockIdent to CPatchBlockIdent array which intersect the bbox.
* NB: this method use a convex hull subdivion to search in O(logn) what part of the patch to insert.
* \param patchId the id of this patch, used to fill PatchBlockIdent.
* \param bbox the bbox to test against.
* \param paBlockIds array to be filled (no clear performed, elements added).
*/
void addPatchBlocksInBBox(CPatchIdent paId, const CAABBox &bbox, std::vector<CPatchBlockIdent> &paBlockIds) const;
/** From the current tesselation of this patch, and a UV in this patch, return tesselated position.
*/
CVector getTesselatedPos(CUV uv) const;
/** From the current tesselation of this patch, append to the list of leaves faces.
*/
void appendTessellationLeaves(std::vector<const CTessFace*> &leaves) const;
// @}
/// \name Lightmap get interface.
// @{
/// Get the lumel under the position.
uint8 getLumel(const CUV &uv) const;
/** Append lights under the position to pointLightList.
* Notice that the PointLight are ensured to be actually CPointLightNamed.
*/
void appendTileLightInfluences(const CUV &uv,
std::vector<CPointLightInfluence> &pointLightList) const;
/** For CTextureFar, compute current TLI Lightmap at tile level. array should be allocated of at least
* sqr(NL_MAX_TILES_BY_PATCH_EDGE+1).
*/
void computeCurrentTLILightmapDiv2(NLMISC::CRGBA *array) const;
// @}
/// \name Tiles get interface.
// @{
/// Get the lumel under the position.
CTileElement *getTileElement(const CUV &uv);
// @}
public:
// Is the patch clipped? true if not yte compiled
bool isRenderClipped() const;
// get the according vertex for a corner. use wisely
const CTessVertex *getCornerVertex(uint corner)
{
return BaseVertices[corner];
}
public:
/// \name VB Allocator mgt.
// @{
// delete all VB allocated in VertexBuffers, according to Far0 and Far1. Do not Test isRenderClipped() state.
// With VB, allocate to he faces array.
void deleteVBAndFaceVector();
// allocate all VB, according to Far0 and Far1. Do not Test isRenderClipped() state.
// With VB, allocate to he faces array.
void allocateVBAndFaceVector();
// fill all VB, according to Far0, Far1 and CTessFace VBInfos. Do not Test isRenderClipped() state.
// Do not fill a VB if reallocationOccurs().
void fillVB();
// if isRenderClipped()==false, fillVB().
void fillVBIfVisible();
// delete Far1 VB allocated in VertexBuffers. do it only if Far1==true. Do not Test isRenderClipped() state.
// With VB, allocate to he faces array.
void deleteVBAndFaceVectorFar1Only();
// allocate Far1 VB, . do it only if Far1==true. Do not Test isRenderClipped() state.
// With VB, allocate to he faces array.
void allocateVBAndFaceVectorFar1Only();
// fill Far0 VB according CTessFace VBInfos and Far0 (do not fill if !Far0). Do not Test isRenderClipped() state.
// Do not fill a VB if reallocationOccurs().
void fillVBFar0Only();
// same for Far1
void fillVBFar1Only();
// fill DLM Uv (ie UV1) for Far0 and Far1 VB only. NB: do not fill Far0 if !Far0 (idem fro Far1).
// Do not Test isRenderClipped() state. Do not fill a VB if reallocationOccurs().
void fillVBFarsDLMUvOnly();
void fillFar0DLMUvOnlyVertexListVB(CTessList<CTessFarVertex> &vertList);
void fillFar1DLMUvOnlyVertexListVB(CTessList<CTessFarVertex> &vertList);
// For Debug only.
void debugAllocationMarkIndices(uint marker);
// Because of refine... tessBlock FaceVector may have been deleted, this method do nothing if isRenderClipped().
// If not, recreate FaceVector for this tessBlock only, according to Far0 and Far1.
void recreateTessBlockFaceVector(CTessBlock &block);
// @}
public:
/// \name MicroVegetation
// @{
/// Delete any vegetable Ig still existing in this patch.
void deleteAllVegetableIgs();
/// Recreate any vegetable block (as possible) in this patch. (useful for edition)
void recreateAllVegetableIgs();
// @}
/// \name TileLightInfluences
// @{
/** make a valid empty array of TileLightInfluences (ie resized to good size, but with empty
* light influences
*/
void resetTileLightInfluences();
// @}
/// \name UpdateLighting Management
// @{
/// For lighting update, insert this before patchNext (CiruclarList). textNext must be !NULL
void linkBeforeNearUL(CPatch *patchNext);
/// For lighting update, unlink (CiruclarList)
void unlinkNearUL();
/// For lighting update, get Next (CiruclarList). If ==this, then list is empty
CPatch *getNextNearUL() const {return _ULNearNext;}
/// get the number of Near TessBlocks. Actually OrderS/2*OrderT/2.
uint getNumNearTessBlocks() const {return TessBlocks.size();}
/**
* recompute the near lightmap of tessBlock "numTb".
* return the number of pixels updated by computing of this tessBlock.
* Actually 0 if the tessBlock lightmap is not computed, or 100
* (NL_TILE_LIGHTMAP_SIZE*NL_TILE_LIGHTMAP_SIZE) pixels.
*/
uint updateTessBlockLighting(uint numTb);
// @}
/// \name Dynamic Lighting Management
// @{
/** begin Dynamic light Process.
* reset texture To Black (if needed)
* Called by CLandscape. _DLMContext must exist
*/
void beginDLMLighting();
/** Process a Dynamic light, creating the DLMContext if necessary. Increment RefCount.
* Called by CLandscape.
*/
void processDLMLight(CPatchDLMPointLight &pl);
/** end Dynamic light Process, deleting the DLMContext if necessary.
* NB: _DLMContext->compileLighting() is not called, since done during render phase.
* Called by CLandscape. _DLMContext must exist
*/
void endDLMLighting();
// @}
/// Get number of TileMaterial created in this Patch
uint getTileMaterialRefCount() const {return MasterBlock.TileMaterialRefCount;}
// Private part.
private:
/*********************************/
friend class CTessFace;
friend class CZone;
friend class CLandscapeVegetableBlock;
friend class CPatchRdrPass;
CZone *Zone;
// Number of this patch in the zone. valid at compile Time.
uint16 PatchId;
// Tile Order for the patch.
uint8 OrderS, OrderT;
// This is especially for Pacs. false by default, and used by CZone::refineAll() and CZone::excludePatchFromRefineAll().
bool ExcludeFromRefineAll;
// For this patch, which level is required for a face to be inserted in the secondary TessBlocks (and not the masterblock)??
sint TessBlockLimitLevel;
// For this patch, which level is required for a face to be a valid Tile??
sint TileLimitLevel;
// For this patch, which level is required for a face to be a "square" face (not rectangular)??
sint SquareLimitLevel;
// The Base Size*bumpiness of the patch (/2 at each subdivide).
float ErrorSize;
// The root for tesselation.
CTessFace *Son0, *Son1;
// The base vertices.
CTessVertex *BaseVertices[4];
// The base Far vertices (always here!!).
CTessFarVertex BaseFarVertices[4];
// Local info for CTessFace tiles. CPatch must setup them at the beginning at refine()/render().
// For Far Texture coordinates.
float Far0UScale, Far0VScale, Far0UBias, Far0VBias;
float Far1UScale, Far1VScale, Far1UBias, Far1VBias;
/**
* Flags NL_PATCH_FAR0_ROTATED and NL_PATCH_FAR1_ROTATED
* NL_PATCH_FAR0_ROTATED for Far0, NL_PATCH_FAR1_ROTATED for Far1
* If the flag is set, the far texture of the patch is rotated of 1
* (to the left of course)
*
* Flags NL_PATCH_SMOOTH_FLAG_MASK
* 4 flag for smooth edge. Same as CPatchInfo::CBindInfo shifted by (<<NL_PATCH_SMOOTH_FLAG_SHIFT).
* See CPatchInfo::CBindInfo::Flags for details.
*/
uint8 Flags;
// Info for alpha transition with Far1.
float TransitionSqrMin;
float OOTransitionSqrDelta;
/*
Cache Optim Note:
NB: for faster Cache Access during preRender(), you must leave those variables packed like this
Far0
Far1
_PatchRdrPassFar0
_NextRdrFar0
_PatchRdrPassFar1
_NextRdrFar1
NumRenderableFaces
TessBlocks (for size() )
MasterBlock.Far0FaceVector
MasterBlock.Far1FaceVector
NB: Far0FaceVector and Far1FaceVector are accessed during renderFar*() (not preRender()) but they must be near
to TessBlocks (for size() )
*/
// Current Far Level computed in preRender()
sint Far0; // The level of First Far: 0,1,2 or 3. 0 means Tile.
sint Far1; // The level of second Far, for transition: 1,2 or 3. 0 means none.
// The render Pass of Far0 and Far1.
CPatchRdrPass *_PatchRdrPassFar0;
CPatch *_NextRdrFar0;
CPatchRdrPass *_PatchRdrPassFar1;
CPatch *_NextRdrFar1;
/// \name Block renders.
// @{
// Tells how many Renderable Face this Patch has. updated in append/remove/FaceToRenderList()
sint NumRenderableFaces;
// The 2*2 block render. For memory optimisation, none is allocated when no faces need it.
// There is (OrderT/2)*(OrderS/2) TessBlocks.
NLMISC::CObjectVector<CTessBlock> TessBlocks;
// The block render of far only. Only Far faces bigger than a block are inserted here.
public: // tmp
CTessBlock MasterBlock;
private:
// The counter of faces which need TessBlocks (FarFaces, TileMaterial and FarVertices). When 0, the vector is contReset()-ed.
sint TessBlockRefCount;
// @}
/// \name Vegetables.
// @{
/// list of vegetable clipBlocks, created/destroyed at same time as TessBlocks.
std::vector<CVegetableClipBlock*> VegetableClipBlocks;
// @}
/**
* Stream version of the class.
*/
static uint32 _Version;
private:
// Guess.
void computeDefaultErrorSize();
// based on BaseVertices, recompute positions, and Make Face roots Son0 and Son1.
void makeRoots();
// Guess. For bind() reasons.
CTessFace *getRootFaceForEdge(sint edge) const;
// Guess. For bind() reasons. return the vertex 0 of edge.
CTessVertex *getRootVertexForEdge(sint edge) const;
void changeEdgeNeighbor(sint edge, CTessFace *to);
/// \name RenderList mgt.
// @{
// reset all list of MasterBlock.
void resetMasterBlock();
// simply clear the tessnbloc array and reset cout to 0.
void clearTessBlocks();
// add a ref to tess blocks, allocate them if necessary.
void addRefTessBlocks();
// dec a ref to tess blocks, destroy them if necessary.
void decRefTessBlocks();
// UGLY SIDE EFFECT: when refcount TessBlocks RefCount reach 0, tessblockas are deleted. think of it in tesselation.cpp.
// Retrieve the tessblockId, depending on face info.
uint getNumTessBlock(CTessFace *face);
// FarVertType.
enum TFarVertType {FVMasterBlock=0, FVTessBlock, FVTessBlockEdge};
// Retrieve the tessblockId, depending on a ParamCoord.
void getNumTessBlock(CParamCoord pc, TFarVertType &type, uint &numtb);
// If pathc is visible, force deletion of this TessBlock.
void dirtTessBlockFaceVector(CTessBlock &block);
// For rdr. Insert in the GOOD TessBlock the face (depending on level, patchcoordinates etc...)
// call appendFaceToTileRenderList() to insert his TileFaces into the good renderList.
void appendFaceToRenderList(CTessFace *face);
// Remove a face and his tileface from the patch renderlist.
// call removeFaceFromTileRenderList() to insert his TileFaces into the good renderList.
void removeFaceFromRenderList(CTessFace *face);
// for changePatchTexture, insert just the TileFace into the good render List.
void appendFaceToTileRenderList(CTessFace *face);
void removeFaceFromTileRenderList(CTessFace *face);
// For refreshTesselationGeometry() only. enlarge the TessBlock (if any) with face->V*->EndPos.
void extendTessBlockWithEndPos(CTessFace *face);
// Set/Unset (to NULL) a TileMaterial from the TessBlocks. Material must exist for both functions.
// And TileS/TileT must be OK.
void appendTileMaterialToRenderList(CTileMaterial *tm);
void removeTileMaterialFromRenderList(CTileMaterial *tm);
// Add/Remove FarVertices. Use fv->PCoord to know where.
void appendFarVertexToRenderList(CTessFarVertex *fv);
void removeFarVertexFromRenderList(CTessFarVertex *fv);
// Add/Remove NearVertices. Use tileMat to know where.
void appendNearVertexToRenderList(CTileMaterial *tileMat, CTessNearVertex *nv);
void removeNearVertexFromRenderList(CTileMaterial *tileMat, CTessNearVertex *nv);
// @}
/// \name Texture mgt.
// @{
// For CTessFace::computeMaterial(). Return the render pass for this material, given the number of the tile, and the
// desired pass. NULL may be returned if the pass is not present (eg: no additive for this tile...).
CPatchRdrPass *getTileRenderPass(sint tileId, sint pass);
// For CTessFace::computeMaterial(). Return the orient/scalebias for the tile in the patchtexture, and the
// desired pass (and the desired stage: RGB/Alpha).
void getTileUvInfo(sint tileId, sint pass, bool alpha, uint8 &orient, CVector &uvScaleBias, bool &is256x256, uint8 &uvOff);
// @}
// Tile LightMap mgt.
// @{
// for a given tile (accessed from the (ts,tt) coordinates), compute a lightmap if necessary, and get a RenderPass.
void getTileLightMap(uint ts, uint tt, CPatchRdrPass *&rdrpass);
// get uvInfo for tile. NB: ts,tt form because simpler.
void getTileLightMapUvInfo(uint ts, uint tt, CVector &uvScaleBias);
// release the tile lightmap. NB: ts,tt form because simpler.
void releaseTileLightMap(uint ts, uint tt);
// Compute the Lightmap for 2x2 tiles. => 10x10 pixels. ts, tt is in [0,OrderS], [0, OrderT].
void computeNearBlockLightmap(uint ts, uint tt, NLMISC::CRGBA *lightText);
void computeTileLightmapPixelAroundCorner(const NLMISC::CVector2f &stIn, NLMISC::CRGBA *dest, bool lookAround);
// Compute a lightmap for a tile (ts,tt). 4x4 lumels are processed. NB: result= lumel*userColor.
void computeTileLightmap(uint ts, uint tt, NLMISC::CRGBA *dest, uint stride);
// Compute a lightmap for an edge of a tile. 1x4 lumels. "edge" say what edge of the tile to compute.
// pixels will be written in (dest+i*stride), where i vary from 0 to 3 or 3 to 0 (according to "inverse").
void computeTileLightmapEdge(uint ts, uint tt, uint edge, NLMISC::CRGBA *dest, uint stride, bool inverse);
// Compute a lightmap just for a pixel (s,t) of a tile (ts,tt). (s,t) E [0;3], [0;3].
void computeTileLightmapPixel(uint ts, uint tt, uint s, uint t, NLMISC::CRGBA *dest);
// Methods for automatic Lighting. NB: result= lumel only (no TileColors).
void computeTileLightmapAutomatic(uint ts, uint tt, NLMISC::CRGBA *dest, uint stride);
void computeTileLightmapEdgeAutomatic(uint ts, uint tt, uint edge, NLMISC::CRGBA *dest, uint stride, bool inverse);
void computeTileLightmapPixelAutomatic(uint ts, uint tt, uint s, uint t, NLMISC::CRGBA *dest);
// Methods for Precomputed Lighting. NB: result= lumel only (no TileColors).
void computeTileLightmapPrecomputed(uint ts, uint tt, NLMISC::CRGBA *dest, uint stride);
void computeTileLightmapEdgePrecomputed(uint ts, uint tt, uint edge, NLMISC::CRGBA *dest, uint stride, bool inverse);
void computeTileLightmapPixelPrecomputed(uint ts, uint tt, uint s, uint t, NLMISC::CRGBA *dest);
// Methods to modulate dest with TileColors. NB: A unmodified.
void modulateTileLightmapWithTileColors(uint ts, uint tt, NLMISC::CRGBA *dest, uint stride);
void modulateTileLightmapEdgeWithTileColors(uint ts, uint tt, uint edge, NLMISC::CRGBA *dest, uint stride, bool inverse);
void modulateTileLightmapPixelWithTileColors(uint ts, uint tt, uint s, uint t, NLMISC::CRGBA *dest);
// get the tileColors at the corners of the tile. corner order: 0,0; 1,0; 0,1; 1,1. NB: A undefined.
void getTileTileColors(uint ts, uint tt, NLMISC::CRGBA corners[4]);
// get the current TLIColor given a TLI coordinate (in (0..OrderS/2+1, 0..OrderT/2+1) )
// NB: returned color is modulated by landscape material and precomputed diffuse factor
CRGBA getCurrentTLIColor(uint x, uint y) const;
// get the current TLIColors at the corners of the tile (according to pointLights current colors)
// corner order: 0,0; 1,0; 0,1; 1,1. NB: A undefined.
void getCurrentTileTLIColors(uint ts, uint tt, NLMISC::CRGBA corners[4]);
// Methods to add dest with result of TLI lighting. NB: A unmodified.
void addTileLightmapWithTLI(uint ts, uint tt, NLMISC::CRGBA *dest, uint stride);
void addTileLightmapEdgeWithTLI(uint ts, uint tt, uint edge, NLMISC::CRGBA *dest, uint stride, bool inverse);
void addTileLightmapPixelWithTLI(uint ts, uint tt, uint s, uint t, NLMISC::CRGBA *dest);
// @}
// Recompute of new Far Values, according to globals. Don't erase Far0 and Far1.
void computeNewFar(const NLMISC::CBSphere &patchSphere, sint &newFar0, sint &newFar1);
// For Render. Those methods compute the vertices for Driver (in CTessFace::Current*VB).
void fillFar0VertexVB(CTessFarVertex *pVert);
void fillFar1VertexVB(CTessFarVertex *pVert);
void fillTileVertexVB(CTessNearVertex *pVert);
void fillFar0VertexListVB(CTessList<CTessFarVertex> &vertList);
void fillFar1VertexListVB(CTessList<CTessFarVertex> &vertList);
void fillTileVertexListVB(CTessList<CTessNearVertex> &vertList);
// For Render. Those methods allocate/delete vertices in VB.
void updateFar0VBAlloc(CTessList<CTessFarVertex> &vertList, bool alloc);
void updateFar1VBAlloc(CTessList<CTessFarVertex> &vertList, bool alloc);
void updateTileVBAlloc(CTessList<CTessNearVertex> &vertList, bool alloc);
void updateVBAlloc(bool alloc);
// For Debug Allcoation only.
void debugAllocationMarkIndicesFarList(CTessList<CTessFarVertex> &vertList, uint marker);
void debugAllocationMarkIndicesNearList(CTessList<CTessNearVertex> &vertList, uint marker);
// For Render. Allocate / Fill FaceVector, according to Far0/Far1.
void createFaceVectorFar1();
void deleteFaceVectorFar1();
void createFaceVectorFar0OrTile();
void deleteFaceVectorFar0OrTile();
// For Refine. Those methods do all the good job, and test if they can allocate the VB.
void checkCreateVertexVBFar(CTessFarVertex *pVert);
void checkCreateVertexVBNear(CTessNearVertex *pVert);
// For Refine. Those methods do all the good job, and test if they can fill the VB.
void checkFillVertexVBFar(CTessFarVertex *pVert);
void checkFillVertexVBNear(CTessNearVertex *pVert);
// For Refine. Those methods do all the good job, and test if they have to delete the VB.
void checkDeleteVertexVBFar(CTessFarVertex *pVert);
void checkDeleteVertexVBNear(CTessNearVertex *pVert);
// For Render, geomorph / Alpha in software.
void computeGeomorphVertexList(CTessList<CTessFarVertex> &vertList);
void computeGeomorphFar0VertexListVB(CTessList<CTessFarVertex> &vertList);
void computeGeomorphAlphaFar1VertexListVB(CTessList<CTessFarVertex> &vertList);
void computeGeomorphTileVertexListVB(CTessList<CTessNearVertex> &vertList);
/// \name Subdivision private.
// @{
/// build a bbox from the convex hull of a bezier patch, enlarged with noise.
void buildBBoxFromBezierPatch(const CBezierPatch &p, CAABBox &ret) const;
/** recurse subdivide of the bezierPatch.
* 3 1st parameters are the parameter of addTrianglesInBBox(). \n
* pa is the bezier patch for this subdivision of this patch. \n
* s0, s1, t0, t1 represent the part of the bezier patch subdivided. At start, s0=0, s1=OrderS, t0=0, t1=OrderT.
*/
void addTrianglesInBBoxRecurs(CPatchIdent paId, const CAABBox &bbox, std::vector<CTrianglePatch> &triangles, uint8 tessLevel,
const CBezierPatch &pa, uint8 s0, uint8 s1, uint8 t0, uint8 t1) const;
/** called by addTrianglesInBBoxRecurs(). effective fill the array of triangles from 1 tile at tile coordinates s0,t0.
* depending of tessLevel (0,1,2), 2, 8 or 32 triangles are added. (2*2m, 1*1*m or 0.5*0.5m).
* NB: only triangles of quad included in the bbox are added.
*/
void addTileTrianglesInBBox(CPatchIdent paId, const CAABBox &bbox, std::vector<CTrianglePatch> &triangles, uint8 tessLevel,
uint8 s0, uint8 t0) const;
/** recurse method of addPatchBlocksInBBox.
*/
void addPatchBlocksInBBoxRecurs(CPatchIdent paId, const CAABBox &bbox, std::vector<CPatchBlockIdent> &paBlockIds,
const CBezierPatch &pa, uint8 s0, uint8 s1, uint8 t0, uint8 t1) const;
/// Used by computeContinousVertex()
CVector computeVertexButCorner(float s, float t, bool &onCorner) const;
// @}
private:
/// Realtime Bind information.
// @{
/// The 4 neighbors zone of this patch (setuped at bind() time). NB: NULL if zone not loaded, or if no patch near us.
CZone *_BindZoneNeighbor[4];
/** Used by bind(). Search in the tesselation the first face which own the edge uv0-uv1. link it with linkTo, and
* return it. NULL if not found (obviously because not so tesselated)
*/
CTessFace *linkTessFaceWithEdge(const NLMISC::CVector2f &uv0, const NLMISC::CVector2f &uv1, CTessFace *linkTo);
// @}
/// Noise Geometry.
// @{
float computeDisplaceRawInteger(sint ts, sint tt, sint ms, sint mt) const;
void computeDisplaceRawCoordinates(float sTile, float tTile, float s, float t,
sint &ts, sint &tt, sint &ms, sint &mt) const;
/** compute the displacement for s,t ([0;OrderS], [0;OrderT])
* (sTile, tTile) choose what NoiseMap to use, and (s,t) choose the coordinate in the patch to compute this NoiseMap.
* Any rotation of the NoiseMap is included in this method.
* NB: s,t does not have to be clamped to ([0;OrderS], [0;OrderT]).
*/
float computeDisplaceRaw(float sTile, float tTile, float s, float t) const;
/** useful only for computeDisplaceCornerSmooth().
* This method, if nessecary (ie sTile or tTile <0 or >Order), look on his neighbor to compute the value.
*/
float computeDisplaceRawOnNeighbor(float sTile, float tTile, float s, float t) const;
/** compute the smoothed displacement for s,t ([0;OrderS], [0;OrderT]).
*/
float computeDisplaceInteriorSmooth(float s, float t) const;
/** compute the smoothed displacement for s,t ([0;OrderS], [0;OrderT]). Special case on edge.
*/
float computeDisplaceEdgeSmooth(float s, float t, sint8 smoothBorderX, sint8 smoothBorderY) const;
/** compute the smoothed displacement for s,t ([0;OrderS], [0;OrderT]). Special case on corner.
*/
float computeDisplaceCornerSmooth(float s, float t, sint8 smoothBorderX, sint8 smoothBorderY) const;
/** compute the smoothed normal for s,t ([0;OrderS], [0;OrderT]). Special case on edge.
*/
CVector computeNormalEdgeSmooth(float s, float t, sint8 smoothBorderX, sint8 smoothBorderY) const;
/** compute the smoothed normal for s,t ([0;OrderS], [0;OrderT]). Special case on corner.
*/
CVector computeNormalCornerSmooth(float s, float t, sint8 smoothBorderX, sint8 smoothBorderY) const;
/** same reasoning as in computeDisplaceRawOnNeighbor().
*/
CVector computeNormalOnNeighbor(float s, float t, uint edgeExclude) const;
/** compute the Final displacement for s,t ([0;1], [0;1]). This is the top call.
* displace.norm() should be <= NL3D_NOISE_MAX.
*/
void computeNoise(float s, float t, CVector &displace) const;
// @}
// From tile coordinates, return the tessBlockId, and the id of the material in this tessBlock.
void computeTbTm(uint &numtb, uint &numtm, uint ts, uint tt);
/// Micro-Vegetation.
// @{
/// Create / init the vegetableBlock in the corresponding TessBlock. TessBlocks must exist
void createVegetableBlock(uint numTb, uint ts, uint tt);
/// release the vegetableBlock in the corresponding TessBlock. TessBlocks must exist
void releaseVegetableBlock(uint numTb);
/*
generate the vegetables for a given tile in the vegetable manager.
instances are added to vegetIg.
Warning! Use OptFastFloor()! So call must be enclosed with a OptFastFloorBegin()/OptFastFloorEnd().
*/
void generateTileVegetable(CVegetableInstanceGroup *vegetIg, uint distType, uint ts, uint tt,
CLandscapeVegetableBlockCreateContext &vbCreateCtx);
// same as computeTileLightmapPrecomputed(), but brut result, not modified by colorTable.
void getTileLumelmapPrecomputed(uint ts, uint tt, uint8 *dest, uint stride);
/** same as computeTileLightmapPixelPrecomputed, but brut result, not modified by colorTable.
* Actually used for Lightmap get interface.
*/
void getTileLumelmapPixelPrecomputed(uint ts, uint tt, uint s, uint t, uint8 &dest) const;
// @}
/// \name UpdateLighting Management
// @{
CPatch *_ULNearPrec;
CPatch *_ULNearNext;
// @}
/// \name Dynamic Lighting Management
// @{
/** The Dynamic LightMap context.
* created only when compiled, AND (when in Near OR (when in Far AND touched by pointLight))
* else NULL.
*/
CPatchDLMContext *_DLMContext;
/** The reference count for DLMContext. Each TileMaterial created add a reference. Each pointLight which
* touch the patch too.
*/
sint _DLMContextRefCount;
/// Add a ref count to the DLMContext, creating it if necessary.
void addRefDLMContext();
/// Dec a ref count to the DLMContext, deleting it if refCount== 0.
void decRefDLMContext(uint count= 1);
// @}
private:
// NB: All global render info are stored in CTessFace class static members....
// The Patch cache (may be a short list/vector later...).
static CBezierPatch CachePatch;
// For cahcing.
static const CPatch *LastPatch;
public:
// unpack the patch into the cache.
CBezierPatch *unpackIntoCache() const;
};
} // NL3D
#endif // NL_PATCH_H
/* End of patch.h */