// NeL - MMORPG Framework // 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 . #include "std3d.h" #include "nel/3d/vegetable.h" #include "nel/misc/common.h" #include "nel/3d/vegetable_manager.h" #include "nel/misc/fast_floor.h" using namespace std; using namespace NLMISC; namespace NL3D { // *************************************************************************** // Generate random value, but seed is spacial. Take a high frequency, so it gets more the aspect of random. static NLMISC::CNoiseValue RandomGenerator(0,1, 7.68f); // *************************************************************************** CVegetable::CVegetable() { // Ground style density. setAngleGround(0); // Density not maximised. MaxDensity= -1; // No scale. Sxy.Abs= Sz.Abs= 1; Sxy.Rand= Sz.Rand= 0; // No rotation. Rx.Abs= Ry.Abs= Rz.Abs= 0; Rx.Rand= Ry.Rand= Rz.Rand= 0; // No BendFactor. BendFactor.Abs= 1; BendFactor.Rand= 0; BendFrequencyFactor= 1; // Appear at 0. DistType= 0; _Manager= NULL; } // *************************************************************************** void CVegetable::setAngleGround(float cosAngleMin) { _AngleType= AngleGround; _CosAngleMin= cosAngleMin; // We must be at densityFactor==1, when cosAngle==1, keeping the same formula. _CosAngleMax= 1 + (1-cosAngleMin); // precalc _CosAngleMiddle= (_CosAngleMin + _CosAngleMax)/2; _OOCosAngleDist= _CosAngleMax - _CosAngleMiddle; if(_OOCosAngleDist) _OOCosAngleDist= 1.0f / _OOCosAngleDist; } // *************************************************************************** void CVegetable::setAngleCeiling(float cosAngleMax) { _AngleType= AngleCeiling; _CosAngleMax= cosAngleMax; // We must be at densityFactor==1, when cosAngle==-1, keeping the same formula. _CosAngleMin= -1 - (cosAngleMax-(-1)); // precalc _CosAngleMiddle= (_CosAngleMin + _CosAngleMax)/2; _OOCosAngleDist= _CosAngleMax - _CosAngleMiddle; if(_OOCosAngleDist) _OOCosAngleDist= 1.0f / _OOCosAngleDist; } // *************************************************************************** void CVegetable::setAngleWall(float cosAngleMin, float cosAngleMax) { _AngleType= AngleWall; _CosAngleMin= cosAngleMin; _CosAngleMax= cosAngleMax; // precalc _CosAngleMiddle= (_CosAngleMin + _CosAngleMax)/2; _OOCosAngleDist= _CosAngleMax - _CosAngleMiddle; if(_OOCosAngleDist) _OOCosAngleDist= 1.0f / _OOCosAngleDist; } // *************************************************************************** void CVegetable::registerToManager(CVegetableManager *manager) { nlassert(manager); _Manager= manager; _VegetableShape= _Manager->getVegetableShape(ShapeName); } // *************************************************************************** void CVegetable::generateGroupEx(float nbInst, const CVector &posInWorld, const CVector &surfaceNormal, uint vegetSeed, std::vector &instances) const { nlassert(_Manager); // Density modulation. //=================== // compute cos of angle between surfaceNormal and K(0,0,1). float cosAngle= surfaceNormal.z; // compute angleFactor density. Use a quadratic, because f'(_CosAngleMiddle)==0. float angleFact= 1 - sqr((cosAngle - _CosAngleMiddle) * _OOCosAngleDist); angleFact= max(0.f, angleFact); // modulate density with angleFactor. nbInst*= angleFact; // modulate result by Global Manager density nbInst*= _Manager->getGlobalDensity(); // Now, 0<=nbInst<+oo. If we have 0.1, it means that we have 10% chance to spawn an instance. // So add a "random" value (with help of a noise with High frequency) // if nbInst==0, we should never have any instance (which may arise if evalOneLevelRandom()==1). // hence the 0.99f* which ensure that we do nbInst+= [0..1[. nbInst+= 0.99f * RandomGenerator.evalOneLevelRandom(posInWorld); // and then get only the integral part. sint nbInstances= NLMISC::OptFastFloor(nbInst); nbInstances= max(0, nbInstances); // resize the instances instances.resize(nbInstances); // Position generation. //=================== // For now, generate them randomly. static CVector2f dSeed(0.513f, 0.267f); // random values. CVector seed= posInWorld; seed.z+= vegetSeed * 0.723f; // 0.723f is a random value. for(sint i=0; i &instances) const { // number of instances to generate float dens= Density.eval(posInWorld); if(MaxDensity >= 0) dens= min(dens, MaxDensity); float nbInst= area * dens; // modulate by normal and generate them. generateGroupEx(nbInst, posInWorld, surfaceNormal, vegetSeed, instances); } // *************************************************************************** void CVegetable::generateGroupBiLinear(const CVector &posInWorld, const CVector posInWorldBorder[4], const CVector &surfaceNormal, float area, uint vegetSeed, std::vector &instances) const { sint i; const float evenDistribFact= 12.25f; // an arbitrary value to have a higher frequency for random. // compute how many instances to generate on borders of the patch // ================== float edgeDensity[4]; for(i=0; i<4; i++) { // Get number of instances generated on edges edgeDensity[i]= area * Density.eval(posInWorldBorder[i]); if(MaxDensity >= 0) edgeDensity[i]= min(edgeDensity[i], area * MaxDensity); edgeDensity[i]= max(0.f, edgeDensity[i]); } // Average on center of the patch for each direction. float edgeDensityCenterX; float edgeDensityCenterY; edgeDensityCenterX= 0.5f * (edgeDensity[0] + edgeDensity[1]); edgeDensityCenterY= 0.5f * (edgeDensity[2] + edgeDensity[3]); // Average for all the patch float nbInstAverage= 0.5f * (edgeDensityCenterX + edgeDensityCenterY); // generate instances on the patch // ================== generateGroupEx(nbInstAverage, posInWorld, surfaceNormal, vegetSeed, instances); // move instances x/y to follow edge repartition // ================== // If on a direction, both edges are 0 density, then must do a special formula bool middleX= edgeDensityCenterX<=1; bool middleY= edgeDensityCenterY<=1; float OOEdgeDCX=0.0; float OOEdgeDCY=0.0; if(!middleX) OOEdgeDCX= 1.0f / edgeDensityCenterX; if(!middleY) OOEdgeDCY= 1.0f / edgeDensityCenterY; // for all instances for(i=0; i<(sint)instances.size(); i++) { float x= instances[i].x; float y= instances[i].y; // a seed for random. CVector randSeed(x*evenDistribFact, y*evenDistribFact, 0); // X change. if(middleX) { // instances are grouped at middle. this is the bijection of easeInEaseOut x= x+x - easeInEaseOut(x); x= x+x - easeInEaseOut(x); instances[i].x= x; } else { // Swap X, randomly. swap more on border // evaluate the density in X direction we have at this point. float densX= edgeDensity[0]*(1-x) + edgeDensity[1]* x ; // If on the side of the lowest density if(densX < edgeDensityCenterX) { // may swap the position float rdSwap= (densX * OOEdgeDCX ); // (densX * OOEdgeDCX) E [0..1[. The more it is near 0, the more is has chance to be swapped. rdSwap+= RandomGenerator.evalOneLevelRandom( randSeed ); if(rdSwap<1) instances[i].x= 1 - instances[i].x; } } // Y change. if(middleY) { // instances are grouped at middle. this is the bijection of easeInEaseOut y= y+y - easeInEaseOut(y); y= y+y - easeInEaseOut(y); instances[i].y= y; } else { // Swap Y, randomly. swap more on border // evaluate the density in Y direction we have at this point. float densY= edgeDensity[2]*(1-y) + edgeDensity[3]* y ; // If on the side of the lowest density if(densY < edgeDensityCenterY) { // may swap the position float rdSwap= (densY * OOEdgeDCY); // (densY * OOEdgeDCY) E [0..1[. The more it is near 0, the more is has chance to be swapped. rdSwap+= RandomGenerator.evalOneLevelRandom( randSeed ); if(rdSwap<1) instances[i].y= 1 - instances[i].y; } } } } // *************************************************************************** void CVegetable::reserveIgAddInstances(CVegetableInstanceGroupReserve &vegetIgReserve, TVegetableWater vegetWaterState, uint numInstances) const { nlassert(_Manager); if (_VegetableShape) _Manager->reserveIgAddInstances(vegetIgReserve, _VegetableShape, (CVegetableManager::TVegetableWater)vegetWaterState, numInstances); } // *************************************************************************** void CVegetable::generateInstance(CVegetableInstanceGroup *ig, const NLMISC::CMatrix &posInWorld, const NLMISC::CRGBAF &modulateAmbientColor, const NLMISC::CRGBAF &modulateDiffuseColor, float blendDistMax, TVegetableWater vegetWaterState, CVegetableUV8 dlmUV) const { nlassert(_Manager); CVector seed= posInWorld.getPos(); // Generate Matrix. // =============== // Generate a random Scale / Rotation matrix. CMatrix randomMat; // setup rotation CVector rot; rot.x= Rx.eval(seed); rot.y= Ry.eval(seed); rot.z= Rz.eval(seed); randomMat.setRot(rot, CMatrix::ZXY); // scale. if(Sxy.Abs!=0 || Sxy.Rand!=0 || Sz.Abs!=0 || Sz.Rand!=0) { CVector scale; scale.x= scale.y= Sxy.eval(seed); scale.z= Sz.eval(seed); randomMat.scale(scale); } // Final Matrix. CMatrix finalMatrix; finalMatrix= posInWorld * randomMat; // Generate Color and factor // =============== CRGBAF materialColor(1,1,1,1); // evaluate gradients. If none, color not modified. Color.eval(seed, materialColor); // modulate with user CRGBAF ambient, diffuse; if(_VegetableShape && _VegetableShape->Lighted) { ambient= modulateAmbientColor * materialColor; diffuse= modulateDiffuseColor * materialColor; } else { ambient= materialColor; diffuse= materialColor; } // Generate a bendFactor float bendFactor= BendFactor.eval(seed); // Generate a bendPhase float bendPhase= BendPhase.eval(seed); // Append to the vegetableManager // =============== if (_VegetableShape) { _Manager->addInstance(ig, _VegetableShape, finalMatrix, ambient, diffuse, bendFactor, bendPhase, BendFrequencyFactor, blendDistMax, (CVegetableManager::TVegetableWater)vegetWaterState, dlmUV); } } // *************************************************************************** void CVegetable::serial(NLMISC::IStream &f) { /* Version 1: - add BendFrequencyFactor Version 0: - base version */ sint ver= f.serialVersion(1); f.serial(ShapeName); f.serial(Density); f.serial(MaxDensity); f.serial(_CosAngleMin, _CosAngleMax, _CosAngleMiddle, _OOCosAngleDist); f.serialEnum(_AngleType); f.serial(Sxy, Sz); f.serial(Rx, Ry, Rz); f.serial(BendFactor); f.serial(BendPhase); f.serial(Color); f.serial(DistType); if(ver>=1) f.serial(BendFrequencyFactor); else BendFrequencyFactor= 1; } } // NL3D