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
// -----------------------------------------------------------------------
//