Why is this simple (I think?) program flickering?

Hey everyone, first-time poster here :nerd_face:

I just started working with OPENRNDR. I have this rotating plumbob/gem program I’m working on, and since I added in some triangles via the contour tool, they always flicker in a weird way for me. I’ve attached the code and a video recording of my screen. I’m guessing it has something to do with depthWrite stuff not being optimized as well for custom shapes. If someone could enlighten me as to why this is happening and any potential solutions I would very much appreciate it!

plumbob-flicker

import org.openrndr.PresentationMode
import org.openrndr.Program
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.DepthTestPass
import org.openrndr.extra.olive.oliveProgram
import org.openrndr.extras.color.presets.DARK_SEA_GREEN
import org.openrndr.extras.color.presets.DIM_GRAY
import org.openrndr.extras.color.presets.SEA_GREEN
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
import org.openrndr.shape.contour
import kotlin.math.PI
import kotlin.math.sin

fun main() = application {
    configure {
        width = 800
        height = 800

    }
    oliveProgram {

        extend {
            drawer.clear(ColorRGBa.DIM_GRAY)
            fun setup() {
                drawer.translate(0.0, 0.0, -400.0)
                drawer.perspective(90.0, 1.0, 1.0, -300.0)
                drawer.depthWrite = true
                drawer.depthTestPass = DepthTestPass.LESS_OR_EQUAL
                drawer.stroke = ColorRGBa.DARK_SEA_GREEN
                drawer.strokeWeight = 5.0
                drawer.fill = ColorRGBa.SEA_GREEN
            }

            val ratioAS = 0.866
            val apothem = 100
            val side = apothem/ratioAS
            val rectWidth = side
            val rectHeight = 80.0

            val tempo = seconds * 0.5

            for (num in (0..5)) {
                setup()
                val offset = (PI/3.0) * num
                drawer.translate(
                    (sin(tempo + offset)) * apothem,
                    0.0,
                    sin(tempo + (0.5 * PI) + offset) * 100.0)
                drawer.rotate(
                    Vector3(0.0, 1.0, 0.0),
                    tempo * (360.0/(2.0* PI)) + (360.0 * (num/6.0))
                )

                drawer.rectangle(
                    -(rectWidth/2.0),
                    -(rectHeight/2.0),
                    rectWidth,
                    rectHeight
                )
                drawer.strokeWeight = 5.0
                val triangleWidth = rectWidth - drawer.strokeWeight
                drawer.translate(0.0, ((rectHeight + drawer.strokeWeight)/2.0));
                drawer.rotate(Vector3(1.0, 0.0, 0.0), -28.0)
                val c = contour {
                    moveTo(Vector2(-triangleWidth/2.0, 0.0))
                    lineTo(cursor + Vector2(triangleWidth, 0.0))
                    lineTo(cursor + Vector2(-triangleWidth/2.0, 200.0))
                    lineTo(anchor)
                    close()
                }
                drawer.contour(c)

                drawer.defaults()
            }

        }
    }
}

Hi @sourmike ! Welcome to the forum :slight_smile:

I had that same issue in the past. The issue is that most drawing operations are optimized for 2D, not for 3D. The current options for combining fills with contours in 3D are limited. Some places to look into 3D drawing are

I’ll post again if I have an example to run.

btw. I tweaked your code to show some helper functions:

