details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); #ifdef WANT_DEBUG if (id == UNKNOWN_ID) { qCDebug(renderutils) << "new renderLine() 2D VBO made -- _line3DVBOs.size():" << _line2DVBOs.size(); } else { qCDebug(renderutils) << "new registered renderLine() 2D VBO made -- _registeredLine2DVBOs.size():" << _registeredLine2DVBOs.size(); }#endif } // this is what it takes to render a quad batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(gpu::LINES, 2, 0);} void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, const glm::vec4& color, float glowIntensity, float glowWidth, int id) { // Disable glow lines on OSX#ifndef Q_OS_WIN glowIntensity = 0.0f;#endif if (glowIntensity <= 0.0f) { if (color.a >= 1.0f) { bindSimpleProgram(batch, false, false, false, true, true); } else { bindSimpleProgram(batch, false, true, false, true, true); } renderLine(batch, p1, p2, color, id); return; } // Compile the shaders static std::once_flag once; std::call_once(once, [&] { auto state = std::make_shared<gpu::State>(); auto program = gpu::Shader::createProgram(shader::render_utils::program::glowLine); state->setCullMode(gpu::State::CULL_NONE); state->setDepthTest(true, false, gpu::LESS_EQUAL); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); PrepareStencil::testMask(*state); _glowLinePipeline = gpu::Pipeline::create(program, state); }); batch.setPipeline(_glowLinePipeline); Vec3Pair key(p1, p2); bool registered = (id != UNKNOWN_ID); BatchItemDetails& details = _registeredLine3DVBOs[id]; // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed if (registered && details.isCreated) { Vec3Pair& lastKey = _lastRegisteredLine3D[id]; if (lastKey != key) { details.clear(); _lastRegisteredLine3D[id] = key; } } const int NUM_VERTICES = 4; if (!details.isCreated) { details.isCreated = true; details.uniformBuffer = std::make_shared<gpu::Buffer>(); struct LineData { vec4 p1; vec4 p2; vec4 color; float width; }; LineData lineData { vec4(p1, 1.0f), vec4(p2, 1.0f), color, glowWidth }; details.uniformBuffer->resize(sizeof(LineData)); details.uniformBuffer->setSubData(0, lineData); } // The shader requires no vertices, only uniforms. batch.setUniformBuffer(0, details.uniformBuffer); batch.draw(gpu::TRIANGLE_STRIP, NUM_VERTICES, 0);} void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { static std::once_flag once; std::call_once(once, [&]() { auto program = gpu::Shader::createProgram(shader::render_utils::program::standardDrawTexture); auto state = std::make_shared<gpu::State>(); // enable decal blend state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); PrepareStencil::testMask(*state); _standardDrawPipeline = gpu::Pipeline::create(program, state); auto stateNoBlend = std::make_shared<gpu::State>(); PrepareStencil::testMaskDrawShape(*stateNoBlend); auto programNoBlend = gpu::Shader::createProgram(shader::render_utils::program::standardDrawTextureNoBlend); _standardDrawPipelineNoBlend = gpu::Pipeline::create(programNoBlend, stateNoBlend); }); if (noBlend) { batch.setPipeline(_standardDrawPipelineNoBlend); } else { batch.setPipeline(_standardDrawPipeline); }} void GeometryCache::useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bool isLayered) { if (!_gridPipeline) { auto program = gpu::Shader::createProgram(shader::render_utils::program::grid); _gridSlot = 0; auto stateLayered = std::make_shared<gpu::State>(); stateLayered->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); PrepareStencil::testMask(*stateLayered); _gridPipelineLayered = gpu::Pipeline::create(program, stateLayered); auto state = std::make_shared<gpu::State>(stateLayered->getValues()); const float DEPTH_BIAS = 0.001f; state->setDepthBias(DEPTH_BIAS); state->setDepthTest(true, false, gpu::LESS_EQUAL); PrepareStencil::testMaskDrawShape(*state); _gridPipeline = gpu::Pipeline::create(program, state); } gpu::PipelinePointer pipeline = isLayered ? _gridPipelineLayered : _gridPipeline; batch.setPipeline(pipeline); batch.setUniformBuffer(_gridSlot, gridBuffer);} class SimpleProgramKey {public: enum FlagBit { IS_TEXTURED_FLAG = 0, IS_TRANSPARENT_FLAG, IS_CULLED_FLAG, IS_UNLIT_FLAG, HAS_DEPTH_BIAS_FLAG, IS_FADING_FLAG, IS_ANTIALIASED_FLAG, NUM_FLAGS, }; enum Flag { IS_TEXTURED = (1 << IS_TEXTURED_FLAG), IS_TRANSPARENT = (1 << IS_TRANSPARENT_FLAG), IS_CULLED = (1 << IS_CULLED_FLAG), IS_UNLIT = (1 << IS_UNLIT_FLAG), HAS_DEPTH_BIAS = (1 << HAS_DEPTH_BIAS_FLAG), IS_FADING = (1 << IS_FADING_FLAG), IS_ANTIALIASED = (1 << IS_ANTIALIASED_FLAG), }; typedef unsigned short Flags; bool isFlag(short flagNum) const { return bool((_flags & flagNum) != 0); } bool isTextured() const { return isFlag(IS_TEXTURED); } bool isTransparent() const { return isFlag(IS_TRANSPARENT); } bool isCulled() const { return isFlag(IS_CULLED); } bool isUnlit() const { return isFlag(IS_UNLIT); } bool hasDepthBias() const { return isFlag(HAS_DEPTH_BIAS); } bool isFading() const { return isFlag(IS_FADING); } bool isAntiAliased() const { return isFlag(IS_ANTIALIASED); } Flags _flags = 0; short _spare = 0; int getRaw() const { return *reinterpret_cast<const int*>(this); } SimpleProgramKey(bool textured = false, bool transparent = false, bool culled = true, bool unlit = false, bool depthBias = false, bool fading = false, bool isAntiAliased = true) { _flags = (textured ? IS_TEXTURED : 0) | (transparent ? IS_TRANSPARENT : 0) | (culled ? IS_CULLED : 0) | (unlit ? IS_UNLIT : 0) | (depthBias ? HAS_DEPTH_BIAS : 0) | (fading ? IS_FADING : 0) | (isAntiAliased ? IS_ANTIALIASED : 0); } SimpleProgramKey(int bitmask) : _flags(bitmask) {}}; inline uint qHash(const SimpleProgramKey& key, uint seed) { return qHash(key.getRaw(), seed);} inline bool operator==(const SimpleProgramKey& a, const SimpleProgramKey& b) { return a.getRaw() == b.getRaw();} static void buildWebShader(int programId, bool blendEnable, gpu::ShaderPointer& shaderPointerOut, gpu::PipelinePointer& pipelinePointerOut) { shaderPointerOut = gpu::Shader::createProgram(programId); auto state = std::make_shared<gpu::State>(); state->setCullMode(gpu::State::CULL_NONE); state->setDepthTest(true, true, gpu::LESS_EQUAL); state->setBlendFunction(blendEnable, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); PrepareStencil::testMaskDrawShapeNoAA(*state); pipelinePointerOut = gpu::Pipeline::create(shaderPointerOut, state);} void GeometryCache::bindWebBrowserProgram(gpu::Batch& batch, bool transparent) { batch.setPipeline(getWebBrowserProgram(transparent));} gpu::PipelinePointer GeometryCache::getWebBrowserProgram(bool transparent) { static std::once_flag once; std::call_once(once, [&]() { buildWebShader(shader::render_utils::program::simple_opaque_web_browser, false, _simpleOpaqueWebBrowserShader, _simpleOpaqueWebBrowserPipeline); buildWebShader(shader::render_utils::program::simple_transparent_web_browser, true, _simpleTransparentWebBrowserShader, _simpleTransparentWebBrowserPipeline); }); return transparent ? _simpleTransparentWebBrowserPipeline : _simpleOpaqueWebBrowserPipeline;} void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool transparent, bool culled, bool unlit, bool depthBiased, bool isAntiAliased) { batch.setPipeline(getSimplePipeline(textured, transparent, culled, unlit, depthBiased, false, isAntiAliased)); // If not textured, set a default albedo map if (!textured) { batch.setResourceTexture(gr::Texture::MaterialAlbedo, DependencyManager::get<TextureCache>()->getWhiteTexture()); }} gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transparent, bool culled, bool unlit, bool depthBiased, bool fading, bool isAntiAliased) { SimpleProgramKey config { textured, transparent, culled, unlit, depthBiased, fading, isAntiAliased }; // If the pipeline already exists, return it auto it = _simplePrograms.find(config); if (it != _simplePrograms.end()) { return it.value(); } // Compile the shaders if (!fading) { static std::once_flag once; std::call_once(once, [&]() { using namespace shader::render_utils::program; auto PS = DISABLE_DEFERRED ? forward_simple_textured : simple_textured; // Use the forward pipeline for both here, otherwise transparents will be unlit auto PSTransparent = DISABLE_DEFERRED ? forward_simple_textured_transparent : forward_simple_textured_transparent; auto PSUnlit = DISABLE_DEFERRED ? forward_simple_textured_unlit : simple_textured_unlit; _simpleShader = gpu::Shader::createProgram(PS); _transparentShader = gpu::Shader::createProgram(PSTransparent); _unlitShader = gpu::Shader::createProgram(PSUnlit); }); } else { static std::once_flag once; std::call_once(once, [&]() { using namespace shader::render_utils::program; _simpleFadeShader = gpu::Shader::createProgram(DISABLE_DEFERRED ? forward_simple_textured : simple_textured_fade); _unlitFadeShader = gpu::Shader::createProgram(DISABLE_DEFERRED ? forward_simple_textured_unlit : simple_textured_unlit_fade); }); } // If the pipeline did not exist, make it auto state = std::make_shared<gpu::State>(); if (config.isCulled()) { state->setCullMode(gpu::State::CULL_BACK); } else { state->setCullMode(gpu::State::CULL_NONE); } state->setDepthTest(true, true, gpu::LESS_EQUAL); if (config.hasDepthBias()) { state->setDepthBias(1.0f); state->setDepthBiasSlopeScale(1.0f); } state->setBlendFunction(config.isTransparent(), gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); if (config.isAntiAliased()) { config.isTransparent() ? PrepareStencil::testMask(*state) : PrepareStencil::testMaskDrawShape(*state); } else { PrepareStencil::testMaskDrawShapeNoAA(*state); } gpu::ShaderPointer program = (config.isUnlit()) ? (config.isFading() ? _unlitFadeShader : _unlitShader) : (config.isFading() ? _simpleFadeShader : (config.isTransparent() ? _transparentShader : _simpleShader)); gpu::PipelinePointer pipeline = gpu::Pipeline::create(program, state); _simplePrograms.insert(config, pipeline); return pipeline;} uint32_t toCompactColor(const glm::vec4& color) { uint32_t compactColor = ((int(color.x * 255.0f) & 0xFF)) | ((int(color.y * 255.0f) & 0xFF) << 8) | ((int(color.z * 255.0f) & 0xFF) << 16) | ((int(color.w * 255.0f) & 0xFF) << 24); return compactColor;} static const size_t INSTANCE_COLOR_BUFFER = 0; void renderInstances(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, bool isWire, const render::ShapePipelinePointer& pipeline, GeometryCache::Shape shape) { // Add pipeline to name std::string instanceName = (isWire ? "wire_shapes_" : "solid_shapes_") + std::to_string(shape) + "_" + std::to_string(std::hash<render::ShapePipelinePointer>()(pipeline)); // Add color to named buffer { gpu::BufferPointer instanceColorBuffer = batch.getNamedBuffer(instanceName, INSTANCE_COLOR_BUFFER); auto compactColor = toCompactColor(color); instanceColorBuffer->append(compactColor); } // Add call to named buffer batch.setupNamedCalls(instanceName, [args, isWire, pipeline, shape](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { batch.setPipeline(pipeline->pipeline); pipeline->prepare(batch, args); if (isWire) { DependencyManager::get<GeometryCache>()->renderWireShapeInstances(batch, shape, data.count(), data.buffers[INSTANCE_COLOR_BUFFER]); } else { DependencyManager::get<GeometryCache>()->renderShapeInstances(batch, shape, data.count(), data.buffers[INSTANCE_COLOR_BUFFER]); } });} static const size_t INSTANCE_FADE_BUFFER1 = 1;static const size_t INSTANCE_FADE_BUFFER2 = 2;static const size_t INSTANCE_FADE_BUFFER3 = 3; void renderFadeInstances(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, int fadeCategory, float fadeThreshold, const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, bool isWire, const render::ShapePipelinePointer& pipeline, GeometryCache::Shape shape) { // Add pipeline to name std::string instanceName = (isWire ? "wire_shapes_" : "solid_shapes_") + std::to_string(shape) + "_" + std::to_string(std::hash<render::ShapePipelinePointer>()(pipeline)); // Add color to named buffer { gpu::BufferPointer instanceColorBuffer = batch.getNamedBuffer(instanceName, INSTANCE_COLOR_BUFFER); auto compactColor = toCompactColor(color); instanceColorBuffer->append(compactColor); } // Add fade parameters to named buffers { gpu::BufferPointer fadeBuffer1 = batch.getNamedBuffer(instanceName, INSTANCE_FADE_BUFFER1); gpu::BufferPointer fadeBuffer2 = batch.getNamedBuffer(instanceName, INSTANCE_FADE_BUFFER2); gpu::BufferPointer fadeBuffer3 = batch.getNamedBuffer(instanceName, INSTANCE_FADE_BUFFER3); // Pack parameters in 3 vec4s glm::vec4 fadeData1; glm::vec4 fadeData2; glm::vec4 fadeData3; FadeEffect::packToAttributes(fadeCategory, fadeThreshold, fadeNoiseOffset, fadeBaseOffset, fadeBaseInvSize, fadeData1, fadeData2, fadeData3); fadeBuffer1->append(fadeData1); fadeBuffer2->append(fadeData2); fadeBuffer3->append(fadeData3); } // Add call to named buffer batch.setupNamedCalls(instanceName, [args, isWire, pipeline, shape](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { auto& buffers = data.buffers; batch.setPipeline(pipeline->pipeline); pipeline->prepare(batch, args); if (isWire) { DependencyManager::get<GeometryCache>()->renderWireFadeShapeInstances(batch, shape, data.count(), buffers[INSTANCE_COLOR_BUFFER], buffers[INSTANCE_FADE_BUFFER1], buffers[INSTANCE_FADE_BUFFER2], buffers[INSTANCE_FADE_BUFFER3]); } else { DependencyManager::get<GeometryCache>()->renderFadeShapeInstances(batch, shape, data.count(), buffers[INSTANCE_COLOR_BUFFER], buffers[INSTANCE_FADE_BUFFER1], buffers[INSTANCE_FADE_BUFFER2], buffers[INSTANCE_FADE_BUFFER3]); } });} void GeometryCache::renderSolidShapeInstance(RenderArgs* args, gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderInstances(args, batch, color, false, pipeline, shape);} void GeometryCache::renderWireShapeInstance(RenderArgs* args, gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderInstances(args, batch, color, true, pipeline, shape);} void GeometryCache::renderSolidFadeShapeInstance(RenderArgs* args, gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, int fadeCategory, float fadeThreshold, const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderFadeInstances(args, batch, color, fadeCategory, fadeThreshold, fadeNoiseOffset, fadeBaseOffset, fadeBaseInvSize, false, pipeline, shape);} void GeometryCache::renderWireFadeShapeInstance(RenderArgs* args, gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, int fadeCategory, float fadeThreshold, const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderFadeInstances(args, batch, color, fadeCategory, fadeThreshold, fadeNoiseOffset, fadeBaseOffset, fadeBaseInvSize, true, pipeline, shape);} void GeometryCache::renderSolidSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderInstances(args, batch, color, false, pipeline, GeometryCache::Sphere);} void GeometryCache::renderWireSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderInstances(args, batch, color, true, pipeline, GeometryCache::Sphere);} // Enable this in a debug build to cause 'box' entities to iterate through all the// available shape types, both solid and wireframes//#define DEBUG_SHAPES void GeometryCache::renderSolidCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr);#ifdef DEBUG_SHAPES static auto startTime = usecTimestampNow(); renderInstances(INSTANCE_NAME, batch, color, pipeline, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { auto usecs = usecTimestampNow(); usecs -= startTime; auto msecs = usecs / USECS_PER_MSEC; float seconds = msecs; seconds /= MSECS_PER_SECOND; float fractionalSeconds = seconds - floor(seconds); int shapeIndex = (int)seconds; // Every second we flip to the next shape. static const int SHAPE_COUNT = 5; GeometryCache::Shape shapes[SHAPE_COUNT] = { GeometryCache::Cube, GeometryCache::Tetrahedron, GeometryCache::Sphere, GeometryCache::Icosahedron, GeometryCache::Line, }; shapeIndex %= SHAPE_COUNT; GeometryCache::Shape shape = shapes[shapeIndex]; // For the first half second for a given shape, show the wireframe, for the second half, show the solid. if (fractionalSeconds > 0.5f) { renderInstances(INSTANCE_NAME, batch, color, true, pipeline, shape); } else { renderInstances(INSTANCE_NAME, batch, color, false, pipeline, shape); } });#else renderInstances(args, batch, color, false, pipeline, GeometryCache::Cube);#endif} void GeometryCache::renderWireCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { static const std::string INSTANCE_NAME = __FUNCTION__; assert(pipeline != nullptr); renderInstances(args, batch, color, true, pipeline, GeometryCache::Cube);} graphics::MeshPointer GeometryCache::meshFromShape(Shape geometryShape, glm::vec3 color) { auto shapeData = getShapeData(geometryShape); qDebug() << "GeometryCache::getMeshProxyListFromShape" << shapeData << stringFromShape(geometryShape); auto positionsBufferView = buffer_helpers::clone(shapeData->_positionView); auto normalsBufferView = buffer_helpers::clone(shapeData->_normalView); auto indexBufferView = buffer_helpers::clone(shapeData->_indicesView); gpu::BufferView::Size numVertices = positionsBufferView.getNumElements(); Q_ASSERT(numVertices == normalsBufferView.getNumElements()); // apply input color across all vertices auto colorsBufferView = buffer_helpers::clone(shapeData->_normalView); for (gpu::BufferView::Size i = 0; i < numVertices; i++) { colorsBufferView.edit<glm::vec3>((gpu::BufferView::Index)i) = color; } graphics::MeshPointer mesh(new graphics::Mesh()); mesh->setVertexBuffer(positionsBufferView); mesh->setIndexBuffer(indexBufferView); mesh->addAttribute(gpu::Stream::NORMAL, normalsBufferView); mesh->addAttribute(gpu::Stream::COLOR, colorsBufferView); const auto startIndex = 0, baseVertex = 0; graphics::Mesh::Part part(startIndex, (graphics::Index)indexBufferView.getNumElements(), baseVertex, graphics::Mesh::TRIANGLES); auto partBuffer = new gpu::Buffer(sizeof(graphics::Mesh::Part), (gpu::Byte*)&part); mesh->setPartBuffer(gpu::BufferView(partBuffer, gpu::Element::PART_DRAWCALL)); mesh->modelName = GeometryCache::stringFromShape(geometryShape).toStdString(); mesh->displayName = QString("GeometryCache/shape::%1").arg(GeometryCache::stringFromShape(geometryShape)).toStdString(); return mesh;Lines 2001–2500 of 2501. Previous lines Load more
details.verticesBuffer->append(sizeof(vertexBuffer), (gpu::Byte*) vertexBuffer); details.colorBuffer->append(sizeof(colors), (gpu::Byte*) colors); #ifdef WANT_DEBUG if (id == UNKNOWN_ID) { qCDebug(renderutils) << "new renderLine() 2D VBO made -- _line3DVBOs.size():" << _line2DVBOs.size(); } else { qCDebug(renderutils) << "new registered renderLine() 2D VBO made -- _registeredLine2DVBOs.size():" << _registeredLine2DVBOs.size(); } #endif } // this is what it takes to render a quad batch.setInputFormat(details.streamFormat); batch.setInputStream(0, *details.stream); batch.draw(gpu::LINES, 2, 0); } void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const glm::vec3& p2, const glm::vec4& color, float glowIntensity, float glowWidth, int id) { // Disable glow lines on OSX #ifndef Q_OS_WIN glowIntensity = 0.0f; #endif if (glowIntensity <= 0.0f) { if (color.a >= 1.0f) { bindSimpleProgram(batch, false, false, false, true, true); } else { bindSimpleProgram(batch, false, true, false, true, true); } renderLine(batch, p1, p2, color, id); return; } // Compile the shaders static std::once_flag once; std::call_once(once, [&] { auto state = std::make_shared<gpu::State>(); auto program = gpu::Shader::createProgram(shader::render_utils::program::glowLine); state->setCullMode(gpu::State::CULL_NONE); state->setDepthTest(true, false, gpu::LESS_EQUAL); state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); PrepareStencil::testMask(*state); _glowLinePipeline = gpu::Pipeline::create(program, state); }); batch.setPipeline(_glowLinePipeline); Vec3Pair key(p1, p2); bool registered = (id != UNKNOWN_ID); BatchItemDetails& details = _registeredLine3DVBOs[id]; // if this is a registered quad, and we have buffers, then check to see if the geometry changed and rebuild if needed if (registered && details.isCreated) { Vec3Pair& lastKey = _lastRegisteredLine3D[id]; if (lastKey != key) { details.clear(); _lastRegisteredLine3D[id] = key; } } const int NUM_VERTICES = 4; if (!details.isCreated) { details.isCreated = true; details.uniformBuffer = std::make_shared<gpu::Buffer>(); struct LineData { vec4 p1; vec4 p2; vec4 color; float width; }; LineData lineData { vec4(p1, 1.0f), vec4(p2, 1.0f), color, glowWidth }; details.uniformBuffer->resize(sizeof(LineData)); details.uniformBuffer->setSubData(0, lineData); } // The shader requires no vertices, only uniforms. batch.setUniformBuffer(0, details.uniformBuffer); batch.draw(gpu::TRIANGLE_STRIP, NUM_VERTICES, 0); } void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { static std::once_flag once; std::call_once(once, [&]() { auto program = gpu::Shader::createProgram(shader::render_utils::program::standardDrawTexture); auto state = std::make_shared<gpu::State>(); // enable decal blend state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); PrepareStencil::testMask(*state); _standardDrawPipeline = gpu::Pipeline::create(program, state); auto stateNoBlend = std::make_shared<gpu::State>(); PrepareStencil::testMaskDrawShape(*stateNoBlend); auto programNoBlend = gpu::Shader::createProgram(shader::render_utils::program::standardDrawTextureNoBlend); _standardDrawPipelineNoBlend = gpu::Pipeline::create(programNoBlend, stateNoBlend); }); if (noBlend) { batch.setPipeline(_standardDrawPipelineNoBlend); } else { batch.setPipeline(_standardDrawPipeline); } } void GeometryCache::useGridPipeline(gpu::Batch& batch, GridBuffer gridBuffer, bool isLayered) { if (!_gridPipeline) { auto program = gpu::Shader::createProgram(shader::render_utils::program::grid); _gridSlot = 0; auto stateLayered = std::make_shared<gpu::State>(); stateLayered->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); PrepareStencil::testMask(*stateLayered); _gridPipelineLayered = gpu::Pipeline::create(program, stateLayered); auto state = std::make_shared<gpu::State>(stateLayered->getValues()); const float DEPTH_BIAS = 0.001f; state->setDepthBias(DEPTH_BIAS); state->setDepthTest(true, false, gpu::LESS_EQUAL); PrepareStencil::testMaskDrawShape(*state); _gridPipeline = gpu::Pipeline::create(program, state); } gpu::PipelinePointer pipeline = isLayered ? _gridPipelineLayered : _gridPipeline; batch.setPipeline(pipeline); batch.setUniformBuffer(_gridSlot, gridBuffer); } class SimpleProgramKey { public: enum FlagBit { IS_TEXTURED_FLAG = 0, IS_TRANSPARENT_FLAG, IS_CULLED_FLAG, IS_UNLIT_FLAG, HAS_DEPTH_BIAS_FLAG, IS_FADING_FLAG, IS_ANTIALIASED_FLAG, NUM_FLAGS, }; enum Flag { IS_TEXTURED = (1 << IS_TEXTURED_FLAG), IS_TRANSPARENT = (1 << IS_TRANSPARENT_FLAG), IS_CULLED = (1 << IS_CULLED_FLAG), IS_UNLIT = (1 << IS_UNLIT_FLAG), HAS_DEPTH_BIAS = (1 << HAS_DEPTH_BIAS_FLAG), IS_FADING = (1 << IS_FADING_FLAG), IS_ANTIALIASED = (1 << IS_ANTIALIASED_FLAG), }; typedef unsigned short Flags; bool isFlag(short flagNum) const { return bool((_flags & flagNum) != 0); } bool isTextured() const { return isFlag(IS_TEXTURED); } bool isTransparent() const { return isFlag(IS_TRANSPARENT); } bool isCulled() const { return isFlag(IS_CULLED); } bool isUnlit() const { return isFlag(IS_UNLIT); } bool hasDepthBias() const { return isFlag(HAS_DEPTH_BIAS); } bool isFading() const { return isFlag(IS_FADING); } bool isAntiAliased() const { return isFlag(IS_ANTIALIASED); } Flags _flags = 0; short _spare = 0; int getRaw() const { return *reinterpret_cast<const int*>(this); } SimpleProgramKey(bool textured = false, bool transparent = false, bool culled = true, bool unlit = false, bool depthBias = false, bool fading = false, bool isAntiAliased = true) { _flags = (textured ? IS_TEXTURED : 0) | (transparent ? IS_TRANSPARENT : 0) | (culled ? IS_CULLED : 0) | (unlit ? IS_UNLIT : 0) | (depthBias ? HAS_DEPTH_BIAS : 0) | (fading ? IS_FADING : 0) | (isAntiAliased ? IS_ANTIALIASED : 0); } SimpleProgramKey(int bitmask) : _flags(bitmask) {} }; inline uint qHash(const SimpleProgramKey& key, uint seed) { return qHash(key.getRaw(), seed); } inline bool operator==(const SimpleProgramKey& a, const SimpleProgramKey& b) { return a.getRaw() == b.getRaw(); } static void buildWebShader(int programId, bool blendEnable, gpu::ShaderPointer& shaderPointerOut, gpu::PipelinePointer& pipelinePointerOut) { shaderPointerOut = gpu::Shader::createProgram(programId); auto state = std::make_shared<gpu::State>(); state->setCullMode(gpu::State::CULL_NONE); state->setDepthTest(true, true, gpu::LESS_EQUAL); state->setBlendFunction(blendEnable, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); PrepareStencil::testMaskDrawShapeNoAA(*state); pipelinePointerOut = gpu::Pipeline::create(shaderPointerOut, state); } void GeometryCache::bindWebBrowserProgram(gpu::Batch& batch, bool transparent) { batch.setPipeline(getWebBrowserProgram(transparent)); } gpu::PipelinePointer GeometryCache::getWebBrowserProgram(bool transparent) { static std::once_flag once; std::call_once(once, [&]() { buildWebShader(shader::render_utils::program::simple_opaque_web_browser, false, _simpleOpaqueWebBrowserShader, _simpleOpaqueWebBrowserPipeline); buildWebShader(shader::render_utils::program::simple_transparent_web_browser, true, _simpleTransparentWebBrowserShader, _simpleTransparentWebBrowserPipeline); }); return transparent ? _simpleTransparentWebBrowserPipeline : _simpleOpaqueWebBrowserPipeline; } void GeometryCache::bindSimpleProgram(gpu::Batch& batch, bool textured, bool transparent, bool culled, bool unlit, bool depthBiased, bool isAntiAliased) { batch.setPipeline(getSimplePipeline(textured, transparent, culled, unlit, depthBiased, false, isAntiAliased)); // If not textured, set a default albedo map if (!textured) { batch.setResourceTexture(gr::Texture::MaterialAlbedo, DependencyManager::get<TextureCache>()->getWhiteTexture()); } } gpu::PipelinePointer GeometryCache::getSimplePipeline(bool textured, bool transparent, bool culled, bool unlit, bool depthBiased, bool fading, bool isAntiAliased) { SimpleProgramKey config { textured, transparent, culled, unlit, depthBiased, fading, isAntiAliased }; // If the pipeline already exists, return it auto it = _simplePrograms.find(config); if (it != _simplePrograms.end()) { return it.value(); } // Compile the shaders if (!fading) { static std::once_flag once; std::call_once(once, [&]() { using namespace shader::render_utils::program; auto PS = DISABLE_DEFERRED ? forward_simple_textured : simple_textured; // Use the forward pipeline for both here, otherwise transparents will be unlit auto PSTransparent = DISABLE_DEFERRED ? forward_simple_textured_transparent : forward_simple_textured_transparent; auto PSUnlit = DISABLE_DEFERRED ? forward_simple_textured_unlit : simple_textured_unlit; _simpleShader = gpu::Shader::createProgram(PS); _transparentShader = gpu::Shader::createProgram(PSTransparent); _unlitShader = gpu::Shader::createProgram(PSUnlit); }); } else { static std::once_flag once; std::call_once(once, [&]() { using namespace shader::render_utils::program; _simpleFadeShader = gpu::Shader::createProgram(DISABLE_DEFERRED ? forward_simple_textured : simple_textured_fade); _unlitFadeShader = gpu::Shader::createProgram(DISABLE_DEFERRED ? forward_simple_textured_unlit : simple_textured_unlit_fade); }); } // If the pipeline did not exist, make it auto state = std::make_shared<gpu::State>(); if (config.isCulled()) { state->setCullMode(gpu::State::CULL_BACK); } else { state->setCullMode(gpu::State::CULL_NONE); } state->setDepthTest(true, true, gpu::LESS_EQUAL); if (config.hasDepthBias()) { state->setDepthBias(1.0f); state->setDepthBiasSlopeScale(1.0f); } state->setBlendFunction(config.isTransparent(), gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); if (config.isAntiAliased()) { config.isTransparent() ? PrepareStencil::testMask(*state) : PrepareStencil::testMaskDrawShape(*state); } else { PrepareStencil::testMaskDrawShapeNoAA(*state); } gpu::ShaderPointer program = (config.isUnlit()) ? (config.isFading() ? _unlitFadeShader : _unlitShader) : (config.isFading() ? _simpleFadeShader : (config.isTransparent() ? _transparentShader : _simpleShader)); gpu::PipelinePointer pipeline = gpu::Pipeline::create(program, state); _simplePrograms.insert(config, pipeline); return pipeline; } uint32_t toCompactColor(const glm::vec4& color) { uint32_t compactColor = ((int(color.x * 255.0f) & 0xFF)) | ((int(color.y * 255.0f) & 0xFF) << 8) | ((int(color.z * 255.0f) & 0xFF) << 16) | ((int(color.w * 255.0f) & 0xFF) << 24); return compactColor; } static const size_t INSTANCE_COLOR_BUFFER = 0; void renderInstances(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, bool isWire, const render::ShapePipelinePointer& pipeline, GeometryCache::Shape shape) { // Add pipeline to name std::string instanceName = (isWire ? "wire_shapes_" : "solid_shapes_") + std::to_string(shape) + "_" + std::to_string(std::hash<render::ShapePipelinePointer>()(pipeline)); // Add color to named buffer { gpu::BufferPointer instanceColorBuffer = batch.getNamedBuffer(instanceName, INSTANCE_COLOR_BUFFER); auto compactColor = toCompactColor(color); instanceColorBuffer->append(compactColor); } // Add call to named buffer batch.setupNamedCalls(instanceName, [args, isWire, pipeline, shape](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { batch.setPipeline(pipeline->pipeline); pipeline->prepare(batch, args); if (isWire) { DependencyManager::get<GeometryCache>()->renderWireShapeInstances(batch, shape, data.count(), data.buffers[INSTANCE_COLOR_BUFFER]); } else { DependencyManager::get<GeometryCache>()->renderShapeInstances(batch, shape, data.count(), data.buffers[INSTANCE_COLOR_BUFFER]); } }); } static const size_t INSTANCE_FADE_BUFFER1 = 1; static const size_t INSTANCE_FADE_BUFFER2 = 2; static const size_t INSTANCE_FADE_BUFFER3 = 3; void renderFadeInstances(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, int fadeCategory, float fadeThreshold, const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, bool isWire, const render::ShapePipelinePointer& pipeline, GeometryCache::Shape shape) { // Add pipeline to name std::string instanceName = (isWire ? "wire_shapes_" : "solid_shapes_") + std::to_string(shape) + "_" + std::to_string(std::hash<render::ShapePipelinePointer>()(pipeline)); // Add color to named buffer { gpu::BufferPointer instanceColorBuffer = batch.getNamedBuffer(instanceName, INSTANCE_COLOR_BUFFER); auto compactColor = toCompactColor(color); instanceColorBuffer->append(compactColor); } // Add fade parameters to named buffers { gpu::BufferPointer fadeBuffer1 = batch.getNamedBuffer(instanceName, INSTANCE_FADE_BUFFER1); gpu::BufferPointer fadeBuffer2 = batch.getNamedBuffer(instanceName, INSTANCE_FADE_BUFFER2); gpu::BufferPointer fadeBuffer3 = batch.getNamedBuffer(instanceName, INSTANCE_FADE_BUFFER3); // Pack parameters in 3 vec4s glm::vec4 fadeData1; glm::vec4 fadeData2; glm::vec4 fadeData3; FadeEffect::packToAttributes(fadeCategory, fadeThreshold, fadeNoiseOffset, fadeBaseOffset, fadeBaseInvSize, fadeData1, fadeData2, fadeData3); fadeBuffer1->append(fadeData1); fadeBuffer2->append(fadeData2); fadeBuffer3->append(fadeData3); } // Add call to named buffer batch.setupNamedCalls(instanceName, [args, isWire, pipeline, shape](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { auto& buffers = data.buffers; batch.setPipeline(pipeline->pipeline); pipeline->prepare(batch, args); if (isWire) { DependencyManager::get<GeometryCache>()->renderWireFadeShapeInstances(batch, shape, data.count(), buffers[INSTANCE_COLOR_BUFFER], buffers[INSTANCE_FADE_BUFFER1], buffers[INSTANCE_FADE_BUFFER2], buffers[INSTANCE_FADE_BUFFER3]); } else { DependencyManager::get<GeometryCache>()->renderFadeShapeInstances(batch, shape, data.count(), buffers[INSTANCE_COLOR_BUFFER], buffers[INSTANCE_FADE_BUFFER1], buffers[INSTANCE_FADE_BUFFER2], buffers[INSTANCE_FADE_BUFFER3]); } }); } void GeometryCache::renderSolidShapeInstance(RenderArgs* args, gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderInstances(args, batch, color, false, pipeline, shape); } void GeometryCache::renderWireShapeInstance(RenderArgs* args, gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderInstances(args, batch, color, true, pipeline, shape); } void GeometryCache::renderSolidFadeShapeInstance(RenderArgs* args, gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, int fadeCategory, float fadeThreshold, const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderFadeInstances(args, batch, color, fadeCategory, fadeThreshold, fadeNoiseOffset, fadeBaseOffset, fadeBaseInvSize, false, pipeline, shape); } void GeometryCache::renderWireFadeShapeInstance(RenderArgs* args, gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, int fadeCategory, float fadeThreshold, const glm::vec3& fadeNoiseOffset, const glm::vec3& fadeBaseOffset, const glm::vec3& fadeBaseInvSize, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderFadeInstances(args, batch, color, fadeCategory, fadeThreshold, fadeNoiseOffset, fadeBaseOffset, fadeBaseInvSize, true, pipeline, shape); } void GeometryCache::renderSolidSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderInstances(args, batch, color, false, pipeline, GeometryCache::Sphere); } void GeometryCache::renderWireSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); renderInstances(args, batch, color, true, pipeline, GeometryCache::Sphere); } // Enable this in a debug build to cause 'box' entities to iterate through all the // available shape types, both solid and wireframes //#define DEBUG_SHAPES void GeometryCache::renderSolidCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { assert(pipeline != nullptr); #ifdef DEBUG_SHAPES static auto startTime = usecTimestampNow(); renderInstances(INSTANCE_NAME, batch, color, pipeline, [](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { auto usecs = usecTimestampNow(); usecs -= startTime; auto msecs = usecs / USECS_PER_MSEC; float seconds = msecs; seconds /= MSECS_PER_SECOND; float fractionalSeconds = seconds - floor(seconds); int shapeIndex = (int)seconds; // Every second we flip to the next shape. static const int SHAPE_COUNT = 5; GeometryCache::Shape shapes[SHAPE_COUNT] = { GeometryCache::Cube, GeometryCache::Tetrahedron, GeometryCache::Sphere, GeometryCache::Icosahedron, GeometryCache::Line, }; shapeIndex %= SHAPE_COUNT; GeometryCache::Shape shape = shapes[shapeIndex]; // For the first half second for a given shape, show the wireframe, for the second half, show the solid. if (fractionalSeconds > 0.5f) { renderInstances(INSTANCE_NAME, batch, color, true, pipeline, shape); } else { renderInstances(INSTANCE_NAME, batch, color, false, pipeline, shape); } }); #else renderInstances(args, batch, color, false, pipeline, GeometryCache::Cube); #endif } void GeometryCache::renderWireCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) { static const std::string INSTANCE_NAME = __FUNCTION__; assert(pipeline != nullptr); renderInstances(args, batch, color, true, pipeline, GeometryCache::Cube); } graphics::MeshPointer GeometryCache::meshFromShape(Shape geometryShape, glm::vec3 color) { auto shapeData = getShapeData(geometryShape); qDebug() << "GeometryCache::getMeshProxyListFromShape" << shapeData << stringFromShape(geometryShape); auto positionsBufferView = buffer_helpers::clone(shapeData->_positionView); auto normalsBufferView = buffer_helpers::clone(shapeData->_normalView); auto indexBufferView = buffer_helpers::clone(shapeData->_indicesView); gpu::BufferView::Size numVertices = positionsBufferView.getNumElements(); Q_ASSERT(numVertices == normalsBufferView.getNumElements()); // apply input color across all vertices auto colorsBufferView = buffer_helpers::clone(shapeData->_normalView); for (gpu::BufferView::Size i = 0; i < numVertices; i++) { colorsBufferView.edit<glm::vec3>((gpu::BufferView::Index)i) = color; } graphics::MeshPointer mesh(new graphics::Mesh()); mesh->setVertexBuffer(positionsBufferView); mesh->setIndexBuffer(indexBufferView); mesh->addAttribute(gpu::Stream::NORMAL, normalsBufferView); mesh->addAttribute(gpu::Stream::COLOR, colorsBufferView); const auto startIndex = 0, baseVertex = 0; graphics::Mesh::Part part(startIndex, (graphics::Index)indexBufferView.getNumElements(), baseVertex, graphics::Mesh::TRIANGLES); auto partBuffer = new gpu::Buffer(sizeof(graphics::Mesh::Part), (gpu::Byte*)&part); mesh->setPartBuffer(gpu::BufferView(partBuffer, gpu::Element::PART_DRAWCALL)); mesh->modelName = GeometryCache::stringFromShape(geometryShape).toStdString(); mesh->displayName = QString("GeometryCache/shape::%1").arg(GeometryCache::stringFromShape(geometryShape)).toStdString(); return mesh;