diff options
| author | Ellie Hermaszewska <ellieh@nvidia.com> | 2024-10-29 14:49:26 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-29 14:49:26 +0800 |
| commit | f65d756bff8d4c5cbc15bd0322a2ae8e6b896a21 (patch) | |
| tree | ea1d61342cd29368e19135000ec2948813096205 /examples/model-viewer/main.cpp | |
| parent | a729c15e9dce9f5116a38afc66329ab2ca4cea54 (diff) | |
format
* format
* Minor test fixes
* enable checking cpp format in ci
Diffstat (limited to 'examples/model-viewer/main.cpp')
| -rw-r--r-- | examples/model-viewer/main.cpp | 605 |
1 files changed, 299 insertions, 306 deletions
diff --git a/examples/model-viewer/main.cpp b/examples/model-viewer/main.cpp index 7ca5eeb74..7faaf5569 100644 --- a/examples/model-viewer/main.cpp +++ b/examples/model-viewer/main.cpp @@ -16,21 +16,21 @@ // We still need to include the Slang header to use the Slang API // -#include "slang.h" #include "slang-com-helper.h" +#include "slang.h" // We will again make use of a graphics API abstraction // layer that implements the shader-object idiom based on Slang's // `ParameterBlock` and `interface` features to simplify shader specialization // and parameter binding. // +#include "examples/example-base/example-base.h" #include "slang-gfx.h" #include "tools/gfx-util/shader-cursor.h" +#include "tools/platform/gui.h" #include "tools/platform/model.h" #include "tools/platform/vector-math.h" #include "tools/platform/window.h" -#include "tools/platform/gui.h" -#include "examples/example-base/example-base.h" #include <map> #include <sstream> @@ -51,7 +51,7 @@ struct RendererContext slang::TypeReflection* perViewShaderType; slang::TypeReflection* perModelShaderType; - TestBase *pTestBase; + TestBase* pTestBase; Result init(IDevice* inDevice, TestBase* inTestBase) { @@ -60,9 +60,8 @@ struct RendererContext pTestBase = inTestBase; Slang::String path = resourceBase.resolveResource("shaders.slang").getBuffer(); - shaderModule = device->getSlangSession()->loadModule( - path.getBuffer(), - diagnostic.writeRef()); + shaderModule = + device->getSlangSession()->loadModule(path.getBuffer(), diagnostic.writeRef()); diagnoseIfNeeded(diagnostic); if (!shaderModule) return SLANG_FAIL; @@ -165,9 +164,9 @@ struct Material : RefObject // struct SimpleMaterial : Material { - glm::vec3 diffuseColor; - glm::vec3 specularColor; - float specularity = 1.0f; + glm::vec3 diffuseColor; + glm::vec3 specularColor; + float specularity = 1.0f; // Create a shader object that contains the type info and parameter values // that represent an instance of `SimpleMaterial`. @@ -194,20 +193,20 @@ struct SimpleMaterial : Material // struct Mesh : RefObject { - RefPtr<Material> material; - int firstIndex; - int indexCount; + RefPtr<Material> material; + int firstIndex; + int indexCount; }; struct Model : RefObject { typedef platform::ModelLoader::Vertex Vertex; - ComPtr<IBufferResource> vertexBuffer; - ComPtr<IBufferResource> indexBuffer; - PrimitiveTopology primitiveTopology; - int vertexCount; - int indexCount; - std::vector<RefPtr<Mesh>> meshes; + ComPtr<IBufferResource> vertexBuffer; + ComPtr<IBufferResource> indexBuffer; + PrimitiveTopology primitiveTopology; + int vertexCount; + int indexCount; + std::vector<RefPtr<Mesh>> meshes; }; // // Loading a model from disk is done with the help of some utility @@ -216,10 +215,10 @@ struct Model : RefObject // used for its representation. // RefPtr<Model> loadModel( - RendererContext* context, - char const* inputPath, + RendererContext* context, + char const* inputPath, platform::ModelLoader::LoadFlags loadFlags = 0, - float scale = 1.0f) + float scale = 1.0f) { // The model loading interface using a C++ interface of // callback functions to handle creating the application-specific @@ -308,7 +307,7 @@ struct Light : RefObject // The shader object for a light will be stashed here // after it is created. -// ComPtr<IShaderObject> shaderObject; + // ComPtr<IShaderObject> shaderObject; }; // Helper function to retrieve the underlying shader type of `T`. @@ -411,8 +410,7 @@ struct LightEnvLayout : public RefObject { auto program = context->slangReflection; std::stringstream typeNameBuilder; - typeNameBuilder << "LightArray<" << lightType->getName() << "," << maximumCount - << ">"; + typeNameBuilder << "LightArray<" << lightType->getName() << "," << maximumCount << ">"; layout.typeName = typeNameBuilder.str(); } @@ -420,7 +418,8 @@ struct LightEnvLayout : public RefObject mapLightTypeToArrayIndex.insert(std::make_pair(lightType, arrayIndex)); } - template<typename T> void addLightType(RendererContext* context, Int maximumCount) + template<typename T> + void addLightType(RendererContext* context, Int maximumCount) { addLightType(context, getShaderType<T>(context), maximumCount); } @@ -459,8 +458,7 @@ struct LightEnv : public RefObject RefPtr<LightEnvLayout> layout; RendererContext* context; LightEnv(RefPtr<LightEnvLayout> layout, RendererContext* inContext) - : layout(layout) - , context(inContext) + : layout(layout), context(inContext) { for (auto arrayLayout : layout->lightArrayLayouts) { @@ -640,329 +638,324 @@ struct LightEnv : public RefObject // struct ModelViewer : WindowedAppBase { -RendererContext context; + RendererContext context; -// Most of the application state is stored in the list of loaded models, -// as well as the active light source (a single light for now). -// -std::vector<RefPtr<Model>> gModels; -RefPtr<LightEnv> lightEnv; + // Most of the application state is stored in the list of loaded models, + // as well as the active light source (a single light for now). + // + std::vector<RefPtr<Model>> gModels; + RefPtr<LightEnv> lightEnv; -// The pipeline state object we will use to draw models. -ComPtr<IPipelineState> gPipelineState; + // The pipeline state object we will use to draw models. + ComPtr<IPipelineState> gPipelineState; -// During startup the application will load one or more models and -// add them to the `gModels` list. -// -void loadAndAddModel( - char const* inputPath, - platform::ModelLoader::LoadFlags loadFlags = 0, - float scale = 1.0f) -{ - auto model = loadModel(&context, inputPath, loadFlags, scale); - if(!model) return; - gModels.push_back(model); -} + // During startup the application will load one or more models and + // add them to the `gModels` list. + // + void loadAndAddModel( + char const* inputPath, + platform::ModelLoader::LoadFlags loadFlags = 0, + float scale = 1.0f) + { + auto model = loadModel(&context, inputPath, loadFlags, scale); + if (!model) + return; + gModels.push_back(model); + } -// Our "simulation" state consists of just a few values. -// -uint64_t lastTime = 0; + // Our "simulation" state consists of just a few values. + // + uint64_t lastTime = 0; -//glm::vec3 lightDir = normalize(glm::vec3(10, 10, 10)); -//glm::vec3 lightColor = glm::vec3(1, 1, 1); + // glm::vec3 lightDir = normalize(glm::vec3(10, 10, 10)); + // glm::vec3 lightColor = glm::vec3(1, 1, 1); -glm::vec3 cameraPosition = glm::vec3(1.75, 1.25, 5); -glm::quat cameraOrientation = glm::quat(1, glm::vec3(0)); + glm::vec3 cameraPosition = glm::vec3(1.75, 1.25, 5); + glm::quat cameraOrientation = glm::quat(1, glm::vec3(0)); -float translationScale = 0.5f; -float rotationScale = 0.025f; + float translationScale = 0.5f; + float rotationScale = 0.025f; -// In order to control camera movement, we will -// use good old WASD -bool wPressed = false; -bool aPressed = false; -bool sPressed = false; -bool dPressed = false; + // In order to control camera movement, we will + // use good old WASD + bool wPressed = false; + bool aPressed = false; + bool sPressed = false; + bool dPressed = false; -bool isMouseDown = false; -float lastMouseX = 0.0f; -float lastMouseY = 0.0f; + bool isMouseDown = false; + float lastMouseX = 0.0f; + float lastMouseY = 0.0f; -void setKeyState(platform::KeyCode key, bool state) -{ - switch (key) + void setKeyState(platform::KeyCode key, bool state) { - default: - break; - case platform::KeyCode::W: - wPressed = state; - break; - case platform::KeyCode::A: - aPressed = state; - break; - case platform::KeyCode::S: - sPressed = state; - break; - case platform::KeyCode::D: - dPressed = state; - break; + switch (key) + { + default: break; + case platform::KeyCode::W: wPressed = state; break; + case platform::KeyCode::A: aPressed = state; break; + case platform::KeyCode::S: sPressed = state; break; + case platform::KeyCode::D: dPressed = state; break; + } } -} -void onKeyDown(platform::KeyEventArgs args) { setKeyState(args.key, true); } -void onKeyUp(platform::KeyEventArgs args) { setKeyState(args.key, false); } + void onKeyDown(platform::KeyEventArgs args) { setKeyState(args.key, true); } + void onKeyUp(platform::KeyEventArgs args) { setKeyState(args.key, false); } -void onMouseDown(platform::MouseEventArgs args) -{ - isMouseDown = true; - lastMouseX = (float)args.x; - lastMouseY = (float)args.y; -} - -void onMouseMove(platform::MouseEventArgs args) -{ - if (isMouseDown) + void onMouseDown(platform::MouseEventArgs args) { - float deltaX = args.x - lastMouseX; - float deltaY = args.y - lastMouseY; - - cameraOrientation = - glm::rotate(cameraOrientation, -deltaX * rotationScale, glm::vec3(0, 1, 0)); - cameraOrientation = - glm::rotate(cameraOrientation, -deltaY * rotationScale, glm::vec3(1, 0, 0)); - - cameraOrientation = normalize(cameraOrientation); - + isMouseDown = true; lastMouseX = (float)args.x; lastMouseY = (float)args.y; } -} -void onMouseUp(platform::MouseEventArgs args) -{ - isMouseDown = false; -} -// The overall initialization logic is quite similar to -// the earlier example. The biggest difference is that we -// create instances of our application-specific parameter -// block layout and effect types instead of just creating -// raw graphics API objects. -// -Result initialize() -{ - initializeBase("Model Viewer", 1024, 768); - if (!isTestMode()) + void onMouseMove(platform::MouseEventArgs args) { - gWindow->events.mouseMove = [this](const platform::MouseEventArgs& e) { onMouseMove(e); }; - gWindow->events.mouseUp = [this](const platform::MouseEventArgs& e) { onMouseUp(e); }; - gWindow->events.mouseDown = [this](const platform::MouseEventArgs& e) { onMouseDown(e); }; - gWindow->events.keyDown = [this](const platform::KeyEventArgs& e) { onKeyDown(e); }; - gWindow->events.keyUp = [this](const platform::KeyEventArgs& e) { onKeyUp(e); }; - } + if (isMouseDown) + { + float deltaX = args.x - lastMouseX; + float deltaY = args.y - lastMouseY; - // Initialize `RendererContext`, which loads the shader module from file. - SLANG_RETURN_ON_FAIL(context.init(gDevice, this)); + cameraOrientation = + glm::rotate(cameraOrientation, -deltaX * rotationScale, glm::vec3(0, 1, 0)); + cameraOrientation = + glm::rotate(cameraOrientation, -deltaY * rotationScale, glm::vec3(1, 0, 0)); + cameraOrientation = normalize(cameraOrientation); - InputElementDesc inputElements[] = { - {"POSITION", 0, Format::R32G32B32_FLOAT, offsetof(Model::Vertex, position) }, - {"NORMAL", 0, Format::R32G32B32_FLOAT, offsetof(Model::Vertex, normal) }, - {"UV", 0, Format::R32G32_FLOAT, offsetof(Model::Vertex, uv) }, - }; - auto inputLayout = gDevice->createInputLayout( - sizeof(Model::Vertex), - &inputElements[0], - 3); - if(!inputLayout) return SLANG_FAIL; - - // Create the pipeline state object for drawing models. - GraphicsPipelineStateDesc pipelineStateDesc = {}; - pipelineStateDesc.program = context.shaderProgram; - pipelineStateDesc.framebufferLayout = gFramebufferLayout; - pipelineStateDesc.inputLayout = inputLayout; - pipelineStateDesc.primitiveType = PrimitiveType::Triangle; - pipelineStateDesc.depthStencil.depthFunc = ComparisonFunc::LessEqual; - pipelineStateDesc.depthStencil.depthTestEnable = true; - gPipelineState = gDevice->createGraphicsPipelineState(pipelineStateDesc); - - // We will create a lighting environment layout that can hold a few point - // and directional lights, and then initialize a lighting environment - // with just a single point light. + lastMouseX = (float)args.x; + lastMouseY = (float)args.y; + } + } + void onMouseUp(platform::MouseEventArgs args) { isMouseDown = false; } + + // The overall initialization logic is quite similar to + // the earlier example. The biggest difference is that we + // create instances of our application-specific parameter + // block layout and effect types instead of just creating + // raw graphics API objects. // - RefPtr<LightEnvLayout> lightEnvLayout = new LightEnvLayout(); - lightEnvLayout->addLightType<PointLight>(&context, 10); - lightEnvLayout->addLightType<DirectionalLight>(&context, 2); + Result initialize() + { + initializeBase("Model Viewer", 1024, 768); + if (!isTestMode()) + { + gWindow->events.mouseMove = [this](const platform::MouseEventArgs& e) + { onMouseMove(e); }; + gWindow->events.mouseUp = [this](const platform::MouseEventArgs& e) { onMouseUp(e); }; + gWindow->events.mouseDown = [this](const platform::MouseEventArgs& e) + { onMouseDown(e); }; + gWindow->events.keyDown = [this](const platform::KeyEventArgs& e) { onKeyDown(e); }; + gWindow->events.keyUp = [this](const platform::KeyEventArgs& e) { onKeyUp(e); }; + } - lightEnv = new LightEnv(lightEnvLayout, &context); + // Initialize `RendererContext`, which loads the shader module from file. + SLANG_RETURN_ON_FAIL(context.init(gDevice, this)); - RefPtr<PointLight> pointLight = new PointLight(); - pointLight->position = glm::vec3(5, 3, 1); - pointLight->intensity = glm::vec3(10); - lightEnv->add(pointLight); - // Once we have created all our graphcis API and application resources, - // we can start to load models. For now we are keeping things extremely - // simple by using a trivial `.obj` file that can be checked into source - // control. - // - // Support for loading more interesting/complex models will be added - // to this example over time (although model loading is *not* the focus). - // - Slang::String path = resourceBase.resolveResource("cube.obj").getBuffer(); - loadAndAddModel(path.getBuffer()); + InputElementDesc inputElements[] = { + {"POSITION", 0, Format::R32G32B32_FLOAT, offsetof(Model::Vertex, position)}, + {"NORMAL", 0, Format::R32G32B32_FLOAT, offsetof(Model::Vertex, normal)}, + {"UV", 0, Format::R32G32_FLOAT, offsetof(Model::Vertex, uv)}, + }; + auto inputLayout = gDevice->createInputLayout(sizeof(Model::Vertex), &inputElements[0], 3); + if (!inputLayout) + return SLANG_FAIL; - return SLANG_OK; -} + // Create the pipeline state object for drawing models. + GraphicsPipelineStateDesc pipelineStateDesc = {}; + pipelineStateDesc.program = context.shaderProgram; + pipelineStateDesc.framebufferLayout = gFramebufferLayout; + pipelineStateDesc.inputLayout = inputLayout; + pipelineStateDesc.primitiveType = PrimitiveType::Triangle; + pipelineStateDesc.depthStencil.depthFunc = ComparisonFunc::LessEqual; + pipelineStateDesc.depthStencil.depthTestEnable = true; + gPipelineState = gDevice->createGraphicsPipelineState(pipelineStateDesc); + + // We will create a lighting environment layout that can hold a few point + // and directional lights, and then initialize a lighting environment + // with just a single point light. + // + RefPtr<LightEnvLayout> lightEnvLayout = new LightEnvLayout(); + lightEnvLayout->addLightType<PointLight>(&context, 10); + lightEnvLayout->addLightType<DirectionalLight>(&context, 2); -// With the setup work done, we can look at the per-frame rendering -// logic to see how the application will drive the `RenderContext` -// type to perform both shader parameter binding and code specialization. -// -void renderFrame(int frameIndex) override -{ - // In order to see that things are rendering properly we need some - // kind of animation, so we will compute a crude delta-time value here. - // - if(!lastTime) lastTime = getCurrentTime(); - uint64_t currentTime = getCurrentTime(); - float deltaTime = float(double(currentTime - lastTime) / double(getTimerFrequency())); - lastTime = currentTime; + lightEnv = new LightEnv(lightEnvLayout, &context); - // We will use the GLM library to do the matrix math required - // to set up our various transformation matrices. - // - glm::mat4x4 identity = glm::mat4x4(1.0f); + RefPtr<PointLight> pointLight = new PointLight(); + pointLight->position = glm::vec3(5, 3, 1); + pointLight->intensity = glm::vec3(10); + lightEnv->add(pointLight); - platform::Rect clientRect{}; - if (isTestMode()) - { - clientRect.width = 1024; - clientRect.height = 768; - } - else - { - clientRect = getWindow()->getClientRect(); - } - if (clientRect.height == 0) - return; - glm::mat4x4 projection = glm::perspectiveRH_ZO( - glm::radians(60.0f), float(clientRect.width) / float(clientRect.height), - 0.1f, - 1000.0f); - - // We are implementing a *very* basic 6DOF first-person - // camera movement model. - // - glm::mat3x3 cameraOrientationMat(cameraOrientation); - glm::vec3 forward = -cameraOrientationMat[2]; - glm::vec3 right = cameraOrientationMat[0]; - - glm::vec3 movement = glm::vec3(0); - if(wPressed) movement += forward; - if(sPressed) movement -= forward; - if(aPressed) movement -= right; - if(dPressed) movement += right; - - cameraPosition += deltaTime * translationScale * movement; - - glm::mat4x4 view = identity; - view *= glm::mat4x4(inverse(cameraOrientation)); - view = glm::translate(view, -cameraPosition); - - glm::mat4x4 viewProjection = projection * view; - auto deviceInfo = gDevice->getDeviceInfo(); - glm::mat4x4 correctionMatrix; - memcpy(&correctionMatrix, deviceInfo.identityProjectionMatrix, sizeof(float)*16); - viewProjection = correctionMatrix * viewProjection; - // glm uses column-major layout, we need to translate it to row-major. - viewProjection = glm::transpose(viewProjection); - - auto drawCommandBuffer = gTransientHeaps[frameIndex]->createCommandBuffer(); - auto drawCommandEncoder = - drawCommandBuffer->encodeRenderCommands(gRenderPass, gFramebuffers[frameIndex]); - gfx::Viewport viewport = {}; - viewport.maxZ = 1.0f; - viewport.extentX = (float)clientRect.width; - viewport.extentY = (float)clientRect.height; - drawCommandEncoder->setViewportAndScissor(viewport); - drawCommandEncoder->setPrimitiveTopology(PrimitiveTopology::TriangleList); - - // We are only rendering one view, so we can fill in a per-view - // shader object once and use it across all draw calls. - // + // Once we have created all our graphcis API and application resources, + // we can start to load models. For now we are keeping things extremely + // simple by using a trivial `.obj` file that can be checked into source + // control. + // + // Support for loading more interesting/complex models will be added + // to this example over time (although model loading is *not* the focus). + // + Slang::String path = resourceBase.resolveResource("cube.obj").getBuffer(); + loadAndAddModel(path.getBuffer()); - auto viewShaderObject = gDevice->createShaderObject(context.perViewShaderType); - { - ShaderCursor cursor(viewShaderObject); - cursor["viewProjection"].setData(&viewProjection, sizeof(viewProjection)); - cursor["eyePosition"].setData(&cameraPosition, sizeof(cameraPosition)); + return SLANG_OK; } - // The majority of our rendering logic is handled as a loop - // over the models in the scene, and their meshes. + + // With the setup work done, we can look at the per-frame rendering + // logic to see how the application will drive the `RenderContext` + // type to perform both shader parameter binding and code specialization. // - for(auto& model : gModels) + void renderFrame(int frameIndex) override { - drawCommandEncoder->setVertexBuffer(0, model->vertexBuffer); - drawCommandEncoder->setIndexBuffer(model->indexBuffer, Format::R32_UINT); - // For each model we provide a parameter - // block that holds the per-model transformation - // parameters, corresponding to the `PerModel` type - // in the shader code. - glm::mat4x4 modelTransform = identity; - glm::mat4x4 inverseTransposeModelTransform = inverse(transpose(modelTransform)); - auto modelShaderObject = gDevice->createShaderObject(context.perModelShaderType); + // In order to see that things are rendering properly we need some + // kind of animation, so we will compute a crude delta-time value here. + // + if (!lastTime) + lastTime = getCurrentTime(); + uint64_t currentTime = getCurrentTime(); + float deltaTime = float(double(currentTime - lastTime) / double(getTimerFrequency())); + lastTime = currentTime; + + // We will use the GLM library to do the matrix math required + // to set up our various transformation matrices. + // + glm::mat4x4 identity = glm::mat4x4(1.0f); + + platform::Rect clientRect{}; + if (isTestMode()) { - ShaderCursor cursor(modelShaderObject); - cursor["modelTransform"].setData(&modelTransform, sizeof(modelTransform)); - cursor["inverseTransposeModelTransform"].setData( - &inverseTransposeModelTransform, sizeof(inverseTransposeModelTransform)); + clientRect.width = 1024; + clientRect.height = 768; } - - auto lightShaderObject = lightEnv->createShaderObject(); - - // Now we loop over the meshes in the model. + else + { + clientRect = getWindow()->getClientRect(); + } + if (clientRect.height == 0) + return; + glm::mat4x4 projection = glm::perspectiveRH_ZO( + glm::radians(60.0f), + float(clientRect.width) / float(clientRect.height), + 0.1f, + 1000.0f); + + // We are implementing a *very* basic 6DOF first-person + // camera movement model. // - // A more advanced rendering loop would sort things by material - // rather than by model, to avoid overly frequent state changes. - // We are just doing something simple for the purposes of an - // exmple program. + glm::mat3x3 cameraOrientationMat(cameraOrientation); + glm::vec3 forward = -cameraOrientationMat[2]; + glm::vec3 right = cameraOrientationMat[0]; + + glm::vec3 movement = glm::vec3(0); + if (wPressed) + movement += forward; + if (sPressed) + movement -= forward; + if (aPressed) + movement -= right; + if (dPressed) + movement += right; + + cameraPosition += deltaTime * translationScale * movement; + + glm::mat4x4 view = identity; + view *= glm::mat4x4(inverse(cameraOrientation)); + view = glm::translate(view, -cameraPosition); + + glm::mat4x4 viewProjection = projection * view; + auto deviceInfo = gDevice->getDeviceInfo(); + glm::mat4x4 correctionMatrix; + memcpy(&correctionMatrix, deviceInfo.identityProjectionMatrix, sizeof(float) * 16); + viewProjection = correctionMatrix * viewProjection; + // glm uses column-major layout, we need to translate it to row-major. + viewProjection = glm::transpose(viewProjection); + + auto drawCommandBuffer = gTransientHeaps[frameIndex]->createCommandBuffer(); + auto drawCommandEncoder = + drawCommandBuffer->encodeRenderCommands(gRenderPass, gFramebuffers[frameIndex]); + gfx::Viewport viewport = {}; + viewport.maxZ = 1.0f; + viewport.extentX = (float)clientRect.width; + viewport.extentY = (float)clientRect.height; + drawCommandEncoder->setViewportAndScissor(viewport); + drawCommandEncoder->setPrimitiveTopology(PrimitiveTopology::TriangleList); + + // We are only rendering one view, so we can fill in a per-view + // shader object once and use it across all draw calls. // - for(auto& mesh : model->meshes) + + auto viewShaderObject = gDevice->createShaderObject(context.perViewShaderType); { - // Set the pipeline and binding state for drawing each mesh. - auto rootObject = drawCommandEncoder->bindPipeline(gPipelineState); - ShaderCursor rootCursor(rootObject); - rootCursor["gViewParams"].setObject(viewShaderObject); - rootCursor["gModelParams"].setObject(modelShaderObject); - rootCursor["gLightEnv"].setObject(lightShaderObject); - - // Each mesh has a material, and each material has its own - // parameter block that was created at load time, so we - // can just re-use the persistent parameter block for the - // chosen material. + ShaderCursor cursor(viewShaderObject); + cursor["viewProjection"].setData(&viewProjection, sizeof(viewProjection)); + cursor["eyePosition"].setData(&cameraPosition, sizeof(cameraPosition)); + } + // The majority of our rendering logic is handled as a loop + // over the models in the scene, and their meshes. + // + for (auto& model : gModels) + { + drawCommandEncoder->setVertexBuffer(0, model->vertexBuffer); + drawCommandEncoder->setIndexBuffer(model->indexBuffer, Format::R32_UINT); + // For each model we provide a parameter + // block that holds the per-model transformation + // parameters, corresponding to the `PerModel` type + // in the shader code. + glm::mat4x4 modelTransform = identity; + glm::mat4x4 inverseTransposeModelTransform = inverse(transpose(modelTransform)); + auto modelShaderObject = gDevice->createShaderObject(context.perModelShaderType); + { + ShaderCursor cursor(modelShaderObject); + cursor["modelTransform"].setData(&modelTransform, sizeof(modelTransform)); + cursor["inverseTransposeModelTransform"].setData( + &inverseTransposeModelTransform, + sizeof(inverseTransposeModelTransform)); + } + + auto lightShaderObject = lightEnv->createShaderObject(); + + // Now we loop over the meshes in the model. // - // Note that binding the material parameter block here is - // both selecting the values to use for various material - // parameters as well as the *code* to use for material - // evaluation (based on the concrete shader type that - // is implementing the `IMaterial` interface). + // A more advanced rendering loop would sort things by material + // rather than by model, to avoid overly frequent state changes. + // We are just doing something simple for the purposes of an + // exmple program. // - rootCursor["gMaterial"].setObject(mesh->material->shaderObject); + for (auto& mesh : model->meshes) + { + // Set the pipeline and binding state for drawing each mesh. + auto rootObject = drawCommandEncoder->bindPipeline(gPipelineState); + ShaderCursor rootCursor(rootObject); + rootCursor["gViewParams"].setObject(viewShaderObject); + rootCursor["gModelParams"].setObject(modelShaderObject); + rootCursor["gLightEnv"].setObject(lightShaderObject); + + // Each mesh has a material, and each material has its own + // parameter block that was created at load time, so we + // can just re-use the persistent parameter block for the + // chosen material. + // + // Note that binding the material parameter block here is + // both selecting the values to use for various material + // parameters as well as the *code* to use for material + // evaluation (based on the concrete shader type that + // is implementing the `IMaterial` interface). + // + rootCursor["gMaterial"].setObject(mesh->material->shaderObject); - // All the shader parameters and pipeline states have been set up, - // we can now issue a draw call for the mesh. - drawCommandEncoder->drawIndexed(mesh->indexCount, mesh->firstIndex); + // All the shader parameters and pipeline states have been set up, + // we can now issue a draw call for the mesh. + drawCommandEncoder->drawIndexed(mesh->indexCount, mesh->firstIndex); + } } - } - drawCommandEncoder->endEncoding(); - drawCommandBuffer->close(); - gQueue->executeCommandBuffer(drawCommandBuffer); + drawCommandEncoder->endEncoding(); + drawCommandBuffer->close(); + gQueue->executeCommandBuffer(drawCommandBuffer); - if (!isTestMode()) - { - gSwapchain->present(); + if (!isTestMode()) + { + gSwapchain->present(); + } } -} - }; // This macro instantiates an appropriate main function to |