imports
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.DepthTestPass
import org.openrndr.draw.isolated
import org.openrndr.extras.color.presets.DARK_SEA_GREEN
import org.openrndr.extras.color.presets.DIM_GRAY
import org.openrndr.extras.color.presets.SEA_GREEN
import org.openrndr.math.Polar
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
import org.openrndr.math.asDegrees
import org.openrndr.shape.Rectangle
import org.openrndr.shape.ShapeContour
fun main() = application {
    configure {
        width = 800
        height = 800
    }
    program {
        val ratioAS = 0.866
        val apothem = 100.0
        val rect = Rectangle.fromCenter(
            Vector2.ZERO, apothem / ratioAS, 80.0
        )
        val strokeW = 5.0
        val triangleWidth = rect.width - strokeW
        val tri = ShapeContour.fromPoints(
            listOf(
                Vector2(-triangleWidth / 2, 0.0),
                Vector2(triangleWidth / 2, 0.0),
                Vector2(0.0, 200.0)
            ), true
        )

        extend {
            drawer.apply {
                clear(ColorRGBa.DIM_GRAY)
                depthWrite = true
                depthTestPass = DepthTestPass.LESS_OR_EQUAL
                stroke = ColorRGBa.DARK_SEA_GREEN
                strokeWeight = strokeW
                fill = ColorRGBa.SEA_GREEN
                perspective(90.0, 1.0, 1.0, -300.0)
                translate(0.0, 0.0, -400.0)
            }

            val tempo = seconds * 0.5

            repeat (6) { num ->
                val angle = tempo.asDegrees + 60 * num
                val tr = Polar(angle, apothem).cartesian
                drawer.isolated {
                    translate(tr.y, 0.0, tr.x)
                    rotate(Vector3.UNIT_Y, angle)
                    rectangle(rect)

                    translate(0.0, (rect.height + strokeW) / 2.0);
                    rotate(Vector3.UNIT_X, -28.0)
                    contour(tri)
                }
            }
        }
    }
}
1 Like

Here I constructed a wireframe box. The vertex data for a filled box is provided by boxMesh() (orx-mesh-generators). It produces triangles to be filled, but the vertices needed for the edges are different ones. In the code below I generated the 12 edges of a cube.

apps.p5.P05_texturecubeWireframe-2022-03-14-10.05.19

Imports
import org.openrndr.WindowMultisample
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.*
import org.openrndr.extras.camera.Orbital
import org.openrndr.extras.color.presets.DARK_SEA_GREEN
import org.openrndr.extras.color.presets.DIM_GRAY
import org.openrndr.extras.color.presets.SEA_GREEN
import org.openrndr.extras.meshgenerators.boxMesh
import org.openrndr.math.Vector3
fun main() = application {
    configure {
        width = 400
        height = 400
        multisample = WindowMultisample.SampleCount(4)
    }
    program {
        val cam = Orbital()
        cam.eye = -Vector3.UNIT_Z * 150.0

        val cube = boxMesh()
        val cubeWire = vertexBuffer(vertexFormat {
            position(3)
        }, 12 * 2)

        val l = 0.5
        val o = -0.5
        val p = listOf(
            Vector3(o, o, o), Vector3(o, l, o), Vector3(l, l, o), Vector3(l, o, o), // front
            Vector3(o, o, l), Vector3(o, l, l), Vector3(l, l, l), Vector3(l, o, l) // back
        )
        cubeWire.put {
            write(p[0], p[1], p[1], p[2], p[2], p[3], p[3], p[0]) // front
            write(p[4], p[5], p[5], p[6], p[6], p[7], p[7], p[4]) // back
            write(p[0], p[4], p[1], p[5], p[2], p[6], p[3], p[7]) // connect front to back
        }

        // extend(ScreenRecorder()) { profile = GIFProfile() }
        extend(cam)
        extend {
            drawer.clear(ColorRGBa.DIM_GRAY)
            drawer.scale(90.0)
            drawer.fill = ColorRGBa.SEA_GREEN
            drawer.vertexBuffer(cube, DrawPrimitive.TRIANGLES)
            drawer.fill = ColorRGBa.DARK_SEA_GREEN
            drawer.vertexBuffer(cubeWire, DrawPrimitive.LINES)
        }
    }
}
1 Like

Hi @abe , thanks for your help! Those helpers look very convenient. I’ll see if I can wrap my head around this way of 3d modelling! :sweat_smile:

There is already a method in the meshgenerators to create a vertexBuffer plane (to be drawn using DrawPrimitive.TRIANGLES). You could create another method to return a vertexBuffer with the edges of that plane to be drawn using DrawPrimitive.LINES.

Maybe you need another method for a triangles edges…

Then the code might look clean? Feel free to ask for help :slight_smile: