Divide Framework 0.1
A free and open-source 3D Framework under heavy development
Loading...
Searching...
No Matches
Vegetation.cpp
Go to the documentation of this file.
1
2
4
10
25
29
30namespace Divide
31{
32
33 namespace
34 {
35 constexpr U16 BYTE_BUFFER_VERSION = 1u;
36
37 constexpr U32 WORK_GROUP_SIZE = 64;
38 constexpr I16 g_maxRadiusSteps = 512;
39 constexpr F32 g_ArBase = 1.0f; // Starting radius of circle A
40 constexpr F32 g_BrBase = 1.0f; // Starting radius of circle B
41 constexpr F32 g_PointRadiusBaseGrass = 0.935f;
42 constexpr F32 g_PointRadiusBaseTrees = 5.f;
43 // Distance between concentric rings
44 constexpr F32 g_distanceRingsBaseGrass = 2.35f;
45 constexpr F32 g_distanceRingsBaseTrees = 2.5f;
46 constexpr F32 g_slopeLimitGrass = 30.0f;
47 constexpr F32 g_slopeLimitTrees = 10.0f;
48 }
49
50 Vegetation::Vegetation( const ResourceDescriptor<Vegetation>& descriptor )
51 : SceneNode( descriptor,
52 GetSceneNodeType<Vegetation>(),
54 , _descriptor( descriptor._propertyDescriptor )
55 {
56 _treeMeshNames.insert( cend( _treeMeshNames ),
57 cbegin( descriptor._propertyDescriptor.treeMeshes ),
58 cend( descriptor._propertyDescriptor.treeMeshes ) );
59
60 DIVIDE_ASSERT( !_descriptor.grassMap->imageLayers().empty() && !_descriptor.treeMap->imageLayers().empty() );
61
62 setBounds( _descriptor.parentTerrain->getBounds() );
63
64 renderState().addToDrawExclusionMask( RenderStage::REFLECTION );
65 renderState().addToDrawExclusionMask( RenderStage::REFRACTION );
66 renderState().addToDrawExclusionMask( RenderStage::SHADOW, RenderPassType::COUNT, static_cast<RenderStagePass::VariantType>(ShadowType::CUBEMAP) );
67 renderState().addToDrawExclusionMask( RenderStage::SHADOW, RenderPassType::COUNT, static_cast<RenderStagePass::VariantType>(ShadowType::SINGLE) );
68 for ( U8 i = 1u; i < Config::Lighting::MAX_CSM_SPLITS_PER_LIGHT; ++i )
69 {
70 renderState().addToDrawExclusionMask(
75 static_cast<RenderStagePass::PassIndex>(i) );
76 }
77 // Because we span an entire terrain chunk, LoD calculation will always be off unless we use the closest possible point to the camera
78 renderState().useBoundsCenterForLoD( false );
79 renderState().lod0OnCollision( true );
80 renderState().drawState( false );
81
83 }
84
85 Vegetation::~Vegetation()
86 {
87 }
88
89 bool Vegetation::unload()
90 {
91 Console::printfn( LOCALE_STR( "UNLOAD_VEGETATION_BEGIN" ), resourceName().c_str() );
92
94
95 assert( getState() != ResourceState::RES_LOADING );
96 {
97 LockGuard<SharedMutex> w_lock( _treeMeshLock );
98 for ( Handle<Mesh>& mesh : _treeMeshes )
99 {
100 DestroyResource( mesh );
101 }
102 _treeMeshes.clear();
103 }
104
105 DestroyResource( _treeMaterial );
106 DestroyResource( _vegetationMaterial );
107 DestroyResource( _cullShaderGrass );
108 DestroyResource( _cullShaderTrees );
109
110 _treeData.reset();
111 _grassData.reset();
112 _buffer.reset();
113
114 Console::printfn( LOCALE_STR( "UNLOAD_VEGETATION_END" ) );
115
116 return SceneNode::unload();
117 }
118
119 bool Vegetation::load( PlatformContext& context )
120 {
121 // Make sure this is ONLY CALLED FROM THE MAIN LOADING THREAD. All instances should call this in a serialized fashion
122 DIVIDE_ASSERT( _buffer == nullptr );
123
124 registerEditorComponent( context );
125 DIVIDE_ASSERT( _editorComponent != nullptr );
126 EditorComponentField visDistanceGrassField = {};
127 visDistanceGrassField._name = "Grass Draw distance";
128 visDistanceGrassField._data = &_grassDistance;
129 visDistanceGrassField._type = EditorComponentFieldType::PUSH_TYPE;
130 visDistanceGrassField._readOnly = true;
131 visDistanceGrassField._basicType = PushConstantType::FLOAT;
132 _editorComponent->registerField( MOV( visDistanceGrassField ) );
133
134 EditorComponentField visDistanceTreesField = {};
135 visDistanceTreesField._name = "Tree Draw Instance";
136 visDistanceTreesField._data = &_treeDistance;
137 visDistanceTreesField._type = EditorComponentFieldType::PUSH_TYPE;
138 visDistanceTreesField._readOnly = true;
139 visDistanceTreesField._basicType = PushConstantType::FLOAT;
140 _editorComponent->registerField( MOV( visDistanceTreesField ) );
141
142 _lodPartitions.fill( 0u );
143
144 constexpr F32 offsetBottom0 = 0.20f;
145 constexpr F32 offsetBottom1 = 0.10f;
146
147 const mat4<F32> transform[] =
148 {
149 mat4<F32>
150 {
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 ) ) )
154 },
155
156 mat4<F32>
157 {
158 vec3<F32>( -offsetBottom1, 0.f, offsetBottom1 ),
159 vec3<F32>( 0.85f ),
160 GetMatrix( Quaternion<F32>( Angle::DEGREES<F32>( -12.5f ), Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( 0.f ) )* //Pitch
161 Quaternion<F32>( Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( 35.f ), Angle::DEGREES<F32>( 0.f ) ) ) //Yaw
162 },
163
164 mat4<F32>
165 {
166 vec3<F32>( offsetBottom0, 0.f, -offsetBottom1 ),
167 vec3<F32>( 1.1f ),
168 GetMatrix( Quaternion<F32>( Angle::DEGREES<F32>( 30.f ), Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( 0.f ) )* //Pitch
169 Quaternion<F32>( Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( -75.f ), Angle::DEGREES<F32>( 0.f ) ) ) //Yaw
170 },
171
172 mat4<F32>
173 {
174 vec3<F32>( offsetBottom1 * 2, 0.f, offsetBottom1 ),
175 vec3<F32>( 0.9f ),
176 GetMatrix( Quaternion<F32>( Angle::DEGREES<F32>( -25.f ), Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( 0.f ) )* //Pitch
177 Quaternion<F32>( Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( -125.f ), Angle::DEGREES<F32>( 0.f ) ) ) //Yaw
178 },
179
180 mat4<F32>
181 {
182 vec3<F32>( -offsetBottom1 * 2, 0.f, -offsetBottom1 * 2 ),
183 vec3<F32>( 1.2f ),
184 GetMatrix( Quaternion<F32>( Angle::DEGREES<F32>( 5.f ), Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( 0.f ) )* //Pitch
185 Quaternion<F32>( Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( -225.f ), Angle::DEGREES<F32>( 0.f ) ) ) //Yaw
186 },
187
188 mat4<F32>
189 {
190 vec3<F32>( offsetBottom0, 0.f, offsetBottom1 * 2 ),
191 vec3<F32>( 0.75f ),
192 GetMatrix( Quaternion<F32>( Angle::DEGREES<F32>( -15.f ), Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( 0.f ) )* //Pitch
193 Quaternion<F32>( Angle::DEGREES<F32>( 0.f ), Angle::DEGREES<F32>( 305.f ), Angle::DEGREES<F32>( 0.f ) ) ) //Yaw
194 }
195 };
196
197 vector<vec3<F32>> vertices{};
198 constexpr U8 billboardsPlaneCount = to_U8( sizeof( transform ) / sizeof( transform[0] ) );
199 vertices.reserve( billboardsPlaneCount * 4 );
200
201 for ( U8 i = 0u; i < billboardsPlaneCount; ++i )
202 {
203 vertices.push_back( transform[i] * vec4<F32>( -1.f, 0.f, 0.f, 1.f ) ); //BL
204 vertices.push_back( transform[i] * vec4<F32>( -1.f, 1.f, 0.f, 1.f ) ); //TL
205 vertices.push_back( transform[i] * vec4<F32>( 1.f, 1.f, 0.f, 1.f ) ); //TR
206 vertices.push_back( transform[i] * vec4<F32>( 1.f, 0.f, 0.f, 1.f ) ); //BR
207 }
208
209 const U16 indices[] = { 0, 1, 2,
210 0, 2, 3 };
211
212 const vec2<F32> texCoords[] =
213 {
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 )
218 };
219
220 VertexBuffer::Descriptor descriptor{};
221 descriptor._name = "Vegetation";
222 descriptor._allowDynamicUpdates = false;
223 descriptor._keepCPUData = false;
224 descriptor._largeIndices = false;
225
226 _buffer = context.gfx().newVB( descriptor );
227 _buffer->setVertexCount( vertices.size() );
228 for ( U8 i = 0u; i < to_U8( vertices.size() ); ++i )
229 {
230 _buffer->modifyPositionValue( i, vertices[i] );
231 _buffer->modifyNormalValue( i, WORLD_Y_AXIS );
232 _buffer->modifyTangentValue( i, WORLD_X_AXIS );
233 _buffer->modifyTexCoordValue( i, texCoords[i % 4].s, texCoords[i % 4].t );
234 }
235
236 const auto addPlanes = [&]( const U8 count )
237 {
238 for ( U8 i = 0u; i < count; ++i )
239 {
240 if ( i > 0 )
241 {
242 _buffer->addRestartIndex();
243 }
244 for ( const U16 idx : indices )
245 {
246 _buffer->addIndex( idx + i * 4 );
247 }
248 }
249 };
250
251 for ( U8 i = 0; i < _lodPartitions.size(); ++i )
252 {
253 addPlanes( billboardsPlaneCount / (i + 1) );
254 _lodPartitions[i] = _buffer->partitionBuffer();
255 }
256
257 //ref: http://mollyrocket.com/casey/stream_0016.html
258 _grassPositions.reserve( to_size( SQUARED(_descriptor.chunkSize )));
259 _treePositions.reserve( to_size( SQUARED( _descriptor.chunkSize )));
260
261 const F32 posOffset = to_F32( _descriptor.chunkSize * 2 );
262
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;
268
269 constexpr F32 dR[2] =
270 {
273 };
274
275 for ( U8 i = 0; i < 2; ++i )
276 {
277 auto& set = (i == 0 ? _grassPositions : _treePositions);
278
279 for ( I16 RadiusStepA = 0; RadiusStepA < g_maxRadiusSteps; ++RadiusStepA )
280 {
281 const F32 Ar = g_ArBase + dR[i] * to_F32( RadiusStepA );
282 for ( I16 RadiusStepB = 0; RadiusStepB < g_maxRadiusSteps; ++RadiusStepB )
283 {
284 const F32 Br = g_BrBase + dR[i] * to_F32( RadiusStepB );
285 circleA.radius = Ar + (RadiusStepB % 3 ? 0.0f : 0.3f * dR[i]);
286 circleB.radius = Br + (RadiusStepA % 3 ? 0.0f : 0.3f * dR[i]);
287 // Intersect circle Ac,UseAr and Bc,UseBr
288 if ( IntersectCircles( circleA, circleB, intersections ) )
289 {
290 // Add the resulting points if they are within the pattern bounds
291 for ( const vec2<F32> record : intersections )
292 {
293 if ( IS_IN_RANGE_EXCLUSIVE( record.x, -to_F32( _descriptor.chunkSize ), to_F32( _descriptor.chunkSize ) ) &&
294 IS_IN_RANGE_EXCLUSIVE( record.y, -to_F32( _descriptor.chunkSize ), to_F32( _descriptor.chunkSize ) ) )
295 {
296 set.insert( record );
297 }
298 }
299 }
300 }
301 }
302 }
303
304 _maxGrassInstances = to_U32(_grassPositions.size());
305 _maxTreeInstances = to_U32(_treePositions.size());
306
307 if ( _maxTreeInstances == 0u && _maxGrassInstances == 0u )
308 {
309 return SceneNode::load( context );
310 }
311
312 _maxGrassInstances += _maxGrassInstances % WORK_GROUP_SIZE;
313 _maxTreeInstances += _maxTreeInstances % WORK_GROUP_SIZE;
314
315 const auto& chunks = _descriptor.parentTerrain->terrainChunks();
316
317 ShaderBufferDescriptor bufferDescriptor = {};
318 bufferDescriptor._bufferParams._elementSize = sizeof( VegetationData );
319 bufferDescriptor._bufferParams._flags._usageType = BufferUsageType::UNBOUND_BUFFER;
320 bufferDescriptor._bufferParams._flags._updateFrequency = BufferUpdateFrequency::ONCE;
321 bufferDescriptor._bufferParams._flags._updateUsage = BufferUpdateUsage::GPU_TO_GPU;
322
323 if ( _maxTreeInstances > 0 )
324 {
325 bufferDescriptor._bufferParams._elementCount = to_U32( _maxTreeInstances * chunks.size() );
326 bufferDescriptor._name = "Tree_data";
327 _treeData = context.gfx().newSB( bufferDescriptor );
328 }
329 if ( _maxGrassInstances > 0 )
330 {
331 bufferDescriptor._bufferParams._elementCount = to_U32( _maxGrassInstances * chunks.size() );
332 bufferDescriptor._name = "Grass_data";
333 _grassData = context.gfx().newSB( bufferDescriptor );
334 }
335
336 std::atomic_uint loadTasks = 0u;
337
338 ResourceDescriptor<Material> matDesc( "Tree_material" );
339 _treeMaterial = CreateResource( matDesc );
340 {
341 Material::ShaderData treeShaderData = {};
342 treeShaderData._depthShaderVertSource = "tree";
343 treeShaderData._depthShaderVertVariant = "";
344 treeShaderData._colourShaderVertSource = "tree";
345 treeShaderData._colourShaderVertVariant = "";
346
347 ResourcePtr<Material> matPtr = Get( _treeMaterial );
348 matPtr->baseShaderData( treeShaderData );
349 matPtr->properties().shadingMode( ShadingMode::BLINN_PHONG );
350 matPtr->properties().isInstanced( true );
351 matPtr->addShaderDefine( ShaderType::COUNT, Util::StringFormat( "MAX_TREE_INSTANCES {}", _maxTreeInstances ).c_str() );
352 }
353
354
355 ResourceDescriptor<Material> vegetationMaterial( "grassMaterial" );
356 _vegetationMaterial = CreateResource( vegetationMaterial );
357 {
358 ResourcePtr<Material> matPtr = Get( _vegetationMaterial );
359 matPtr->properties().shadingMode( ShadingMode::BLINN_PHONG );
360 matPtr->properties().baseColour( DefaultColours::WHITE );
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 );
366 matPtr->setPipelineLayout( PrimitiveTopology::TRIANGLE_STRIP, _buffer->generateAttributeMap() );
367
368 SamplerDescriptor grassSampler = {};
369 grassSampler._wrapU = TextureWrap::CLAMP_TO_EDGE;
370 grassSampler._wrapV = TextureWrap::CLAMP_TO_EDGE;
371 grassSampler._wrapW = TextureWrap::CLAMP_TO_EDGE;
372 grassSampler._anisotropyLevel = 8u;
373
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;
379 grassTexDescriptor._texType = TextureType::TEXTURE_2D_ARRAY;
380 grassTexDescriptor._packing = GFXImagePacking::NORMALIZED_SRGB;
381 Handle<Texture> grassBillboardArray = CreateResource( vegetationBillboards, loadTasks );
382 matPtr->setTexture( TextureSlot::UNIT0, grassBillboardArray, grassSampler, TextureOperation::REPLACE, true );
383 }
384
385 ShaderModuleDescriptor compModule = {};
386 compModule._moduleType = ShaderType::COMPUTE;
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 ) );
391
392
393 ResourceDescriptor<ShaderProgram> instanceCullShaderGrass( "instanceCullVegetation_Grass" );
394 instanceCullShaderGrass.waitForReady( false );
395 ShaderProgramDescriptor& shaderCompDescriptorGrass = instanceCullShaderGrass._propertyDescriptor;
396 shaderCompDescriptorGrass._modules.push_back( compModule );
397
398 _cullShaderGrass = CreateResource( instanceCullShaderGrass, loadTasks );
399
400 compModule._defines.emplace_back( "CULL_TREES" );
401 ResourceDescriptor<ShaderProgram> instanceCullShaderTrees( "instanceCullVegetation_Trees" );
402 instanceCullShaderTrees.waitForReady( false );
403 ShaderProgramDescriptor& shaderCompDescriptorTrees = instanceCullShaderTrees._propertyDescriptor;
404 shaderCompDescriptorTrees._modules.push_back( compModule );
405 _cullShaderTrees = CreateResource( instanceCullShaderTrees, loadTasks );
406
407 WAIT_FOR_CONDITION( loadTasks.load() == 0u );
408
409 PipelineDescriptor pipeDesc;
410 pipeDesc._primitiveTopology = PrimitiveTopology::COMPUTE;
411 pipeDesc._shaderProgramHandle = _cullShaderGrass;
412 _cullPipelineGrass = context.gfx().newPipeline( pipeDesc );
413 pipeDesc._shaderProgramHandle = _cullShaderTrees;
414 _cullPipelineTrees = context.gfx().newPipeline( pipeDesc );
415
416 Get( _vegetationMaterial )->computeShaderCBK( [grassInstances = _maxGrassInstances]( [[maybe_unused]] Material* material, const RenderStagePass stagePass )
417 {
418 ShaderProgramDescriptor shaderDescriptor = {};
419 shaderDescriptor._modules.emplace_back( ShaderType::VERTEX, "grass.glsl" );
420 shaderDescriptor._globalDefines.emplace_back( "ENABLE_TBN" );
421 shaderDescriptor._globalDefines.emplace_back( Util::StringFormat( "MAX_GRASS_INSTANCES {}", grassInstances ) );
422
423 ShaderModuleDescriptor fragModule{ ShaderType::FRAGMENT, "grass.glsl" };
424 if ( IsDepthPass( stagePass ) )
425 {
426 if ( stagePass._stage == RenderStage::DISPLAY )
427 {
428 fragModule._variant = "PrePass";
429
430 shaderDescriptor._modules.push_back( fragModule );
431 shaderDescriptor._name = "grassPrePass";
432 }
433 else if ( stagePass._stage == RenderStage::SHADOW )
434 {
435 fragModule._variant = "Shadow.VSM";
436
437 shaderDescriptor._modules.push_back( fragModule );
438 shaderDescriptor._name = "grassShadow";
439 }
440 else
441 {
442 shaderDescriptor._name = "grassDepth";
443 }
444 }
445 else
446 {
447 if ( stagePass._passType == RenderPassType::OIT_PASS )
448 {
449 fragModule._variant = "Colour.OIT";
450 shaderDescriptor._name = "grassColourOIT";
451 }
452 else
453 {
454 fragModule._variant = "Colour";
455 shaderDescriptor._name = "GrassColour";
456 }
457 shaderDescriptor._modules.push_back( fragModule );
458 }
459
460 return shaderDescriptor;
461 } );
462
463 WAIT_FOR_CONDITION( Get(_cullShaderGrass)->getState() == ResourceState::RES_LOADED &&
464 Get(_cullShaderTrees)->getState() == ResourceState::RES_LOADED );
465
466 return SceneNode::load(context);
467 }
468
469 void Vegetation::prepareDraw( SceneGraphNode* sgn )
470 {
471 VegetationInstance* instance = nullptr;
472 {
473 SharedLock<SharedMutex> w_lock( _instanceLock );
474 for ( const auto& [id, vegInstance] : _instances )
475 {
476 if ( id == sgn->dataFlag() )
477 {
478 instance = vegInstance;
479 break;
480 }
481 }
482 }
483 if ( instance == nullptr )
484 {
485 return;
486 }
487
488 if ( instance->_instanceCountGrass > 0u || instance->_instanceCountTrees > 0u )
489 {
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 );
495 }
496
497 const U32 ID = sgn->dataFlag();
498 const U32 meshID = to_U32( ID % _treeMeshNames.size() );
499
500 if ( instance->_instanceCountTrees > 0 && !_treeMeshNames.empty() )
501 {
502 LockGuard<SharedMutex> w_lock( _treeMeshLock );
503 if ( _treeMeshes.empty() )
504 {
505 for ( const auto& meshName : _treeMeshNames )
506 {
507 if ( !eastl::any_of( eastl::cbegin( _treeMeshes ),
508 eastl::cend( _treeMeshes ),
509 [&meshName]( const Handle<Mesh> ptr ) noexcept
510 {
511 return Get(ptr)->assetName() == meshName;
512 }))
513 {
514 ResourceDescriptor<Mesh> model( "Tree" );
515 model.assetLocation( Paths::g_modelsLocation );
516 model.flag( true );
517 model.waitForReady( true );
518 model.assetName( meshName );
519 Handle<Mesh> meshPtr = CreateResource( model );
520 Get(meshPtr)->setMaterialTpl( _treeMaterial );
521 // CSM last split should probably avoid rendering trees since it would cover most of the scene :/
522 Get(meshPtr)->renderState().addToDrawExclusionMask( RenderStage::SHADOW,
527 _treeMeshes.push_back( meshPtr );
528 }
529 }
530 }
531
532 Handle<Mesh> crtMesh = INVALID_HANDLE<Mesh>;
533 {
534 SharedLock<SharedMutex> r_lock( _treeMeshLock );
535 crtMesh = _treeMeshes.front();
536 const auto& meshName = _treeMeshNames[meshID];
537 for ( const Handle<Mesh> mesh : _treeMeshes )
538 {
539 if ( Get(mesh)->resourceName() == meshName )
540 {
541 crtMesh = mesh;
542 break;
543 }
544 }
545 }
546
547 constexpr U32 normalMask = to_base( ComponentType::TRANSFORM ) |
551
552 SceneGraphNodeDescriptor nodeDescriptor = {};
553 nodeDescriptor._componentMask = normalMask;
554 nodeDescriptor._usageContext = NodeUsageContext::NODE_STATIC;
555 nodeDescriptor._serialize = false;
556 nodeDescriptor._instanceCount = instance->_instanceCountTrees;
557 nodeDescriptor._nodeHandle = FromHandle(crtMesh);
558 Util::StringFormat( nodeDescriptor._name, "Trees_chunk_{}", ID );
559 _treeParentNode = sgn->addChildNode( nodeDescriptor );
560
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] );
566
567 const SceneGraphNode::ChildContainer& children = _treeParentNode->getChildren();
568 const U32 childCount = children._count;
569 for ( U32 i = 0u; i < childCount; ++i )
570 {
571 RenderingComponent* rComp = children._data[i]->get<RenderingComponent>();
572 rComp->dataFlag( to_F32( ID ) );
573 rComp->occlusionCull( false );
574 }
575
576 const BoundingBox aabb = _treeParentNode->get<BoundsComponent>()->getBoundingBox();
577 BoundingSphere bs;
578 bs.fromBoundingBox( aabb );
579
580 const vec3<F32>& extents = aabb.getExtent();
581 _treeExtents.set( extents, bs.getRadius() );
582 _grassExtents.w = _grassExtents.xyz.length();
583 }
584
585 _grassExtents.w = _grassExtents.xyz.length();
586
587 setState( ResourceState::RES_LOADED );
588 }
589
590 void Vegetation::prepareRender( SceneGraphNode* sgn,
591 RenderingComponent& rComp,
592 RenderPackage& pkg,
593 GFX::MemoryBarrierCommand& postDrawMemCmd,
594 RenderStagePass renderStagePass,
595 const CameraSnapshot& cameraSnapshot,
596 bool refreshData )
597 {
598 VegetationInstance* instance = nullptr;
599 {
600 SharedLock<SharedMutex> w_lock( _instanceLock );
601 for ( const auto& [id, vegInstance] : _instances )
602 {
603 if ( id == sgn->dataFlag() )
604 {
605 instance = vegInstance;
606 break;
607 }
608 }
609 }
610 if ( instance == nullptr )
611 {
612 return;
613 }
614
615 pkg.pushConstantsCmd()._uniformData->set( _ID( "dvd_terrainChunkOffset" ), PushConstantType::UINT, sgn->dataFlag() );
616
617 Handle<GFX::CommandBuffer> cmdBuffer = GetCommandBuffer( pkg );
618 DIVIDE_ASSERT(cmdBuffer != INVALID_HANDLE<GFX::CommandBuffer>);
619
620 GFX::CommandBuffer& bufferInOut = *GFX::Get(cmdBuffer);
621 bufferInOut.clear();
622
623 if ( _grassData || _treeData )
624 {
625 auto cmd = GFX::EnqueueCommand<GFX::BindShaderResourcesCommand>( bufferInOut );
626 cmd->_usage = DescriptorSetUsage::PER_PASS;
627 if ( _treeData )
628 {
629 DescriptorSetBinding& binding = AddBinding( cmd->_set, 5u, ShaderStageVisibility::ALL );
630 Set( binding._data, _treeData.get(), { 0u, _treeData->getPrimitiveCount() } );
631 }
632 if ( _grassData )
633 {
634 DescriptorSetBinding& binding = AddBinding( cmd->_set, 6u, ShaderStageVisibility::ALL );
635 Set( binding._data, _grassData.get(), { 0u, _grassData->getPrimitiveCount() } );
636 }
637 }
638
639 // Culling lags one full frame
640 if ( renderState().drawState( renderStagePass ) &&
641 refreshData &&
642 (instance->_instanceCountGrass > 0 || instance->_instanceCountTrees > 0) )
643 {
644 const RenderTargetID hiZSourceTarget = renderStagePass._stage == RenderStage::REFLECTION
647
648 const RenderTarget* hizTarget = sgn->context().gfx().renderTargetPool().getRenderTarget( hiZSourceTarget );
649 const RTAttachment* hizAttachment = hizTarget->getAttachment( RTAttachmentType::COLOUR );
650
651 if ( hizAttachment != nullptr )
652 {
653 ResourcePtr<Texture> hizTexture = Get(hizAttachment->texture());
654
655 mat4<F32> viewProjectionMatrix;
656 mat4<F32>::Multiply( cameraSnapshot._projectionMatrix, cameraSnapshot._viewMatrix, viewProjectionMatrix );
657
658
659 GFX::SendPushConstantsCommand cullConstantsCmd{};
660 UniformData* uniforms = cullConstantsCmd._uniformData;
661 uniforms->set( _ID( "dvd_viewSize" ), PushConstantType::VEC2, vec2<F32>( hizTexture->width(), hizTexture->height() ) );
662 uniforms->set( _ID( "dvd_cameraPosition" ), PushConstantType::VEC3, cameraSnapshot._eye );
663 uniforms->set( _ID( "dvd_frustumPlanes" ), PushConstantType::VEC4, cameraSnapshot._frustumPlanes );
664 uniforms->set( _ID( "dvd_grassVisibilityDistance" ), PushConstantType::FLOAT, _grassDistance );
665 uniforms->set( _ID( "dvd_treeVisibilityDistance" ), PushConstantType::FLOAT, _treeDistance );
666 uniforms->set( _ID( "dvd_treeExtents" ), PushConstantType::VEC4, _treeExtents );
667 uniforms->set( _ID( "dvd_grassExtents" ), PushConstantType::VEC4, _grassExtents );
668 uniforms->set( _ID( "dvd_terrainChunkOffset" ), PushConstantType::UINT, sgn->dataFlag() );
669
670 PushConstantsStruct& fastConstants = cullConstantsCmd._fastData;
671 fastConstants.data[0] = viewProjectionMatrix;
672 fastConstants.data[1] = cameraSnapshot._viewMatrix;
673
674 GFX::EnqueueCommand<GFX::BeginDebugScopeCommand>( bufferInOut)->_scopeName = "Occlusion Cull Vegetation";
675
676 {
677 auto cmd = GFX::EnqueueCommand<GFX::BindShaderResourcesCommand>( bufferInOut );
678 cmd->_usage = DescriptorSetUsage::PER_DRAW;
679 DescriptorSetBinding& binding = AddBinding( cmd->_set, 0u, ShaderStageVisibility::COMPUTE );
680 Set( binding._data, hizTexture->getView(), hizAttachment->_descriptor._sampler );
681 }
682
683 GFX::DispatchComputeCommand computeCmd = {};
684
685 auto memCmd = GFX::EnqueueCommand<GFX::MemoryBarrierCommand>( bufferInOut ); // GPU to GPU command needed BEFORE draw (so ignore postDrawMemCmd)
686 if ( instance->_instanceCountGrass > 0 )
687 {
688 computeCmd._computeGroupSize.set( (instance->_instanceCountGrass + WORK_GROUP_SIZE - 1) / WORK_GROUP_SIZE, 1, 1 );
689
690 //Cull grass
691 GFX::EnqueueCommand<GFX::BindPipelineCommand>( bufferInOut )->_pipeline = _cullPipelineGrass;
692 GFX::EnqueueCommand( bufferInOut, cullConstantsCmd );
693 GFX::EnqueueCommand( bufferInOut, computeCmd );
694 memCmd->_bufferLocks.emplace_back( BufferLock
695 {
696 ._range = { 0u, U32_MAX },
698 ._buffer = _grassData->getBufferImpl()
699 });
700 }
701 if ( instance->_instanceCountTrees > 0 )
702 {
703 computeCmd._computeGroupSize.set( (instance->_instanceCountTrees + WORK_GROUP_SIZE - 1) / WORK_GROUP_SIZE, 1, 1 );
704 // Cull trees
705 GFX::EnqueueCommand<GFX::BindPipelineCommand>( bufferInOut )->_pipeline = _cullPipelineTrees;
706 GFX::EnqueueCommand( bufferInOut, cullConstantsCmd );
707 GFX::EnqueueCommand( bufferInOut, computeCmd );
708
709 memCmd->_bufferLocks.emplace_back( BufferLock
710 {
711 ._range = { 0u, U32_MAX },
713 ._buffer = _treeData->getBufferImpl()
714 });
715 }
716
717 GFX::EnqueueCommand<GFX::EndDebugScopeCommand>( bufferInOut );
718 }
719 }
720
721 SceneNode::prepareRender( sgn, rComp, pkg, postDrawMemCmd, renderStagePass, cameraSnapshot, refreshData );
722 }
723
724 void Vegetation::sceneUpdate( const U64 deltaTimeUS,
725 SceneGraphNode* sgn,
726 SceneState& sceneState )
727 {
729
730 if ( !renderState().drawState() )
731 {
732 prepareDraw( sgn );
733 sgn->get<RenderingComponent>()->dataFlag( to_F32( sgn->dataFlag() ) );
734 }
735 else
736 {
737 assert( getState() == ResourceState::RES_LOADED );
738 // Query shadow state every "_stateRefreshInterval" microseconds
739 if ( _stateRefreshIntervalBufferUS >= _stateRefreshIntervalUS )
740 {
741 _windX = sceneState.windDirX();
742 _windZ = sceneState.windDirZ();
743 _windS = sceneState.windSpeed();
744 _stateRefreshIntervalBufferUS -= _stateRefreshIntervalUS;
745 }
746 _stateRefreshIntervalBufferUS += deltaTimeUS;
747
748 const SceneRenderState& renderState = sceneState.renderState();
749
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 ) )
754 {
755 _grassDistance = sceneGrassDistance;
756 sgn->get<RenderingComponent>()->setMaxRenderRange( _grassDistance );
757 }
758 if ( !COMPARE( sceneTreeDistance, _treeDistance ) )
759 {
760 _treeDistance = sceneTreeDistance;
761 if ( _treeParentNode != nullptr )
762 {
763 const SceneGraphNode::ChildContainer& children = _treeParentNode->getChildren();
764 const U32 childCount = children._count;
765 for ( U32 i = 0u; i < childCount; ++i )
766 {
767 RenderingComponent* rComp = children._data[i]->get<RenderingComponent>();
768 rComp->setMaxRenderRange( sceneTreeDistance );
769 }
770 }
771 }
772 }
773
774 SceneNode::sceneUpdate( deltaTimeUS, sgn, sceneState );
775 }
776
777 void Vegetation::buildDrawCommands( SceneGraphNode* sgn, GenericDrawCommandContainer& cmdsOut )
778 {
779 const U16 partitionID = _lodPartitions[0];
780
781 VegetationInstance* instance = nullptr;
782 {
783 SharedLock<SharedMutex> w_lock( _instanceLock );
784 for ( const auto&[id, vegInstance] : _instances )
785 {
786 if (id == sgn->dataFlag())
787 {
788 instance = vegInstance;
789 break;
790 }
791 }
792 }
793 if (instance == nullptr)
794 {
795 return;
796 }
797
798 GenericDrawCommand& cmd = cmdsOut.emplace_back();
800
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 ) );
805
806 RenderingComponent* rComp = sgn->get<RenderingComponent>();
807 U16 prevID = 0;
808 for ( U8 i = 0; i < to_U8( _lodPartitions.size() ); ++i )
809 {
810 U16 id = _lodPartitions[i];
811 if ( id == U16_MAX )
812 {
813 assert( i > 0 );
814 id = prevID;
815 }
816 rComp->setLoDIndexOffset( i, _buffer->getPartitionOffset( id ), _buffer->getPartitionIndexCount( id ) );
817 prevID = id;
818 }
819
820 SceneNode::buildDrawCommands( sgn, cmdsOut );
821 }
822
823 namespace
824 {
825 FORCE_INLINE U8 BestIndex( const UColour4& in ) noexcept
826 {
827 U8 maxValue = 0;
828 U8 bestIndex = 0;
829 for ( U8 i = 0; i < 4; ++i )
830 {
831 if ( in[i] > maxValue )
832 {
833 maxValue = in[i];
834 bestIndex = i;
835 }
836 }
837
838 return bestIndex;
839 }
840
841 FORCE_INLINE bool ScaleAndCheckBounds( const vec2<F32> chunkPos, const vec2<F32> chunkSize, vec2<F32>& point ) noexcept
842 {
843 if ( point.x > -chunkSize.x && point.x < chunkSize.x &&
844 point.y > -chunkSize.y && point.y < chunkSize.y )
845 {
846 // [-chunkSize * 0.5f, chunkSize * 0.5f] to [0, chunkSize]
847 point = (point + chunkSize) * 0.5f;
848 point += chunkPos;
849 return true;
850 }
851
852 return false;
853 }
854 };
855
856 void Vegetation::registerInstance( const U32 chunkID, VegetationInstance* instance )
857 {
858 UniqueLock<SharedMutex> w_lock(_instanceLock);
859 for ( auto&[id, vegInstance] : _instances)
860 {
861 if ( id == chunkID)
862 {
863 vegInstance = instance;
864 return;
865 }
866 }
867
868 _instances.emplace_back(chunkID, instance);
869 }
870
871 void Vegetation::unregisterInstance( const U32 chunkID )
872 {
873 UniqueLock<SharedMutex> w_lock( _instanceLock );
874 dvd_erase_if(_instances, [chunkID]( const auto& entry ){ return entry.first == chunkID; });
875 }
876
878 : PlatformContextComponent(context)
879 , _parent( parent )
880 , _chunk( chunk )
881 {
882 Get(_parent)->registerInstance( _chunk->id(), this);
883 }
884
886 {
887 Get( _parent )->unregisterInstance( _chunk->id() );
888 }
889
891 {
893
894 if ( context().config().debug.renderFilter.grassInstances )
895 {
897 _instanceCountGrass = to_U32(data.size());
898
899 const BufferLock lock = parentPtr->_grassData->writeData(BufferRange{._startOffset = _chunk->id() * parentPtr->_maxGrassInstances, ._length = _instanceCountGrass}, data.data());
900 DIVIDE_UNUSED(lock);
901 }
902
903 if ( context().config().debug.renderFilter.treeInstances )
904 {
906 _instanceCountTrees = to_U32(data.size());
907 const BufferLock lock = parentPtr->_treeData->writeData( BufferRange{ ._startOffset = _chunk->id() * parentPtr->_maxTreeInstances, ._length = _instanceCountTrees }, data.data() );
908 DIVIDE_UNUSED( lock );
909 }
910 }
911
913 {
914 const U32 ID = _chunk->id();
915
916 const string cacheFileName = Util::StringFormat( "{}_{}_{}_{}.cache", _chunk->parent().resourceName().c_str(), Get(_parent)->resourceName().c_str(), treeData ? "trees" : "grass", ID );
917 Console::printfn( Locale::Get( treeData ? _ID( "CREATE_TREE_START" ) : _ID( "CREATE_GRASS_BEGIN" ) ), ID );
918
919 vector<VegetationData> container;
920
921 ByteBuffer chunkCache;
922 if ( context().config().debug.cache.enabled &&
923 context().config().debug.cache.vegetation &&
924 chunkCache.loadFromFile( Paths::g_terrainCacheLocation, cacheFileName ) )
925 {
926 auto tempVer = decltype(BYTE_BUFFER_VERSION){0};
927 chunkCache >> tempVer;
928 if ( tempVer == BYTE_BUFFER_VERSION )
929 {
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() );
934 }
935 else
936 {
937 chunkCache.clear();
938 }
939 }
940 else
941 {
943 const VegetationDescriptor& descriptor = parent->_descriptor;
944
945 const size_t maxInstances = treeData ? parent->_maxTreeInstances : parent->_maxGrassInstances;
946
947 container.reserve( maxInstances);
948
949 static std::discrete_distribution<> distribution[] =
950 {
951 {5, 2, 2, 1},
952 {1, 5, 2, 2},
953 {2, 1, 5, 2},
954 {2, 2, 1, 5}
955 };
956
957 std::default_random_engine generator( to_U32( std::chrono::system_clock::now().time_since_epoch().count() ) );
958
959 const U32 meshID = to_U32( ID % parent->_treeMeshNames.size() );
960
961 const vec2<F32> chunkSize = _chunk->getOffsetAndSize().zw;
962 const vec2<F32> chunkPos = _chunk->getOffsetAndSize().xy;
963 //const F32 waterLevel = 0.0f;// ToDo: make this dynamic! (cull underwater points later on?)
964 const auto& map = treeData ? descriptor.treeMap : descriptor.grassMap;
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;
968 const auto& scales = treeData ? descriptor.treeScales : descriptor.grassScales;
969 const F32 slopeLimit = treeData ? g_slopeLimitTrees : g_slopeLimitGrass;
970
971 const Terrain& terrain = _chunk->parent();
972
973 for ( vec2<F32> pos : positions )
974 {
975 if ( !ScaleAndCheckBounds( chunkPos, chunkSize, pos ) )
976 {
977 continue;
978 }
979
980 const vec2<F32> mapCoord( pos.x + mapWidth * 0.5f, pos.y + mapHeight * 0.5f );
981
982 const F32 x_fac = mapCoord.x / mapWidth;
983 const F32 y_fac = mapCoord.y / mapHeight;
984
985 const Terrain::Vert vert = terrain.getVert( x_fac, y_fac, true );
986
987 // terrain slope should be taken into account
988 const F32 dot = Dot( vert._normal, WORLD_Y_AXIS );
989 //const F32 lengthSq1 = vert._normal.lengthSquared(); = 1.0f (normalised)
990 //const F32 lengthSq2 = WORLD_Y_AXIS.lengthSquared(); = 1.0f (normalised)
991 //const F32 length = Divide::Sqrt(lengthSq1 * lengthSq2); = 1.0f
992 constexpr F32 length = 1.0f;
993 const Angle::DEGREES<F32> angle = Angle::to_DEGREES( std::acos( dot / length ) );
994 if ( angle > slopeLimit )
995 {
996 continue;
997 }
998 const F32 slopeScaleFactor = 1.f - MAP( angle, 0.f, slopeLimit, 0.f, 0.9f );
999
1000 assert( vert._position != VECTOR3_ZERO );
1001
1002 const UColour4 colour = map->getColour( to_I32( mapCoord.x ), to_I32( mapCoord.y ) );
1003 const U8 index = BestIndex( colour );
1004 const F32 colourVal = colour[index];
1005 if ( colourVal <= EPSILON_F32 )
1006 {
1007 continue;
1008 }
1009
1010 const U8 arrayLayer = to_U8( distribution[index]( generator ) );
1011
1012 const F32 xmlScale = scales[treeData ? meshID : index];
1013 // Don't go under 75% of the scale specified in the data files
1014 const F32 minXmlScale = xmlScale * 7.5f / 10.0f;
1015 // Don't go over 75% of the scale specified in the data files
1016 const F32 maxXmlScale = xmlScale * 1.25f;
1017
1018 const F32 scale = CLAMPED( (colourVal + 1.f) / 256.0f * xmlScale, minXmlScale, maxXmlScale ) * slopeScaleFactor;
1019
1020 assert( scale > EPSILON_F32 );
1021
1022 //vert._position.y = (((0.0f*heightExtent) + vert._position.y) - ((0.0f*scale) + vert._position.y)) + vert._position.y;
1023 VegetationData entry = {};
1024 entry._positionAndScale.set( vert._position, scale );
1025 Quaternion<F32> modelRotation;
1026 if ( treeData )
1027 {
1028 modelRotation.fromEuler( descriptor.treeRotations[meshID] );
1029 }
1030 else
1031 {
1032 modelRotation = RotationFromVToU( WORLD_Y_AXIS, vert._normal, WORLD_Z_NEG_AXIS );
1033 }
1034
1035 entry._orientationQuat = (Quaternion<F32>( vert._normal, Random( 360.0f ) ) * modelRotation).asVec4();
1036 entry._data = {
1037 to_F32( arrayLayer ),
1038 to_F32( ID ),
1039 1.0f,
1040 1.0f
1041 };
1042
1043 container.push_back( entry );
1044 }
1045
1046 container.shrink_to_fit();
1047 chunkCache << BYTE_BUFFER_VERSION;
1048 chunkCache << container.size();
1049 chunkCache.append( container.data(), container.size() );
1050 if ( !chunkCache.dumpToFile(Paths::g_terrainCacheLocation, cacheFileName ) )
1051 {
1053 }
1054 }
1055
1056 Console::printfn( LOCALE_STR( "CREATE_GRASS_END" ) );
1057
1058 return container;
1059 }
1060
1061}
#define WAIT_FOR_CONDITION_TIMEOUT(...)
#define WAIT_FOR_CONDITION(...)
#define LOCALE_STR(X)
Definition: Localization.h:91
#define DIVIDE_UNUSED(X)
#define MOV(...)
#define DIVIDE_ASSERT(...)
#define DIVIDE_UNEXPECTED_CALL()
#define FORCE_INLINE
#define PROFILE_SCOPE_AUTO(CATEGORY)
Definition: Profiler.h:87
void append(const Byte *src, size_t cnt)
Appends 'cnt' bytes from 'src' to the buffer.
Definition: ByteBuffer.cpp:33
void read(T &out)
Reads sizeof(T) data from the buffer and returns it. Reading moves the read head forward!
Definition: ByteBuffer.inl:239
bool loadFromFile(const ResourcePath &path, std::string_view fileName, const U8 version=BUFFER_FORMAT_VERSION)
Definition: ByteBuffer.cpp:57
void clear() noexcept
Resets the entire storage and the read and write positions.
Definition: ByteBuffer.cpp:28
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.
Definition: ByteBuffer.cpp:47
void setState(ResourceState currentState) final
Definition: Resource.cpp:70
PlatformContext & context() noexcept
void fromEuler(const vec3< Angle::DEGREES< T > > &v) noexcept
Definition: Quaternion.inl:316
bool unload() override
Definition: SceneNode.cpp:108
virtual void sceneUpdate(U64 deltaTimeUS, SceneGraphNode *sgn, SceneState &sceneState)
Called from SceneGraph "sceneUpdate".
Definition: SceneNode.cpp:44
virtual void prepareRender(SceneGraphNode *sgn, RenderingComponent &rComp, RenderPackage &pkg, GFX::MemoryBarrierCommand &postDrawMemCmd, RenderStagePass renderStagePass, const CameraSnapshot &cameraSnapshot, bool refreshData)
Definition: SceneNode.cpp:50
bool load(PlatformContext &context) override
Loading and unloading interface.
Definition: SceneNode.cpp:98
virtual void buildDrawCommands(SceneGraphNode *sgn, GenericDrawCommandContainer &cmdsOut)
Definition: SceneNode.cpp:115
vec4< F32 > getOffsetAndSize() const noexcept
Definition: TerrainChunk.h:67
const Terrain & parent() const noexcept
Definition: TerrainChunk.h:71
VegetationInstance(PlatformContext &context, Handle< Vegetation > parent, TerrainChunk *chunk)
Definition: Vegetation.cpp:877
const Handle< Vegetation > _parent
Definition: Vegetation.h:185
const TerrainChunk * _chunk
Definition: Vegetation.h:186
static mat4< T > Multiply(const mat4< T > &matrixA, const mat4< T > &matrixB) noexcept
ret = A * B
vec2< T > xy
Definition: MathVectors.h:1366
vec2< T > zw
Definition: MathVectors.h:1366
constexpr DEGREES< T > to_DEGREES(RADIANS< T > angle) noexcept
Definition: MathHelper.inl:380
constexpr U8 MAX_CSM_SPLITS_PER_LIGHT
Used for CSM or PSSM to determine the maximum number of frustum splits.
Definition: config.h:159
FColour4 WHITE
Random stuff added for convenience.
Definition: Colours.cpp:8
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
Definition: Profiler.h:66
constexpr T Milliseconds(T a)
Definition: MathHelper.inl:653
Str StringFormat(const char *fmt, Args &&...args)
bool IntersectCircles(const Circle &cA, const Circle &cB, vec2< F32 > *pointsOut) noexcept
Definition: MathHelper.cpp:150
FORCE_INLINE bool ScaleAndCheckBounds(const vec2< F32 > chunkPos, const vec2< F32 > chunkSize, vec2< F32 > &point) noexcept
Definition: Vegetation.cpp:841
FORCE_INLINE U8 BestIndex(const UColour4 &in) noexcept
Definition: Vegetation.cpp:825
Handle console commands that start with a forward slash.
Definition: AIProcessor.cpp:7
constexpr SceneNodeType GetSceneNodeType()
Definition: SceneNodeFwd.h:111
static constexpr bool IsDepthPass(const RenderStagePass stagePass) noexcept
std::byte Byte
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
Definition: MathHelper.inl:156
static const vec3< F32 > WORLD_X_AXIS
Definition: MathVectors.h:1439
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!...
Definition: Quaternion.inl:669
T * ResourcePtr
Definition: Resource.h:112
uint8_t U8
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.
int16_t I16
void toggleOption(GenericDrawCommand &cmd, CmdRenderOptions option) noexcept
constexpr F32 to_F32(const T value)
T Random()
Definition: MathHelper.inl:95
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
Definition: Vector.h:42
constexpr U16 U16_MAX
static const vec3< F32 > VECTOR3_UNIT
Definition: MathVectors.h:1437
Project & parent
Definition: DefaultScene.h:41
uint16_t U16
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
Definition: MathHelper.inl:141
constexpr U8 to_U8(const T value)
::value constexpr T CLAMPED(T n, T min, T max) noexcept
Definition: MathHelper.inl:126
static const vec3< F32 > WORLD_Z_NEG_AXIS
Definition: MathVectors.h:1444
constexpr U32 U32_MAX
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
Definition: Quaternion.inl:719
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
Definition: MathVectors.h:1434
U32 RenderTargetID
bool dvd_erase_if(eastl::vector< T, A > &vec, Predicate &&pred)
Definition: Vector.h:109
FORCE_INLINE T * Get(const Handle< T > handle)
uint32_t U32
Project const SceneEntry & entry
Definition: DefaultScene.h:41
uint64_t U64
static const vec3< F32 > WORLD_Y_AXIS
Definition: MathVectors.h:1440
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
Definition: GFXDevice.h:204
static RenderTargetID HI_Z_REFLECT
Definition: GFXDevice.h:205
PropertyDescriptor< T > _propertyDescriptor
Definition: Resource.h:151