/**
  * Updates a subregion of the content area of this texture using the given data. If automatic
  * mipmap generation is in use (see {@link #isUsingAutoMipmapGeneration
  * isUsingAutoMipmapGeneration}), updates to the base (level 0) mipmap will cause the lower-level
  * mipmaps to be regenerated, and updates to other mipmap levels will be ignored. Otherwise, if
  * automatic mipmap generation is not in use, only updates the specified mipmap level and does not
  * re-generate mipmaps if they were originally produced or loaded.
  *
  * @param data the image data to be uploaded to this texture
  * @param mipmapLevel the mipmap level of the texture to set. If this is non-zero and the
  *     TextureData contains mipmap data, the appropriate mipmap level will be selected.
  * @param x the x offset (in pixels) relative to the lower-left corner of this texture
  * @param y the y offset (in pixels) relative to the lower-left corner of this texture
  * @throws GLException if no OpenGL context was current or if any OpenGL-related errors occurred
  */
 public void updateSubImage(TextureData data, int mipmapLevel, int x, int y) throws GLException {
   if (usingAutoMipmapGeneration && mipmapLevel != 0) {
     // When we're using mipmap generation via GL_GENERATE_MIPMAP, we
     // don't need to update other mipmap levels
     return;
   }
   bind();
   updateSubImageImpl(data, target, mipmapLevel, x, y, 0, 0, data.getWidth(), data.getHeight());
 }
