OPENRNDR & Processing - Texture Cube

TextureCube

One more example taken from the Processing examples, this time drawing a simple textured 3D cube which you can rotate by dragging the mouse.

2020-04-06-153657_640x360_scrot

Processing / Java

/**
 * Texture Cube
 * by Dave Bollinger.
 * 
 * Drag mouse to rotate cube. Demonstrates use of u/v coords in 
 * vertex() and effect on texture(). The textures get distorted using
 * the P3D renderer as you can see, but they look great using OPENGL.
*/

PImage tex;
float rotx = PI/4;
float roty = PI/4;

void setup() {
  size(640, 360, P3D);
  tex = loadImage("berlin-1.jpg");
  textureMode(NORMAL);
  fill(255);
}

void draw() {
  background(255);
  noStroke();
  translate(width/2.0, height/2.0, -80);
  rotateX(rotx);
  rotateY(roty);
  scale(90);
  TexturedCube(tex);
}

void TexturedCube(PImage tex) {
  beginShape(QUADS);
  texture(tex);

  // Given one texture and six faces, we can easily set up the uv coordinates
  // such that four of the faces tile "perfectly" along either u or v, but the other
  // two faces cannot be so aligned.  This code tiles "along" u, "around" the X/Z faces
  // and fudges the Y faces - the Y faces are arbitrarily aligned such that a
  // rotation along the X axis will put the "top" of either texture at the "top"
  // of the screen, but is not otherwised aligned with the X/Z faces. (This
  // just affects what type of symmetry is required if you need seamless
  // tiling all the way around the cube)
  
  // +Z "front" face
  vertex(-1, -1,  1, 0, 0);
  vertex( 1, -1,  1, 1, 0);
  vertex( 1,  1,  1, 1, 1);
  vertex(-1,  1,  1, 0, 1);

  // -Z "back" face
  vertex( 1, -1, -1, 0, 0);
  vertex(-1, -1, -1, 1, 0);
  vertex(-1,  1, -1, 1, 1);
  vertex( 1,  1, -1, 0, 1);

  // +Y "bottom" face
  vertex(-1,  1,  1, 0, 0);
  vertex( 1,  1,  1, 1, 0);
  vertex( 1,  1, -1, 1, 1);
  vertex(-1,  1, -1, 0, 1);

  // -Y "top" face
  vertex(-1, -1, -1, 0, 0);
  vertex( 1, -1, -1, 1, 0);
  vertex( 1, -1,  1, 1, 1);
  vertex(-1, -1,  1, 0, 1);

  // +X "right" face
  vertex( 1, -1,  1, 0, 0);
  vertex( 1, -1, -1, 1, 0);
  vertex( 1,  1, -1, 1, 1);
  vertex( 1,  1,  1, 0, 1);

  // -X "left" face
  vertex(-1, -1, -1, 0, 0);
  vertex(-1, -1,  1, 1, 0);
  vertex(-1,  1,  1, 1, 1);
  vertex(-1,  1, -1, 0, 1);

  endShape();
}

void mouseDragged() {
  float rate = 0.01;
  rotx += (pmouseY-mouseY) * rate;
  roty += (mouseX-pmouseX) * rate;
}

OPENRNDR / Kotlin

Assets

berlin-1.jpg

Imports
import org.openrndr.WindowMultisample
import org.openrndr.application
import org.openrndr.draw.DepthTestPass
import org.openrndr.draw.DrawPrimitive
import org.openrndr.draw.loadImage
import org.openrndr.draw.shadeStyle
import org.openrndr.extra.meshgenerators.boxMesh
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
fun main() = application {
    configure {
        width = 640
        height = 360
        multisample = WindowMultisample.SampleCount(2)
    }
    program {
        val cube = boxMesh()
        var rot = Vector2(45.0)
        val tex = loadImage("data/images/berlin-1.jpg")

        extend {
            drawer.perspective(60.0, width * 1.0 / height, 0.01, 1000.0)

            drawer.depthWrite = true
            drawer.depthTestPass = DepthTestPass.LESS_OR_EQUAL
            drawer.shadeStyle = shadeStyle {
                fragmentTransform = "x_fill = texture(p_tex, va_texCoord0.xy);"
                parameter("tex", tex)
            }
            drawer.translate(0.0, 0.0, -150.0)
            drawer.rotate(Vector3.UNIT_X, rot.x)
            drawer.rotate(Vector3.UNIT_Y, rot.y)
            drawer.scale(90.0)
            drawer.vertexBuffer(cube, DrawPrimitive.TRIANGLES)
        }

        mouse.dragged.listen {
            val rate = 0.5
            rot += it.dragDisplacement.yx * rate
        }
    }
}
Concept Processing OPENRNDR
graphics mode choose between JAVA2D (default), P2D, P3D and others one mode only, works for 2D and 3D
view type perspective by default orthographic by default, therefore we call drawer.perspective which also centers the origin
multisampling calls smooth(N) by default you can set multisample inside configure
depth writing enabled by default must be enabled in 3D, otherwise objects far away may appear in front of objects which are closer
creating a cube with texture (uv) coordinates not built-in. you need to write your own or use a library provided by orx-mesh-generators via the boxMesh() method
texture mapping shader by calling texture() by setting a GLSL shadeStyle
drawing an existing mesh shape(), but this sketch reconstructs the cube on every animation frame instead drawer.vertexBuffer()
rotations expressed in radians expressed in degrees

To obtain a similar effect when dragging the mouse we use different values: 0.5 degrees vs 0.01 radians (per dragged pixel). If we would rotate 0.01 degrees per pixel the effect might be too subtle to notice.

OPENRNDR sets up less defaults for us and we must therefore write a few extra lines. On the other hand it comes with many extensions providing very powerful features.

:point_down: Share your questions and comments below | :mag_right: Find other OPENRNDR & Processing posts

1 Like

A simpler version making use of the Orbital camera extension, equivalent to Peasycam in Processing.

OPENRNDR / Kotlin

Imports
import org.openrndr.WindowMultisample
import org.openrndr.application
import org.openrndr.draw.DrawPrimitive
import org.openrndr.draw.loadImage
import org.openrndr.draw.shadeStyle
import org.openrndr.extra.camera.Orbital
import org.openrndr.extra.meshgenerators.boxMesh
import org.openrndr.math.Vector3
fun main() = application {
    configure {
        width = 640
        height = 360
        multisample = WindowMultisample.SampleCount(2)
    }
    program {
        val cube = boxMesh()
        val tex = loadImage("data/images/berlin-1.jpg")
        val cam = Orbital()
        cam.eye = -Vector3.UNIT_Z * 150.0

        extend(cam)
        extend {
            drawer.shadeStyle = shadeStyle {
                fragmentTransform = "x_fill = texture(p_tex, va_texCoord0.xy);"
                parameter("tex", tex)
            }
            drawer.scale(90.0)
            drawer.vertexBuffer(cube, DrawPrimitive.TRIANGLES)
        }
    }
}

As you might have guessed, the Orbital extension takes care of calling drawer.perspective, depthWrite, dethTestPass and deals with mouse and keyboard interaction, so there’s not much left to do :slight_smile: Simply 1. create the camera, 2. set the eye position and 3. enable it.

2 Likes