Home | History | Annotate | Download | only in utils
      1 /*******************************************************************************
      2  * Copyright 2011 See AUTHORS file.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *   http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  ******************************************************************************/
     16 
     17 package com.badlogic.gdx.graphics.g3d.utils;
     18 
     19 import com.badlogic.gdx.graphics.Color;
     20 import com.badlogic.gdx.graphics.GL20;
     21 import com.badlogic.gdx.graphics.Mesh;
     22 import com.badlogic.gdx.graphics.VertexAttributes;
     23 import com.badlogic.gdx.graphics.g3d.Material;
     24 import com.badlogic.gdx.graphics.g3d.Model;
     25 import com.badlogic.gdx.graphics.g3d.model.MeshPart;
     26 import com.badlogic.gdx.graphics.g3d.model.Node;
     27 import com.badlogic.gdx.graphics.g3d.model.NodePart;
     28 import com.badlogic.gdx.math.Matrix4;
     29 import com.badlogic.gdx.math.Vector3;
     30 import com.badlogic.gdx.utils.Array;
     31 import com.badlogic.gdx.utils.Disposable;
     32 import com.badlogic.gdx.utils.GdxRuntimeException;
     33 
     34 /** Helper class to create {@link Model}s from code. To start building use the {@link #begin()} method, when finished building use
     35  * the {@link #end()} method. The end method returns the model just build. Building cannot be nested, only one model (per
     36  * ModelBuilder) can be build at the time. The same ModelBuilder can be used to build multiple models sequential. Use the
     37  * {@link #node()} method to start a new node. Use one of the #part(...) methods to add a part within a node. The
     38  * {@link #part(String, int, VertexAttributes, Material)} method will return a {@link MeshPartBuilder} which can be used to build
     39  * the node part.
     40  * @author Xoppa */
     41 public class ModelBuilder {
     42 	/** The model currently being build */
     43 	private Model model;
     44 	/** The node currently being build */
     45 	private Node node;
     46 	/** The mesh builders created between begin and end */
     47 	private Array<MeshBuilder> builders = new Array<MeshBuilder>();
     48 
     49 	private Matrix4 tmpTransform = new Matrix4();
     50 
     51 	private MeshBuilder getBuilder (final VertexAttributes attributes) {
     52 		for (final MeshBuilder mb : builders)
     53 			if (mb.getAttributes().equals(attributes) && mb.lastIndex() < Short.MAX_VALUE / 2) return mb;
     54 		final MeshBuilder result = new MeshBuilder();
     55 		result.begin(attributes);
     56 		builders.add(result);
     57 		return result;
     58 	}
     59 
     60 	/** Begin building a new model */
     61 	public void begin () {
     62 		if (model != null) throw new GdxRuntimeException("Call end() first");
     63 		node = null;
     64 		model = new Model();
     65 		builders.clear();
     66 	}
     67 
     68 	/** End building the model.
     69 	 * @return The newly created model. Call the {@link Model#dispose()} method when no longer used. */
     70 	public Model end () {
     71 		if (model == null) throw new GdxRuntimeException("Call begin() first");
     72 		final Model result = model;
     73 		endnode();
     74 		model = null;
     75 
     76 		for (final MeshBuilder mb : builders)
     77 			mb.end();
     78 		builders.clear();
     79 
     80 		rebuildReferences(result);
     81 		return result;
     82 	}
     83 
     84 	private void endnode () {
     85 		if (node != null) {
     86 			node = null;
     87 		}
     88 	}
     89 
     90 	/** Adds the {@link Node} to the model and sets it active for building. Use any of the part(...) method to add a NodePart. */
     91 	protected Node node (final Node node) {
     92 		if (model == null) throw new GdxRuntimeException("Call begin() first");
     93 
     94 		endnode();
     95 
     96 		model.nodes.add(node);
     97 		this.node = node;
     98 
     99 		return node;
    100 	}
    101 
    102 	/** Add a node to the model. Use any of the part(...) method to add a NodePart.
    103 	 * @return The node being created. */
    104 	public Node node () {
    105 		final Node node = new Node();
    106 		node(node);
    107 		node.id = "node" + model.nodes.size;
    108 		return node;
    109 	}
    110 
    111 	/** Adds the nodes of the specified model to a new node of the model being build. After this method the given model can no
    112 	 * longer be used. Do not call the {@link Model#dispose()} method on that model.
    113 	 * @return The newly created node containing the nodes of the given model. */
    114 	public Node node (final String id, final Model model) {
    115 		final Node node = new Node();
    116 		node.id = id;
    117 		node.addChildren(model.nodes);
    118 		node(node);
    119 		for (final Disposable disposable : model.getManagedDisposables())
    120 			manage(disposable);
    121 		return node;
    122 	}
    123 
    124 	/** Add the {@link Disposable} object to the model, causing it to be disposed when the model is disposed. */
    125 	public void manage (final Disposable disposable) {
    126 		if (model == null) throw new GdxRuntimeException("Call begin() first");
    127 		model.manageDisposable(disposable);
    128 	}
    129 
    130 	/** Adds the specified MeshPart to the current Node. The Mesh will be managed by the model and disposed when the model is
    131 	 * disposed. The resources the Material might contain are not managed, use {@link #manage(Disposable)} to add those to the
    132 	 * model. */
    133 	public void part (final MeshPart meshpart, final Material material) {
    134 		if (node == null) node();
    135 		node.parts.add(new NodePart(meshpart, material));
    136 	}
    137 
    138 	/** Adds the specified mesh part to the current node. The Mesh will be managed by the model and disposed when the model is
    139 	 * disposed. The resources the Material might contain are not managed, use {@link #manage(Disposable)} to add those to the
    140 	 * model.
    141 	 * @return The added MeshPart. */
    142 	public MeshPart part (final String id, final Mesh mesh, int primitiveType, int offset, int size, final Material material) {
    143 		final MeshPart meshPart = new MeshPart();
    144 		meshPart.id = id;
    145 		meshPart.primitiveType = primitiveType;
    146 		meshPart.mesh = mesh;
    147 		meshPart.offset = offset;
    148 		meshPart.size = size;
    149 		part(meshPart, material);
    150 		return meshPart;
    151 	}
    152 
    153 	/** Adds the specified mesh part to the current node. The Mesh will be managed by the model and disposed when the model is
    154 	 * disposed. The resources the Material might contain are not managed, use {@link #manage(Disposable)} to add those to the
    155 	 * model.
    156 	 * @return The added MeshPart. */
    157 	public MeshPart part (final String id, final Mesh mesh, int primitiveType, final Material material) {
    158 		return part(id, mesh, primitiveType, 0, mesh.getNumIndices(), material);
    159 	}
    160 
    161 	/** Creates a new MeshPart within the current Node and returns a {@link MeshPartBuilder} which can be used to build the shape of
    162 	 * the part. If possible a previously used {@link MeshPartBuilder} will be reused, to reduce the number of mesh binds.
    163 	 * Therefore you can only build one part at a time. The resources the Material might contain are not managed, use
    164 	 * {@link #manage(Disposable)} to add those to the model.
    165 	 * @return The {@link MeshPartBuilder} you can use to build the MeshPart. */
    166 	public MeshPartBuilder part (final String id, int primitiveType, final VertexAttributes attributes, final Material material) {
    167 		final MeshBuilder builder = getBuilder(attributes);
    168 		part(builder.part(id, primitiveType), material);
    169 		return builder;
    170 	}
    171 
    172 	/** Creates a new MeshPart within the current Node and returns a {@link MeshPartBuilder} which can be used to build the shape of
    173 	 * the part. If possible a previously used {@link MeshPartBuilder} will be reused, to reduce the number of mesh binds.
    174 	 * Therefore you can only build one part at a time. The resources the Material might contain are not managed, use
    175 	 * {@link #manage(Disposable)} to add those to the model.
    176 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    177 	 *           and TextureCoordinates is supported.
    178 	 * @return The {@link MeshPartBuilder} you can use to build the MeshPart. */
    179 	public MeshPartBuilder part (final String id, int primitiveType, final long attributes, final Material material) {
    180 		return part(id, primitiveType, MeshBuilder.createAttributes(attributes), material);
    181 	}
    182 
    183 	/** Convenience method to create a model with a single node containing a box shape. The resources the Material might contain are
    184 	 * not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    185 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    186 	 *           and TextureCoordinates is supported. */
    187 	public Model createBox (float width, float height, float depth, final Material material, final long attributes) {
    188 		return createBox(width, height, depth, GL20.GL_TRIANGLES, material, attributes);
    189 	}
    190 
    191 	/** Convenience method to create a model with a single node containing a box shape. The resources the Material might contain are
    192 	 * not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    193 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    194 	 *           and TextureCoordinates is supported. */
    195 	public Model createBox (float width, float height, float depth, int primitiveType, final Material material,
    196 		final long attributes) {
    197 		begin();
    198 		part("box", primitiveType, attributes, material).box(width, height, depth);
    199 		return end();
    200 	}
    201 
    202 	/** Convenience method to create a model with a single node containing a rectangle shape. The resources the Material might
    203 	 * contain are not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    204 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    205 	 *           and TextureCoordinates is supported. */
    206 	public Model createRect (float x00, float y00, float z00, float x10, float y10, float z10, float x11, float y11, float z11,
    207 		float x01, float y01, float z01, float normalX, float normalY, float normalZ, final Material material, final long attributes) {
    208 		return createRect(x00, y00, z00, x10, y10, z10, x11, y11, z11, x01, y01, z01, normalX, normalY, normalZ, GL20.GL_TRIANGLES,
    209 			material, attributes);
    210 	}
    211 
    212 	/** Convenience method to create a model with a single node containing a rectangle shape. The resources the Material might
    213 	 * contain are not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    214 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    215 	 *           and TextureCoordinates is supported. */
    216 	public Model createRect (float x00, float y00, float z00, float x10, float y10, float z10, float x11, float y11, float z11,
    217 		float x01, float y01, float z01, float normalX, float normalY, float normalZ, int primitiveType, final Material material,
    218 		final long attributes) {
    219 		begin();
    220 		part("rect", primitiveType, attributes, material).rect(x00, y00, z00, x10, y10, z10, x11, y11, z11, x01, y01, z01, normalX,
    221 			normalY, normalZ);
    222 		return end();
    223 	}
    224 
    225 	/** Convenience method to create a model with a single node containing a cylinder shape. The resources the Material might
    226 	 * contain are not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    227 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    228 	 *           and TextureCoordinates is supported. */
    229 	public Model createCylinder (float width, float height, float depth, int divisions, final Material material,
    230 		final long attributes) {
    231 		return createCylinder(width, height, depth, divisions, GL20.GL_TRIANGLES, material, attributes);
    232 	}
    233 
    234 	/** Convenience method to create a model with a single node containing a cylinder shape. The resources the Material might
    235 	 * contain are not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    236 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    237 	 *           and TextureCoordinates is supported. */
    238 	public Model createCylinder (float width, float height, float depth, int divisions, int primitiveType,
    239 		final Material material, final long attributes) {
    240 		return createCylinder(width, height, depth, divisions, primitiveType, material, attributes, 0, 360);
    241 	}
    242 
    243 	/** Convenience method to create a model with a single node containing a cylinder shape. The resources the Material might
    244 	 * contain are not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    245 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    246 	 *           and TextureCoordinates is supported. */
    247 	public Model createCylinder (float width, float height, float depth, int divisions, final Material material,
    248 		final long attributes, float angleFrom, float angleTo) {
    249 		return createCylinder(width, height, depth, divisions, GL20.GL_TRIANGLES, material, attributes, angleFrom, angleTo);
    250 	}
    251 
    252 	/** Convenience method to create a model with a single node containing a cylinder shape. The resources the Material might
    253 	 * contain are not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    254 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    255 	 *           and TextureCoordinates is supported. */
    256 	public Model createCylinder (float width, float height, float depth, int divisions, int primitiveType,
    257 		final Material material, final long attributes, float angleFrom, float angleTo) {
    258 		begin();
    259 		part("cylinder", primitiveType, attributes, material).cylinder(width, height, depth, divisions, angleFrom, angleTo);
    260 		return end();
    261 	}
    262 
    263 	/** Convenience method to create a model with a single node containing a cone shape. The resources the Material might contain
    264 	 * are not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    265 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    266 	 *           and TextureCoordinates is supported. */
    267 	public Model createCone (float width, float height, float depth, int divisions, final Material material, final long attributes) {
    268 		return createCone(width, height, depth, divisions, GL20.GL_TRIANGLES, material, attributes);
    269 	}
    270 
    271 	/** Convenience method to create a model with a single node containing a cone shape. The resources the Material might contain
    272 	 * are not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    273 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    274 	 *           and TextureCoordinates is supported. */
    275 	public Model createCone (float width, float height, float depth, int divisions, int primitiveType, final Material material,
    276 		final long attributes) {
    277 		return createCone(width, height, depth, divisions, primitiveType, material, attributes, 0, 360);
    278 	}
    279 
    280 	/** Convenience method to create a model with a single node containing a cone shape. The resources the Material might contain
    281 	 * are not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    282 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    283 	 *           and TextureCoordinates is supported. */
    284 	public Model createCone (float width, float height, float depth, int divisions, final Material material,
    285 		final long attributes, float angleFrom, float angleTo) {
    286 		return createCone(width, height, depth, divisions, GL20.GL_TRIANGLES, material, attributes, angleFrom, angleTo);
    287 	}
    288 
    289 	/** Convenience method to create a model with a single node containing a cone shape. The resources the Material might contain
    290 	 * are not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    291 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    292 	 *           and TextureCoordinates is supported. */
    293 	public Model createCone (float width, float height, float depth, int divisions, int primitiveType, final Material material,
    294 		final long attributes, float angleFrom, float angleTo) {
    295 		begin();
    296 		part("cone", primitiveType, attributes, material).cone(width, height, depth, divisions, angleFrom, angleTo);
    297 		return end();
    298 	}
    299 
    300 	/** Convenience method to create a model with a single node containing a sphere shape. The resources the Material might contain
    301 	 * are not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    302 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    303 	 *           and TextureCoordinates is supported. */
    304 	public Model createSphere (float width, float height, float depth, int divisionsU, int divisionsV, final Material material,
    305 		final long attributes) {
    306 		return createSphere(width, height, depth, divisionsU, divisionsV, GL20.GL_TRIANGLES, material, attributes);
    307 	}
    308 
    309 	/** Convenience method to create a model with a single node containing a sphere shape. The resources the Material might contain
    310 	 * are not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    311 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    312 	 *           and TextureCoordinates is supported. */
    313 	public Model createSphere (float width, float height, float depth, int divisionsU, int divisionsV, int primitiveType,
    314 		final Material material, final long attributes) {
    315 		return createSphere(width, height, depth, divisionsU, divisionsV, primitiveType, material, attributes, 0, 360, 0, 180);
    316 	}
    317 
    318 	/** Convenience method to create a model with a single node containing a sphere shape. The resources the Material might contain
    319 	 * are not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    320 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    321 	 *           and TextureCoordinates is supported. */
    322 	public Model createSphere (float width, float height, float depth, int divisionsU, int divisionsV, final Material material,
    323 		final long attributes, float angleUFrom, float angleUTo, float angleVFrom, float angleVTo) {
    324 		return createSphere(width, height, depth, divisionsU, divisionsV, GL20.GL_TRIANGLES, material, attributes, angleUFrom,
    325 			angleUTo, angleVFrom, angleVTo);
    326 	}
    327 
    328 	/** Convenience method to create a model with a single node containing a sphere shape. The resources the Material might contain
    329 	 * are not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    330 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    331 	 *           and TextureCoordinates is supported. */
    332 	public Model createSphere (float width, float height, float depth, int divisionsU, int divisionsV, int primitiveType,
    333 		final Material material, final long attributes, float angleUFrom, float angleUTo, float angleVFrom, float angleVTo) {
    334 		begin();
    335 		part("cylinder", primitiveType, attributes, material).sphere(width, height, depth, divisionsU, divisionsV, angleUFrom,
    336 			angleUTo, angleVFrom, angleVTo);
    337 		return end();
    338 	}
    339 
    340 	/** Convenience method to create a model with a single node containing a capsule shape. The resources the Material might contain
    341 	 * are not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    342 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    343 	 *           and TextureCoordinates is supported. */
    344 	public Model createCapsule (float radius, float height, int divisions, final Material material, final long attributes) {
    345 		return createCapsule(radius, height, divisions, GL20.GL_TRIANGLES, material, attributes);
    346 	}
    347 
    348 	/** Convenience method to create a model with a single node containing a capsule shape. The resources the Material might contain
    349 	 * are not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    350 	 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage}, only Position, Color, Normal
    351 	 *           and TextureCoordinates is supported. */
    352 	public Model createCapsule (float radius, float height, int divisions, int primitiveType, final Material material,
    353 		final long attributes) {
    354 		begin();
    355 		part("capsule", primitiveType, attributes, material).capsule(radius, height, divisions);
    356 		return end();
    357 	}
    358 
    359 	/** Resets the references to {@link Material}s, {@link Mesh}es and {@link MeshPart}s within the model to the ones used within
    360 	 * it's nodes. This will make the model responsible for disposing all referenced meshes. */
    361 	public static void rebuildReferences (final Model model) {
    362 		model.materials.clear();
    363 		model.meshes.clear();
    364 		model.meshParts.clear();
    365 		for (final Node node : model.nodes)
    366 			rebuildReferences(model, node);
    367 	}
    368 
    369 	private static void rebuildReferences (final Model model, final Node node) {
    370 		for (final NodePart mpm : node.parts) {
    371 			if (!model.materials.contains(mpm.material, true)) model.materials.add(mpm.material);
    372 			if (!model.meshParts.contains(mpm.meshPart, true)) {
    373 				model.meshParts.add(mpm.meshPart);
    374 				if (!model.meshes.contains(mpm.meshPart.mesh, true)) model.meshes.add(mpm.meshPart.mesh);
    375 				model.manageDisposable(mpm.meshPart.mesh);
    376 			}
    377 		}
    378 		for (final Node child : node.getChildren())
    379 			rebuildReferences(model, child);
    380 	}
    381 
    382 	/** Convenience method to create a model with three orthonormal vectors shapes. The resources the Material might contain are not
    383 	 * managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    384 	 * @param axisLength Length of each axis.
    385 	 * @param capLength is the height of the cap in percentage, must be in (0,1)
    386 	 * @param stemThickness is the percentage of stem diameter compared to cap diameter, must be in (0,1]
    387 	 * @param divisions the amount of vertices used to generate the cap and stem ellipsoidal bases */
    388 	public Model createXYZCoordinates (float axisLength, float capLength, float stemThickness, int divisions, int primitiveType,
    389 		Material material, long attributes) {
    390 		begin();
    391 		MeshPartBuilder partBuilder;
    392 		Node node = node();
    393 
    394 		partBuilder = part("xyz", primitiveType, attributes, material);
    395 		partBuilder.setColor(Color.RED);
    396 		partBuilder.arrow(0, 0, 0, axisLength, 0, 0, capLength, stemThickness, divisions);
    397 		partBuilder.setColor(Color.GREEN);
    398 		partBuilder.arrow(0, 0, 0, 0, axisLength, 0, capLength, stemThickness, divisions);
    399 		partBuilder.setColor(Color.BLUE);
    400 		partBuilder.arrow(0, 0, 0, 0, 0, axisLength, capLength, stemThickness, divisions);
    401 
    402 		return end();
    403 	}
    404 
    405 	public Model createXYZCoordinates (float axisLength, Material material, long attributes) {
    406 		return createXYZCoordinates(axisLength, 0.1f, 0.1f, 5, GL20.GL_TRIANGLES, material, attributes);
    407 	}
    408 
    409 	/** Convenience method to create a model with an arrow. The resources the Material might contain are not managed, use
    410 	 * {@link Model#manageDisposable(Disposable)} to add those to the model.
    411 	 * @param material
    412 	 * @param capLength is the height of the cap in percentage, must be in (0,1)
    413 	 * @param stemThickness is the percentage of stem diameter compared to cap diameter, must be in (0,1]
    414 	 * @param divisions the amount of vertices used to generate the cap and stem ellipsoidal bases */
    415 	public Model createArrow (float x1, float y1, float z1, float x2, float y2, float z2, float capLength, float stemThickness,
    416 		int divisions, int primitiveType, Material material, long attributes) {
    417 		begin();
    418 		part("arrow", primitiveType, attributes, material).arrow(x1, y1, z1, x2, y2, z2, capLength, stemThickness, divisions);
    419 		return end();
    420 	}
    421 
    422 	/** Convenience method to create a model with an arrow. The resources the Material might contain are not managed, use
    423 	 * {@link Model#manageDisposable(Disposable)} to add those to the model. */
    424 	public Model createArrow (Vector3 from, Vector3 to, Material material, long attributes) {
    425 		return createArrow(from.x, from.y, from.z, to.x, to.y, to.z, 0.1f, 0.1f, 5, GL20.GL_TRIANGLES, material, attributes);
    426 	}
    427 
    428 	/** Convenience method to create a model which represents a grid of lines on the XZ plane. The resources the Material might
    429 	 * contain are not managed, use {@link Model#manageDisposable(Disposable)} to add those to the model.
    430 	 * @param xDivisions row count along x axis.
    431 	 * @param zDivisions row count along z axis.
    432 	 * @param xSize Length of a single row on x.
    433 	 * @param zSize Length of a single row on z. */
    434 	public Model createLineGrid (int xDivisions, int zDivisions, float xSize, float zSize, Material material, long attributes) {
    435 		begin();
    436 		MeshPartBuilder partBuilder = part("lines", GL20.GL_LINES, attributes, material);
    437 		float xlength = xDivisions * xSize, zlength = zDivisions * zSize, hxlength = xlength / 2, hzlength = zlength / 2;
    438 		float x1 = -hxlength, y1 = 0, z1 = hzlength, x2 = -hxlength, y2 = 0, z2 = -hzlength;
    439 		for (int i = 0; i <= xDivisions; ++i) {
    440 			partBuilder.line(x1, y1, z1, x2, y2, z2);
    441 			x1 += xSize;
    442 			x2 += xSize;
    443 		}
    444 
    445 		x1 = -hxlength;
    446 		y1 = 0;
    447 		z1 = -hzlength;
    448 		x2 = hxlength;
    449 		y2 = 0;
    450 		z2 = -hzlength;
    451 		for (int j = 0; j <= zDivisions; ++j) {
    452 			partBuilder.line(x1, y1, z1, x2, y2, z2);
    453 			z1 += zSize;
    454 			z2 += zSize;
    455 		}
    456 
    457 		return end();
    458 	}
    459 
    460 }
    461