P3D: Two towers with a connecting bridge - QuadStrip

I have a more general question concerning how to make two towers with a connecting bridge or more complex stuff.

I understand I can make ONE tower using QuadStrip. And the other tower and the bridge too.

But how would I connect the three? Or just have them positioned appropriately and stay unconnected?

What about more complex stuff like with multiple balconies or a castle or fortress?

I understand that Computer games don’t use QuadStrip but single triangles. Would that be the way to go in processing?

Thanks!

Regards

Chrisir

1 Like

this one would be most likely, its not really that worthwhile, computationally or code wise, to calculate mesh intersections and a completly new mesh for something that can be acheived by having the shapes seperatly drawn. They would just intersect each other and let opengl depth sort do its job. This particularly applies for more complex shapes as you have mentioned, where instead of having to figure the mesh for each balcony intersection with the building you can just copy paste and draw a bunch of balconys in.
If you specifically want the mesh to be wholly closed and one entity youd just load an obj file in,
pre-modelled with a third party tool such as blender.

2 Likes

Thanks a lot!

Chrisir

It might be useful to think about them like box(). Box only takes dimensions – it requires the use of translate() for positioning.

The different here is that it, if you are arranging landscape elements like towers and bridges, you probably want them all resting flat on the x,z coordinate plane.

So, to connect your two towers and your bridge, you would do something like:

void setup() {
  size(600, 600, P3D);
  ortho();
  noFill();
}

void draw() {
  background(255);
  
  // create perspective
  translate(width/2, height/2);
  rotateX(-PI/6);
  rotateY(PI/3);
  
  // draw ground
  box(600, 0, 600);
  
  // draw scene
  towerBridgeLayout(50, 200, 50, 175, 40, 20, 30);
}

void towerBridgeLayout(float tw, float th, float tz, float bw, float bh, float bz, float bloft) {
  tower(tw, th, tz);
  translate(tw/2 + bw/2, 0, 0);
  bridge(bw, bh, bz, bloft);
  translate(tw/2 + bw/2, 0, 0);
  tower(tw, th, tz);
  
  rotateY(-PI/2); // turn right
  
  translate(tw/2 + bw/2, 0, 0);
  bridge(bw, bh, bz, bloft);
  translate(tw/2 + bw/2, 0, 0);
  tower(tw, th, tz);
}

void tower(float w, float h, float d) {
  pushMatrix();
  translate(0, -h/2, 0); // align bottom
  box(w, h, d);
  popMatrix();
}

void bridge(float w, float h, float d, float loft) {
  pushMatrix();
  translate(0, -h/2, 0); // align bottom
  translate(0, -loft); // raise up
  box(w, h, d);
  popMatrix();
}

…now change tower() and bridge() into quadstrip-based renderer functions that work based on box dimensions, and you can start dropping towers and bridges anywhere you like with simple translate and rotate steps. You matrix position will almost always stay in the 2D x,z plane, and you will use rotateY to spin things around in that plane and place them.

For getting into something very complex like a fortress, you might want to consider working with meshes and using a mesh authoring environment (xelo mentioned Blender), then importing the meshes using a mesh library. If you want to do simple procedurally generated meshes interactively in processing, you could also look at hemesh_gui.

1 Like

still a long way to go…





// https://discourse.processing.org/t/p3d-two-towers-with-a-connecting-bridge-quadstrip/14109/3

PShape moebius1, bridge1;


void setup() {
  size(1600, 800, P3D);
  ortho();
  noFill();
}

void draw() {
  background(255);
  lights(); 

  // create perspective
  translate(width/2, height/2);
  rotateX(-PI/6);
  rotateY(PI/3);

  // draw ground
  box(600, 0, 600);

  // draw scene
  towerBridgeLayout(50, 300, 50, 
    175, 40, 20, 
    220);
}

// ------------------------------------------------------------------------

void towerBridgeLayout(float tw, float th, float tz, 
  float bw, float bh, float bz, 
  float bloft) {

  tower(tw, th, tz);
  translate(tw/2 + bw/2, 0, 0);
  bridge(bw, bh, bz, bloft);
  translate(tw/2 + bw/2, 0, 0);
  tower(tw, th, tz);

  rotateY(-PI/2); // turn right

  translate(tw/2 + bw/2, 0, 0);
  bridge(bw, bh, bz, bloft);
  translate(tw/2 + bw/2, 0, 0);
  tower(tw, th, tz);
}