Ejemplo n.º 2
0
 @Override
 public void apply(AssetDataFile input, TextureData assetData) throws IOException {
   try (InputStreamReader reader = new InputStreamReader(input.openStream(), Charsets.UTF_8)) {
     TextureMetadata metadata = gson.fromJson(reader, TextureMetadata.class);
     if (metadata.filterMode != null) {
       assetData.setFilterMode(metadata.filterMode);
     }
     if (metadata.wrapMode != null) {
       assetData.setWrapMode(metadata.wrapMode);
     }
     if (metadata.type != null) {
       assetData.setType(metadata.type);
     }
   }
 }
  public TextureReference getTextureFromResource(GL10 gl, int resourceID) {
    // If the texture already exists, return it.
    TextureData texData = mResourceIdToTextureMap.get(resourceID);
    if (texData != null) {
      // Increment the reference count
      texData.refCount++;
      return texData.ref;
    }

    TextureReferenceImpl tex = createTextureFromResource(gl, resourceID);

    // Add it to the map.
    TextureData data = new TextureData();
    data.ref = tex;
    data.refCount = 1;
    mResourceIdToTextureMap.put(resourceID, data);

    return tex;
  }
 private void checkCompressedTextureExtensions(TextureData data) {
   GL gl = GLU.getCurrentGL();
   if (data.isDataCompressed()) {
     switch (data.getInternalFormat()) {
       case GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
       case GL.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
       case GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
       case GL.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
         if (!gl.isExtensionAvailable("GL_EXT_texture_compression_s3tc")
             && !gl.isExtensionAvailable("GL_NV_texture_compression_vtc")) {
           throw new GLException("DXTn compressed textures not supported by this graphics card");
         }
         break;
       default:
         // FIXME: should test availability of more texture
         // compression extensions here
         break;
     }
   }
 }
 /**
  * Updates a subregion of the content area of this texture using the specified sub-region of the
  * given data. If automatic mipmap generation is in use (see {@link #isUsingAutoMipmapGeneration
  * isUsingAutoMipmapGeneration}), updates to the base (level 0) mipmap will cause the lower-level
  * mipmaps to be regenerated, and updates to other mipmap levels will be ignored. Otherwise, if
  * automatic mipmap generation is not in use, only updates the specified mipmap level and does not
  * re-generate mipmaps if they were originally produced or loaded. This method is only supported
  * for uncompressed TextureData sources.
  *
  * @param data the image data to be uploaded to this texture
  * @param mipmapLevel the mipmap level of the texture to set. If this is non-zero and the
  *     TextureData contains mipmap data, the appropriate mipmap level will be selected.
  * @param dstx the x offset (in pixels) relative to the lower-left corner of this texture where
  *     the update will be applied
  * @param dsty the y offset (in pixels) relative to the lower-left corner of this texture where
  *     the update will be applied
  * @param srcx the x offset (in pixels) relative to the lower-left corner of the supplied
  *     TextureData from which to fetch the update rectangle
  * @param srcy the y offset (in pixels) relative to the lower-left corner of the supplied
  *     TextureData from which to fetch the update rectangle
  * @param width the width (in pixels) of the rectangle to be updated
  * @param height the height (in pixels) of the rectangle to be updated
  * @throws GLException if no OpenGL context was current or if any OpenGL-related errors occurred
  */
 public void updateSubImage(
     TextureData data,
     int mipmapLevel,
     int dstx,
     int dsty,
     int srcx,
     int srcy,
     int width,
     int height)
     throws GLException {
   if (data.isDataCompressed()) {
     throw new GLException(
         "updateSubImage specifying a sub-rectangle is not supported for compressed TextureData");
   }
   if (usingAutoMipmapGeneration && mipmapLevel != 0) {
     // When we're using mipmap generation via GL_GENERATE_MIPMAP, we
     // don't need to update other mipmap levels
     return;
   }
   bind();
   updateSubImageImpl(data, target, mipmapLevel, dstx, dsty, srcx, srcy, width, height);
 }
  private void updateSubImageImpl(
      TextureData data,
      int newTarget,
      int mipmapLevel,
      int dstx,
      int dsty,
      int srcx,
      int srcy,
      int width,
      int height)
      throws GLException {
    GL gl = GLU.getCurrentGL();
    data.setHaveEXTABGR(gl.isExtensionAvailable("GL_EXT_abgr"));
    data.setHaveGL12(gl.isExtensionAvailable("GL_VERSION_1_2"));

    Buffer buffer = data.getBuffer();
    if (buffer == null && data.getMipmapData() == null) {
      // Assume user just wanted to get the Texture object allocated
      return;
    }

    int rowlen = data.getRowLength();
    int dataWidth = data.getWidth();
    int dataHeight = data.getHeight();
    if (data.getMipmapData() != null) {
      // Compute the width, height and row length at the specified mipmap level
      // Note we do not support specification of the row length for
      // mipmapped textures at this point
      for (int i = 0; i < mipmapLevel; i++) {
        width = Math.max(width / 2, 1);
        height = Math.max(height / 2, 1);

        dataWidth = Math.max(dataWidth / 2, 1);
        dataHeight = Math.max(dataHeight / 2, 1);
      }
      rowlen = 0;
      buffer = data.getMipmapData()[mipmapLevel];
    }

    // Clip incoming rectangles to what is available both on this
    // texture and in the incoming TextureData
    if (srcx < 0) {
      width += srcx;
      srcx = 0;
    }
    if (srcy < 0) {
      height += srcy;
      srcy = 0;
    }
    // NOTE: not sure whether the following two are the correct thing to do
    if (dstx < 0) {
      width += dstx;
      dstx = 0;
    }
    if (dsty < 0) {
      height += dsty;
      dsty = 0;
    }

    if (srcx + width > dataWidth) {
      width = dataWidth - srcx;
    }
    if (srcy + height > dataHeight) {
      height = dataHeight - srcy;
    }
    if (dstx + width > texWidth) {
      width = texWidth - dstx;
    }
    if (dsty + height > texHeight) {
      height = texHeight - dsty;
    }

    checkCompressedTextureExtensions(data);

    if (data.isDataCompressed()) {
      gl.glCompressedTexSubImage2D(
          newTarget,
          mipmapLevel,
          dstx,
          dsty,
          width,
          height,
          data.getInternalFormat(),
          buffer.remaining(),
          buffer);
    } else {
      int[] align = new int[1];
      int[] rowLength = new int[1];
      int[] skipRows = new int[1];
      int[] skipPixels = new int[1];
      gl.glGetIntegerv(GL.GL_UNPACK_ALIGNMENT, align, 0); // save alignment
      gl.glGetIntegerv(GL.GL_UNPACK_ROW_LENGTH, rowLength, 0); // save row length
      gl.glGetIntegerv(GL.GL_UNPACK_SKIP_ROWS, skipRows, 0); // save skipped rows
      gl.glGetIntegerv(GL.GL_UNPACK_SKIP_PIXELS, skipPixels, 0); // save skipped pixels
      gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, data.getAlignment());
      if (DEBUG && VERBOSE) {
        System.out.println("Row length  = " + rowlen);
        System.out.println("skip pixels = " + srcx);
        System.out.println("skip rows   = " + srcy);
        System.out.println("dstx        = " + dstx);
        System.out.println("dsty        = " + dsty);
        System.out.println("width       = " + width);
        System.out.println("height      = " + height);
      }
      gl.glPixelStorei(GL.GL_UNPACK_ROW_LENGTH, rowlen);
      gl.glPixelStorei(GL.GL_UNPACK_SKIP_ROWS, srcy);
      gl.glPixelStorei(GL.GL_UNPACK_SKIP_PIXELS, srcx);

      gl.glTexSubImage2D(
          newTarget,
          mipmapLevel,
          dstx,
          dsty,
          width,
          height,
          data.getPixelFormat(),
          data.getPixelType(),
          buffer);
      gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, align[0]); // restore alignment
      gl.glPixelStorei(GL.GL_UNPACK_ROW_LENGTH, rowLength[0]); // restore row length
      gl.glPixelStorei(GL.GL_UNPACK_SKIP_ROWS, skipRows[0]); // restore skipped rows
      gl.glPixelStorei(GL.GL_UNPACK_SKIP_PIXELS, skipPixels[0]); // restore skipped pixels
    }
  }
  /**
   * Updates the content area of the specified target of this texture using the data in the given
   * image. In general this is intended for construction of cube maps.
   *
   * @throws GLException if no OpenGL context was current or if any OpenGL-related errors occurred
   */
  public void updateImage(TextureData data, int target) throws GLException {
    GL gl = GLU.getCurrentGL();

    imgWidth = data.getWidth();
    imgHeight = data.getHeight();
    aspectRatio = (float) imgWidth / (float) imgHeight;
    mustFlipVertically = data.getMustFlipVertically();

    int texTarget = 0;
    int texParamTarget = this.target;

    // See whether we have automatic mipmap generation support
    boolean haveAutoMipmapGeneration =
        (gl.isExtensionAvailable("GL_VERSION_1_4")
            || gl.isExtensionAvailable("GL_SGIS_generate_mipmap"));

    // Indicate to the TextureData what functionality is available
    data.setHaveEXTABGR(gl.isExtensionAvailable("GL_EXT_abgr"));
    data.setHaveGL12(gl.isExtensionAvailable("GL_VERSION_1_2"));

    // Note that automatic mipmap generation doesn't work for
    // GL_ARB_texture_rectangle
    if ((!isPowerOfTwo(imgWidth) || !isPowerOfTwo(imgHeight)) && !haveNPOT(gl)) {
      haveAutoMipmapGeneration = false;
    }

    boolean expandingCompressedTexture = false;
    if (data.getMipmap() && !haveAutoMipmapGeneration) {
      // GLU always scales the texture's dimensions to be powers of
      // two. It also doesn't really matter exactly what the texture
      // width and height are because the texture coords are always
      // between 0.0 and 1.0.
      imgWidth = nextPowerOfTwo(imgWidth);
      imgHeight = nextPowerOfTwo(imgHeight);
      texWidth = imgWidth;
      texHeight = imgHeight;
      texTarget = GL.GL_TEXTURE_2D;
    } else if ((isPowerOfTwo(imgWidth) && isPowerOfTwo(imgHeight)) || haveNPOT(gl)) {
      if (DEBUG) {
        if (isPowerOfTwo(imgWidth) && isPowerOfTwo(imgHeight)) {
          System.err.println("Power-of-two texture");
        } else {
          System.err.println("Using GL_ARB_texture_non_power_of_two");
        }
      }

      texWidth = imgWidth;
      texHeight = imgHeight;
      texTarget = GL.GL_TEXTURE_2D;
    } else if (haveTexRect(gl) && !data.isDataCompressed()) {
      // GL_ARB_texture_rectangle does not work for compressed textures
      if (DEBUG) {
        System.err.println("Using GL_ARB_texture_rectangle");
      }

      texWidth = imgWidth;
      texHeight = imgHeight;
      texTarget = GL.GL_TEXTURE_RECTANGLE_ARB;
    } else {
      // If we receive non-power-of-two compressed texture data and
      // don't have true hardware support for compressed textures, we
      // can fake this support by producing an empty "compressed"
      // texture image, using glCompressedTexImage2D with that to
      // allocate the texture, and glCompressedTexSubImage2D with the
      // incoming data.
      if (data.isDataCompressed()) {
        if (data.getMipmapData() != null) {

          // We don't currently support expanding of compressed,
          // mipmapped non-power-of-two textures to the nearest power
          // of two; the obvious port of the non-mipmapped code didn't
          // work
          throw new GLException(
              "Mipmapped non-power-of-two compressed textures only supported on OpenGL 2.0 hardware (GL_ARB_texture_non_power_of_two)");
        }

        expandingCompressedTexture = true;
      }

      if (DEBUG) {
        System.err.println("Expanding texture to power-of-two dimensions");
      }

      if (data.getBorder() != 0) {
        throw new RuntimeException(
            "Scaling up a non-power-of-two texture which has a border won't work");
      }
      texWidth = nextPowerOfTwo(imgWidth);
      texHeight = nextPowerOfTwo(imgHeight);
      texTarget = GL.GL_TEXTURE_2D;
    }

    texParamTarget = texTarget;
    setImageSize(imgWidth, imgHeight, texTarget);

    if (target != 0) {
      // Allow user to override auto detection and skip bind step (for
      // cubemap construction)
      texTarget = target;
      if (this.target == 0) {
        throw new GLException("Override of target failed; no target specified yet");
      }
      texParamTarget = this.target;
      gl.glBindTexture(texParamTarget, texID);
    } else {
      gl.glBindTexture(texTarget, texID);
    }

    if (data.getMipmap() && !haveAutoMipmapGeneration) {
      int[] align = new int[1];
      gl.glGetIntegerv(GL.GL_UNPACK_ALIGNMENT, align, 0); // save alignment
      gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, data.getAlignment());

      if (data.isDataCompressed()) {
        throw new GLException("May not request mipmap generation for compressed textures");
      }

      try {
        GLU glu = new GLU();
        glu.gluBuild2DMipmaps(
            texTarget,
            data.getInternalFormat(),
            data.getWidth(),
            data.getHeight(),
            data.getPixelFormat(),
            data.getPixelType(),
            data.getBuffer());
      } finally {
        gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, align[0]); // restore alignment
      }
    } else {
      checkCompressedTextureExtensions(data);
      Buffer[] mipmapData = data.getMipmapData();
      if (mipmapData != null) {
        int width = texWidth;
        int height = texHeight;
        for (int i = 0; i < mipmapData.length; i++) {
          if (data.isDataCompressed()) {
            // Need to use glCompressedTexImage2D directly to allocate and fill this image
            // Avoid spurious memory allocation when possible
            gl.glCompressedTexImage2D(
                texTarget,
                i,
                data.getInternalFormat(),
                width,
                height,
                data.getBorder(),
                mipmapData[i].remaining(),
                mipmapData[i]);
          } else {
            // Allocate texture image at this level
            gl.glTexImage2D(
                texTarget,
                i,
                data.getInternalFormat(),
                width,
                height,
                data.getBorder(),
                data.getPixelFormat(),
                data.getPixelType(),
                null);
            updateSubImageImpl(data, texTarget, i, 0, 0, 0, 0, data.getWidth(), data.getHeight());
          }

          width = Math.max(width / 2, 1);
          height = Math.max(height / 2, 1);
        }
      } else {
        if (data.isDataCompressed()) {
          if (!expandingCompressedTexture) {
            // Need to use glCompressedTexImage2D directly to allocate and fill this image
            // Avoid spurious memory allocation when possible
            gl.glCompressedTexImage2D(
                texTarget,
                0,
                data.getInternalFormat(),
                texWidth,
                texHeight,
                data.getBorder(),
                data.getBuffer().capacity(),
                data.getBuffer());
          } else {
            ByteBuffer buf =
                DDSImage.allocateBlankBuffer(texWidth, texHeight, data.getInternalFormat());
            gl.glCompressedTexImage2D(
                texTarget,
                0,
                data.getInternalFormat(),
                texWidth,
                texHeight,
                data.getBorder(),
                buf.capacity(),
                buf);
            updateSubImageImpl(data, texTarget, 0, 0, 0, 0, 0, data.getWidth(), data.getHeight());
          }
        } else {
          if (data.getMipmap() && haveAutoMipmapGeneration) {
            // For now, only use hardware mipmapping for uncompressed 2D
            // textures where the user hasn't explicitly specified
            // mipmap data; don't know about interactions between
            // GL_GENERATE_MIPMAP and glCompressedTexImage2D
            gl.glTexParameteri(texParamTarget, GL.GL_GENERATE_MIPMAP, GL.GL_TRUE);
            usingAutoMipmapGeneration = true;
          }

          gl.glTexImage2D(
              texTarget,
              0,
              data.getInternalFormat(),
              texWidth,
              texHeight,
              data.getBorder(),
              data.getPixelFormat(),
              data.getPixelType(),
              null);
          updateSubImageImpl(data, texTarget, 0, 0, 0, 0, 0, data.getWidth(), data.getHeight());
        }
      }
    }

    int minFilter = (data.getMipmap() ? GL.GL_LINEAR_MIPMAP_LINEAR : GL.GL_LINEAR);
    int magFilter = GL.GL_LINEAR;
    int wrapMode = (gl.isExtensionAvailable("GL_VERSION_1_2") ? GL.GL_CLAMP_TO_EDGE : GL.GL_CLAMP);

    // REMIND: figure out what to do for GL_TEXTURE_RECTANGLE_ARB
    if (texTarget != GL.GL_TEXTURE_RECTANGLE_ARB) {
      gl.glTexParameteri(texParamTarget, GL.GL_TEXTURE_MIN_FILTER, minFilter);
      gl.glTexParameteri(texParamTarget, GL.GL_TEXTURE_MAG_FILTER, magFilter);
      gl.glTexParameteri(texParamTarget, GL.GL_TEXTURE_WRAP_S, wrapMode);
      gl.glTexParameteri(texParamTarget, GL.GL_TEXTURE_WRAP_T, wrapMode);
      if (this.target == GL.GL_TEXTURE_CUBE_MAP) {
        gl.glTexParameteri(texParamTarget, GL.GL_TEXTURE_WRAP_R, wrapMode);
      }
    }

    // Don't overwrite target if we're loading e.g. faces of a cube
    // map
    if ((this.target == 0)
        || (this.target == GL.GL_TEXTURE_2D)
        || (this.target == GL.GL_TEXTURE_RECTANGLE_ARB)) {
      this.target = texTarget;
    }

    // This estimate will be wrong for cube maps
    estimatedMemorySize = data.getEstimatedMemorySize();
  }
