Exemple #1
0
void VoxelTree::createSphere(float radius, float xc, float yc, float zc, float voxelSize,
                             bool solid, creationMode mode, bool destructive, bool debug) {

    bool wantColorRandomizer = (mode == RANDOM);
    bool wantNaturalSurface  = (mode == NATURAL);
    bool wantNaturalColor    = (mode == NATURAL);

    // About the color of the sphere... we're going to make this sphere be a mixture of two colors
    // in NATURAL mode, those colors will be green dominant and blue dominant. In GRADIENT mode we
    // will randomly pick which color family red, green, or blue to be dominant. In RANDOM mode we
    // ignore these dominant colors and make every voxel a completely random color.
    unsigned char r1, g1, b1, r2, g2, b2;

    if (wantNaturalColor) {
        r1 = r2 = b2 = g1 = 0;
        b1 = g2 = 255;
    } else if (!wantColorRandomizer) {
        unsigned char dominantColor1 = randIntInRange(1, 3); //1=r, 2=g, 3=b dominant
        unsigned char dominantColor2 = randIntInRange(1, 3);

        if (dominantColor1 == dominantColor2) {
            dominantColor2 = dominantColor1 + 1 % 3;
        }

        r1 = (dominantColor1 == 1) ? randIntInRange(200, 255) : randIntInRange(40, 100);
        g1 = (dominantColor1 == 2) ? randIntInRange(200, 255) : randIntInRange(40, 100);
        b1 = (dominantColor1 == 3) ? randIntInRange(200, 255) : randIntInRange(40, 100);
        r2 = (dominantColor2 == 1) ? randIntInRange(200, 255) : randIntInRange(40, 100);
        g2 = (dominantColor2 == 2) ? randIntInRange(200, 255) : randIntInRange(40, 100);
        b2 = (dominantColor2 == 3) ? randIntInRange(200, 255) : randIntInRange(40, 100);
    }

    // We initialize our rgb to be either "grey" in case of randomized surface, or
    // the average of the gradient, in the case of the gradient sphere.
    unsigned char red   = wantColorRandomizer ? 128 : (r1 + r2) / 2; // average of the colors
    unsigned char green = wantColorRandomizer ? 128 : (g1 + g2) / 2;
    unsigned char blue  = wantColorRandomizer ? 128 : (b1 + b2) / 2;

    // I want to do something smart like make these inside circles with bigger voxels, but this doesn't seem to work.
    float thisVoxelSize = voxelSize; // radius / 2.0f;
    float thisRadius = 0.0;
    if (!solid) {
        thisRadius = radius; // just the outer surface
        thisVoxelSize = voxelSize;
    }

    // If you also iterate form the interior of the sphere to the radius, making
    // larger and larger spheres you'd end up with a solid sphere. And lots of voxels!
    bool lastLayer = false;
    while (!lastLayer) {
        lastLayer = (thisRadius + (voxelSize * 2.0) >= radius);

        // We want to make sure that as we "sweep" through our angles we use a delta angle that voxelSize
        // small enough to not skip any voxels we can calculate theta from our desired arc length
        //      lenArc = ndeg/360deg * 2pi*R  --->  lenArc = theta/2pi * 2pi*R
        //      lenArc = theta*R ---> theta = lenArc/R ---> theta = g/r
        float angleDelta = (thisVoxelSize / thisRadius);

        if (debug) {
            int percentComplete = 100 * (thisRadius/radius);
            qDebug("percentComplete=%d\n",percentComplete);
        }

        for (float theta=0.0; theta <= 2 * M_PI; theta += angleDelta) {
            for (float phi=0.0; phi <= M_PI; phi += angleDelta) {
                bool naturalSurfaceRendered = false;
                float x = xc + thisRadius * cos(theta) * sin(phi);
                float y = yc + thisRadius * sin(theta) * sin(phi);
                float z = zc + thisRadius * cos(phi);

                // if we're on the outer radius, then we do a couple of things differently.
                // 1) If we're in NATURAL mode we will actually draw voxels from our surface outward (from the surface) up
                //    some random height. This will give our sphere some contours.
                // 2) In all modes, we will use our "outer" color to draw the voxels. Otherwise we will use the average color
                if (lastLayer) {
                    if (false && debug) {
                        qDebug("adding candy shell: theta=%f phi=%f thisRadius=%f radius=%f\n",
                                 theta, phi, thisRadius,radius);
                    }
                    switch (mode) {
                    case RANDOM: {
                        red   = randomColorValue(165);
                        green = randomColorValue(165);
                        blue  = randomColorValue(165);
                    } break;
                    case GRADIENT: {
                        float gradient = (phi / M_PI);
                        red   = r1 + ((r2 - r1) * gradient);
                        green = g1 + ((g2 - g1) * gradient);
                        blue  = b1 + ((b2 - b1) * gradient);
                    } break;
                    case NATURAL: {
                        glm::vec3 position = glm::vec3(theta,phi,radius);
                        float perlin = glm::perlin(position) + .25f * glm::perlin(position * 4.f)
                                + .125f * glm::perlin(position * 16.f);
                        float gradient = (1.0f + perlin)/ 2.0f;
                        red   = (unsigned char)std::min(255, std::max(0, (int)(r1 + ((r2 - r1) * gradient))));
                        green = (unsigned char)std::min(255, std::max(0, (int)(g1 + ((g2 - g1) * gradient))));
                        blue  = (unsigned char)std::min(255, std::max(0, (int)(b1 + ((b2 - b1) * gradient))));
                        if (debug) {
                            qDebug("perlin=%f gradient=%f color=(%d,%d,%d)\n",perlin, gradient, red, green, blue);
                        }
                        } break;
                    }
                    if (wantNaturalSurface) {
                        // for natural surfaces, we will render up to 16 voxel's above the surface of the sphere
                        glm::vec3 position = glm::vec3(theta,phi,radius);
                        float perlin = glm::perlin(position) + .25f * glm::perlin(position * 4.f)
                                + .125f * glm::perlin(position * 16.f);
                        float gradient = (1.0f + perlin)/ 2.0f;

                        int height = (4 * gradient)+1; // make it at least 4 thick, so we get some averaging
                        float subVoxelScale = thisVoxelSize;
                        for (int i = 0; i < height; i++) {
                            x = xc + (thisRadius + i * subVoxelScale) * cos(theta) * sin(phi);
                            y = yc + (thisRadius + i * subVoxelScale) * sin(theta) * sin(phi);
                            z = zc + (thisRadius + i * subVoxelScale) * cos(phi);
                            this->createVoxel(x, y, z, subVoxelScale, red, green, blue, destructive);
                        }
                        naturalSurfaceRendered = true;
                    }
                }
                if (!naturalSurfaceRendered) {
                    this->createVoxel(x, y, z, thisVoxelSize, red, green, blue, destructive);
                }
            }
        }
        thisRadius += thisVoxelSize;
        thisVoxelSize = std::max(voxelSize, thisVoxelSize / 2.0f);
    }
}
Exemple #2
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();
        }
    }