void tower(float w, float h, float d) {

  initTower(w, h, int(d)); 

  /*
   pushMatrix();
   translate(0, -h/2, 0); // align bottom
   box(w, h, d);
   popMatrix(); 
   */

  pushMatrix();
  translate(0, -h, 0); // align bottom
  shape(moebius1);
  popMatrix();
}

void bridge(float w, float h, float d, 
  float loft) {

  initBridge(w, h, int(d)); 

  /*
   pushMatrix();
   translate(0, -h/2, 0); // align bottom
   translate(0, -loft); // raise up
   box(w, h, d);
   popMatrix();
   */

  pushMatrix();
  translate(-w/2, -h/2, 0); // align bottom
  translate(0, -loft); // raise up
  shape(bridge1);
  popMatrix();
}

//------------------------------------------------------------------------------

void initTower(float w, float h, int d) {

  // https://de.wikipedia.org/wiki/M%C3%B6biusband

  boolean grid=false; 

  moebius1=createShape(); 
  moebius1.beginShape(QUAD_STRIP);

  if (grid) {
    // only grid 
    moebius1.noFill();
    moebius1.stroke(0);
  } else {
    // full fill
    moebius1.fill(255, 0, 0);
    moebius1.noStroke();
    // moebius1.stroke(0);
  }

  // i2 -> radius (how wide the red strip is) 
  // i3 / i  -> alpha (angle) 

  for (int i2=int(h); i2>0; i2--) {  // height 
    for (int i3=0; i3<361; i3++) { // ANGLE 

      int i=i3; 
      if (i>360) 
        i=i-360; 

      makeVertex3D(i2, i, d); 
      makeVertex3D(i2+1, i, d); 
      //
    }//for
    //
    // makeVertex3D(-50, 1);  //Added !
    //
  }//for

  // ---------------------------------

  moebius1.endShape();
}

void makeVertex3D( int i2, int i, int d ) {

  float factor1 = 1; 
  float yadd1   = 0; 

  //  float r = i2/i2_span; 

  float alpha=radians(i); 

  PVector v = new PVector(
    d*cos(alpha), 
    i2, 
    d*sin(alpha)
    );

  v.mult(factor1);
  v.y+=yadd1;

  moebius1.vertex(v.x, v.y, v.z);
}
//
//--------------------------------------------------------------------------------------

void initBridge(float w, float h, int d) {

  // https://de.wikipedia.org/wiki/M%C3%B6biusband

  boolean grid=false; 

  bridge1=createShape(); 
  bridge1.beginShape(QUAD_STRIP);

  if (grid) {
    // only grid 
    bridge1.noFill();
    bridge1.stroke(0);
  } else {
    // full fill
    bridge1.fill(255, 0, 0);
    bridge1.noStroke();
    // bridge1.stroke(0);
  }

  // i2 -> radius (how wide the red strip is) 
  // i3 / i  -> alpha (angle) 

  for (int i2=int(w); i2>0; i2--) {  // height 
    for (int i3=0; i3<361; i3++) { // ANGLE 

      int i=i3; 
      if (i>360) 
        i=i-360; 

      makeVertex3D2(i2, i, d); 
      makeVertex3D2(i2+1, i, d); 
      //
    }//for
    //
    // makeVertex3D2(-50, 1);  //Added !
    //
  }//for

  // ---------------------------------

  bridge1.endShape();
}

void makeVertex3D2( int i2, int i, int d ) {

  float factor1 = 1; 
  float yadd1   = 0; 

  //  float r = i2/i2_span; 

  float alpha=radians(i); 

  PVector v = new PVector(
    i2, 
    d*cos(alpha), 
    d*sin(alpha)
    );

  v.mult(factor1);
  v.y+=yadd1;

  bridge1.vertex(v.x, v.y, v.z);
}
//
1 Like

improved the bridges a bit…


// https://discourse.processing.org/t/p3d-two-towers-with-a-connecting-bridge-quadstrip/14109/3

import peasy.*;
PeasyCam cam;

PShape shapeTower1, shapeBridge1;

// ------------------------------------------------------------------------
// processing core functions 

void setup() {
  size(1600, 800, P3D);
  ortho();
  noFill();

  cam = new PeasyCam(this, 
    width/2+211, height/2-200, 40, 
    111);

  initTower(50, 300, 50);
}

void draw() {
  background(255);

  avoidClipping(); 
  // my_Camera(); 
  lights(); 

  // create perspective
  translate(width/2, height/2, 0);
  rotateX(-PI/6);
  rotateY(PI/3);

  // draw ground
  fill(0, 255, 0);
  box(600, 3, 600);

  // draw scene
  towerBridgeLayout(50, 300, 50, 
    175, 40, 20, 
    220);
}

