54 , _descriptor( descriptor._propertyDescriptor )
56 _treeMeshNames.insert( cend( _treeMeshNames ),
60 DIVIDE_ASSERT( !_descriptor.grassMap->imageLayers().empty() && !_descriptor.treeMap->imageLayers().empty() );
62 setBounds( _descriptor.parentTerrain->getBounds() );
70 renderState().addToDrawExclusionMask(
78 renderState().useBoundsCenterForLoD(
false );
79 renderState().lod0OnCollision(
true );
80 renderState().drawState(
false );
85 Vegetation::~Vegetation()
89 bool Vegetation::unload()
97 LockGuard<SharedMutex> w_lock( _treeMeshLock );
98 for ( Handle<Mesh>& mesh : _treeMeshes )
119 bool Vegetation::load( PlatformContext& context )
124 registerEditorComponent( context );
126 EditorComponentField visDistanceGrassField = {};
127 visDistanceGrassField._name =
"Grass Draw distance";
128 visDistanceGrassField._data = &_grassDistance;
130 visDistanceGrassField._readOnly =
true;
132 _editorComponent->registerField(
MOV( visDistanceGrassField ) );
134 EditorComponentField visDistanceTreesField = {};
135 visDistanceTreesField._name =
"Tree Draw Instance";
136 visDistanceTreesField._data = &_treeDistance;
138 visDistanceTreesField._readOnly =
true;
140 _editorComponent->registerField(
MOV( visDistanceTreesField ) );
142 _lodPartitions.fill( 0u );
144 constexpr F32 offsetBottom0 = 0.20f;
145 constexpr F32 offsetBottom1 = 0.10f;
147 const mat4<F32> transform[] =
151 vec3<F32>( -offsetBottom0, 0.f, -offsetBottom0 ),
153 GetMatrix( Quaternion<F32>( Angle::DEGREES<F32>( 25.f ), Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( 0.f ) ) )
158 vec3<F32>( -offsetBottom1, 0.f, offsetBottom1 ),
160 GetMatrix( Quaternion<F32>( Angle::DEGREES<F32>( -12.5f ), Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( 0.f ) )*
161 Quaternion<F32>( Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( 35.f ), Angle::DEGREES<F32>( 0.f ) ) )
166 vec3<F32>( offsetBottom0, 0.f, -offsetBottom1 ),
168 GetMatrix( Quaternion<F32>( Angle::DEGREES<F32>( 30.f ), Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( 0.f ) )*
169 Quaternion<F32>( Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( -75.f ), Angle::DEGREES<F32>( 0.f ) ) )
174 vec3<F32>( offsetBottom1 * 2, 0.f, offsetBottom1 ),
176 GetMatrix( Quaternion<F32>( Angle::DEGREES<F32>( -25.f ), Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( 0.f ) )*
177 Quaternion<F32>( Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( -125.f ), Angle::DEGREES<F32>( 0.f ) ) )
182 vec3<F32>( -offsetBottom1 * 2, 0.f, -offsetBottom1 * 2 ),
184 GetMatrix( Quaternion<F32>( Angle::DEGREES<F32>( 5.f ), Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( 0.f ) )*
185 Quaternion<F32>( Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( -225.f ), Angle::DEGREES<F32>( 0.f ) ) )
190 vec3<F32>( offsetBottom0, 0.f, offsetBottom1 * 2 ),
192 GetMatrix( Quaternion<F32>( Angle::DEGREES<F32>( -15.f ), Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( 0.f ) )*
193 Quaternion<F32>( Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( 305.f ), Angle::DEGREES<F32>( 0.f ) ) )
197 vector<vec3<F32>> vertices{};
198 constexpr U8 billboardsPlaneCount =
to_U8(
sizeof( transform ) /
sizeof( transform[0] ) );
199 vertices.reserve( billboardsPlaneCount * 4 );
201 for (
U8 i = 0u; i < billboardsPlaneCount; ++i )
203 vertices.push_back( transform[i] * vec4<F32>( -1.f, 0.f, 0.f, 1.f ) );
204 vertices.push_back( transform[i] * vec4<F32>( -1.f, 1.f, 0.f, 1.f ) );
205 vertices.push_back( transform[i] * vec4<F32>( 1.f, 1.f, 0.f, 1.f ) );
206 vertices.push_back( transform[i] * vec4<F32>( 1.f, 0.f, 0.f, 1.f ) );
209 const U16 indices[] = { 0, 1, 2,
212 const vec2<F32> texCoords[] =
214 vec2<F32>( 0.f, 0.f ),
215 vec2<F32>( 0.f, 1.f ),
216 vec2<F32>( 1.f, 1.f ),
217 vec2<F32>( 1.f, 0.f )
220 VertexBuffer::Descriptor descriptor{};
221 descriptor._name =
"Vegetation";
222 descriptor._allowDynamicUpdates =
false;
223 descriptor._keepCPUData =
false;
224 descriptor._largeIndices =
false;
226 _buffer = context.gfx().newVB( descriptor );
227 _buffer->setVertexCount( vertices.size() );
228 for (
U8 i = 0u; i <
to_U8( vertices.size() ); ++i )
230 _buffer->modifyPositionValue( i, vertices[i] );
233 _buffer->modifyTexCoordValue( i, texCoords[i % 4].s, texCoords[i % 4].t );
236 const auto addPlanes = [&](
const U8 count )
238 for (
U8 i = 0u; i < count; ++i )
242 _buffer->addRestartIndex();
244 for (
const U16 idx : indices )
246 _buffer->addIndex( idx + i * 4 );
251 for (
U8 i = 0; i < _lodPartitions.size(); ++i )
253 addPlanes( billboardsPlaneCount / (i + 1) );
254 _lodPartitions[i] = _buffer->partitionBuffer();
258 _grassPositions.reserve(
to_size(
SQUARED(_descriptor.chunkSize )));
259 _treePositions.reserve(
to_size(
SQUARED( _descriptor.chunkSize )));
261 const F32 posOffset =
to_F32( _descriptor.chunkSize * 2 );
263 vec2<F32> intersections[2]{};
264 Util::Circle circleA{}, circleB{};
265 circleA.center[0] = circleB.center[0] = -posOffset;
266 circleA.center[1] = -posOffset;
267 circleB.center[1] = posOffset;
269 constexpr F32 dR[2] =
275 for (
U8 i = 0; i < 2; ++i )
277 auto& set = (i == 0 ? _grassPositions : _treePositions);
285 circleA.radius = Ar + (RadiusStepB % 3 ? 0.0f : 0.3f * dR[i]);
286 circleB.radius = Br + (RadiusStepA % 3 ? 0.0f : 0.3f * dR[i]);
291 for (
const vec2<F32> record : intersections )
296 set.insert( record );
304 _maxGrassInstances =
to_U32(_grassPositions.size());
305 _maxTreeInstances =
to_U32(_treePositions.size());
307 if ( _maxTreeInstances == 0u && _maxGrassInstances == 0u )
315 const auto& chunks = _descriptor.parentTerrain->terrainChunks();
317 ShaderBufferDescriptor bufferDescriptor = {};
318 bufferDescriptor._bufferParams._elementSize =
sizeof( VegetationData );
323 if ( _maxTreeInstances > 0 )
325 bufferDescriptor._bufferParams._elementCount =
to_U32( _maxTreeInstances * chunks.size() );
326 bufferDescriptor._name =
"Tree_data";
327 _treeData = context.gfx().newSB( bufferDescriptor );
329 if ( _maxGrassInstances > 0 )
331 bufferDescriptor._bufferParams._elementCount =
to_U32( _maxGrassInstances * chunks.size() );
332 bufferDescriptor._name =
"Grass_data";
333 _grassData = context.gfx().newSB( bufferDescriptor );
336 std::atomic_uint loadTasks = 0u;
338 ResourceDescriptor<Material> matDesc(
"Tree_material" );
341 Material::ShaderData treeShaderData = {};
342 treeShaderData._depthShaderVertSource =
"tree";
343 treeShaderData._depthShaderVertVariant =
"";
344 treeShaderData._colourShaderVertSource =
"tree";
345 treeShaderData._colourShaderVertVariant =
"";
347 ResourcePtr<Material> matPtr =
Get( _treeMaterial );
348 matPtr->baseShaderData( treeShaderData );
350 matPtr->properties().isInstanced(
true );
355 ResourceDescriptor<Material> vegetationMaterial(
"grassMaterial" );
358 ResourcePtr<Material> matPtr =
Get( _vegetationMaterial );
361 matPtr->properties().roughness( 0.9f );
362 matPtr->properties().metallic( 0.02f );
363 matPtr->properties().doubleSided(
true );
364 matPtr->properties().isStatic(
false );
365 matPtr->properties().isInstanced(
true );
368 SamplerDescriptor grassSampler = {};
372 grassSampler._anisotropyLevel = 8u;
374 ResourceDescriptor<Texture> vegetationBillboards(
"Vegetation Billboards" );
375 vegetationBillboards.assetLocation( ResourcePath {
GetVariable( _descriptor.parentTerrain->descriptor(),
"vegetationTextureLocation" ) } );
376 vegetationBillboards.assetName(_descriptor.billboardTextureArray );
377 vegetationBillboards.waitForReady(
false );
378 TextureDescriptor& grassTexDescriptor = vegetationBillboards._propertyDescriptor;
381 Handle<Texture> grassBillboardArray =
CreateResource( vegetationBillboards, loadTasks );
385 ShaderModuleDescriptor compModule = {};
387 compModule._sourceFile =
"instanceCullVegetation.glsl";
388 compModule._defines.emplace_back(
Util::StringFormat(
"WORK_GROUP_SIZE {}", WORK_GROUP_SIZE ) );
389 compModule._defines.emplace_back(
Util::StringFormat(
"MAX_TREE_INSTANCES {}", _maxTreeInstances ) );
390 compModule._defines.emplace_back(
Util::StringFormat(
"MAX_GRASS_INSTANCES {}", _maxGrassInstances ) );
393 ResourceDescriptor<ShaderProgram> instanceCullShaderGrass(
"instanceCullVegetation_Grass" );
394 instanceCullShaderGrass.waitForReady(
false );
396 shaderCompDescriptorGrass.
_modules.push_back( compModule );
398 _cullShaderGrass =
CreateResource( instanceCullShaderGrass, loadTasks );
400 compModule._defines.emplace_back(
"CULL_TREES" );
401 ResourceDescriptor<ShaderProgram> instanceCullShaderTrees(
"instanceCullVegetation_Trees" );
402 instanceCullShaderTrees.waitForReady(
false );
404 shaderCompDescriptorTrees.
_modules.push_back( compModule );
405 _cullShaderTrees =
CreateResource( instanceCullShaderTrees, loadTasks );
409 PipelineDescriptor pipeDesc;
411 pipeDesc._shaderProgramHandle = _cullShaderGrass;
412 _cullPipelineGrass = context.gfx().newPipeline( pipeDesc );
413 pipeDesc._shaderProgramHandle = _cullShaderTrees;
414 _cullPipelineTrees = context.gfx().newPipeline( pipeDesc );
416 Get( _vegetationMaterial )->computeShaderCBK( [grassInstances = _maxGrassInstances]( [[maybe_unused]] Material* material,
const RenderStagePass stagePass )
420 shaderDescriptor._globalDefines.emplace_back(
"ENABLE_TBN" );
421 shaderDescriptor._globalDefines.emplace_back(
Util::StringFormat(
"MAX_GRASS_INSTANCES {}", grassInstances ) );
428 fragModule._variant =
"PrePass";
430 shaderDescriptor._modules.push_back( fragModule );
431 shaderDescriptor._name =
"grassPrePass";
435 fragModule._variant =
"Shadow.VSM";
437 shaderDescriptor._modules.push_back( fragModule );
438 shaderDescriptor._name =
"grassShadow";
442 shaderDescriptor._name =
"grassDepth";
449 fragModule._variant =
"Colour.OIT";
450 shaderDescriptor._name =
"grassColourOIT";
454 fragModule._variant =
"Colour";
455 shaderDescriptor._name =
"GrassColour";
457 shaderDescriptor._modules.push_back( fragModule );
460 return shaderDescriptor;
469 void Vegetation::prepareDraw( SceneGraphNode* sgn )
471 VegetationInstance* instance =
nullptr;
473 SharedLock<SharedMutex> w_lock( _instanceLock );
474 for (
const auto& [
id, vegInstance] : _instances )
476 if (
id == sgn->dataFlag() )
478 instance = vegInstance;
483 if ( instance ==
nullptr )
488 if ( instance->_instanceCountGrass > 0u || instance->_instanceCountTrees > 0u )
490 sgn->get<RenderingComponent>()->primitiveRestartRequired(
true );
491 sgn->get<RenderingComponent>()->instantiateMaterial( _vegetationMaterial );
492 sgn->get<RenderingComponent>()->occlusionCull(
false );
493 sgn->get<BoundsComponent>()->collisionsEnabled(
false );
494 renderState().drawState(
true );
497 const U32 ID = sgn->dataFlag();
498 const U32 meshID =
to_U32( ID % _treeMeshNames.size() );
500 if ( instance->_instanceCountTrees > 0 && !_treeMeshNames.empty() )
502 LockGuard<SharedMutex> w_lock( _treeMeshLock );
503 if ( _treeMeshes.empty() )
505 for (
const auto& meshName : _treeMeshNames )
507 if ( !eastl::any_of( eastl::cbegin( _treeMeshes ),
508 eastl::cend( _treeMeshes ),
509 [&meshName](
const Handle<Mesh> ptr )
noexcept
511 return Get(ptr)->assetName() == meshName;
514 ResourceDescriptor<Mesh> model(
"Tree" );
515 model.assetLocation( Paths::g_modelsLocation );
517 model.waitForReady(
true );
518 model.assetName( meshName );
520 Get(meshPtr)->setMaterialTpl( _treeMaterial );
527 _treeMeshes.push_back( meshPtr );
532 Handle<Mesh> crtMesh = INVALID_HANDLE<Mesh>;
534 SharedLock<SharedMutex> r_lock( _treeMeshLock );
535 crtMesh = _treeMeshes.front();
536 const auto& meshName = _treeMeshNames[meshID];
537 for (
const Handle<Mesh> mesh : _treeMeshes )
539 if (
Get(mesh)->resourceName() == meshName )
552 SceneGraphNodeDescriptor nodeDescriptor = {};
553 nodeDescriptor._componentMask = normalMask;
555 nodeDescriptor._serialize =
false;
556 nodeDescriptor._instanceCount = instance->_instanceCountTrees;
557 nodeDescriptor._nodeHandle =
FromHandle(crtMesh);
559 _treeParentNode = sgn->addChildNode( nodeDescriptor );
561 TransformComponent* tComp = _treeParentNode->get<TransformComponent>();
562 const vec4<F32>& offset = instance->_chunk->getOffsetAndSize();
563 tComp->setPositionX( offset.x + offset.z * 0.5f );
564 tComp->setPositionZ( offset.y + offset.w * 0.5f );
565 tComp->setScale( _descriptor.treeScales[meshID] );
567 const SceneGraphNode::ChildContainer& children = _treeParentNode->getChildren();
568 const U32 childCount = children._count;
569 for (
U32 i = 0u; i < childCount; ++i )
571 RenderingComponent* rComp = children._data[i]->get<RenderingComponent>();
572 rComp->dataFlag(
to_F32( ID ) );
573 rComp->occlusionCull(
false );
576 const BoundingBox aabb = _treeParentNode->get<BoundsComponent>()->getBoundingBox();
578 bs.fromBoundingBox( aabb );
580 const vec3<F32>& extents = aabb.getExtent();
581 _treeExtents.set( extents, bs.getRadius() );
582 _grassExtents.w = _grassExtents.xyz.length();
585 _grassExtents.w = _grassExtents.xyz.length();
590 void Vegetation::prepareRender( SceneGraphNode* sgn,
591 RenderingComponent& rComp,
593 GFX::MemoryBarrierCommand& postDrawMemCmd,
594 RenderStagePass renderStagePass,
595 const CameraSnapshot& cameraSnapshot,
598 VegetationInstance* instance =
nullptr;
600 SharedLock<SharedMutex> w_lock( _instanceLock );
601 for (
const auto& [
id, vegInstance] : _instances )
603 if (
id == sgn->dataFlag() )
605 instance = vegInstance;
610 if ( instance ==
nullptr )
618 DIVIDE_ASSERT(cmdBuffer != INVALID_HANDLE<GFX::CommandBuffer>);
620 GFX::CommandBuffer& bufferInOut = *
GFX::Get(cmdBuffer);
623 if ( _grassData || _treeData )
625 auto cmd = GFX::EnqueueCommand<GFX::BindShaderResourcesCommand>( bufferInOut );
630 Set( binding._data, _treeData.get(), { 0u, _treeData->getPrimitiveCount() } );
635 Set( binding._data, _grassData.get(), { 0u, _grassData->getPrimitiveCount() } );
640 if ( renderState().drawState( renderStagePass ) &&
642 (instance->_instanceCountGrass > 0 || instance->_instanceCountTrees > 0) )
648 const RenderTarget* hizTarget = sgn->context().gfx().renderTargetPool().getRenderTarget( hiZSourceTarget );
651 if ( hizAttachment !=
nullptr )
653 ResourcePtr<Texture> hizTexture =
Get(hizAttachment->texture());
655 mat4<F32> viewProjectionMatrix;
656 mat4<F32>::Multiply( cameraSnapshot._projectionMatrix, cameraSnapshot._viewMatrix, viewProjectionMatrix );
659 GFX::SendPushConstantsCommand cullConstantsCmd{};
660 UniformData* uniforms = cullConstantsCmd._uniformData;
670 PushConstantsStruct& fastConstants = cullConstantsCmd._fastData;
671 fastConstants.data[0] = viewProjectionMatrix;
672 fastConstants.data[1] = cameraSnapshot._viewMatrix;
674 GFX::EnqueueCommand<GFX::BeginDebugScopeCommand>( bufferInOut)->_scopeName =
"Occlusion Cull Vegetation";
677 auto cmd = GFX::EnqueueCommand<GFX::BindShaderResourcesCommand>( bufferInOut );
680 Set( binding._data, hizTexture->getView(), hizAttachment->_descriptor._sampler );
683 GFX::DispatchComputeCommand computeCmd = {};
685 auto memCmd = GFX::EnqueueCommand<GFX::MemoryBarrierCommand>( bufferInOut );
686 if ( instance->_instanceCountGrass > 0 )
688 computeCmd._computeGroupSize.set( (instance->_instanceCountGrass + WORK_GROUP_SIZE - 1) / WORK_GROUP_SIZE, 1, 1 );
691 GFX::EnqueueCommand<GFX::BindPipelineCommand>( bufferInOut )->_pipeline = _cullPipelineGrass;
694 memCmd->_bufferLocks.emplace_back( BufferLock
698 ._buffer = _grassData->getBufferImpl()
701 if ( instance->_instanceCountTrees > 0 )
703 computeCmd._computeGroupSize.set( (instance->_instanceCountTrees + WORK_GROUP_SIZE - 1) / WORK_GROUP_SIZE, 1, 1 );
705 GFX::EnqueueCommand<GFX::BindPipelineCommand>( bufferInOut )->_pipeline = _cullPipelineTrees;
709 memCmd->_bufferLocks.emplace_back( BufferLock
713 ._buffer = _treeData->getBufferImpl()
717 GFX::EnqueueCommand<GFX::EndDebugScopeCommand>( bufferInOut );
724 void Vegetation::sceneUpdate(
const U64 deltaTimeUS,
726 SceneState& sceneState )
730 if ( !renderState().drawState() )
733 sgn->get<RenderingComponent>()->dataFlag(
to_F32( sgn->dataFlag() ) );
739 if ( _stateRefreshIntervalBufferUS >= _stateRefreshIntervalUS )
741 _windX = sceneState.windDirX();
742 _windZ = sceneState.windDirZ();
743 _windS = sceneState.windSpeed();
744 _stateRefreshIntervalBufferUS -= _stateRefreshIntervalUS;
746 _stateRefreshIntervalBufferUS += deltaTimeUS;
748 const SceneRenderState& renderState = sceneState.renderState();
750 const F32 sceneRenderRange = renderState.generalVisibility();
751 const F32 sceneGrassDistance = std::min( renderState.grassVisibility(), sceneRenderRange );
752 const F32 sceneTreeDistance = std::min( renderState.treeVisibility(), sceneRenderRange );
753 if ( !
COMPARE( sceneGrassDistance, _grassDistance ) )
755 _grassDistance = sceneGrassDistance;
756 sgn->get<RenderingComponent>()->setMaxRenderRange( _grassDistance );
758 if ( !
COMPARE( sceneTreeDistance, _treeDistance ) )
760 _treeDistance = sceneTreeDistance;
761 if ( _treeParentNode !=
nullptr )
763 const SceneGraphNode::ChildContainer& children = _treeParentNode->getChildren();
764 const U32 childCount = children._count;
765 for (
U32 i = 0u; i < childCount; ++i )
767 RenderingComponent* rComp = children._data[i]->get<RenderingComponent>();
768 rComp->setMaxRenderRange( sceneTreeDistance );
779 const U16 partitionID = _lodPartitions[0];
781 VegetationInstance* instance =
nullptr;
783 SharedLock<SharedMutex> w_lock( _instanceLock );
784 for (
const auto&[
id, vegInstance] : _instances )
786 if (
id == sgn->dataFlag())
788 instance = vegInstance;
793 if (instance ==
nullptr)
798 GenericDrawCommand& cmd = cmdsOut.emplace_back();
801 cmd._sourceBuffer = _buffer->handle();
802 cmd._cmd.instanceCount = instance->_instanceCountGrass;
803 cmd._cmd.indexCount =
to_U32( _buffer->getPartitionIndexCount( partitionID ) );
804 cmd._cmd.firstIndex =
to_U32( _buffer->getPartitionOffset( partitionID ) );
806 RenderingComponent* rComp = sgn->get<RenderingComponent>();
808 for (
U8 i = 0; i <
to_U8( _lodPartitions.size() ); ++i )
810 U16 id = _lodPartitions[i];
816 rComp->setLoDIndexOffset( i, _buffer->getPartitionOffset(
id ), _buffer->getPartitionIndexCount(
id ) );
829 for (
U8 i = 0; i < 4; ++i )
831 if ( in[i] > maxValue )
843 if ( point.x > -chunkSize.x && point.x < chunkSize.x &&
844 point.y > -chunkSize.y && point.y < chunkSize.y )
847 point = (point + chunkSize) * 0.5f;
856 void Vegetation::registerInstance(
const U32 chunkID, VegetationInstance* instance )
858 UniqueLock<SharedMutex> w_lock(_instanceLock);
859 for (
auto&[
id, vegInstance] : _instances)
863 vegInstance = instance;
868 _instances.emplace_back(chunkID, instance);
871 void Vegetation::unregisterInstance(
const U32 chunkID )
873 UniqueLock<SharedMutex> w_lock( _instanceLock );
894 if (
context().config().debug.renderFilter.grassInstances )
903 if (
context().config().debug.renderFilter.treeInstances )
922 if (
context().config().debug.cache.enabled &&
923 context().config().debug.cache.vegetation &&
924 chunkCache.
loadFromFile( Paths::g_terrainCacheLocation, cacheFileName ) )
927 chunkCache >> tempVer;
930 size_t containerSize = 0u;
931 chunkCache.
read<
size_t>(containerSize);
932 container.resize( containerSize );
933 chunkCache.
read(
reinterpret_cast<Byte*
>(container.data()),
sizeof(
VegetationData ) * container.size() );
945 const size_t maxInstances = treeData ?
parent->_maxTreeInstances :
parent->_maxGrassInstances;
947 container.reserve( maxInstances);
949 static std::discrete_distribution<> distribution[] =
957 std::default_random_engine generator(
to_U32( std::chrono::system_clock::now().time_since_epoch().count() ) );
965 const U16 mapWidth = map->dimensions( 0u, 0u ).width;
966 const U16 mapHeight = map->dimensions( 0u, 0u ).height;
967 const auto& positions = treeData ?
parent->_treePositions :
parent->_grassPositions;
969 const F32 slopeLimit = treeData ? g_slopeLimitTrees : g_slopeLimitGrass;
975 if ( !ScaleAndCheckBounds( chunkPos, chunkSize, pos ) )
980 const vec2<F32> mapCoord( pos.x + mapWidth * 0.5f, pos.y + mapHeight * 0.5f );
982 const F32 x_fac = mapCoord.
x / mapWidth;
983 const F32 y_fac = mapCoord.
y / mapHeight;
985 const Terrain::Vert vert = terrain.getVert( x_fac, y_fac,
true );
992 constexpr F32 length = 1.0f;
994 if ( angle > slopeLimit )
998 const F32 slopeScaleFactor = 1.f -
MAP( angle, 0.f, slopeLimit, 0.f, 0.9f );
1003 const U8 index = BestIndex( colour );
1004 const F32 colourVal = colour[index];
1010 const U8 arrayLayer =
to_U8( distribution[index]( generator ) );
1012 const F32 xmlScale = scales[treeData ? meshID : index];
1014 const F32 minXmlScale = xmlScale * 7.5f / 10.0f;
1016 const F32 maxXmlScale = xmlScale * 1.25f;
1018 const F32 scale =
CLAMPED( (colourVal + 1.f) / 256.0f * xmlScale, minXmlScale, maxXmlScale ) * slopeScaleFactor;
1024 entry._positionAndScale.set( vert._position, scale );
1043 container.push_back(
entry );
1046 container.shrink_to_fit();
1048 chunkCache << container.size();
1049 chunkCache.
append( container.data(), container.size() );
1050 if ( !chunkCache.
dumpToFile(Paths::g_terrainCacheLocation, cacheFileName ) )
#define WAIT_FOR_CONDITION_TIMEOUT(...)
#define WAIT_FOR_CONDITION(...)
#define PROFILE_SCOPE_AUTO(CATEGORY)
void append(const Byte *src, size_t cnt)
Appends 'cnt' bytes from 'src' to the buffer.
void read(T &out)
Reads sizeof(T) data from the buffer and returns it. Reading moves the read head forward!
bool loadFromFile(const ResourcePath &path, std::string_view fileName, const U8 version=BUFFER_FORMAT_VERSION)
void clear() noexcept
Resets the entire storage and the read and write positions.
bool dumpToFile(const ResourcePath &path, std::string_view fileName, const U8 version=BUFFER_FORMAT_VERSION)
Saves the entire buffer contents to file. Always appends the version at the end of the file.
void setState(ResourceState currentState) final
PlatformContext & context() noexcept
void fromEuler(const vec3< Angle::DEGREES< T > > &v) noexcept
virtual void sceneUpdate(U64 deltaTimeUS, SceneGraphNode *sgn, SceneState &sceneState)
Called from SceneGraph "sceneUpdate".
virtual void prepareRender(SceneGraphNode *sgn, RenderingComponent &rComp, RenderPackage &pkg, GFX::MemoryBarrierCommand &postDrawMemCmd, RenderStagePass renderStagePass, const CameraSnapshot &cameraSnapshot, bool refreshData)
bool load(PlatformContext &context) override
Loading and unloading interface.
virtual void buildDrawCommands(SceneGraphNode *sgn, GenericDrawCommandContainer &cmdsOut)
vec4< F32 > getOffsetAndSize() const noexcept
const Terrain & parent() const noexcept
VegetationInstance(PlatformContext &context, Handle< Vegetation > parent, TerrainChunk *chunk)
const Handle< Vegetation > _parent
const TerrainChunk * _chunk
static mat4< T > Multiply(const mat4< T > &matrixA, const mat4< T > &matrixB) noexcept
ret = A * B
constexpr DEGREES< T > to_DEGREES(RADIANS< T > angle) noexcept
constexpr U8 MAX_CSM_SPLITS_PER_LIGHT
Used for CSM or PSSM to determine the maximum number of frustum splits.
FColour4 WHITE
Random stuff added for convenience.
CommandBuffer * Get(Handle< CommandBuffer > handle)
FORCE_INLINE T * EnqueueCommand(CommandBuffer &buffer)
const char * Get(U64 key, bool appendSection=true, const char *defaultValue=nullptr)
constexpr Optick::Category::Type Scene
constexpr T Milliseconds(T a)
Str StringFormat(const char *fmt, Args &&...args)
bool IntersectCircles(const Circle &cA, const Circle &cB, vec2< F32 > *pointsOut) noexcept
constexpr I16 g_maxRadiusSteps
constexpr F32 g_slopeLimitTrees
constexpr F32 g_PointRadiusBaseTrees
FORCE_INLINE bool ScaleAndCheckBounds(const vec2< F32 > chunkPos, const vec2< F32 > chunkSize, vec2< F32 > &point) noexcept
FORCE_INLINE U8 BestIndex(const UColour4 &in) noexcept
constexpr F32 g_slopeLimitGrass
constexpr F32 g_PointRadiusBaseGrass
constexpr F32 g_distanceRingsBaseGrass
constexpr U32 WORK_GROUP_SIZE
constexpr F32 g_distanceRingsBaseTrees
Handle console commands that start with a forward slash.
constexpr SceneNodeType GetSceneNodeType()
static constexpr bool IsDepthPass(const RenderStagePass stagePass) noexcept
constexpr U32 to_U32(const T value)
FORCE_INLINE void DestroyResource(Handle< T > &handle, const bool immediate=false)
bool IS_IN_RANGE_EXCLUSIVE(const T x, const U min, const U max) noexcept
static constexpr U16 g_AllIndicesID
constexpr T SQUARED(T input) noexcept
static const vec3< F32 > WORLD_X_AXIS
Quaternion< T > RotationFromVToU(const vec3< T > &v, const vec3< T > &u, const vec3< T > &fallbackAxis=VECTOR3_ZERO) noexcept
get the shortest arc quaternion to rotate vector 'v' to the target vector 'u'(from Ogre3D!...
Handle< GFX::CommandBuffer > GetCommandBuffer(RenderPackage &pkg)
@ RES_LOADED
The resource is available for usage.
@ RES_LOADING
The resource is loading, creating data, parsing scripts, etc.
void toggleOption(GenericDrawCommand &cmd, CmdRenderOptions option) noexcept
constexpr F32 to_F32(const T value)
T Dot(vec2< T > a, vec2< T > b) noexcept
general vec2 dot product
constexpr U16 BYTE_BUFFER_VERSION
constexpr F32 EPSILON_F32
eastl::vector< Type > vector
static const vec3< F32 > VECTOR3_UNIT
void Set(DescriptorSetBindingData &dataInOut, ShaderBuffer *buffer, const BufferRange range) noexcept
constexpr U64 _ID(const char *const str, const U64 value=val_64_const) noexcept
DescriptorSetBinding & AddBinding(DescriptorSet &setInOut, U8 slot, U16 stageVisibilityMask)
FORCE_INLINE Handle< T > CreateResource(const ResourceDescriptor< T > &descriptor, bool &wasInCache, std::atomic_uint &taskCounter)
::value constexpr T MAP(T input, T in_min, T in_max, T out_min, T out_max, D64 &slopeOut) noexcept
constexpr U8 to_U8(const T value)
::value constexpr T CLAMPED(T n, T min, T max) noexcept
static const vec3< F32 > WORLD_Z_NEG_AXIS
eastl::fixed_vector< GenericDrawCommand, 1, true > GenericDrawCommandContainer
string GetVariable(const TerrainDescriptor &descriptor, std::string_view name)
SceneNodeHandle FromHandle(const Handle< T > handle)
constexpr size_t to_size(const T value)
mat3< T > GetMatrix(const Quaternion< T > &q) noexcept
PropertyDescriptor< Texture > TextureDescriptor
bool COMPARE(T X, U Y) noexcept
PropertyDescriptor< ShaderProgram > ShaderProgramDescriptor
constexpr I32 to_I32(const T value)
static const vec3< F32 > VECTOR3_ZERO
bool dvd_erase_if(eastl::vector< T, A > &vec, Predicate &&pred)
FORCE_INLINE T * Get(const Handle< T > handle)
Project const SceneEntry & entry
static const vec3< F32 > WORLD_Y_AXIS
constexpr auto to_base(const Type value) -> Type
static NO_INLINE void printfn(const char *format, T &&... args)
vector< ShaderModuleDescriptor > _modules
std::shared_ptr< ImageTools::ImageData > grassMap
std::shared_ptr< ImageTools::ImageData > treeMap
std::array< vec3< F32 >, 4 > treeRotations
static RenderTargetID HI_Z
static RenderTargetID HI_Z_REFLECT
PropertyDescriptor< T > _propertyDescriptor