null by default,
* causing the respective elements to be ignored. The callbacks may be set
* individually. For example, in order to print all vertices and faces that
* are read from an OBJ file, the following may be used:
*
* BasicWritableObj obj = new BasicWritableObj();
* obj.setVertexConsumer(t -> System.out.println(t));
* obj.setFaceConsumer(t -> System.out.println(t));
* ObjReader.read(inputStream, obj);
*
*/
public class BasicWritableObj implements WritableObj
{
/**
* The vertex consumer
*/
private Consumer super FloatTuple> vertexConsumer;
/**
* The texture coordinate consumer
*/
private Consumer super FloatTuple> texCoordConsumer;
/**
* The normal consumer
*/
private Consumer super FloatTuple> normalConsumer;
/**
* The face consumer
*/
private Consumer super ObjFace> faceConsumer;
/**
* The consumer for group names
*/
private Consumer super Collection extends String>> groupNamesConsumer;
/**
* The consumer for material group names
*/
private Consumer super String> materialGroupNameConsumer;
/**
* The consumer for MTL file names
*/
private Consumer super Collection extends String>> mtlFileNamesConsumer;
/**
* Default constructor
*/
public BasicWritableObj()
{
// Default constructor
}
/**
* Set the vertex consumer
*
* @param vertexConsumer The consumer
*/
public void setVertexConsumer(Consumer super FloatTuple> vertexConsumer)
{
this.vertexConsumer = vertexConsumer;
}
/**
* Set the texture coordinate consumer
*
* @param texCoordConsumer The consumer
*/
public void setTexCoordConsumer(
Consumer super FloatTuple> texCoordConsumer)
{
this.texCoordConsumer = texCoordConsumer;
}
/**
* Set the normal consumer
*
* @param normalConsumer The consumer
*/
public void setNormalConsumer(Consumer super FloatTuple> normalConsumer)
{
this.normalConsumer = normalConsumer;
}
/**
* Set the face consumer
*
* @param faceConsumer The consumer
*/
public void setFaceConsumer(Consumer super ObjFace> faceConsumer)
{
this.faceConsumer = faceConsumer;
}
/**
* Set the group names consumer
*
* @param groupNamesConsumer The consumer
*/
public void setGroupNamesConsumer(
Consumer super Collection extends String>> groupNamesConsumer)
{
this.groupNamesConsumer = groupNamesConsumer;
}
/**
* Set the material group name consumer
*
* @param materialGroupNameConsumer The consumer
*/
public void setMaterialGroupNameConsumer(
Consumer super String> materialGroupNameConsumer)
{
this.materialGroupNameConsumer = materialGroupNameConsumer;
}
/**
* Set the MTL file names consumer
*
* @param mtlFileNamesConsumer The consumer
*/
public void setMtlFileNamesConsumer(
Consumer super Collection extends String>> mtlFileNamesConsumer)
{
this.mtlFileNamesConsumer = mtlFileNamesConsumer;
}
@Override
public final void addVertex(FloatTuple vertex)
{
if (vertexConsumer != null)
{
vertexConsumer.accept(vertex);
}
}
@Override
public final void addVertex(float x, float y, float z)
{
addVertex(FloatTuples.create(x, y, z));
}
@Override
public final void addTexCoord(FloatTuple texCoord)
{
if (texCoordConsumer != null)
{
texCoordConsumer.accept(texCoord);
}
}
@Override
public final void addTexCoord(float x)
{
addTexCoord(FloatTuples.create(x));
}
@Override
public final void addTexCoord(float x, float y)
{
addTexCoord(FloatTuples.create(x, y));
}
@Override
public final void addTexCoord(float x, float y, float z)
{
addTexCoord(FloatTuples.create(x, y, z));
}
@Override
public final void addNormal(FloatTuple normal)
{
if (normalConsumer != null)
{
normalConsumer.accept(normal);
}
}
@Override
public final void addNormal(float x, float y, float z)
{
addNormal(FloatTuples.create(x, y, z));
}
@Override
public final void setActiveGroupNames(
Collection extends String> groupNames)
{
if (groupNamesConsumer != null)
{
groupNamesConsumer.accept(groupNames);
}
}
@Override
public final void setActiveMaterialGroupName(String materialGroupName)
{
if (materialGroupNameConsumer != null)
{
materialGroupNameConsumer.accept(materialGroupName);
}
}
@Override
public final void addFace(ObjFace face)
{
if (faceConsumer != null)
{
faceConsumer.accept(face);
}
}
@Override
public final void addFace(int ... v)
{
addFace(v, null, null);
}
@Override
public final void addFaceWithTexCoords(int... v)
{
addFace(v, v, null);
}
@Override
public final void addFaceWithNormals(int... v)
{
addFace(v, null, v);
}
@Override
public final void addFaceWithAll(int... v)
{
addFace(v, v, v);
}
@Override
public final void addFace(int[] v, int[] vt, int[] vn)
{
Objects.requireNonNull(v, "The vertex indices are null");
if (faceConsumer != null)
{
addFace(ObjFaces.create(v, vt, vn));
}
}
@Override
public final void setMtlFileNames(Collection extends String> mtlFileNames)
{
if (mtlFileNamesConsumer != null)
{
mtlFileNamesConsumer.accept(mtlFileNames);
}
}
}
================================================
FILE: src/main/java/de/javagl/obj/DefaultFloatTuple.java
================================================
/*
* www.javagl.de - Obj
*
* Copyright (c) 2008-2015 Marco Hutter - http://www.javagl.de
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package de.javagl.obj;
import java.util.Arrays;
/**
* Default implementation of a {@link FloatTuple}
*/
final class DefaultFloatTuple implements FloatTuple
{
/**
* The values of this tuple
*/
private final float[] values;
/**
* Creates a new DefaultFloatTuple with the given values
*
* @param values The values
*/
DefaultFloatTuple(float[] values)
{
this.values = values;
}
/**
* Creates a new DefaultFloatTuple with the given values
*
* @param x The x value
* @param y The y value
* @param z The z value
* @param w The w value
*/
DefaultFloatTuple(float x, float y, float z, float w)
{
this(new float[]{x,y,z,w});
}
/**
* Creates a new DefaultFloatTuple with the given values
*
* @param x The x value
* @param y The y value
* @param z The z value
*/
DefaultFloatTuple(float x, float y, float z)
{
this(new float[]{x,y,z});
}
/**
* Creates a new DefaultFloatTuple with the given values
*
* @param x The x value
* @param y The y value
*/
DefaultFloatTuple(float x, float y)
{
this(new float[]{x,y});
}
/**
* Creates a new DefaultFloatTuple with the given value
*
* @param x The x value
*/
DefaultFloatTuple(float x)
{
this(new float[]{x});
}
/**
* Copy constructor.
*
* @param other The other FloatTuple
*/
DefaultFloatTuple(FloatTuple other)
{
this(getValues(other));
}
/**
* Returns the values of the given {@link FloatTuple} as an array
*
* @param f The {@link FloatTuple}
* @return The values
*/
private static float[] getValues(FloatTuple f)
{
if (f instanceof DefaultFloatTuple)
{
DefaultFloatTuple other = (DefaultFloatTuple)f;
return other.values.clone();
}
float[] values = new float[f.getDimensions()];
for (int i=0; inull, then this method will
* do nothing. Otherwise, it will check whether the given indices
* are valid, and throw an IllegalArgumentException if not. They
* are valid when they are all not negative, and all smaller than
* the given maximum.
*
* @param indices The indices
* @param max The maximum index, exclusive
* @param name The name of the index set
* @throws IllegalArgumentException If the given indices are not valid
*/
private static void checkIndices(int[] indices, int max, String name)
{
if (indices == null)
{
return;
}
for (int index : indices) {
if (index < 0) {
throw new IllegalArgumentException(
name + " index is negative: " + index);
}
if (index >= max) {
throw new IllegalArgumentException(
name + " index is " + index +
", but must be smaller than " + max);
}
}
}
}
================================================
FILE: src/main/java/de/javagl/obj/DefaultObjFace.java
================================================
/*
* www.javagl.de - Obj
*
* Copyright (c) 2008-2015 Marco Hutter - http://www.javagl.de
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package de.javagl.obj;
/**
* Default implementation of an ObjFace
*/
final class DefaultObjFace implements ObjFace
{
/**
* The vertex indices of this face
*/
private final int[] vertexIndices;
/**
* The texture coordinate indices of this face
*/
private final int[] texCoordIndices;
/**
* The normal indices of this face
*/
private final int[] normalIndices;
/**
* Creates a face from the given parameters. References to the
* given objects will be stored.
*
* @param vertexIndices The vertex indices
* @param texCoordIndices The texture coordinate indices
* @param normalIndices The normal indices
*/
DefaultObjFace(
int[] vertexIndices, int[] texCoordIndices, int[] normalIndices)
{
this.vertexIndices = vertexIndices;
this.texCoordIndices = texCoordIndices;
this.normalIndices = normalIndices;
}
@Override
public boolean containsTexCoordIndices()
{
return texCoordIndices != null;
}
@Override
public boolean containsNormalIndices()
{
return normalIndices != null;
}
@Override
public int getVertexIndex(int number)
{
return this.vertexIndices[number];
}
@Override
public int getTexCoordIndex(int number)
{
return this.texCoordIndices[number];
}
@Override
public int getNormalIndex(int number)
{
return this.normalIndices[number];
}
/**
* Set the specified index to the given value
*
* @param n The index to set
* @param index The value of the index
*/
void setVertexIndex(int n, int index)
{
vertexIndices[n] = index;
}
/**
* Set the specified index to the given value
*
* @param n The index to set
* @param index The value of the index
*/
void setNormalIndex(int n, int index)
{
normalIndices[n] = index;
}
/**
* Set the specified index to the given value
*
* @param n The index to set
* @param index The value of the index
*/
void setTexCoordIndex(int n, int index)
{
texCoordIndices[n] = index;
}
@Override
public int getNumVertices()
{
return this.vertexIndices.length;
}
@Override
public String toString()
{
String result = "ObjFace[";
for(int i = 0; i < getNumVertices(); i++)
{
result += vertexIndices[i];
if(texCoordIndices != null || normalIndices != null)
{
result += "/";
}
if(texCoordIndices != null)
{
result += texCoordIndices[i];
}
if(normalIndices != null)
{
result += "/" + normalIndices[i];
}
if(i < getNumVertices() - 1)
{
result += " ";
}
}
result += "]";
return result;
}
}
================================================
FILE: src/main/java/de/javagl/obj/DefaultObjGroup.java
================================================
/*
* www.javagl.de - Obj
*
* Copyright (c) 2008-2015 Marco Hutter - http://www.javagl.de
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package de.javagl.obj;
import java.util.ArrayList;
import java.util.List;
/**
* Default implementation of an ObjGroup
*/
final class DefaultObjGroup implements ObjGroup
{
/**
* The name of this group.
*/
private String name;
/**
* The faces in this group
*/
private List-illum) of the material,
* or null if it was not specified
*
* @return The illumination mode of the material.
*/
Integer getIllum();
/**
* Set the illumination mode of the material
*
* @param illum The illumination mode of the material.
*/
void setIllum(Integer illum);
/**
* Returns the optical density, also known as index of refraction, of
* the material, or null if it was not specified
*
* @return The optical density
*/
Float getNi();
/**
* Set the optical density of the material
*
* @param ni The optical density
*/
void setNi(Float ni);
/**
* Returns the transmission filter of the material,
* or null if it was not specified
*
* @return The transmission filter
*/
FloatTuple getTf();
/**
* Set the transmission filter of this material
*
* @param r The red component
* @param g The green component
* @param b The blue component
*/
void setTf(Float r, Float g, Float b);
/**
* Returns the sharpness of reflections from the reflection map,
* or null if it was not specified
*
* @return The sharpness
*/
Float getSharpness();
/**
* Set the sharpness of reflections
*
* @param sharpness The sharpness
*/
void setSharpness(Float sharpness);
//--------------------------------------------------------------------------
// Ambient
/**
* Returns the ambient component of the material,
* or null if it was not specified
*
* @return The ambient component of the material
*/
FloatTuple getKa();
/**
* Set the ambient part of this material
*
* @param r The red component
* @param g The green component
* @param b The blue component
*/
void setKa(Float r, Float g, Float b);
/**
* Returns the name of the ambient map of the material,
* or null if it has no such map.
*
* @return The name of the ambient map of the material
*/
String getMapKa();
/**
* Set the ambient map name of this material
*
* @param mapKa The ambient map name of this material
*/
void setMapKa(String mapKa);
/**
* Returns the ambient map options of the material,
* or null if it has no options.
*
* @return The ambient map {@link TextureOptions}
*/
TextureOptions getMapKaOptions();
/**
* Set the ambient map {@link TextureOptions}
*
* @param options The ambient map {@link TextureOptions}
*/
void setMapKaOptions(TextureOptions options);
//--------------------------------------------------------------------------
// Diffuse
/**
* Returns the diffuse component of the material,
* or null if it was not specified
*
* @return The diffuse component of the material
*/
FloatTuple getKd();
/**
* Set the diffuse part of this material
*
* @param r The red component
* @param g The green component
* @param b The blue component
*/
void setKd(Float r, Float g, Float b);
/**
* Returns the name of the diffuse map of the material,
* or null if it has no such map.
*
* @return The name of the diffuse map of the material
*/
String getMapKd();
/**
* Set the diffuse map name of this material
*
* @param mapKd The diffuse map name of this material
*/
void setMapKd(String mapKd);
/**
* Returns the diffuse map options of the material,
* or null if it has no options.
*
* @return The diffuse map {@link TextureOptions}
*/
TextureOptions getMapKdOptions();
/**
* Set the diffuse map {@link TextureOptions}
*
* @param options The diffuse map {@link TextureOptions}
*/
void setMapKdOptions(TextureOptions options);
//--------------------------------------------------------------------------
// Specular reflectivity
/**
* Returns the specular component of the material,
* or null if it was not specified
*
* @return The specular component of the material
*/
FloatTuple getKs();
/**
* Set the specular part of this material
*
* @param r The red component
* @param g The green component
* @param b The blue component
*/
void setKs(Float r, Float g, Float b);
/**
* Returns the name of the specular reflectivity map of the material,
* or null if it has no such map.
*
* @return The name of the specular reflectivity map of the material
*/
String getMapKs();
/**
* Set the specular reflectivity map name of this material
*
* @param mapKs The specular reflectivity map name of this material
*/
void setMapKs(String mapKs);
/**
* Returns the specular reflectivity map options of the material,
* or null if it has no options.
*
* @return The specular reflectivity map {@link TextureOptions}
*/
TextureOptions getMapKsOptions();
/**
* Set the specular reflectivity map {@link TextureOptions}
*
* @param options The specular reflectivity map {@link TextureOptions}
*/
void setMapKsOptions(TextureOptions options);
//--------------------------------------------------------------------------
// Specular exponent (shininess)
/**
* Returns the shininess of the material.
*
* @return The shininess of the material.
*/
Float getNs();
/**
* Set the shininess of this material,
* or null if it was not specified
*
* @param ns The shininess of this material
*/
void setNs(Float ns);
/**
* Returns the name of the shininess map of the material,
* or null if it has no map.
*
* @return The name of the shininess map of the material
*/
String getMapNs();
/**
* Set the shininess map name of this material
*
* @param mapNs The shininess map name of this material
*/
void setMapNs(String mapNs);
/**
* Returns the shininess map options of the material,
* or null if it has no options.
*
* @return The shininess map {@link TextureOptions}
*/
TextureOptions getMapNsOptions();
/**
* Set the shininess map {@link TextureOptions}
*
* @param options The shininess map {@link TextureOptions}
*/
void setMapNsOptions(TextureOptions options);
//--------------------------------------------------------------------------
// Opacity
/**
* Returns the opacity of the material,
* or null if it was not specified
*
* @return The opacity of the material.
*/
Float getD();
/**
* Set the opacity of the material
*
* @param d The opacity of the material
*/
void setD(Float d);
/**
* Returns whether dissolve (opacity) is dependent on the surface
* orientation relative to the viewer
*
* @return The halo flag
*/
Boolean isHalo();
/**
* Set the halo flag
*
* @param halo The halo flag
*/
void setHalo(Boolean halo);
/**
* Returns the name of the opacity map of the material,
* or null if it has no map.
*
* @return The name of the opacity map of the material
*/
String getMapD();
/**
* Set the opacity map name of this material
*
* @param mapD The opacity map name of this material
*/
void setMapD(String mapD);
/**
* Returns the opacity map options of the material,
* or null if it has no options.
*
* @return The opacity map {@link TextureOptions}
*/
TextureOptions getMapDOptions();
/**
* Set the opacity map {@link TextureOptions}
*
* @param options The opacity map {@link TextureOptions}
*/
void setMapDOptions(TextureOptions options);
//--------------------------------------------------------------------------
// Bump
/**
* Returns the name of the bump map of the material,
* or null if it has no map.
*
* @return The name of the bump map of the material
*/
String getBump();
/**
* Set the bump map name of this material
*
* @param bump The bump map name of this material
*/
void setBump(String bump);
/**
* Returns the bump map options of the material,
* or null if it has no options.
*
* @return The bump map {@link TextureOptions}
*/
TextureOptions getBumpOptions();
/**
* Set the bump map {@link TextureOptions}
*
* @param options The bump map {@link TextureOptions}
*/
void setBumpOptions(TextureOptions options);
//--------------------------------------------------------------------------
// Disp (displacement)
/**
* Returns the name of the displacement map of the material,
* or null if it has no map.
*
* @return The name of the displacement map of the material
*/
String getDisp();
/**
* Set the displacement map name of this material
*
* @param disp The displacement map name of this material
*/
void setDisp(String disp);
/**
* Returns the displacement map options of the material,
* or null if it has no options.
*
* @return The displacement map {@link TextureOptions}
*/
TextureOptions getDispOptions();
/**
* Set the displacement map {@link TextureOptions}
*
* @param options The displacement map {@link TextureOptions}
*/
void setDispOptions(TextureOptions options);
//--------------------------------------------------------------------------
// Decal
/**
* Returns the name of the decal map of the material,
* or null if it has no map.
*
* @return The name of the decal map of the material
*/
String getDecal();
/**
* Set the decal map name of this material
*
* @param decal The decal map name of this material
*/
void setDecal(String decal);
/**
* Returns the decal map options of the material,
* or null if it has no options.
*
* @return The decal map {@link TextureOptions}
*/
TextureOptions getDecalOptions();
/**
* Set the decal map {@link TextureOptions}
*
* @param options The decal map {@link TextureOptions}
*/
void setDecalOptions(TextureOptions options);
//--------------------------------------------------------------------------
// Refl (reflection)
/**
* Returns the list of {@link TextureOptions} objects for the reflection
* maps of the material. This will never be null, but may
* be an empty list if no reflection maps have been defined.
*
* @return The reflection map {@link TextureOptions}
*/
Listnull if it was not specified
*
* @return The roughness component of the material
*/
Float getPr();
/**
* Set the roughness part of this material
*
* @param pr The roughness
*/
void setPr(Float pr);
/**
* Returns the name of the roughness map of the material,
* or null if it has no such map.
*
* @return The name of the roughness map of the material
*/
String getMapPr();
/**
* Set the roughness map name of this material
*
* @param mapPr The roughness map name of this material
*/
void setMapPr(String mapPr);
/**
* Returns the roughness map options of the material,
* or null if it has no options.
*
* @return The roughness map {@link TextureOptions}
*/
TextureOptions getMapPrOptions();
/**
* Set the roughness map {@link TextureOptions}
*
* @param options The roughness map {@link TextureOptions}
*/
void setMapPrOptions(TextureOptions options);
//--------------------------------------------------------------------------
// Metallic
/**
* Returns the metallic component of the material,
* or null if it was not specified
*
* @return The metallic component of the material
*/
Float getPm();
/**
* Set the metallic part of this material
*
* @param pm The metallic part
*/
void setPm(Float pm);
/**
* Returns the name of the metallic map of the material,
* or null if it has no such map.
*
* @return The name of the metallic map of the material
*/
String getMapPm();
/**
* Set the metallic map name of this material
*
* @param mapPm The metallic map name of this material
*/
void setMapPm(String mapPm);
/**
* Returns the metallic map options of the material,
* or null if it has no options.
*
* @return The metallic map {@link TextureOptions}
*/
TextureOptions getMapPmOptions();
/**
* Set the metallic map {@link TextureOptions}
*
* @param options The metallic map {@link TextureOptions}
*/
void setMapPmOptions(TextureOptions options);
//--------------------------------------------------------------------------
// Sheen
/**
* Returns the sheen component of the material,
* or null if it was not specified
*
* @return The sheen component of the material
*/
Float getPs();
/**
* Set the sheen part of this material
*
* @param ps The sheen part
*/
void setPs(Float ps);
/**
* Returns the name of the sheen map of the material,
* or null if it has no such map.
*
* @return The name of the sheen map of the material
*/
String getMapPs();
/**
* Set the sheen map name of this material
*
* @param mapPs The sheen map name of this material
*/
void setMapPs(String mapPs);
/**
* Returns the sheen map options of the material,
* or null if it has no options.
*
* @return The sheen map {@link TextureOptions}
*/
TextureOptions getMapPsOptions();
/**
* Set the sheen map {@link TextureOptions}
*
* @param options The sheen map {@link TextureOptions}
*/
void setMapPsOptions(TextureOptions options);
//--------------------------------------------------------------------------
// Pc (clearcoat thickness)
/**
* Returns the clearcoat thickness of the material,
* or null if it was not specified
*
* @return The clearcoat thickness
*/
Float getPc();
/**
* Set the clearcoat thickness of the material
*
* @param pc The clearcoat thickness
*/
void setPc(Float pc);
//--------------------------------------------------------------------------
// Pcr (clearcoat roughness)
/**
* Returns the clearcoat roughness of the material,
* or null if it was not specified
*
* @return The clearcoat roughness
*/
Float getPcr();
/**
* Set the clearcoat roughness of the material
*
* @param pcr The clearcoat roughness
*/
void setPcr(Float pcr);
//--------------------------------------------------------------------------
// Emissive
/**
* Returns the emissive component of the material,
* or null if it was not specified
*
* @return The emissive component of the material
*/
FloatTuple getKe();
/**
* Set the emissive part of this material
*
* @param r The red component
* @param g The green component
* @param b The blue component
*/
void setKe(Float r, Float g, Float b);
/**
* Returns the name of the emissive map of the material,
* or null if it has no such map.
*
* @return The name of the emissive map of the material
*/
String getMapKe();
/**
* Set the emissive map name of this material
*
* @param mapKe The emissive map name of this material
*/
void setMapKe(String mapKe);
/**
* Returns the emissive map options of the material,
* or null if it has no options.
*
* @return The emissive map {@link TextureOptions}
*/
TextureOptions getMapKeOptions();
/**
* Set the emissive map {@link TextureOptions}
*
* @param options The emissive map {@link TextureOptions}
*/
void setMapKeOptions(TextureOptions options);
//--------------------------------------------------------------------------
// aniso (anisotropy)
/**
* Returns the anisotropy of the material,
* or null if it was not specified
*
* @return The anisotropy
*/
Float getAniso();
/**
* Set the anisotropy of the material
*
* @param aniso The anisotropy
*/
void setAniso(Float aniso);
//--------------------------------------------------------------------------
// anisor (anisotropy rotation)
/**
* Returns the anisotropy rotation of the material,
* or null if it was not specified
*
* @return The anisotropy rotation
*/
Float getAnisor();
/**
* Set the anisotropy rotation of the material
*
* @param anisor The anisotropy rotation
*/
void setAnisor(Float anisor);
//--------------------------------------------------------------------------
// Normal
/**
* Returns the name of the normal map of the material,
* or null if it has no such map.
*
* @return The name of the normal map of the material
*/
String getNorm();
/**
* Set the normal map name of this material
*
* @param norm The normal map name of this material
*/
void setNorm(String norm);
/**
* Returns the normal map options of the material,
* or null if it has no options.
*
* @return The normal map {@link TextureOptions}
*/
TextureOptions getNormOptions();
/**
* Set the normal map {@link TextureOptions}
*
* @param options The normal map {@link TextureOptions}
*/
void setNormOptions(TextureOptions options);
}
================================================
FILE: src/main/java/de/javagl/obj/MtlReader.java
================================================
/*
* www.javagl.de - Obj
*
* Copyright (c) 2008-2015 Marco Hutter - http://www.javagl.de
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package de.javagl.obj;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/**
* A class that may read MTL data, and return the materials as a
* list of {@link Mtl} objects.
*/
public class MtlReader
{
/**
* Read the MTL data from the given stream, and return
* it as {@link Mtl} objects.
* The caller is responsible for closing the given stream.
*
* @param inputStream The stream to read from.
* @return The list of Mtl object.
* @throws IOException If an IO error occurs
*/
public static Listnewmtl
* material definition, and write the result into the given {@link Mtl}.
*
* @param mtl The {@link Mtl}
* @param line The line
* @throws IOException If an IO error occurs
*/
private static void processLine(Mtl mtl, String line)
throws IOException
{
Queue"map_Ka" or
* "refl"
*
* @param mtl The {@link Mtl}
* @param command The command at the beginning of the line
* @param tokens The tokens that have been created from the line
* @throws IOException If an IO error occurs
*/
private static void readTextureMap(
Mtl mtl, String command, Queuenull
*
* @param sb The string builder
* @param key The key
* @param options The {@link TextureOptions}
*/
private static void appendTextureOptions(
StringBuilder sb, String key, TextureOptions options)
{
if (options != null)
{
sb.append(key).append(" ");
sb.append(createString(options)).append("\n");
}
}
/**
* Create the string representation for the given {@link TextureOptions},
* as a single line that may be written to the MTL file
*
* @param options The {@link TextureOptions}
* @return The string representation
*/
static String createString(TextureOptions options)
{
StringBuilder sb = new StringBuilder();
append(sb, "-blendu", options.isBlendu(), " ");
append(sb, "-blendv", options.isBlendv(), " ");
append(sb, "-boost", options.getBoost(), " ");
appendTuple(sb, "-mm", options.getMm(), " ");
appendTuple(sb, "-o", options.getO(), " ");
appendTuple(sb, "-s", options.getS(), " ");
appendTuple(sb, "-t", options.getT(), " ");
append(sb, "-texres", options.getTexres(), " ");
append(sb, "-clamp", options.isClamp(), " ");
append(sb, "-bm", options.getBm(), " ");
append(sb, "-imfchan", options.getImfchan(), " ");
append(sb, "-type", options.getType(), " ");
sb.append(options.getFileName());
return sb.toString();
}
/**
* Append the given key-value mapping to the given string builder, if
* the given value is not null
*
* @param sb The string builder
* @param key The key
* @param value The value
* @param separator The separator to append after the value
*/
private static void append(
StringBuilder sb, String key, Object value, String separator)
{
if (value != null)
{
sb.append(key).append(" ");
sb.append(value);
sb.append(separator);
}
}
/**
* Append the given key-value mapping to the given string builder, if
* the given value is not null
*
* @param sb The string builder
* @param key The key
* @param value The value
* @param separator The separator to append after the value
*/
private static void append(
StringBuilder sb, String key, Boolean value, String separator)
{
if (value != null)
{
sb.append(key).append(" ");
if (value)
{
sb.append("on");
}
else
{
sb.append("off");
}
sb.append(separator);
}
}
/**
* Append the given key-value mapping to the given string builder, if
* the given value is not null
*
* @param sb The string builder
* @param key The key
* @param value The value
* @param separator The separator to append after the value
*/
private static void appendTuple(
StringBuilder sb, String key, FloatTuple value, String separator)
{
if (value != null)
{
sb.append(key).append(" ");
sb.append(FloatTuples.createString(value));
sb.append(separator);
}
}
/**
* Private constructor to prevent instantiation
*/
private MtlWriter()
{
// Private constructor to prevent instantiation
}
}
================================================
FILE: src/main/java/de/javagl/obj/Mtls.java
================================================
/*
* www.javagl.de - Obj
*
* Copyright (c) 2008-2015 Marco Hutter - http://www.javagl.de
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package de.javagl.obj;
/**
* Methods to create {@link Mtl} instances
*/
public class Mtls
{
/**
* Creates a new default {@link Mtl}
*
* @param name The name of the material
* @return The {@link Mtl}
*/
public static Mtl create(String name)
{
return new DefaultMtl(name);
}
/**
* Private constructor to prevent instantiation
*/
private Mtls()
{
// Private constructor to prevent instantiation
}
}
================================================
FILE: src/main/java/de/javagl/obj/Obj.java
================================================
/*
* www.javagl.de - Obj
*
* Copyright (c) 2008-2015 Marco Hutter - http://www.javagl.de
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package de.javagl.obj;
/**
* An in-memory representation of an OBJ file. This interface is only
* a combination of a {@link ReadableObj} and a {@link WritableObj}.obj.getNumFaces() * 3.
*
* @param obj The {@link ReadableObj}
* @return The number of face vertex indices
*/
public static int getTotalNumFaceVertices(ReadableObj obj)
{
return sum(getNumFaceVertices(obj));
}
/**
* Returns the sum of the elements of the given array
*
* @param array The array
* @return The sum of the elements of the given array
*/
private static int sum(int[] array)
{
int sum = 0;
for (int i : array)
{
sum += i;
}
return sum;
}
//=========================================================================
// Vertex indices
/**
* Returns the vertex indices from the faces of the given
* {@link ReadableObj} as an array. n = obj.getNumFaces() * numVerticesPerFace
*
* @param obj The {@link ReadableObj}
* @param target The buffer that will store the result
* @throws BufferOverflowException If the buffer can not store the result
*/
public static void getFaceVertexIndices(
ReadableObj obj, IntBuffer target)
{
for(int i = 0; i < obj.getNumFaces(); i++)
{
ObjFace face = obj.getFace(i);
for (int j=0; jn = obj.getNumFaces() * numVerticesPerFace.n = obj.getNumFaces() * numVerticesPerFace.1.0f - y. Most image loaders provide
* image data with the first pixel being the upper left pixel of
* the image. But OpenGL glTexImage2D calls expect the first
* pixel to be the lower left. Flipping the texture coordinates
* by passing flipY=true to this method allows to compensate
* for this mismatch.
* @return The resulting array
*/
public static float[] getTexCoordsArray(
ReadableObj obj, int dimensions, boolean flipY)
{
float[] array = new float[obj.getNumTexCoords() * dimensions];
getTexCoords(obj, FloatBuffer.wrap(array), dimensions, flipY);
return array;
}
/**
* Returns all texture coordinates of the given
* {@link ReadableObj} as direct FloatBuffer. The position
* of the returned buffer will be 0, and its limit and
* capacity will match the stored data.
*
* @param obj The {@link ReadableObj}
* @param dimensions The dimensions that are assumed for the coordinates
* @return The resulting buffer
*/
public static FloatBuffer getTexCoords(ReadableObj obj, int dimensions)
{
return getTexCoords(obj, dimensions, false);
}
/**
* Returns all texture coordinates of the given
* {@link ReadableObj} as direct FloatBuffer. The position
* of the returned buffer will be 0, and its limit and
* capacity will match the stored data.
*
* @param obj The {@link ReadableObj}
* @param dimensions The dimensions that are assumed for the coordinates
* @param flipY Whether the texture coordinates should be flipped
* vertically. This means that the y-coordinates (at dimension index 1)
* will be replaced with 1.0f - y. Most image loaders provide
* image data with the first pixel being the upper left pixel of
* the image. But OpenGL glTexImage2D calls expect the first
* pixel to be the lower left. Flipping the texture coordinates
* by passing flipY=true to this method allows to compensate
* for this mismatch.
* @return The resulting buffer
*/
public static FloatBuffer getTexCoords(
ReadableObj obj, int dimensions, boolean flipY)
{
FloatBuffer buffer =
createDirectFloatBuffer(obj.getNumTexCoords() * dimensions);
getTexCoords(obj, buffer, dimensions, flipY);
buffer.position(0);
return buffer;
}
/**
* Stores the texture coordinates of the given {@link ReadableObj}
* in the given buffer. The position of the target will be increased by
* obj.getNumTexCoords() * dimensions. The position
* of the given buffer will be advanced accordingly.
*
* @param obj The {@link ReadableObj}
* @param target The target that will store the result
* @param dimensions The dimensions that are assumed for the coordinates
* @throws BufferOverflowException If the target can not store the result
*/
public static void getTexCoords(
ReadableObj obj, FloatBuffer target, int dimensions)
{
getTexCoords(obj, target, dimensions, false);
}
/**
* Stores the texture coordinates of the given {@link ReadableObj}
* in the given buffer. The position of the target will be increased by
* obj.getNumTexCoords() * dimensions. The position
* of the given buffer will be advanced accordingly.
*
* @param obj The {@link ReadableObj}
* @param target The target that will store the result
* @param dimensions The dimensions that are assumed for the coordinates
* @param flipY Whether the texture coordinates should be flipped
* vertically. This means that the y-coordinates (at dimension index 1)
* will be replaced with 1.0f - y. Most image loaders provide
* image data with the first pixel being the upper left pixel of
* the image. But OpenGL glTexImage2D calls expect the first
* pixel to be the lower left. Flipping the texture coordinates
* by passing flipY=true to this method allows to compensate
* for this mismatch.
* @throws BufferOverflowException If the target can not store the result
*/
public static void getTexCoords(
ReadableObj obj, FloatBuffer target, int dimensions, boolean flipY)
{
if (flipY)
{
for(int i = 0; i < obj.getNumTexCoords(); i++)
{
FloatTuple tuple = obj.getTexCoord(i);
for (int j=0; jshort. short can represent, then the
* short value will become negative. The resulting buffer
* will then still be valid for passing it to OpenGL when the index
* mode is GL_UNSIGNED_SHORT, because the bitwise
* representation is the same. When one of the integer values is larger
* than the value that can be represented with an unsigned
* short, then the resulting indices will be invalid
* and cause rendering artifacts.
*
* @param intBuffer The IntBuffer
* @return The ShortBuffer
*/
public static ShortBuffer convertToShortBuffer(IntBuffer intBuffer)
{
ShortBuffer shortBuffer = createDirectShortBuffer(intBuffer.capacity());
for (int i = 0; i < intBuffer.capacity(); i++)
{
shortBuffer.put(i, (short) intBuffer.get());
}
return shortBuffer;
}
/**
* Create a direct IntBuffer with the given size
*
* @param size The size
* @return The IntBuffer
*/
private static IntBuffer createDirectIntBuffer(int size)
{
return ByteBuffer.allocateDirect(size * 4)
.order(ByteOrder.nativeOrder())
.asIntBuffer();
}
/**
* Create a direct ShortBuffer with the given size
*
* @param size The size
* @return The ShortBuffer
*/
private static ShortBuffer createDirectShortBuffer(int size)
{
return ByteBuffer.allocateDirect(size * 2)
.order(ByteOrder.nativeOrder())
.asShortBuffer();
}
/**
* Create a direct FloatBuffer with the given size
*
* @param size The size
* @return The FloatBuffer
*/
private static FloatBuffer createDirectFloatBuffer(int size)
{
return ByteBuffer.allocateDirect(size * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
}
/**
* Private constructor to prevent instantiation
*/
private ObjData()
{
// Private constructor to prevent instantiation
}
}
================================================
FILE: src/main/java/de/javagl/obj/ObjFace.java
================================================
/*
* www.javagl.de - Obj
*
* Copyright (c) 2008-2015 Marco Hutter - http://www.javagl.de
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package de.javagl.obj;
/**
* A single face that is stored in an OBJ file
*/
public interface ObjFace
{
/**
* Returns the number of vertices this face consists of.
*
* @return The number of vertices this face consists of.
*/
int getNumVertices();
/**
* Returns whether this face contains texture coordinate indices
*
* @return Whether this face contains texture coordinate indices
*/
boolean containsTexCoordIndices();
/**
* Returns whether this face contains normal indices
*
* @return Whether this face contains normal indices
*/
boolean containsNormalIndices();
/**
* Returns the index of the vertex with the given number.
* The index that is returned will be ZERO-based, in contrast
* to the ONE-based storage in the OBJ file.
*
* @param number The number of the vertex
* @return The index of the vertex.
* @throws IndexOutOfBoundsException If the given number is negative
* or not smaller than {@link #getNumVertices()}
*/
int getVertexIndex(int number);
/**
* Returns the index of the texture coordinate with the given number.
* The index that is returned will be ZERO-based, in contrast
* to the ONE-based storage in the OBJ file.
*
* @param number The number of the texture coordinate
* @return The index of the texture coordinate.
* @throws IndexOutOfBoundsException If the given number is negative
* or not smaller than {@link #getNumVertices()}
*/
int getTexCoordIndex(int number);
/**
* Returns the index of the normal with the given number.
* The index that is returned will be ZERO-based, in contrast
* to the ONE-based storage in the OBJ file.
*
* @param number The number of the normal
* @return The index of the normal.
* @throws IndexOutOfBoundsException If the given number is negative
* or not smaller than {@link #getNumVertices()}
*/
int getNormalIndex(int number);
}
================================================
FILE: src/main/java/de/javagl/obj/ObjFaceParser.java
================================================
/*
* www.javagl.de - Obj
*
* Copyright (c) 2008-2015 Marco Hutter - http://www.javagl.de
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package de.javagl.obj;
import java.io.IOException;
import java.util.Arrays;
/**
* A class for reading the index data for an {@link ObjFace} from an
* 'f'-line that was read from an OBJ file
*/
final class ObjFaceParser
{
/**
* The initial size for the index buffers
*/
private static final int INITIAL_BUFFER_SIZE = 6;
/**
* Buffer for vertex indices
*/
private int[] vertexIndexBuffer = new int[INITIAL_BUFFER_SIZE];
/**
* Buffer for texture coordinates
*/
private int[] texCoordIndexBuffer = new int[INITIAL_BUFFER_SIZE];
/**
* Buffer normal indices
*/
private int[] normalIndexBuffer = new int[INITIAL_BUFFER_SIZE];
/**
* Flag whether texture coordinates have been found during the last
* call to {@link #parse(String)}
*/
private boolean foundTexCoordIndices = false;
/**
* Flag whether normal indices have been found during the last
* call to {@link #parse(String)}
*/
private boolean foundNormalIndices = false;
/**
* Counter for the vertices
*/
private int vertexCounter = 0;
/**
* Index in the input array
*/
private int idx = 0;
/**
* The input array to parse
*/
private char[] lineData;
/**
* Parse the given 'f'-line that was read from an OBJ file
*
* @param line The line
* @throws IOException If the given line can not be parsed
*/
void parse(String line) throws IOException
{
parseLine(line);
}
/**
* Returns a new array containing the vertex indices that have
* been parsed during the last call to
* {@link #parse(String)}.
*
* @return The vertex indices
*/
int[] getVertexIndices()
{
return Arrays.copyOf(vertexIndexBuffer, vertexCounter);
}
/**
* Returns a new array containing the texCoord indices that have
* been parsed during the last call to
* {@link #parse(String)}, or null
* if no texture coordinate indices have been read
*
* @return The texCord indices
*/
int[] getTexCoordIndices()
{
if (foundTexCoordIndices)
{
return Arrays.copyOf(texCoordIndexBuffer, vertexCounter);
}
return null;
}
/**
* Returns a new array containing the normal indices that have
* been parsed during the last call to
* {@link #parse(String)}, or null
* if no normal indices have been read
*
* @return The normal indices
*/
int[] getNormalIndices()
{
if(foundNormalIndices)
{
return Arrays.copyOf(normalIndexBuffer, vertexCounter);
}
return null;
}
/**
* Parse the Face from the given line null. In any case, it is assumed
* that all non-null arrays have equal length. References
* to the given arrays will be stored internally, so they should not
* be modified after they have been passed to this method.
*
* @param v The vertex indices
* @param vt The texCoord indices
* @param vn The normal indices
* @return The face
*/
public static ObjFace create(int[] v, int[] vt, int[] vn)
{
return createDefault(v, vt, vn);
}
/**
* Create a face with the given indices. The texCoord indices and the
* normal indices may be null. In any case, it is assumed
* that all non-null arrays have equal length. References
* to the given arrays will be stored internally, so they should not
* be modified after they have been passed to this method.
*
* @param v The vertex indices
* @param vt The texCoord indices
* @param vn The normal indices
* @return The face
*/
static DefaultObjFace createDefault(int[] v, int[] vt, int[] vn)
{
DefaultObjFace result = new DefaultObjFace(v, vt, vn);
return result;
}
/**
* Returns the string for the given face that makes up one 'f' line
* in an OBJ file
*
* @param face The face
* @return The string for the face
*/
public static String createString(ObjFace face)
{
StringBuilder sb = new StringBuilder("f ");
for(int i = 0; i < face.getNumVertices(); i++)
{
if (i > 0)
{
sb.append(" ");
}
sb.append(face.getVertexIndex(i) + 1);
if(face.containsTexCoordIndices() || face.containsNormalIndices())
{
sb.append("/");
}
if(face.containsTexCoordIndices())
{
sb.append(face.getTexCoordIndex(i) + 1);
}
if(face.containsNormalIndices())
{
sb.append("/").append(face.getNormalIndex(i) + 1);
}
}
return sb.toString();
}
/**
* Private constructor to prevent instantiation
*/
private ObjFaces()
{
// Private constructor to prevent instantiation
}
}
================================================
FILE: src/main/java/de/javagl/obj/ObjGroup.java
================================================
/*
* www.javagl.de - Obj
*
* Copyright (c) 2008-2015 Marco Hutter - http://www.javagl.de
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package de.javagl.obj;
/**
* Interface describing a single group of an OBJ file. This may either
* be a geometry group that is identified by the 'g' token,
* or a group that is implied by a common material, identified by the
* 'usemtl' token.
*/
public interface ObjGroup
{
/**
* Returns the name of this group.
*
* @return The name of this group.
*/
String getName();
/**
* Returns the number of faces in this group.
*
* @return The number of faces in this group.
*/
int getNumFaces();
/**
* Returns the face with the given index.
*
* @param index The index of the face
* @return The face with the given index.
* @throws IndexOutOfBoundsException If the index is negative or not
* smaller than {@link #getNumFaces()}
*/
ObjFace getFace(int index);
}
================================================
FILE: src/main/java/de/javagl/obj/ObjReader.java
================================================
/*
* www.javagl.de - Obj
*
* Copyright (c) 2008-2015 Marco Hutter - http://www.javagl.de
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package de.javagl.obj;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.StringTokenizer;
/**
* A class that may read OBJ data from a stream and
* store the read data in an {@link WritableObj}.
*/
public class ObjReader
{
/**
* Read the OBJ data from the given stream and return it as an {@link Obj}.
* The caller is responsible for closing the given stream.
*
* @param inputStream The stream to read from
* @return The {@link Obj}
* @throws IOException If an IO error occurs
*/
public static Obj read(InputStream inputStream) throws IOException
{
return read(inputStream, Objs.create());
}
/**
* Read the OBJ data from the given stream and store the read
* elements in the given {@link WritableObj}.
* The caller is responsible for closing the given stream.
*
* @param null, nothing will
* be done
* @param count The count
*/
private static void makeIndicesAbsolute(int[] array, int count)
{
if (array == null)
{
return;
}
for (int i=0; iusemtl directive), then the resulting map will
* be empty. usemtl directives in the input file) to the {@link Obj}
* that represents this material group.
*/
public static Mapn vertices,
* the resulting parts will have at most maxNumVertices + n - 1
* vertices. For example, if the given input contains only triangles,
* then the parts will have at most maxNumVertices + 2
* vertices.unsigned short value,
* this method may be called with maxNumVertices=65000.
*
* @param obj The input {@link ReadableObj}
* @param maxNumVertices The maximum number of vertices
* @return One {@link Obj} for each part.
* @throws IllegalArgumentException If the given number is smaller than 3.
*/
public static Listnull
*/
public class ObjUtils
{
/**
* Convert the given {@link ReadableObj} into an {@link Obj} that has
* a structure appropriate for rendering it with OpenGL:
* vertexIndexMapping may be null. If it
* is not null, vertexIndexMapping.get(i)
* afterwards stores the index that vertex i had in the
* input.
*
* @param input The input {@link ReadableObj}
* @param inputGroup The group of the input
* @param vertexIndexMapping The optional index mapping
* @return The resulting {@link Obj}
*/
public static Obj groupToObj(
ReadableObj input, ObjGroup inputGroup,
ListvertexIndexMapping may be null. If it
* is not null, vertexIndexMapping.get(i)
* afterwards stores the index that vertex i had in the
* input.
*
* @param null. Otherwise it is assumed that
* it already contains the index mapping that was obtained by a call
* to {@link #groupToObj(ReadableObj, ObjGroup, List, WritableObj)}, and
* this mapping will be updated appropriately.
*
* @param null. Otherwise it is assumed that
* it already contains the index mapping that was obtained by a call
* to {@link #groupToObj(ReadableObj, ObjGroup, List, WritableObj)}, and
* this mapping will be updated appropriately.
*
* @param null. Otherwise it is assumed that
* it already contains the index mapping that was obtained by a call
* to {@link #groupToObj(ReadableObj, ObjGroup, List, WritableObj)}, and
* this mapping will be updated appropriately.
*
* @param input The input {@link ReadableObj}
* @param propertyIndexAccessor The accessor for the property index
* @param indexMapping The optional index mapping
* @param output The output {@link WritableObj}
*/
private static void makePropertiesUnique(
ReadableObj input, PropertyIndexAccessor propertyIndexAccessor,
Listnull.null will be accessed using
* the absolute access methods, up to their capacity. This means
* that the position of the given buffers will not be affected.
*
* @param indices The indices. Three consecutive elements of this buffer
* are assumed to form one triangle.
* @param vertices The vertices
* @param texCoords The texture coordinates, assumed to be 2D.
* @param normals The normals
* @return The {@link Obj}
* @throws NullPointerException If the indices or vertices are
* null.
*/
public static Obj createFromIndexedTriangleData(
IntBuffer indices,
FloatBuffer vertices,
FloatBuffer texCoords,
FloatBuffer normals)
{
int numTriangles = indices.capacity() / 3;
int numVertices = vertices.capacity() / 3;
Obj obj = Objs.create();
for (int i=0; inull will be returned.
*
* @param face The face
* @return The names of the groups that are activated with the
* given face
*/
Setnull
* will be returned.
*
* @param face The face
* @return The name of the material group that is activated with the
* given face
*/
String getActivatedMaterialGroupName(ObjFace face);
/**
* Returns the number of groups in this Obj.
*
* @return The number of groups in this Obj.
*/
int getNumGroups();
/**
* Returns the group with the given index.
*
* @param index The index of the group.
* @return The group with the given index.
* @throws IndexOutOfBoundsException If the index is negative or not
* smaller than {@link #getNumGroups()}
*/
ObjGroup getGroup(int index);
/**
* Returns the group with the given name, or null if
* there is no such group in this Obj.
*
* @param name The name of the group.
* @return The group with the given name.
*/
ObjGroup getGroup(String name);
/**
* Returns the number of material groups in this Obj.
*
* @return The number of material groups in this Obj.
*/
int getNumMaterialGroups();
/**
* Returns the material group with the given index.
*
* @param index The index of the material group.
* @return The material group with the given index.
* @throws IndexOutOfBoundsException If the index is negative or not
* smaller than {@link #getNumMaterialGroups()}
*/
ObjGroup getMaterialGroup(int index);
/**
* Returns the material group with the given name, or null if
* there is no such group in this Obj.
*
* @param name The name of the material group.
* @return The material group with the given name.
*/
ObjGroup getMaterialGroup(String name);
/**
* Returns an unmodifiable list containing the names of the MTL file
* that are associated with this OBJ, as they have been read from
* the mtllib line.
* This may be an empty list, if no MTL file names have been read.
*
* @return The names of the MTL files.
*/
List-blendu),
* or null if it was not specified
*
* @return Whether horizontal blending is enabled
*/
Boolean isBlendu();
/**
* Set the horizontal texture blending state
*
* @param blendu The blending state
*/
void setBlendu(Boolean blendu);
/**
* Returns the vertical texture blending state (-blendv),
* or null if it was not specified
*
* @return Whether vertical blending is enabled
*/
Boolean isBlendv();
/**
* Set the vertical texture blending state
*
* @param blendv The blending state
*/
void setBlendv(Boolean blendv);
/**
* Returns the color correction state (-cc),
* or null if it was not specified
*
* @return The color correction state
*/
Boolean isCc();
/**
* Set the color correction state
*
* @param cc The color correction state
*/
void setCc(Boolean cc);
/**
* Returns the boost mip-map sharpness value (-boost),
* or null if it was not specified
*
* @return The boost mip-map sharpness.
*/
Float getBoost();
/**
* Set the mip-map boost value
*
* @param boost The boost value
*/
void setBoost(Float boost);
/**
* Returns the texture map modifier (-mm),
* or null if it was not specified
*
* @return The modified texture map contrast.
*/
FloatTuple getMm();
/**
* Set the texture map modifier values
*
* @param base The base
* @param gain The gain
*/
void setMm(Float base, Float gain);
/**
* Returns the origin offset (-o),
* or null if it was not specified
*
* @return The origin offset.
*/
FloatTuple getO();
/**
* Set the origin offset
*
* @param u The u component
* @param v The v component
* @param w The w component
*/
void setO(Float u, Float v, Float w);
/**
* Returns the scale (-s),
* or null if it was not specified
*
* @return The scale.
*/
FloatTuple getS();
/**
* Set the scale
*
* @param u The u component
* @param v The v component
* @param w The w component
*/
void setS(Float u, Float v, Float w);
/**
* Returns the turbulence (-t),
* or null if it was not specified
*
* @return The turbulence.
*/
FloatTuple getT();
/**
* Set the turbulence
*
* @param u The u component
* @param v The v component
* @param w The w component
*/
void setT(Float u, Float v, Float w);
/**
* Returns the texture resolution (-texres),
* or null if it was not specified
*
* @return The texture resolution.
*/
Float getTexres();
/**
* Set the texture resolution
*
* @param texres The texture resolution
*/
void setTexres(Float texres);
/**
* Returns the clamping state (-clamp),
* or null if it was not specified
*
* @return Whether or not clamping is enabled.
*/
Boolean isClamp();
/**
* Set the clamping state
*
* @param clamp The clamping state
*/
void setClamp(Boolean clamp);
/**
* Returns the bump multiplier (-bm),
* or null if it was not specified
*
* @return The bump multiplier.
*/
Float getBm();
/**
* Set the bump multiplier
*
* @param bm The bump multiplier
*/
void setBm(Float bm);
/**
* Returns the IMF channel to use (-imfchan),
* or null if it was not specified
*
* @return The IMF channel to use.
*/
String getImfchan();
/**
* Set the IMF channel
*
* @param imfchan The IMF channel
*/
void setImfchan(String imfchan);
/**
* Returns the type of texture map (-type),
* or null if it was not specified
*
* @return The type of texture map
*/
String getType();
/**
* Set the type
*
* @param type The type
*/
void setType(String type);
}
================================================
FILE: src/main/java/de/javagl/obj/Utils.java
================================================
/*
* www.javagl.de - Obj
*
* Copyright (c) 2008-2015 Marco Hutter - http://www.javagl.de
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package de.javagl.obj;
import java.io.IOException;
import java.util.Queue;
import java.util.StringTokenizer;
/**
* Utility methods for reading and parsing
*/
class Utils
{
/**
* Reads a float tuple from the given StringTokenizer
*
* @param st The StringTokenizer
* @return The FloatTuple
* @throws IOException If the tuple can not be read
*/
static FloatTuple readFloatTuple(StringTokenizer st)
throws IOException
{
float x = parseFloat(st.nextToken());
if (st.hasMoreTokens())
{
float y = parseFloat(st.nextToken());
if (st.hasMoreTokens())
{
float z = parseFloat(st.nextToken());
if (st.hasMoreTokens())
{
float w = parseFloat(st.nextToken());
return FloatTuples.create(x,y,z,w);
}
return FloatTuples.create(x,y,z);
}
return FloatTuples.create(x,y);
}
return FloatTuples.create(x);
}
/**
* Parse a float from the given string, wrapping number format
* exceptions into an IOException
*
* @param s The string
* @return The float
* @throws IOException If the string does not contain a valid float value
*/
static float parseFloat(String s) throws IOException
{
try
{
return Float.parseFloat(s);
}
catch (NumberFormatException e)
{
throw new IOException(e);
}
}
/**
* Returns whether the given string can be parsed into a float value
*
* @param s The string
* @return Whether the string is a float value. If the given string is
* null, then false is returned.
*/
private static boolean isFloat(String s)
{
if (s == null)
{
return false;
}
try
{
Float.parseFloat(s);
return true;
}
catch (NumberFormatException e)
{
return false;
}
}
/**
* Parse up to max float values from the given tokens.
* If there are fewer than max tokens, then the
* resulting values will be null. The parsing will
* stop when a value is encountered that is not a float value.
*
* @param tokens The input tokens
* @param max The maximum number of tokens to process
* @return The array containing the parsed values
*/
static Float[] parseFloats(Queue"true" and "on" to true,
* and anything else to false.
*
* @param s The string
* @return The boolean value
*/
static boolean parseBoolean(String s)
{
if ("true".equalsIgnoreCase(s))
{
return true;
}
if ("on".equalsIgnoreCase(s))
{
return true;
}
return false;
}
/**
* Parse an int from the given string, wrapping number format
* exceptions into an IOException
*
* @param s The string
* @return The int
* @throws IOException If the string does not contain a valid int value
*/
static int parseInt(String s) throws IOException
{
try
{
return Integer.parseInt(s);
}
catch (NumberFormatException e)
{
throw new IOException(e);
}
}
/**
* Creates a {@link FloatTuple} from the given RGB values, treating
* optional values as described in the MTL specification: If
* the r component is null, then
* null is returned. If the g or
* b component is null, then the r
* component will be used instead.
*
* @param r The r-component
* @param g The g-component
* @param b The b-component
* @return The {@link FloatTuple}
*/
static FloatTuple createRgbTuple(Float r, Float g, Float b)
{
if (r == null)
{
return null;
}
float fr = r;
float fg = r;
float fb = r;
if (g != null)
{
fg = g;
}
if (b != null)
{
fb = b;
}
return FloatTuples.create(fr, fg, fb);
}
/**
* Creates a {@link FloatTuple} from the given UVW values, treating
* optional values as described in the MTL specification: If
* the u component is null, then
* null is returned. If the v or
* w component is null, then the given default value
* will be used.
*
* @param u The u-component
* @param v The v-component
* @param w The w-component
* @param defaultValue The default value for v and w
* @return The {@link FloatTuple}
*/
static FloatTuple createUvwTuple(
Float u, Float v, Float w, float defaultValue)
{
if (u == null)
{
return null;
}
float fu = u;
float fv = (v == null ? defaultValue : v);
float fw = (w == null ? defaultValue : w);
return FloatTuples.create(fu, fv, fw);
}
/**
* Private constructor to prevent instantiation
*/
private Utils()
{
// Private constructor to prevent instantiation
}
}
================================================
FILE: src/main/java/de/javagl/obj/WritableObj.java
================================================
/*
* www.javagl.de - Obj
*
* Copyright (c) 2008-2015 Marco Hutter - http://www.javagl.de
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package de.javagl.obj;
import java.util.Collection;
/**
* Interface for all classes that may receive the data that is read
* from an OBJ file by an {@link ObjReader}
*/
public interface WritableObj
{
/**
* Add the given vertex
*
* @param vertex The vertex to add.
* @throws NullPointerException If the vertex is null
*/
void addVertex(FloatTuple vertex);
/**
* Add the given vertex
*
* @param x The x-coordinate
* @param y The y-coordinate
* @param z The z-coordinate
*/
void addVertex(float x, float y, float z);
/**
* Add the given texture coordinate
*
* @param texCoord The texture coordinate to add.
* @throws NullPointerException If the texture coordinate is
* null
*/
void addTexCoord(FloatTuple texCoord);
/**
* Add the given texture coordinate
*
* @param x The x-coordinate
*/
void addTexCoord(float x);
/**
* Add the given texture coordinate
*
* @param x The x-coordinate
* @param y The y-coordinate
*/
void addTexCoord(float x, float y);
/**
* Add the given texture coordinate
*
* @param x The x-coordinate
* @param y The y-coordinate
* @param z The z-coordinate
*/
void addTexCoord(float x, float y, float z);
/**
* Add the given normal
*
* @param normal The normal to add.
* @throws NullPointerException If the normal is null
*/
void addNormal(FloatTuple normal);
/**
* Add the given normal
*
* @param x The x-coordinate
* @param y The y-coordinate
* @param z The z-coordinate
*/
void addNormal(float x, float y, float z);
/**
* Set the groups with the given names to be active right now. Faces that
* are added subsequently will be added to all active groups, creating
* these groups if necessary. If the given collection is null,
* then this call will have no effect. If the given collection is empty,
* then the default group (named "default") will be activated.
*
* @param groupNames The group names
* @throws NullPointerException If the given collection contains
* null elements
*/
void setActiveGroupNames(Collection extends String> groupNames);
/**
* Set the material group with the given names to be active right now
* Faces that are added subsequently will be added to the active
* material group, creating this material group if necessary. If
* the given name is null, then this call will have no
* effect.
*
* @param materialGroupName The material group name
*/
void setActiveMaterialGroupName(String materialGroupName);
/**
* Add the given face.
* The indices in the given face are absolute (non-negative) and
* 0-based.
* The implementation is free to store a reference to the given
* face.
*
* @param face The face to add.
* @throws NullPointerException If the face is null
*/
void addFace(ObjFace face);
/**
* Add the specified face with the given vertex indices, but without
* texture- or normal indices.
* The given indices are absolute (non-negative) and 0-based.
* The implementation is free to store a reference to the given
* array. So the array should not be modified after this method has
* been called.
*
* @param v The vertex indices
* @throws IllegalArgumentException If one of the given indices is
* negative or not smaller than the number of vertices that have been
* added until now.
*/
void addFace(int ... v);
/**
* Add the specified face with the given vertex and texture coordinate
* indices, but without normal indices.
* The given indices are absolute (non-negative) and 0-based.
* The implementation is free to store a reference to the given
* array. So the array should not be modified after this method has
* been called.
*
* @param v The vertex- and texture coordinate indices
* @throws IllegalArgumentException If one of the given indices is
* negative or not smaller than the number of vertices or texture
* coordinates that have been added until now.
*/
void addFaceWithTexCoords(int ... v);
/**
* Add the specified face with the given vertex and normal indices,
* but without texture coordinate indices.
* The given indices are absolute (non-negative) and 0-based.
* The implementation is free to store a reference to the given
* array. So the array should not be modified after this method has
* been called.
*
* @param v The vertex- and normal indices
* @throws IllegalArgumentException If one of the given indices is
* negative or not smaller than the number of vertices or normals
* that have been added until now.
*/
void addFaceWithNormals(int ... v);
/**
* Add the specified face with the given vertex, texture coordinate
* and normal indices.
* The given indices are absolute (non-negative) and 0-based.
* The implementation is free to store a reference to the given
* array. So the array should not be modified after this method has
* been called.
*
* @param v The vertex- texture coordinate and normal indices
* @throws IllegalArgumentException If one of the given indices is
* negative or not smaller than the number of vertices, texture
* coordinates or normals that have been added until now.
*/
void addFaceWithAll(int ... v);
/**
* Add the specified face.
* The given indices are absolute (non-negative) and 0-based.
* The implementation is free to store a reference to the given
* arrays. So the arrays should not be modified after this method has
* been called.
*
* @param v The vertex indices
* @param vt The texture coordinate indices. May be null.
* @param vn The normal indices. May be null
* @throws NullPointerException If the vertex indices array is
* null
* @throws IllegalArgumentException If one of the given indices is
* negative or not smaller than the number of corresponding vertices,
* texture coordinates or normals that have been added until now,
* respectively.
* @throws IllegalArgumentException If the given (non-null) arrays
* have different lengths
*/
void addFace(int[] v, int[] vt, int[] vn);
/**
* Set the given MTL file names. A copy of the given
* collection will be stored.
*
* @param mtlFileNames The names of the MTL file
*/
void setMtlFileNames(Collection extends String> mtlFileNames);
}
================================================
FILE: src/main/java/de/javagl/obj/package-info.java
================================================
/**
* Classes for reading and writing Wavefront OBJ and MTL files.
*
* {@link de.javagl.obj.Obj} objects may be created with
* {@link de.javagl.obj.Objs#create()} or by reading an OBJ file with
* {@link de.javagl.obj.ObjReader#read(java.io.InputStream)}.
* {@link de.javagl.obj.Obj} objects may be written as OBJ files with
* {@link de.javagl.obj.ObjWriter#write(ReadableObj, java.io.OutputStream)}.
*
*/
package de.javagl.obj;
================================================
FILE: src/test/java/de/javagl/obj/TestMtlReader.java
================================================
package de.javagl.obj;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import org.junit.Test;
@SuppressWarnings("javadoc")
public class TestMtlReader
{
/**
* An epsilon for floating-point comparisons
*/
private static final float FLOAT_ERROR = 1e-6f;
@Test
public void readMtl()
throws IOException
{
List mtls = MtlReader.read(getClass().getResourceAsStream(
"/twoMaterialsA.mtl"));
assertEquals(2, mtls.size());
assertEquals(new DefaultFloatTuple(1,1,1), mtls.get(1).getKa());
assertEquals(new DefaultFloatTuple(1,1,1), mtls.get(1).getKd());
assertEquals(new DefaultFloatTuple(1,1,1), mtls.get(1).getKs());
assertEquals(0, mtls.get(1).getNs(), FLOAT_ERROR);
assertEquals(0.5f, mtls.get(1).getD(), FLOAT_ERROR);
assertEquals("texture.png", mtls.get(1).getMapKd());
}
@Test
public void readMtlWithWhitespace()
throws IOException
{
List mtls = MtlReader.read(getClass().getResourceAsStream(
"/mtlWithWhitespace.mtl"));
assertEquals(1, mtls.size());
Mtl mtl = mtls.get(0);
assertEquals("material0", mtl.getName());
assertEquals(new DefaultFloatTuple(1,0,0), mtl.getKa());
assertEquals(new DefaultFloatTuple(1,1,0), mtl.getKd());
assertEquals(new DefaultFloatTuple(1,1,1), mtl.getKs());
assertEquals(500, mtl.getNs(), FLOAT_ERROR);
assertEquals(1.0f, mtl.getD(), FLOAT_ERROR);
assertEquals("texture.png", mtl.getMapKd());
}
@Test
public void readMtlWithBrokenLines()
throws IOException
{
List mtls = MtlReader.read(getClass().getResourceAsStream(
"/mtlWithBrokenLines.mtl"));
assertEquals(1, mtls.size());
Mtl mtl = mtls.get(0);
assertEquals("material0", mtl.getName());
assertEquals(new DefaultFloatTuple(1,0,0), mtl.getKa());
assertEquals(new DefaultFloatTuple(1,1,0), mtl.getKd());
assertEquals(new DefaultFloatTuple(1,1,1), mtl.getKs());
assertEquals(500, mtl.getNs(), FLOAT_ERROR);
assertEquals(123.0f, mtl.getD(), FLOAT_ERROR);
assertEquals("texture.png", mtl.getMapKd());
}
@Test
public void readTextureOptionsWithAllOptions()
throws IOException
{
String[] tokens = new String[]
{
"-blendu", "off",
"-blendv", "off",
"-boost", "0.4",
"-cc", "on",
"-mm", "0.2", "0.33",
"-o", "0.01", "0.02", "0.03",
"-s", "0.04", "0.05", "0.06",
"-t", "0.07", "0.08", "0.09",
"-texres", ".44",
"-clamp", "on",
"-bm", "3.45",
"-imfchan", "g",
"-type", "sphere",
"texture.png"
};
TextureOptions options = MtlReader.readTextureOptions(
new LinkedList<>(Arrays.asList(tokens)));
assertEquals(Boolean.FALSE, options.isBlendu());
assertEquals(Boolean.FALSE, options.isBlendv());
assertEquals(0.4f, options.getBoost(), FLOAT_ERROR);
assertEquals(Boolean.TRUE, options.isCc());
assertEquals(0.2f, options.getMm().get(0), FLOAT_ERROR);
assertEquals(0.33f, options.getMm().get(1), FLOAT_ERROR);
assertEquals(0.01f, options.getO().getX(), FLOAT_ERROR);
assertEquals(0.02f, options.getO().getY(), FLOAT_ERROR);
assertEquals(0.03f, options.getO().getZ(), FLOAT_ERROR);
assertEquals(0.04f, options.getS().getX(), FLOAT_ERROR);
assertEquals(0.05f, options.getS().getY(), FLOAT_ERROR);
assertEquals(0.06f, options.getS().getZ(), FLOAT_ERROR);
assertEquals(0.07f, options.getT().getX(), FLOAT_ERROR);
assertEquals(0.08f, options.getT().getY(), FLOAT_ERROR);
assertEquals(0.09f, options.getT().getZ(), FLOAT_ERROR);
assertEquals(0.44f, options.getTexres(), FLOAT_ERROR);
assertEquals(Boolean.TRUE, options.isClamp());
assertEquals(3.45f, options.getBm(), FLOAT_ERROR);
assertEquals("g", options.getImfchan());
assertEquals("sphere", options.getType());
}
@Test
public void readTextureOptionsWithSingleOriginOffsetValue()
throws IOException
{
String[] tokens = new String[]
{
"-o", "0.1", "texture.png"
};
TextureOptions options = MtlReader.readTextureOptions(
new LinkedList<>(Arrays.asList(tokens)));
assertEquals(0.1f, options.getO().getX(), FLOAT_ERROR);
assertEquals(0.0f, options.getO().getY(), FLOAT_ERROR);
assertEquals(0.0f, options.getO().getZ(), FLOAT_ERROR);
}
@Test
public void readTextureOptionsWithDoubleOriginOffsetValue()
throws Exception
{
String[] tokens = new String[]
{
"-o", "0.1", "0.2", "texture.png"
};
TextureOptions options = MtlReader.readTextureOptions(
new LinkedList<>(Arrays.asList(tokens)));
assertEquals(0.1f, options.getO().getX(), FLOAT_ERROR);
assertEquals(0.2f, options.getO().getY(), FLOAT_ERROR);
assertEquals(0.0f, options.getO().getZ(), FLOAT_ERROR);
}
@Test
public void readMtlWithPbrProperties()
throws IOException {
List mtls = MtlReader.read(getClass().getResourceAsStream(
"/mtlWithPbrProperties.mtl"));
assertEquals(1, mtls.size());
Mtl mtl = mtls.get(0);
assertEquals("Material.001", mtl.getName());
assertEquals(0.5, mtl.getPr(), FLOAT_ERROR);
assertEquals(0.7, mtl.getPm(), FLOAT_ERROR);
assertEquals(0.1, mtl.getPs(), FLOAT_ERROR);
assertEquals(0.4, mtl.getPc(), FLOAT_ERROR);
assertEquals(0.03, mtl.getPcr(), FLOAT_ERROR);
assertEquals(0.001, mtl.getAniso(), FLOAT_ERROR);
assertEquals(0.01, mtl.getAnisor(), FLOAT_ERROR);
}
}
================================================
FILE: src/test/java/de/javagl/obj/TestMtlWriter.java
================================================
package de.javagl.obj;
import static org.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Scanner;
import org.junit.Test;
@SuppressWarnings("javadoc")
public class TestMtlWriter
{
@Test
public void writeMtl()
throws IOException
{
String inputString = readResourceAsString(
"/twoMaterialsA.mtl");
List mtls = MtlReader.read(
new ByteArrayInputStream(inputString.getBytes()));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
MtlWriter.write(mtls, baos);
String outputString = new String(baos.toByteArray());
//System.out.println(outputString);
assertEquals(inputString, outputString);
}
@Test
public void writeComplexMtl()
throws IOException
{
String inputString = readResourceAsString(
"/complexMaterial.mtl");
List mtls = MtlReader.read(
new ByteArrayInputStream(inputString.getBytes()));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
MtlWriter.write(mtls, baos);
String outputString = new String(baos.toByteArray());
//System.out.println(outputString);
assertEquals(inputString, outputString);
}
@Test
public void writePbrMtl()
throws IOException
{
String inputString = readResourceAsString(
"/pbrMaterial.mtl");
List mtls = MtlReader.read(
new ByteArrayInputStream(inputString.getBytes()));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
MtlWriter.write(mtls, baos);
String outputString = new String(baos.toByteArray());
System.out.println(outputString);
assertEquals(inputString, outputString);
}
private static String readResourceAsString(String name)
{
InputStream inputStream =
TestObjWriter.class.getResourceAsStream(name);
String string = readAsString(inputStream);
string = string.replaceAll("\r\n", "\n");
return string;
}
private static String readAsString(InputStream inputStream)
{
try (Scanner scanner = new Scanner(inputStream))
{
scanner.useDelimiter("\\A");
String string = scanner.next();
return string;
}
}
}
================================================
FILE: src/test/java/de/javagl/obj/TestObjData.java
================================================
package de.javagl.obj;
import static org.junit.Assert.*;
import java.io.IOException;
import org.junit.Test;
@SuppressWarnings("javadoc")
public class TestObjData
{
@Test
public void testGetVertices()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/squareTextured.obj"));
obj = ObjUtils.convertToRenderable(obj);
assertEquals(2, obj.getNumFaces());
assertEquals(4, obj.getNumVertices());
assertEquals(4, obj.getNumTexCoords());
assertEquals(0, obj.getNumNormals());
assertEquals(1, obj.getNumGroups()); // default
assertEquals(0, obj.getNumMaterialGroups());
float[] actualVertices = ObjData.getVerticesArray(obj);
float[] expectedVertices =
{
0.0f, 0.0f, 0.0f,
4.0f, 0.0f, 0.0f,
4.0f, 4.0f, 0.0f,
0.0f, 4.0f, 0.0f
};
assertArrayEquals(expectedVertices, actualVertices, 0.0f);
}
@Test
public void testGetTexCoords()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/squareTextured.obj"));
obj = ObjUtils.convertToRenderable(obj);
assertEquals(2, obj.getNumFaces());
assertEquals(4, obj.getNumVertices());
assertEquals(4, obj.getNumTexCoords());
assertEquals(0, obj.getNumNormals());
assertEquals(1, obj.getNumGroups()); // default
assertEquals(0, obj.getNumMaterialGroups());
float[] actualTexCoords = ObjData.getTexCoordsArray(obj, 2);
float[] expectedTexCoords =
{
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f
};
assertArrayEquals(expectedTexCoords, actualTexCoords, 0.0f);
}
@Test
public void testGetTexCoordsFlipped()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/squareTextured.obj"));
obj = ObjUtils.convertToRenderable(obj);
assertEquals(2, obj.getNumFaces());
assertEquals(4, obj.getNumVertices());
assertEquals(4, obj.getNumTexCoords());
assertEquals(0, obj.getNumNormals());
assertEquals(1, obj.getNumGroups()); // default
assertEquals(0, obj.getNumMaterialGroups());
float[] actualTexCoords = ObjData.getTexCoordsArray(obj, 2, true);
float[] expectedTexCoords =
{
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f,
0.0f, 0.0f
};
assertArrayEquals(expectedTexCoords, actualTexCoords, 0.0f);
}
}
================================================
FILE: src/test/java/de/javagl/obj/TestObjReader.java
================================================
package de.javagl.obj;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.util.List;
import org.junit.Test;
@SuppressWarnings("javadoc")
public class TestObjReader
{
@Test
public void readSquare()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/square.obj"));
assertEquals(1, obj.getNumFaces());
assertEquals(4, obj.getNumVertices());
assertEquals(0, obj.getNumTexCoords());
assertEquals(0, obj.getNumNormals());
assertEquals(1, obj.getNumGroups());
assertEquals(0, obj.getNumMaterialGroups());
}
@Test
public void readSquareAndTriangle()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/squareAndTriangle.obj"));
assertEquals(2, obj.getNumFaces());
assertEquals(5, obj.getNumVertices());
assertEquals(0, obj.getNumTexCoords());
assertEquals(0, obj.getNumNormals());
assertEquals(1, obj.getNumGroups());
assertEquals(0, obj.getNumMaterialGroups());
}
@Test
public void readSquareAndTriangleInTwoGroups()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/squareAndTriangleInTwoGroups.obj"));
assertEquals(2, obj.getNumFaces());
assertEquals(5, obj.getNumVertices());
assertEquals(0, obj.getNumTexCoords());
assertEquals(0, obj.getNumNormals());
assertEquals(3, obj.getNumGroups()); // 2 + default
assertEquals(0, obj.getNumMaterialGroups());
}
@Test
public void readFourTrianglesInMixedGroups()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/fourTrianglesInMixedGroups.obj"));
assertEquals(4, obj.getNumFaces());
assertEquals(6, obj.getNumVertices());
assertEquals(0, obj.getNumTexCoords());
assertEquals(0, obj.getNumNormals());
assertEquals(3, obj.getNumGroups()); // 2 + default
assertEquals(4, obj.getNumMaterialGroups());
assertEquals("twoMaterialsA.mtl", obj.getMtlFileNames().get(0));
}
@Test
public void readTwoTrianglesOneInDefaultGroup()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/twoTrianglesOneInDefaultGroup.obj"));
assertEquals(2, obj.getNumFaces());
assertEquals(4, obj.getNumVertices());
assertEquals(0, obj.getNumTexCoords());
assertEquals(0, obj.getNumNormals());
assertEquals(2, obj.getNumGroups()); // 1 + default
assertEquals(0, obj.getNumMaterialGroups());
}
@Test
public void readTwoTrianglesSharedInThreeGroups()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/twoTrianglesSharedInThreeGroups.obj"));
assertEquals(2, obj.getNumFaces());
assertEquals(4, obj.getNumVertices());
assertEquals(0, obj.getNumTexCoords());
assertEquals(0, obj.getNumNormals());
assertEquals(4, obj.getNumGroups()); // 3 + default
assertEquals(0, obj.getNumMaterialGroups());
}
@Test
public void readSquareAndTriangleWithRelativeIndices()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/squareAndTriangleWithRelativeIndices.obj"));
assertEquals(2, obj.getNumFaces());
assertEquals(5, obj.getNumVertices());
assertEquals(0, obj.getNumTexCoords());
assertEquals(0, obj.getNumNormals());
assertEquals(1, obj.getNumGroups()); // default
assertEquals(0, obj.getNumMaterialGroups());
}
@Test
public void readMtls()
throws IOException
{
List mtls = MtlReader.read(getClass().getResourceAsStream(
"/twoMaterialsA.mtl"));
assertEquals(2, mtls.size());
Mtl mtl0 = mtls.get(0);
assertEquals("material0", mtl0.getName());
assertEquals(new DefaultFloatTuple(1,0,0), mtl0.getKa());
assertEquals(new DefaultFloatTuple(1,0,0), mtl0.getKd());
assertEquals(new DefaultFloatTuple(1,1,1), mtl0.getKs());
assertEquals(500, mtl0.getNs(), 1e-6);
}
}
================================================
FILE: src/test/java/de/javagl/obj/TestObjSplitting.java
================================================
package de.javagl.obj;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import org.junit.Test;
@SuppressWarnings("javadoc")
public class TestObjSplitting
{
@Test
public void testSplitByGroup()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/squareAndTriangleInTwoGroups.obj"));
Map objs = ObjSplitting.splitByGroups(obj);
assertEquals(2, objs.size());
}
@Test
public void testSplitByGroupOnlyDefault()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/square.obj"));
Map objs = ObjSplitting.splitByGroups(obj);
assertEquals(1, objs.size());
}
@Test
public void testSplitByMaterialGroup()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/fourTrianglesInMixedGroups.obj"));
Map objs = ObjSplitting.splitByMaterialGroups(obj);
assertEquals(4, objs.size());
}
@Test
public void testSplitByMaterialGroupWithoutMaterial()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/square.obj"));
Map objs = ObjSplitting.splitByMaterialGroups(obj);
// There are no material groups in the "square.obj"
assertEquals(0, objs.size());
}
@Test
public void testSplitByMaterialGroupForPartialGroups()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/fourTrianglesPartiallyInMaterialGroups.obj"));
Map objs = ObjSplitting.splitByMaterialGroups(obj);
// Only two of the triangles are in material groups
assertEquals(2, objs.size());
}
@Test
public void testSplitByNumVertices()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/fourTrianglesInMixedGroups.obj"));
int maxNumVertices = 4;
List objs =
ObjSplitting.splitByMaxNumVertices(obj, maxNumVertices);
assertEquals(2, objs.size());
for (ReadableObj r : objs)
{
assertTrue(r.getNumVertices() <= maxNumVertices);
}
}
@Test
public void testSplitByNumVerticesForInvalidInput()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/square.obj"));
int maxNumVertices = 3;
List objs =
ObjSplitting.splitByMaxNumVertices(obj, maxNumVertices);
assertEquals(1, objs.size());
for (ReadableObj r : objs)
{
// This is larger than the maximum, but an OBJ containing
// a square cannot be split otherwise:
assertEquals(4, r.getNumVertices());
}
}
}
================================================
FILE: src/test/java/de/javagl/obj/TestObjUtilsAdd.java
================================================
package de.javagl.obj;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import org.junit.Test;
@SuppressWarnings("javadoc")
public class TestObjUtilsAdd
{
@Test
public void testAdd()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/twoTrianglesOneInDefaultGroup.obj"));
assertEquals(2, obj.getNumFaces());
assertEquals(4, obj.getNumVertices());
assertEquals(0, obj.getNumTexCoords());
assertEquals(0, obj.getNumNormals());
assertEquals(2, obj.getNumGroups()); // 1 + default
assertEquals(0, obj.getNumMaterialGroups());
Obj combinedObj = Objs.create();
ObjUtils.add(obj, combinedObj);
ObjUtils.add(obj, combinedObj);
assertEquals(4, combinedObj.getNumFaces());
assertEquals(8, combinedObj.getNumVertices());
assertEquals(0, combinedObj.getNumTexCoords());
assertEquals(0, combinedObj.getNumNormals());
assertEquals(2, combinedObj.getNumGroups()); // Still only two groups!
assertEquals(0, combinedObj.getNumMaterialGroups());
}
@Test
public void testAddDifferent()
throws IOException
{
Obj obj0 = ObjReader.read(getClass().getResourceAsStream(
"/twoTrianglesOneInDefaultGroup.obj"));
Obj obj1 = ObjReader.read(getClass().getResourceAsStream(
"/squareAndTriangleInTwoGroups.obj"));
assertEquals(2, obj0.getNumFaces());
assertEquals(4, obj0.getNumVertices());
assertEquals(0, obj0.getNumTexCoords());
assertEquals(0, obj0.getNumNormals());
assertEquals(2, obj0.getNumGroups()); // 1 + default
assertEquals(0, obj0.getNumMaterialGroups());
assertEquals(2, obj1.getNumFaces());
assertEquals(5, obj1.getNumVertices());
assertEquals(0, obj1.getNumTexCoords());
assertEquals(0, obj1.getNumNormals());
assertEquals(3, obj1.getNumGroups()); // 2 + default
assertEquals(0, obj1.getNumMaterialGroups());
Obj combinedObj = Objs.create();
ObjUtils.add(obj0, combinedObj);
ObjUtils.add(obj1, combinedObj);
assertEquals(4, combinedObj.getNumFaces());
assertEquals(9, combinedObj.getNumVertices());
assertEquals(0, combinedObj.getNumTexCoords());
assertEquals(0, combinedObj.getNumNormals());
// The default group, "group0" and "group1"
assertEquals(3, combinedObj.getNumGroups());
assertEquals(0, combinedObj.getNumMaterialGroups());
}
@Test
public void testAddWithMaterialGroups()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/fourTrianglesInMixedGroups.obj"));
assertEquals(4, obj.getNumFaces());
assertEquals(6, obj.getNumVertices());
assertEquals(0, obj.getNumTexCoords());
assertEquals(0, obj.getNumNormals());
assertEquals(3, obj.getNumGroups()); // 2 + (empty) default
assertEquals(4, obj.getNumMaterialGroups()); // 4 groups
Obj combinedObj = Objs.create();
ObjUtils.add(obj, combinedObj);
ObjUtils.add(obj, combinedObj);
assertEquals(8, combinedObj.getNumFaces());
assertEquals(12, combinedObj.getNumVertices());
assertEquals(0, combinedObj.getNumTexCoords());
assertEquals(0, combinedObj.getNumNormals());
assertEquals(3, combinedObj.getNumGroups());
assertEquals(4, combinedObj.getNumMaterialGroups()); // Still 4
}
}
================================================
FILE: src/test/java/de/javagl/obj/TestObjUtilsGroupToObj.java
================================================
package de.javagl.obj;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import org.junit.Test;
@SuppressWarnings("javadoc")
public class TestObjUtilsGroupToObj
{
@Test
public void testGroupToObj()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/squareAndTriangleInTwoGroups.obj"));
assertEquals(2, obj.getNumFaces());
assertEquals(5, obj.getNumVertices());
assertEquals(0, obj.getNumTexCoords());
assertEquals(0, obj.getNumNormals());
assertEquals(3, obj.getNumGroups()); // 2 + default
assertEquals(0, obj.getNumMaterialGroups());
Obj groupObj0 = ObjUtils.groupToObj(obj, obj.getGroup("group0"), null);
Obj groupObj1 = ObjUtils.groupToObj(obj, obj.getGroup("group1"), null);
assertEquals(1, groupObj0.getNumFaces());
assertEquals(4, groupObj0.getNumVertices());
assertEquals(0, groupObj0.getNumTexCoords());
assertEquals(0, groupObj0.getNumNormals());
assertEquals(2, groupObj0.getNumGroups());
assertEquals(0, groupObj0.getNumMaterialGroups());
assertEquals(1, groupObj1.getNumFaces());
assertEquals(3, groupObj1.getNumVertices());
assertEquals(0, groupObj1.getNumTexCoords());
assertEquals(0, groupObj1.getNumNormals());
assertEquals(2, groupObj1.getNumGroups());
assertEquals(0, groupObj1.getNumMaterialGroups());
}
}
================================================
FILE: src/test/java/de/javagl/obj/TestObjUtilsMakeTexCoordsUnique.java
================================================
package de.javagl.obj;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import org.junit.Test;
@SuppressWarnings("javadoc")
public class TestObjUtilsMakeTexCoordsUnique
{
@Test
public void testMakeTexCoordsUnique()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/twoTrianglesWithAmbiguousTexCoords.obj"));
Obj unique = ObjUtils.makeTexCoordsUnique(obj);
assertEquals(2, unique.getNumFaces());
assertEquals(5, unique.getNumVertices());
assertEquals(4, unique.getNumTexCoords());
assertEquals(0, unique.getNumNormals());
assertEquals(1, unique.getNumGroups());
assertEquals(0, unique.getNumMaterialGroups());
}
}
================================================
FILE: src/test/java/de/javagl/obj/TestObjUtilsTriangulate.java
================================================
package de.javagl.obj;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import org.junit.Test;
@SuppressWarnings("javadoc")
public class TestObjUtilsTriangulate
{
@Test
public void testTriangulateSquareAndTriangle()
throws IOException
{
Obj obj = ObjReader.read(getClass().getResourceAsStream(
"/squareAndTriangle.obj"));
assertEquals(2, obj.getNumFaces());
assertEquals(5, obj.getNumVertices());
assertEquals(0, obj.getNumTexCoords());
assertEquals(0, obj.getNumNormals());
assertEquals(1, obj.getNumGroups());
assertEquals(0, obj.getNumMaterialGroups());
Obj triangulatedObj = ObjUtils.triangulate(obj);
assertEquals(3, triangulatedObj.getNumFaces());
assertEquals(5, triangulatedObj.getNumVertices());
assertEquals(0, triangulatedObj.getNumTexCoords());
assertEquals(0, triangulatedObj.getNumNormals());
assertEquals(1, triangulatedObj.getNumGroups());
assertEquals(0, triangulatedObj.getNumMaterialGroups());
}
}
================================================
FILE: src/test/java/de/javagl/obj/TestObjWriter.java
================================================
package de.javagl.obj;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
import static org.junit.Assert.*;
import org.junit.Test;
@SuppressWarnings("javadoc")
public class TestObjWriter
{
@Test
public void readWriteSquare()
throws IOException
{
String inputString = readResourceAsString(
"/square.obj");
Obj obj = ObjReader.read(
new ByteArrayInputStream(inputString.getBytes()));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjWriter.write(obj, baos);
String outputString = new String(baos.toByteArray());
//System.out.println(outputString);
assertEquals(inputString, outputString);
}
@Test
public void readWriteSquareAndTriangle()
throws IOException
{
String inputString = readResourceAsString(
"/squareAndTriangle.obj");
Obj obj = ObjReader.read(
new ByteArrayInputStream(inputString.getBytes()));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjWriter.write(obj, baos);
String outputString = new String(baos.toByteArray());
//System.out.println(outputString);
assertEquals(inputString, outputString);
}
@Test
public void readWriteSquareAndTriangleInTwoGroups()
throws IOException
{
String inputString = readResourceAsString(
"/squareAndTriangleInTwoGroups.obj");
Obj obj = ObjReader.read(
new ByteArrayInputStream(inputString.getBytes()));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjWriter.write(obj, baos);
String outputString = new String(baos.toByteArray());
//System.out.println(outputString);
assertEquals(inputString, outputString);
}
@Test
public void readWriteFourTrianglesInMixedGroups()
throws IOException
{
String inputString = readResourceAsString(
"/fourTrianglesInMixedGroups.obj");
Obj obj = ObjReader.read(
new ByteArrayInputStream(inputString.getBytes()));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjWriter.write(obj, baos);
String outputString = new String(baos.toByteArray());
//System.out.println(outputString);
assertEquals(inputString, outputString);
}
@Test
public void readWriteTwoTrianglesOneInDefaultGroup()
throws IOException
{
String inputString = readResourceAsString(
"/twoTrianglesOneInDefaultGroup.obj");
Obj obj = ObjReader.read(
new ByteArrayInputStream(inputString.getBytes()));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjWriter.write(obj, baos);
String outputString = new String(baos.toByteArray());
//System.out.println(outputString);
assertEquals(inputString, outputString);
}
@Test
public void readTwoTrianglesSharedInThreeGroups()
throws IOException
{
String inputString = readResourceAsString(
"/twoTrianglesSharedInThreeGroups.obj");
Obj obj = ObjReader.read(
new ByteArrayInputStream(inputString.getBytes()));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjWriter.write(obj, baos);
String outputString = new String(baos.toByteArray());
//System.out.println(outputString);
assertEquals(inputString, outputString);
}
private static String readResourceAsString(String name)
{
InputStream inputStream =
TestObjWriter.class.getResourceAsStream(name);
String string = readAsString(inputStream);
string = string.replaceAll("\r\n", "\n");
return string;
}
private static String readAsString(InputStream inputStream)
{
try (Scanner scanner = new Scanner(inputStream))
{
scanner.useDelimiter("\\A");
String string = scanner.next();
return string;
}
}
}
================================================
FILE: src/test/java/de/javagl/obj/TestObjsCreate.java
================================================
package de.javagl.obj;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import org.junit.Test;
@SuppressWarnings("javadoc")
public class TestObjsCreate
{
@Test
public void createFromIndexedTriangleData()
throws IOException
{
float[] vertices =
{
0,0,0,
1,0,0,
1,1,0,
0,1,0,
};
float[] texCoords =
{
0,0,
1,0,
1,1,
0,1,
};
float[] normals =
{
0,0,1,
0,0,1,
0,0,1,
0,0,1,
};
int[] indices =
{
0,1,3,
0,1,2
};
Obj obj = Objs.createFromIndexedTriangleData(
IntBuffer.wrap(indices),
FloatBuffer.wrap(vertices),
FloatBuffer.wrap(texCoords),
FloatBuffer.wrap(normals));
assertEquals(2, obj.getNumFaces());
assertEquals(4, obj.getNumVertices());
assertEquals(4, obj.getNumTexCoords());
assertEquals(4, obj.getNumNormals());
assertEquals(1, obj.getNumGroups());
assertEquals(0, obj.getNumMaterialGroups());
}
}
================================================
FILE: src/test/resources/complexMaterial.mtl
================================================
newmtl material0
illum 3
Ns 500.0
Ni 2.3
d 1.0
Ka 1.0 1.0 1.0
Kd 1.0 0.0 1.0
Ks 1.0 1.0 1.0
Tf 0.0 1.0 0.0
sharpness 100.0
map_Ka -blendu off -blendv on -mm 2.3 4.5 -clamp on ambient.png
map_Kd -texres 512.0 diffuse.png
map_Ks -o 0.1 0.2 0.3 -s 1.1 1.2 1.3 -t 0.1 0.2 0.3 specularReflectivity.png
map_Ns -o 0.1 0.2 0.3 -s 1.1 1.2 1.3 specularExponent.png
map_d -mm 0.2 0.8 opacity.png
bump -imfchan -bm bump.png
disp displacement.png
decal decal.png
refl -type cube_top cubeTop.png
refl -type cube_bottom cubeBottom.png
refl -type cube_front cubeFront.png
refl -type cube_back cubeBack.png
refl -type cube_left cubeLeft.png
refl -type cube_right cubeRight.png
================================================
FILE: src/test/resources/fourTrianglesInMixedGroups.obj
================================================
mtllib twoMaterialsA.mtl
v 0.0 0.0 0.0
v 4.0 0.0 0.0
v 4.0 4.0 0.0
v 0.0 4.0 0.0
v 8.0 0.0 0.0
v 8.0 4.0 0.0
g group0
usemtl material0
f 1 2 3
usemtl material1
f 1 3 4
g group1
usemtl material2
f 2 5 6
usemtl material3
f 2 6 3
================================================
FILE: src/test/resources/fourTrianglesPartiallyInMaterialGroups.obj
================================================
mtllib twoMaterialsA.mtl
v 0.0 0.0 0.0
v 4.0 0.0 0.0
v 4.0 4.0 0.0
v 0.0 4.0 0.0
v 8.0 0.0 0.0
v 8.0 4.0 0.0
f 1 2 3
f 1 3 4
g group1
usemtl material0
f 2 5 6
usemtl material1
f 2 6 3
================================================
FILE: src/test/resources/mtlWithBrokenLines.mtl
================================================
# Some lines are broken with a backslash
newmtl material0
Ka 1.0 \
0.0 \
0.0
Kd 1.0 1.0 0.0
Ks 1.0 1.0 1.0
Ns 500.0
map_Kd texture.png
d \
123.0
================================================
FILE: src/test/resources/mtlWithPbrProperties.mtl
================================================
# www.blender.org
newmtl Material.001
Kd 0.800000 0.304983 0.243147
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
Pr 0.500000
Pm 0.700000
Ps 0.100000
Pc 0.400000
Pcr 0.030000
aniso 0.001000
anisor 0.010000
================================================
FILE: src/test/resources/mtlWithWhitespace.mtl
================================================
# Some lines have leading and trailing whitespace,
# in form of space characters or tabs
newmtl material0
Ka 1.0 0.0 0.0
Kd 1.0 1.0 0.0
Ks 1.0 1.0 1.0
Ns 500.0
map_Kd texture.png
d 1.0
================================================
FILE: src/test/resources/pbrMaterial.mtl
================================================
newmtl material1
Pr 0.12
map_Pr roughness.png
Pm 0.23
map_Pm metallic.png
Ps 0.34
map_Ps sheen.png
Pc 0.11
Pcr 0.222
Ke 0.2 0.3 0.4
map_Ke emissive.png
aniso 0.2
anisor 0.3
norm normals.png
================================================
FILE: src/test/resources/square.obj
================================================
v 0.0 0.0 0.0
v 4.0 0.0 0.0
v 4.0 4.0 0.0
v 0.0 4.0 0.0
f 1 2 3 4
================================================
FILE: src/test/resources/squareAndTriangle.obj
================================================
v 0.0 0.0 0.0
v 4.0 0.0 0.0
v 4.0 4.0 0.0
v 0.0 4.0 0.0
v 2.0 6.0 0.0
f 1 2 3 4
f 3 4 5
================================================
FILE: src/test/resources/squareAndTriangleInTwoGroups.obj
================================================
v 0.0 0.0 0.0
v 4.0 0.0 0.0
v 4.0 4.0 0.0
v 0.0 4.0 0.0
v 2.0 6.0 0.0
g group0
f 1 2 3 4
g group1
f 3 4 5
================================================
FILE: src/test/resources/squareAndTriangleWithRelativeIndices.obj
================================================
v 0.0 0.0 0.0
v 4.0 0.0 0.0
v 4.0 4.0 0.0
v 0.0 4.0 0.0
v 2.0 6.0 0.0
f 1 -4 -3 4
f 3 -2 5
================================================
FILE: src/test/resources/squareTextured.obj
================================================
v 0.0 0.0 0.0
v 4.0 0.0 0.0
v 4.0 4.0 0.0
v 0.0 4.0 0.0
vt 0.0 0.0
vt 1.0 0.0
vt 1.0 1.0
vt 0.0 1.0
f 1/1 2/2 3/3 4/4
================================================
FILE: src/test/resources/twoMaterialsA.mtl
================================================
newmtl material0
Ns 500.0
d 1.0
Ka 1.0 0.0 0.0
Kd 1.0 0.0 0.0
Ks 1.0 1.0 1.0
newmtl material1
Ns 0.0
d 0.5
Ka 1.0 1.0 1.0
Kd 1.0 1.0 1.0
Ks 1.0 1.0 1.0
map_Kd texture.png
================================================
FILE: src/test/resources/twoMaterialsB.mtl
================================================
newmtl material2
Ka 1 0 1
Kd 1 0 1
Ks 1 1 1
Ns 200
newmtl material3
Ka 0 1 1
Kd 0 1 1
Ks 0 1 1
Ns 100
d 0.25
================================================
FILE: src/test/resources/twoTrianglesOneInDefaultGroup.obj
================================================
v 0.0 0.0 0.0
v 4.0 0.0 0.0
v 4.0 4.0 0.0
v 0.0 4.0 0.0
f 1 2 4
g group0
f 2 3 4
================================================
FILE: src/test/resources/twoTrianglesSharedInThreeGroups.obj
================================================
v 0.0 0.0 0.0
v 4.0 0.0 0.0
v 4.0 4.0 0.0
v 0.0 4.0 0.0
g group0 group1
f 1 2 4
g group1 group2
f 2 3 4
================================================
FILE: src/test/resources/twoTrianglesWithAmbiguousTexCoords.obj
================================================
v 0 0 0
v 4 0 0
v 4 4 0
v 0 4 0
vt 0 0
vt 1 0
vt 1 1
vt 0 1
f 1/1 2/2 4/4
f 2/1 3/3 4/4