// -------------------------------------------------------------

void towerBridgeLayout(float tw, float th, float tz, 
  float bw, float bh, float bz, 
  float bloft) {

  // the entire scene 

  tower(tw, th, tz);
  translate(tw/2 + bw/2, 0, 0);
  bridge(bw, bh, bz, bloft);
  translate(tw/2 + bw/2, 0, 0);
  tower(tw, th, tz);

  rotateY(-PI/2); // turn right

  translate(tw/2 + bw/2, 0, 0);
  bridge(bw, bh, bz, bloft);
  translate(tw/2 + bw/2, 0, 0);
  tower(tw, th, tz);
}

void tower(float w, float h, float d) {

  /*
   pushMatrix();
   translate(0, -h/2, 0); // align bottom
   box(w, h, d);
   popMatrix(); 
   */

  pushMatrix();
  translate(0, -h, 0); // align bottom
  shape(shapeTower1);
  popMatrix();
}

void bridge(float w, float h, float d, 
  float loft) {

  initBridge(w, h, int(d)); 

  /*
   pushMatrix();
   translate(0, -h/2, 0); // align bottom
   translate(0, -loft); // raise up
   box(w, h, d);
   popMatrix();
   */

  pushMatrix();
  translate(-w/2, -h/2, 0); // align bottom
  translate(0, -loft); // raise up
  //stroke(0);
  //shapeBridge1.setStroke(0);
  //shapeBridge1.stroke(0);
  shape(shapeBridge1);
  popMatrix();
}

//------------------------------------------------------------------------------

void initTower(float w, float h, int d) {

  // https://de.wikipedia.org/wiki/M%C3%B6biusband

  boolean grid=false; 

  shapeTower1=createShape(); 
  shapeTower1.beginShape(QUAD_STRIP);

  if (grid) {
    // only grid 
    shapeTower1.noFill();
    shapeTower1.stroke(0);
  } else {
    // full fill
    shapeTower1.fill(255, 0, 0);
    shapeTower1.noStroke();
    // shapeTower1.stroke(0);
  }

  // i2 -> radius (how wide the red strip is) 
  // i3 / i  -> alpha (angle) 

  for (int i2=int(h); i2>0; i2--) {  // height 
    for (int i3=0; i3<361; i3++) { // ANGLE 

      int i=i3; 
      if (i>360) 
        i=i-360; 

      makeVertex3D(i2, i, d); 
      makeVertex3D(i2+1, i, d); 
      //
    }//for
    //
  }//for

  // ---------------------------------

  shapeTower1.endShape();
}

void makeVertex3D( int i2, int i, int d ) {

  float factor1 = 1; 
  float yadd1   = 0; 
  float alpha=radians(i); 

  PVector v = new PVector(
    d*cos(alpha), 
    i2, 
    d*sin(alpha)
    );

  v.mult(factor1);
  v.y+=yadd1;

  shapeTower1.vertex(v.x, v.y, v.z);
}
//
//--------------------------------------------------------------------------------------

void initBridge(float w, float h, int d) { // width (length), height (downwards), depth (breadth of the street over the bridge)

  // https://de.wikipedia.org/wiki/M%C3%B6biusband

  boolean grid=false; 

  shapeBridge1=createShape(); 
  shapeBridge1.beginShape(QUAD_STRIP);

  if (grid) {
    // only grid 
    shapeBridge1.noFill();
    shapeBridge1.stroke(0);
  } else {
    // full fill
    shapeBridge1.fill(255, 0, 0);
    shapeBridge1.noStroke();
    // shapeBridge1.stroke(0);
  }

  // make ArrayLists
  ArrayList[] arrList = new ArrayList [6] ;
  for (  ArrayList<PVector> a1 : arrList) { 
    a1 = new ArrayList();
  }

  // make ArrayLists
  float depthHalf= d/2; 
  arrList[0] =  getLine( 10, -depthHalf ); // upper left side 
  arrList[1] =  getLine( 10, depthHalf ); // upper right 

  arrList[2] =  getLine( 25, depthHalf ); // upper right middle

  arrList[3] =  getLineArc( 115, depthHalf );// lower right  // arc 
  arrList[4] =  getLineArc( 115, -depthHalf );// lower left   // arc

  arrList[5] =  getLine( 25, -depthHalf ); // lower left  right middle

  // Eval ArrayLists 
  for (int i3=0; i3<200-1; i3++) { // 

    //  showVertext3DBox(a1, i3);
    //  showVertext3DBox(a2, i3);

    /*  showVertext3DSphere(a3, i3);
     showVertext3DSphere(a4, i3);
     */

    makeVertex3D2( arrList[0], i3);
    makeVertex3D2( arrList[0], i3+1);

    makeVertex3D2( arrList[1], i3);
    makeVertex3D2( arrList[1], i3+1);

    makeVertex3D2( arrList[2], i3);
    makeVertex3D2( arrList[2], i3+1);

    makeVertex3D2( arrList[3], i3);
    makeVertex3D2( arrList[3], i3+1);

    makeVertex3D2( arrList[4], i3);
    makeVertex3D2( arrList[4], i3+1);

    makeVertex3D2( arrList[5], i3);
    makeVertex3D2( arrList[5], i3+1);

    // makeVertex3D2(a5, i3);
    // makeVertex3D2(a5, i3+1); 

    //
  }//for
  //

  // ---------------------------------

  shapeBridge1.endShape();
}

