void VegetationSpawnManager::Render( Metal::DeviceContext& context, LightingParserContext& parserContext, unsigned techniqueIndex, RenderCore::Assets::DelayStep delayStep) { if (_pimpl->_cfg._objectTypes.empty()) return; _pimpl->FillInDrawCallSets(); auto& sharedStates = _pimpl->_modelCache->GetSharedStateSet(); auto captureMarker = sharedStates.CaptureState( context, parserContext.GetStateSetResolver(), parserContext.GetStateSetEnvironment()); auto& state = parserContext.GetTechniqueContext()._runtimeState; state.SetParameter(u("SPAWNED_INSTANCE"), 1); auto cleanup = MakeAutoCleanup( [&state]() { state.SetParameter(u("SPAWNED_INSTANCE"), 0); }); auto& resources = *_pimpl->_resources; for (unsigned b=0; b<unsigned(_pimpl->_drawCallSets.size()); ++b) ModelRenderer::RenderPrepared( RenderCore::Assets::ModelRendererContext(context, parserContext, techniqueIndex), sharedStates, _pimpl->_drawCallSets[b], delayStep, [&context, b, &resources](ModelRenderer::DrawCallEvent evnt) { VegetationSpawn_DrawInstances( context, resources, b, evnt._indexCount, evnt._firstIndex, evnt._firstVertex); }); }
void BasicSceneParser::Model::RenderOpaque( RenderCore::Metal::DeviceContext* context, LightingParserContext& parserContext, unsigned techniqueIndex) { // This class shows a simple method for rendering an object // using the ModelRenderer class. // // There are a few steps involved. The ModelRenderer/ModelScaffold // classes are very flexible and efficient. But the flexibility // brings with it a little bit of complexity. // // Basically, I think of a "model" is an discrete single exporter from Max // (or Maya, etc). // It might come via a Collada file or some intermediate format. But at // run-time we want some highly optimized format that can be loaded quickly, // and that also allows for a certain amount of flexibility while rendering. // // There are 3 important classes: // ModelRenderer is the class that can actually render a model file. It's // mostly a static class, however. Once it's constructed, we can't change // it (other than by setting animation parameters). ModelRenderer manages // the low-level graphics API objects required for rendering. // // ModelScaffold is a light weight representation of what it contained in // the model file. It never uses the low level graphics API, and we can't // render from it directly. // // SharedStateSet contains state related information (from the low level // graphics API) for rendering one or more models. Normally we want multiple // models to share the same SharedStateSet. When multiple models use the // same SharedStateSet, we can reorder rendering operations between all those // models. But we can choose the granularity at which this occurs. // So, for example if we have a city made from many models, we can created a // single SharedStateSet for that city. That would means we can perform // draw command sorting on the city as a whole (rather than just within a // single model) // // First, we need to load the ModelScaffold object. We have to load this before // we can construct the ModelRenderer. using namespace RenderCore::Assets; if (!_modelRenderer) { // We're going to use the Assets::GetAssetComp<> function to initialize // our ModelScaffold. These are other ways to create a ModelScaffold, // though (eg, Assets::GetAsset<>, or just using the constructor directly) // // In this case, we use GetAssetComp to cause the system to execute a // Collada compile when required. This will compile the input Collada // file into our run-time format in a file in the intermediate store. // // The compile can occur in a background thread. When this happens, // we will get thrown a Assets::Exceptions::PendingAsset exception // until the compile is finished. We aware that some assets that are // compiled or loaded in the background can throw PendingAsset when // they are not ready! // // We can also get a Assets::Exceptions::InvalidAsset if asset can // never be correctly loaded (eg, missing file or something) const char sampleAsset[] = "game/model/galleon/galleon.dae"; const char sampleMaterial[] = "game/model/galleon/galleon.material"; auto& scaffold = Assets::GetAssetComp<ModelScaffold>(sampleAsset); auto& matScaffold = Assets::GetAssetComp<MaterialScaffold>(sampleMaterial, sampleAsset); // We want to create a Assets::DirectorySearchRules object before we // make the ModelRenderer. This is used when we need to find the // dependent assets (like textures). In this case, we just need to // add the directory that contains the dae file as a search path // (though if we needed, we could add other paths as well). auto searchRules = Assets::DefaultDirectorySearchRules(sampleAsset); // Now, finally we can construct the model render. // // Each model renderer is associated with a single level of detail // (though the ModelScaffold could contain information for multiple // levels of detail) // // During the constructor of ModelRenderer, all of the low level // graphics API resources will be constructed. So it can be expensive // in some cases. // // Also note that if we get an allocation failure while making a // low level resource (like a vertex buffer), it will throw an // exception. const unsigned levelOfDetail = 0; _modelRenderer = std::unique_ptr<ModelRenderer>( new ModelRenderer( scaffold, matScaffold, ModelRenderer::Supplements(), *_sharedStateSet, &searchRules, levelOfDetail)); } // Before using SharedStateSet for the first time, we need to capture the device // context state. If we were rendering multiple models with the same shared state, we would // capture once and render multiple times with the same capture. auto captureMarker = _sharedStateSet->CaptureState(*context, parserContext.GetStateSetResolver(), parserContext.GetStateSetEnvironment()); // Finally, we can render the object! _modelRenderer->Render( RenderCore::Assets::ModelRendererContext(*context, parserContext, techniqueIndex), *_sharedStateSet, Identity<Float4x4>()); }