void ApplicationCompositor::buildHemiVertices( const float fov, const float aspectRatio, const int slices, const int stacks) { static float textureFOV = 0.0f, textureAspectRatio = 1.0f; if (textureFOV == fov && textureAspectRatio == aspectRatio) { return; } textureFOV = fov; textureAspectRatio = aspectRatio; auto geometryCache = DependencyManager::get<GeometryCache>(); _hemiVertices = gpu::BufferPointer(new gpu::Buffer()); _hemiIndices = gpu::BufferPointer(new gpu::Buffer()); if (fov >= PI) { qDebug() << "TexturedHemisphere::buildVBO(): FOV greater or equal than Pi will create issues"; } //UV mapping source: http://www.mvps.org/directx/articles/spheremap.htm vec3 pos; vec2 uv; // Compute vertices positions and texture UV coordinate // Create and write to buffer for (int i = 0; i < stacks; i++) { uv.y = (float)i / (float)(stacks - 1); // First stack is 0.0f, last stack is 1.0f // abs(theta) <= fov / 2.0f float pitch = -fov * (uv.y - 0.5f); for (int j = 0; j < slices; j++) { uv.x = (float)j / (float)(slices - 1); // First slice is 0.0f, last slice is 1.0f // abs(phi) <= fov * aspectRatio / 2.0f float yaw = -fov * aspectRatio * (uv.x - 0.5f); pos = getPoint(yaw, pitch); static const vec4 color(1); _hemiVertices->append(sizeof(pos), (gpu::Byte*)&pos); _hemiVertices->append(sizeof(vec2), (gpu::Byte*)&uv); _hemiVertices->append(sizeof(vec4), (gpu::Byte*)&color); } } // Compute number of indices needed static const int VERTEX_PER_TRANGLE = 3; static const int TRIANGLE_PER_RECTANGLE = 2; int numberOfRectangles = (slices - 1) * (stacks - 1); _hemiIndexCount = numberOfRectangles * TRIANGLE_PER_RECTANGLE * VERTEX_PER_TRANGLE; // Compute indices order std::vector<GLushort> indices; for (int i = 0; i < stacks - 1; i++) { for (int j = 0; j < slices - 1; j++) { GLushort bottomLeftIndex = i * slices + j; GLushort bottomRightIndex = bottomLeftIndex + 1; GLushort topLeftIndex = bottomLeftIndex + slices; GLushort topRightIndex = topLeftIndex + 1; // FIXME make a z-order curve for better vertex cache locality indices.push_back(topLeftIndex); indices.push_back(bottomLeftIndex); indices.push_back(topRightIndex); indices.push_back(topRightIndex); indices.push_back(bottomLeftIndex); indices.push_back(bottomRightIndex); } } _hemiIndices->append(sizeof(GLushort) * indices.size(), (gpu::Byte*)&indices[0]); }
void draw() { // Attempting to draw before we're visible and have a valid size will // produce GL errors. if (!isVisible() || _size.width() <= 0 || _size.height() <= 0) { return; } makeCurrent(); gpu::Batch batch; batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 0.0f, 0.0f, 0.0f, 1.0f }); batch.clearDepthFramebuffer(1e4); batch.setViewportTransform({ 0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio() }); batch.setProjectionTransform(_projectionMatrix); float t = _time.elapsed() * 1e-3f; glm::vec3 unitscale { 1.0f }; glm::vec3 up { 0.0f, 1.0f, 0.0f }; float distance = 3.0f; glm::vec3 camera_position{ distance * sinf(t), 0.0f, distance * cosf(t) }; static const vec3 camera_focus(0); static const vec3 camera_up(0, 1, 0); glm::mat4 camera = glm::inverse(glm::lookAt(camera_position, camera_focus, up)); batch.setViewTransform(camera); batch.setPipeline(_pipeline); batch.setModelTransform(Transform()); auto geometryCache = DependencyManager::get<GeometryCache>(); // Render grid on xz plane (not the optimal way to do things, but w/e) // Note: GeometryCache::renderGrid will *not* work, as it is apparenly unaffected by batch rotations and renders xy only { static const std::string GRID_INSTANCE = "Grid"; static auto compactColor1 = toCompactColor(vec4{ 0.35f, 0.25f, 0.15f, 1.0f }); static auto compactColor2 = toCompactColor(vec4{ 0.15f, 0.25f, 0.35f, 1.0f }); static std::vector<glm::mat4> transforms; static gpu::BufferPointer colorBuffer; if (!transforms.empty()) { transforms.reserve(200); colorBuffer = std::make_shared<gpu::Buffer>(); for (int i = 0; i < 100; ++i) { { glm::mat4 transform = glm::translate(mat4(), vec3(0, -1, -50 + i)); transform = glm::scale(transform, vec3(100, 1, 1)); transforms.push_back(transform); colorBuffer->append(compactColor1); } { glm::mat4 transform = glm::mat4_cast(quat(vec3(0, PI / 2.0f, 0))); transform = glm::translate(transform, vec3(0, -1, -50 + i)); transform = glm::scale(transform, vec3(100, 1, 1)); transforms.push_back(transform); colorBuffer->append(compactColor2); } } } auto pipeline = geometryCache->getSimplePipeline(); for (auto& transform : transforms) { batch.setModelTransform(transform); batch.setupNamedCalls(GRID_INSTANCE, [=](gpu::Batch& batch, gpu::Batch::NamedBatchData& data) { batch.setViewTransform(camera); batch.setPipeline(_pipeline); geometryCache->renderWireShapeInstances(batch, GeometryCache::Line, data.count(), colorBuffer); }); } } { static const size_t ITEM_COUNT = 1000; static const float SHAPE_INTERVAL = (PI * 2.0f) / ITEM_COUNT; static const float ITEM_INTERVAL = SHAPE_INTERVAL / TYPE_COUNT; static const gpu::Element POSITION_ELEMENT{ gpu::VEC3, gpu::FLOAT, gpu::XYZ }; static const gpu::Element NORMAL_ELEMENT{ gpu::VEC3, gpu::FLOAT, gpu::XYZ }; static const gpu::Element COLOR_ELEMENT{ gpu::VEC4, gpu::NUINT8, gpu::RGBA }; static const gpu::Element TRANSFORM_ELEMENT{ gpu::MAT4, gpu::FLOAT, gpu::XYZW }; static std::vector<Transform> transforms; static std::vector<vec4> colors; static gpu::BufferPointer indirectBuffer; static gpu::BufferPointer transformBuffer; static gpu::BufferPointer colorBuffer; static gpu::BufferView colorView; static gpu::BufferView instanceXfmView; if (!transformBuffer) { transformBuffer = std::make_shared<gpu::Buffer>(); colorBuffer = std::make_shared<gpu::Buffer>(); indirectBuffer = std::make_shared<gpu::Buffer>(); static const float ITEM_RADIUS = 20; static const vec3 ITEM_TRANSLATION{ 0, 0, -ITEM_RADIUS }; for (size_t i = 0; i < TYPE_COUNT; ++i) { GeometryCache::Shape shape = SHAPE[i]; GeometryCache::ShapeData shapeData = geometryCache->_shapes[shape]; { gpu::Batch::DrawIndexedIndirectCommand indirectCommand; indirectCommand._count = (uint)shapeData._indexCount; indirectCommand._instanceCount = ITEM_COUNT; indirectCommand._baseInstance = (uint)(i * ITEM_COUNT); indirectCommand._firstIndex = (uint)shapeData._indexOffset / 2; indirectCommand._baseVertex = 0; indirectBuffer->append(indirectCommand); } //indirectCommand._count float startingInterval = ITEM_INTERVAL * i; for (size_t j = 0; j < ITEM_COUNT; ++j) { float theta = j * SHAPE_INTERVAL + startingInterval; auto transform = glm::rotate(mat4(), theta, Vectors::UP); transform = glm::rotate(transform, (randFloat() - 0.5f) * PI / 4.0f, Vectors::UNIT_X); transform = glm::translate(transform, ITEM_TRANSLATION); transform = glm::scale(transform, vec3(randFloat() / 2.0f + 0.5f)); transformBuffer->append(transform); transforms.push_back(transform); auto color = vec4{ randomColorValue(64), randomColorValue(64), randomColorValue(64), 255 }; color /= 255.0f; colors.push_back(color); colorBuffer->append(toCompactColor(color)); } } colorView = gpu::BufferView(colorBuffer, COLOR_ELEMENT); instanceXfmView = gpu::BufferView(transformBuffer, TRANSFORM_ELEMENT); } #if 1 GeometryCache::ShapeData shapeData = geometryCache->_shapes[GeometryCache::Icosahedron]; { batch.setViewTransform(camera); batch.setModelTransform(Transform()); batch.setPipeline(_pipeline); batch.setInputFormat(getInstancedSolidStreamFormat()); batch.setInputBuffer(gpu::Stream::COLOR, colorView); batch.setIndirectBuffer(indirectBuffer); shapeData.setupBatch(batch); batch.multiDrawIndexedIndirect(TYPE_COUNT, gpu::TRIANGLES); } #else batch.setViewTransform(camera); batch.setPipeline(_pipeline); for (size_t i = 0; i < TYPE_COUNT; ++i) { GeometryCache::Shape shape = SHAPE[i]; for (size_t j = 0; j < ITEM_COUNT; ++j) { int index = i * ITEM_COUNT + j; batch.setModelTransform(transforms[index]); const vec4& color = colors[index]; batch._glColor4f(color.r, color.g, color.b, 1.0); geometryCache->renderShape(batch, shape); } } #endif } // Render unlit cube + sphere static auto startUsecs = usecTimestampNow(); float seconds = getSeconds(startUsecs); seconds /= 4.0f; int shapeIndex = ((int)seconds) % TYPE_COUNT; bool wire = (seconds - floorf(seconds) > 0.5f); batch.setModelTransform(Transform()); batch._glColor4f(0.8f, 0.25f, 0.25f, 1.0f); if (wire) { geometryCache->renderWireShape(batch, SHAPE[shapeIndex]); } else { geometryCache->renderShape(batch, SHAPE[shapeIndex]); } batch.setModelTransform(Transform().setScale(2.05f)); batch._glColor4f(1, 1, 1, 1); geometryCache->renderWireCube(batch); _context->render(batch); _qGlContext.swapBuffers(this); fps.increment(); if (fps.elapsed() >= 0.5f) { qDebug() << "FPS: " << fps.rate(); fps.reset(); } }