// -----------------------------------------------

ArrayList<PVector> getLine (  float y_, float z_  ) {

  // fill ArrayList with line

  ArrayList<PVector> newArrayList = new ArrayList();  

  // make  ArrayLists 
  for (int i3=0; i3<200; i3++) { //
    // store points in list 
    PVector pv1 = new PVector(i3, y_, z_); 
    newArrayList.add(pv1);
  } //for 

  return newArrayList;
  //
}// func --- 

ArrayList<PVector> getLineArc ( float y_, float z_ ) {

  // fill ArrayList with Arc

  ArrayList<PVector> newArrayList = new ArrayList();  

  // make  ArrayLists 
  for (int i3=0; i3<200; i3++) { //

    float farAngle=PI/3; 
    float angle=map(i3, 0, 200, -farAngle, farAngle);
    angle+=radians(270); 
    float r = 80 ;  
    float x=cos(angle)*r; 
    float y=sin(angle)*r+y_; 

    // store points in list 

    PVector pv1 = new PVector(x+175/2, y, zValue(z_, angle)); 

    newArrayList.add(pv1);
  } //for 

  return newArrayList;
  //
}// func --- 

float zValue(float z_, float angle_) {
  float factor1=3.4; 
  if (z_<0) 
    return z_-angle_*factor1;
  else 
  return z_+angle_*factor1;
}

void makeVertex3D2( ArrayList<PVector> arrL_, int i ) {

  // ArrayList version 

  PVector v = arrL_.get(i); 
  shapeBridge1.vertex(v.x, v.y, v.z);
}

// --------------------------------------------------------------------------

void showVertext3DBox( ArrayList<PVector> arrL_, int i ) {

  // ArrayList version 

  PVector v = arrL_.get(i);

  pushMatrix();

  translate(50/2 + 300/2, 0, 0);
  translate(v.x, v.y, v.z); //

  noStroke(); 
  fill(255, 0, 0); // RED  
  box(14);
  popMatrix();
}

void showVertext3DSphere( ArrayList<PVector> arrL_, int i ) {

  // ArrayList version 

  PVector v = arrL_.get(i);

  pushMatrix();

  translate(50/2 + 300/2, 0, 0);
  translate(v.x, v.y, v.z); //

  noStroke(); 
  fill(255, 0, 0); // RED  
  sphere(2);
  popMatrix();
}
// 
// ----------------------------------------------------------------------------
// Minor tools 

void my_Camera() {                                   

  // limit from +- PI to +- PI/2 like more natural view??
  float x = map(mouseX, 0, width, -PI/2, PI/2);
  float y = Y*map(mouseY, 0, height, -PI/2, PI/2);   // invert processing Y
  translate(width/2-660, height/2);
  // scale(zmag);
  rotateY(x);
  rotateX(y);  
  // here the view is axis correct if mouse is middle/center of canvas!!!
  // right hand rule: thumb X RIGHT (RED), point finger Y UP(GREEN), middle finger Z FRONT (BLUE)( points to you )
}

void avoidClipping() {
  // avoid clipping :
  // https : //
  // forum.processing.org/two/discussion/4128/quick-q-how-close-is-too-close-why-when-do-3d-objects-disappear
  perspective(PI/3.0, (float) width/height, 1, 1000000);
}//func

// -----------------------------------------------------------------------
//
1 Like

See also the bridge on the gate here Some Movies I made

1 Like