22 const std::array<float, 3>& direction)
24 using Vector = std::array<float, 3>;
26 {
return a[0]*
b[0] +
a[1]*
b[1] +
a[2]*
b[2]; };
28 {
return {
a[1]*
b[2]-
a[2]*
b[1],
a[2]*
b[0]-
a[0]*
b[2],
a[0]*
b[1]-
a[1]*
b[0]}; };
31 if (!std::isfinite(
dot(direction,direction)) ||
dot(direction,direction) < 0.000001F)
41 for (
const auto id :
slot.visibleOrder())
43 const auto*
object =
slot.find(
id);
46 const auto mesh =
object->renderData ?
object->renderData :
object->asset->renderData;
48 const auto&
m =
object->worldTransform.values;
49 for (
int corner = 0; corner < 8; ++corner)
53 local[
axis] = (corner & (1 <<
axis)) ? mesh->bounds.maximum[
axis] : mesh->bounds.minimum[
axis];
54 const Vector world{local[0]*
m[0]+local[1]*
m[4]+local[2]*
m[8]+
m[12],
55 local[0]*
m[1]+local[1]*
m[5]+local[2]*
m[9]+
m[13],
56 local[0]*
m[2]+local[1]*
m[6]+local[2]*
m[10]+
m[14]};
63 if (!
found)
return std::nullopt;
67 if (!std::isfinite(
low[
axis]) || !std::isfinite(
high[
axis]))
return std::nullopt;
80 result.values[14]=-
low[2]/
span[2];
92 return std::isfinite(viewport.x) && std::isfinite(viewport.y) &&
93 std::isfinite(viewport.w) && std::isfinite(viewport.h) &&
94 viewport.x >= 0.0F && viewport.y >= 0.0F &&
95 viewport.w > 0.0F && viewport.h > 0.0F &&
96 viewport.x + viewport.w <=
static_cast<float>(
width) &&
97 viewport.y + viewport.h <=
static_cast<float>(
height) &&
98 viewport.min_depth >= 0.0F && viewport.max_depth <= 1.0F &&
99 viewport.min_depth <= viewport.max_depth;
105 World3DRendererError result;
112 std::expected<SDL_GPUTexture*, World3DRendererError>
128 "could not create 1x1 white World3D fallback texture"));
141 "could not create fallback texture upload buffer"));
151 "could not map fallback texture upload buffer"));
153 std::memset(
mapped, 0, 256U);
159 if (command ==
nullptr)
165 "could not acquire fallback texture upload command buffer"));
175 "could not begin fallback texture upload copy pass"));
180 source.pixels_per_row = 64U;
181 source.rows_per_layer = 1U;
196 "could not submit fallback texture upload"));
210 *
this = std::move(
other);
215 if (
this == &
other)
return *
this;
217 device_ = std::exchange(
other.device_,
nullptr);
218 pipeline_ = std::move(
other.pipeline_);
219 modernPipeline_ = std::move(
other.modernPipeline_);
220 modernPipelineAttempted_ = std::exchange(
other.modernPipelineAttempted_,
false);
221 shaderDirectory_ = std::move(
other.shaderDirectory_);
223 meshCache_ = std::move(
other.meshCache_);
224 studioEnvironment_ = std::move(
other.studioEnvironment_);
225 studioEnvironmentEnabled_ = std::exchange(
other.studioEnvironmentEnabled_,
false);
226 textureSampler_ = std::exchange(
other.textureSampler_,
nullptr);
227 linearSampler_ = std::exchange(
other.linearSampler_,
nullptr);
228 bilinearFiltering_ = std::exchange(
other.bilinearFiltering_,
false);
229 whiteTexture_ = std::exchange(
other.whiteTexture_,
nullptr);
230 depthTarget_ = std::exchange(
other.depthTarget_,
nullptr);
231 depthWidth_ = std::exchange(
other.depthWidth_, 0
U);
232 depthHeight_ = std::exchange(
other.depthHeight_, 0
U);
233 lighting_ =
other.lighting_;
234 other.lighting_ = {};
235 modernPresentation_ = std::exchange(
other.modernPresentation_,
false);
236 presentationShadows_ = std::exchange(
other.presentationShadows_,
false);
237 presentationAntialiasing_ = std::exchange(
other.presentationAntialiasing_,
false);
238 shadowsActive_ = std::exchange(
other.shadowsActive_,
false);
239 antialiasingActive_ = std::exchange(
other.antialiasingActive_,
false);
241 multisampleColor_ = std::exchange(
other.multisampleColor_,
nullptr);
242 multisampleWidth_ = std::exchange(
other.multisampleWidth_, 0
U);
243 multisampleHeight_ = std::exchange(
other.multisampleHeight_, 0
U);
244 multisampleLegacyPipeline_ = std::move(
other.multisampleLegacyPipeline_);
245 multisampleModernPipeline_ = std::move(
other.multisampleModernPipeline_);
246 shadowMap_ = std::exchange(
other.shadowMap_,
nullptr);
247 shadowDepth_ = std::exchange(
other.shadowDepth_,
nullptr);
248 shadowSampler_ = std::exchange(
other.shadowSampler_,
nullptr);
249 shadowMapPipeline_ = std::move(
other.shadowMapPipeline_);
253 void World3DRenderer::releaseDepthTarget()
noexcept
255 if (device_ && depthTarget_)
257 depthTarget_ =
nullptr;
263 void World3DRenderer::releasePresentationResources()
noexcept
269 multisampleColor_ = shadowMap_ = shadowDepth_ =
nullptr;
270 shadowSampler_ =
nullptr;
271 multisampleWidth_ = multisampleHeight_ = 0
U;
272 multisampleLegacyPipeline_.reset();
273 multisampleModernPipeline_.reset();
274 shadowMapPipeline_.reset();
277 bool World3DRenderer::ensureShadowResources()
279 if (shadowMap_ && shadowDepth_ && shadowMapPipeline_ && shadowSampler_)
return true;
283 if (!shadowMapPipeline_)
287 shadowMapPipeline_ = std::move(*
pipeline);
292 info.layer_count_or_depth = info.num_levels = 1U;
302 info.format = shadowMapPipeline_->depthFormat();
314 return shadowMap_ && shadowDepth_ && shadowSampler_;
317 bool World3DRenderer::ensurePresentationTargets(std::uint32_t
width, std::uint32_t
height)
322 if (!multisampleLegacyPipeline_)
326 multisampleLegacyPipeline_ = std::move(*
pipeline);
328 if (!multisampleModernPipeline_)
332 multisampleModernPipeline_ = std::move(*
pipeline);
334 if (multisampleColor_ && multisampleWidth_ ==
width && multisampleHeight_ ==
height)
return true;
337 info.format = colorFormat_;
340 info.layer_count_or_depth = info.num_levels = 1U;
343 if (!replacement)
return false;
345 multisampleColor_ = replacement;
346 multisampleWidth_ =
width; multisampleHeight_ =
height;
350 void World3DRenderer::releaseSamplingResources()
noexcept
352 if (device_ && whiteTexture_)
354 if (device_ && textureSampler_)
356 if (device_ && linearSampler_)
358 whiteTexture_ =
nullptr;
359 textureSampler_ =
nullptr;
360 linearSampler_ =
nullptr;
365 releaseDepthTarget();
366 releasePresentationResources();
367 if (meshCache_) meshCache_->clear();
369 studioEnvironment_.
reset();
370 studioEnvironmentEnabled_ =
false;
371 releaseSamplingResources();
373 modernPipeline_.reset();
374 modernPipelineAttempted_ =
false;
375 shaderDirectory_.clear();
379 bilinearFiltering_ =
false;
380 modernPresentation_ = presentationShadows_ = presentationAntialiasing_ =
false;
381 shadowsActive_ = antialiasingActive_ =
false;
384 bool World3DRenderer::ensureDepthTarget(
387 if (depthTarget_ && depthWidth_ ==
width && depthHeight_ ==
height && depthSamples_ ==
samples)
392 info.format = pipeline_.depthFormat();
396 info.layer_count_or_depth = 1U;
397 info.num_levels = 1U;
400 if (!replacement)
return false;
402 releaseDepthTarget();
403 depthTarget_ = replacement;
419 "missing SDL GPU device", {}, {}});
436 environment.error().detail, {}, {}});
443 std::expected<StudioEnvironmentGPU, StudioEnvironmentError>{std::unexpected(
data.error())};
450 "Optional studio environment disabled: %s",
uploaded.error().detail.c_str());
464 if (sampler ==
nullptr)
469 "could not create retail-compatible World3D point sampler"));
481 "could not create World3D bilinear sampler"));
484 result.device_ = device;
486 result.textureSampler_ = sampler;
488 result.studioEnvironment_ = std::move(*environment);
490 result.pipeline_ = std::move(*
pipeline);
493 result.meshCache_ = std::make_unique<MeshGPUCache>(device);
497 std::expected<World3DRenderStats, World3DRendererError>
509 "missing SDL GPU command buffer", {}, {}});
513 "missing World3D color target", {}, {}});
518 "invalid World3D target or viewport", {}, {}});
522 "World3D slot has no configured camera/view", {}, {}});
528 batches.error().detail, {}, batches.error()});
533 return !batch.legacyShadow &&
534 batch.material.model == data::MeshMaterialModel::MetallicRoughness;
538 modernPipelineAttempted_ =
true;
541 modernPipeline_ = std::move(*
modern);
544 "Modern PBR pipeline unavailable; drawing modern meshes with legacy diffuse: %s",
545 modern.error().detail.c_str());
548 shadowsActive_ = antialiasingActive_ =
false;
571 "could not begin presentation shadow pass"));
575 for (
const auto&
batch : *batches)
585 fragment.
mapFlags[0] =
batch.material.baseColorTexture ? 1.0F : 0.0F;
593 std::array<SDL_GPUTextureSamplerBinding,7> textures{};
594 for (
auto&
texture : textures)
texture = {whiteTexture_,linearSampler_};
595 if (
batch.modernTextures[0].texture) textures[0]=
batch.modernTextures[0];
596 textures[5]={studioEnvironment_.
texture(),studioEnvironment_.
sampler()};
605 const bool fullTarget = viewport.x == 0 && viewport.y == 0 &&
620 if (antialiasingActive_)
622 color.texture = multisampleColor_;
623 color.clear_color = {0.035F,0.04F,0.05F,1.0F};
631 depth.texture = depthTarget_;
632 depth.clear_depth = 1.0F;
649 stats.
batches = batches->size();
650 const auto& projection = *
slot.view();
652 for (
const auto&
batch : *batches)
654 const bool modern = modernPipeline_ && !
batch.legacyShadow &&
656 const auto&
legacyPipeline = antialiasingActive_ ? *multisampleLegacyPipeline_ : pipeline_;
657 const auto*
modernPipeline = antialiasingActive_ ? &*multisampleModernPipeline_ :
658 (modernPipeline_ ? &*modernPipeline_ :
nullptr);
671 batch.worldTransform, projection.view);
716 batch.material.emissive,
batch.material.emissiveStrength);
719 modernFragment.sceneAmbient[3] = studioEnvironmentEnabled_ ? 1.0F : 0.0F;
730 batch.material.baseColorTexture ? 1.0F : 0.0F,
731 batch.material.metallicRoughnessTexture ? 1.0F : 0.0F,
732 batch.material.normalTexture ? 1.0F : 0.0F,
733 batch.material.emissiveTexture ? 1.0F : 0.0F};
735 batch.material.occlusionTexture ? 1.0F : 0.0F,
736 batch.material.normalTexture ?
batch.material.normalTexture->scale : 1.0F,
737 batch.material.occlusionTexture ?
batch.material.occlusionTexture->scale : 1.0F,
738 batch.material.alphaCutoff};
741 1.0F/
static_cast<float>(
ShadowSize),0.00025F,modernPresentation_ ? 1.0F : 0.0F};
762 batch.gpuTexture !=
nullptr ?
batch.gpuTexture : whiteTexture_,
763 bilinearFiltering_ ? linearSampler_ : textureSampler_};
766 std::array<SDL_GPUTextureSamplerBinding, 7>
maps{};
767 std::copy(
batch.modernTextures.begin(),
batch.modernTextures.end(),
maps.begin());
768 for (std::size_t map = 0; map < 5U; ++map)
std::vector< std::uint8_t > data
std::atomic< float > right
std::shared_ptr< const data::HmdTextureImage > source
static std::expected< StudioEnvironmentGPU, StudioEnvironmentError > upload(SDL_GPUDevice *, const StudioEnvironmentData &)
SDL_GPUTexture * texture() const noexcept
static std::expected< StudioEnvironmentGPU, StudioEnvironmentError > blackFallback(SDL_GPUDevice *)
SDL_GPUSampler * sampler() const noexcept
SDL_GPUGraphicsPipeline * handle() const noexcept
SDL_GPUGraphicsPipeline * doubleSidedHandle() const noexcept
SDL_GPUTextureFormat depthFormat() const noexcept
static std::expected< World3DPipeline, World3DPipelineError > load(SDL_GPUDevice *device, const std::filesystem::path &shaderDirectory, SDL_GPUTextureFormat colorFormat, bool modernPBR=false, SDL_GPUSampleCount samples=SDL_GPU_SAMPLECOUNT_1)
World3DRenderer()=default
const World3DPipeline * modernPipeline() const noexcept
World3DRenderer & operator=(const World3DRenderer &)=delete
std::uint32_t presentationSampleCount() const noexcept
static std::expected< World3DRenderer, World3DRendererError > load(SDL_GPUDevice *device, const std::filesystem::path &shaderDirectory, SDL_GPUTextureFormat colorFormat, const std::filesystem::path &studioEnvironmentPath={})
const World3DPipeline & pipeline() const noexcept
std::expected< World3DRenderStats, World3DRendererError > render(SDL_GPUCommandBuffer *commandBuffer, SDL_GPUTexture *colorTarget, std::uint32_t targetWidth, std::uint32_t targetHeight, const SDL_GPUViewport &viewport, const SequenceWorld3DSlot &slot)
@ RenderPassCreationFailed
@ FallbackTextureUploadFailed
@ FallbackTextureCreationFailed
@ DepthTargetCreationFailed
std::expected< StudioEnvironmentData, StudioEnvironmentError > readStudioEnvironment(const std::filesystem::path &path)
std::expected< std::vector< World3DGPUIndexedBatch >, MeshGPUError > buildWorld3DGPUScene(const SequenceWorld3DSlot &slot, MeshGPUCache &cache)
Matrix3D identity3D() noexcept
Matrix2D multiply(const Matrix2D &left, const Matrix2D &right) noexcept
std::array< float, 3 > direction
std::array< float, 3 > color
World3DDirectionalLight sun
std::array< float, 3 > ambient
World3DDirectionalLight boardReflection
World3DSpotLight spotlight
std::size_t shadowBatches
std::size_t shadowTriangles
std::uint32_t sampleCount
std::array< float, 3 > attenuation
std::array< float, 3 > color
std::array< float, 3 > position
std::array< float, 3 > direction
std::array< float, 9 > values