Ejemplo n.º 8
0
  @Override
  public void buildWorldObject() {

    // FIXME object is not in local coordinates!
    setPoint(new Point3d());

    List<Point2d> pointList = new ArrayList<Point2d>();

    for (int i = 0; i < way.getNodesCount(); i++) {
      Node node = way.getNode(i);
      pointList.add(perspective.calcPoint(node));
    }

    list = pointList;

    roadWidth = (float) DEFAULT_ROAD_WIDTH;

    roadWidth = getRoadWidth();

    TextureData texture = getTexture();

    Material m = MaterialFactory.createTextureMaterial(texture.getFile());

    ModelFactory modelBuilder = ModelFactory.modelBuilder();

    int mi = modelBuilder.addMaterial(m);

    MeshFactory meshWalls = modelBuilder.addMesh("road");

    meshWalls.materialID = mi;
    meshWalls.hasTexture = true;

    if (list.size() > 1) {

      FaceFactory leftBorder = meshWalls.addFace(FaceType.QUAD_STRIP);
      FaceFactory leftPart = meshWalls.addFace(FaceType.QUAD_STRIP);
      FaceFactory rightBorder = meshWalls.addFace(FaceType.QUAD_STRIP);
      FaceFactory rightPart = meshWalls.addFace(FaceType.QUAD_STRIP);

      Vector3d flatSurface = new Vector3d(0, 1, 0);

      int flatNormalI = meshWalls.addNormal(flatSurface);

      Point2d beginPoint = list.get(0);
      for (int i = 1; i < list.size(); i++) {
        Point2d endPoint = list.get(i);

        double x = endPoint.x - beginPoint.x;
        double y = endPoint.y - beginPoint.y;
        // calc lenght of road segment
        double mod = Math.sqrt(x * x + y * y);

        double distance = beginPoint.distance(endPoint);

        // calc orthogonal for road segment
        double orthX = x * cos90 + y * sin90;
        double orthY = -x * sin90 + y * cos90;

        // calc vector for road width;
        double normX = roadWidth / 2 * orthX / mod;
        double normY = roadWidth / 2 * orthY / mod;
        // calc vector for border width;
        double borderX = normX + 0.2 * orthX / mod;
        double borderY = normY + 0.2 * orthY / mod;

        double uEnd = distance / texture.getLenght();

        // left border
        int tcb1 = meshWalls.addTextCoord(new TextCoord(0, 0.99999d));
        // left part of road
        int tcb2 = meshWalls.addTextCoord(new TextCoord(0, 1 - 0.10d));
        // Middle part of road
        int tcb3 = meshWalls.addTextCoord(new TextCoord(0, 0.00001d));
        // right part of road
        int tcb4 = meshWalls.addTextCoord(new TextCoord(0, 1 - 0.10d));
        // right border
        int tcb5 = meshWalls.addTextCoord(new TextCoord(0, 0.99999d));

        // left border
        int tce1 = meshWalls.addTextCoord(new TextCoord(uEnd, 0.99999d));
        // left part of road
        int tce2 = meshWalls.addTextCoord(new TextCoord(uEnd, 1 - 0.10d));
        // Middle part of road
        int tce3 = meshWalls.addTextCoord(new TextCoord(uEnd, 0.00001d));
        // right part of road
        int tce4 = meshWalls.addTextCoord(new TextCoord(uEnd, 1 - 0.10d));
        // right border
        int tce5 = meshWalls.addTextCoord(new TextCoord(uEnd, 0.99999d));

        // left border
        int wbi1 =
            meshWalls.addVertex(
                new Point3d(beginPoint.x + borderX, 0.0d, -(beginPoint.y + borderY)));
        // left part of road
        int wbi2 =
            meshWalls.addVertex(new Point3d(beginPoint.x + normX, 0.1d, -(beginPoint.y + normY)));
        // middle part of road
        int wbi3 = meshWalls.addVertex(new Point3d(beginPoint.x, 0.15d, -beginPoint.y));
        // right part of road
        int wbi4 =
            meshWalls.addVertex(new Point3d(beginPoint.x - normX, 0.1d, -(beginPoint.y - normY)));
        // right border
        int wbi5 =
            meshWalls.addVertex(
                new Point3d(beginPoint.x - borderX, 0.0d, -(beginPoint.y - borderY)));

        // left border
        int wei1 =
            meshWalls.addVertex(new Point3d(endPoint.x + borderX, 0.0d, -(endPoint.y + borderY)));
        // left part of road
        int wei2 =
            meshWalls.addVertex(new Point3d(endPoint.x + normX, 0.1d, -(endPoint.y + normY)));
        // middle part of road
        int wei3 = meshWalls.addVertex(new Point3d(endPoint.x, 0.15d, -endPoint.y));
        // right part of road
        int wei4 =
            meshWalls.addVertex(new Point3d(endPoint.x - normX, 0.1d, -(endPoint.y - normY)));
        // right border
        int wei5 =
            meshWalls.addVertex(new Point3d(endPoint.x - borderX, 0.0d, -(endPoint.y - borderY)));

        leftBorder.addVert(wbi1, tcb1, flatNormalI);
        leftBorder.addVert(wbi2, tcb2, flatNormalI);
        leftBorder.addVert(wei1, tce1, flatNormalI);
        leftBorder.addVert(wei2, tce2, flatNormalI);

        leftPart.addVert(wbi2, tcb2, flatNormalI);
        leftPart.addVert(wbi3, tcb3, flatNormalI);
        leftPart.addVert(wei2, tce2, flatNormalI);
        leftPart.addVert(wei3, tce3, flatNormalI);

        rightBorder.addVert(wbi3, tcb3, flatNormalI);
        rightBorder.addVert(wbi4, tcb4, flatNormalI);
        rightBorder.addVert(wei3, tce3, flatNormalI);
        rightBorder.addVert(wei4, tce4, flatNormalI);

        rightPart.addVert(wbi4, tcb4, flatNormalI);
        rightPart.addVert(wbi5, tcb5, flatNormalI);
        rightPart.addVert(wei4, tce4, flatNormalI);
        rightPart.addVert(wei5, tce5, flatNormalI);

        beginPoint = endPoint;
      }
    }

    model = modelBuilder.toModel();
    model.setUseLight(true);
    model.setUseTexture(true);

    buildModel = true;
  }
 /**
  * Specifies texture data for the tile. If texture data is non-null, a new texture is created from
  * the texture data when the tile is next bound.
  *
  * <p>When a texture is created from the texture data, the texture data field is set to null to
  * indicate that the data has been converted to a texture and its resources may be released.
  *
  * @param textureData the texture data, which may be null.
  */
 protected void setTextureData(TextureData textureData) {
   this.textureData = textureData;
   if (textureData != null && textureData.getMipmapData() != null) this.hasMipmapData = true;
 }