# OPENRNDR & Processing - Texture Cube

**URL:** <https://openrndr.discourse.group/t/openrndr-processing-texture-cube/97>\
**Category:** Tutorials\
**Created:** [April 30, 2020, 9:00am UTC](https://openrndr.discourse.group/t/openrndr-processing-texture-cube/97 "2020-04-30T09:00:00Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [April 30, 2020, 9:00am UTC](https://openrndr.discourse.group/t/openrndr-processing-texture-cube/97/1 "2020-04-30T09:00:00Z")

</div>

# 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](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/8fa989221dddf69f4bad7d4675a5f392cb8ce334.png)

### Processing / Java

```auto
/**
 * 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](https://github.com/processing/processing-docs/blob/master/content/examples/Topics/Textures/TextureTriangle/data/berlin-1.jpg)

> **Imports**
>
> ```auto
> 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
> 
> ```

```auto
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`](https://guide.openrndr.org/drawingAndTransformations/transformations.html#transform-pipeline) 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](https://github.com/openrndr/orx/tree/master/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](https://github.com/openrndr/orx/) providing very powerful features.

👇 Share your questions and comments below | 🔎 [Find other OPENRNDR & Processing posts](https://openrndr.discourse.group/search?q=Processing%20%23openrndr%3Atutorials)

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [April 30, 2020, 9:47am UTC](https://openrndr.discourse.group/t/openrndr-processing-texture-cube/97/2 "2020-04-30T09:47:20Z")

</div>

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

### OPENRNDR / Kotlin

> **Imports**
>
> ```auto
> 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
> 
> ```

```kotlin
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 🙂 Simply 1. create the camera, 2. set the eye position and 3. enable it.
