com.irurueta.geometry.io.MeshWriterBinary Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of irurueta-geometry-io Show documentation
Show all versions of irurueta-geometry-io Show documentation
Reads, writes and converts 3D files into different formats
The newest version!
/*
* Copyright (C) 2012 Alberto Irurueta Carro ([email protected])
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.irurueta.geometry.io;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
/**
* Reads a 3D object and converts it into custom binary format.
*/
public class MeshWriterBinary extends MeshWriter {
/**
* Version of the binary file supported by this class.
*/
public static final byte VERSION = 2;
/**
* Buffer size to write data into output stream.
*/
public static final int BUFFER_SIZE = 1024;
/**
* Stream to write binary data to output.
*/
private DataOutputStream dataStream;
/**
* Constructor.
*
* @param loader loader to load a 3D file.
* @param stream stream where trans-coded data will be written to.
*/
public MeshWriterBinary(final Loader loader, final OutputStream stream) {
super(loader, stream);
}
/**
* Constructor.
*
* @param loader loader to load a 3D file.
* @param stream stream where trans-coded data will be written to.
* @param listener listener to be notified of progress changes or when
* transcoding process starts or finishes.
*/
public MeshWriterBinary(final Loader loader, final OutputStream stream, final MeshWriterListener listener) {
super(loader, stream, listener);
}
/**
* Processes input file provided to loader and writes it trans-coded into
* output stream.
*
* @throws LoaderException if 3D file loading fails.
* @throws IOException if an I/O error occurs.
* @throws NotReadyException if mesh writer is not ready because either a
* loader has not been provided or an output stream has not been provided.
* @throws LockedException if this mesh writer is locked processing a file.
*/
@SuppressWarnings("DuplicatedCode")
@Override
public void write() throws LoaderException, IOException, NotReadyException, LockedException {
if (!isReady()) {
throw new NotReadyException();
}
if (isLocked()) {
throw new LockedException();
}
try {
dataStream = new DataOutputStream(new BufferedOutputStream(stream));
locked = true;
if (listener != null) {
listener.onWriteStart(this);
}
loader.setListener(this.internalListeners);
// write version
dataStream.writeByte(VERSION);
final var iter = loader.load();
var minX = Float.MAX_VALUE;
var minY = Float.MAX_VALUE;
var minZ = Float.MAX_VALUE;
var maxX = -Float.MAX_VALUE;
var maxY = -Float.MAX_VALUE;
var maxZ = -Float.MAX_VALUE;
while (iter.hasNext()) {
final var chunk = iter.next();
if (listener != null) {
listener.onChunkAvailable(this, chunk);
}
final var coords = chunk.getVerticesCoordinatesData();
final var colors = chunk.getColorData();
final var indices = chunk.getIndicesData();
final var textureCoords = chunk.getTextureCoordinatesData();
final var normals = chunk.getNormalsData();
final var colorComponents = chunk.getColorComponents();
final var coordsAvailable = (coords != null);
final var colorsAvailable = (colors != null);
final var indicesAvailable = (indices != null);
final var textureCoordsAvailable = (textureCoords != null);
final var normalsAvailable = (normals != null);
if (chunk.getMinX() < minX) {
minX = chunk.getMinX();
}
if (chunk.getMinY() < minY) {
minY = chunk.getMinY();
}
if (chunk.getMinZ() < minZ) {
minZ = chunk.getMinZ();
}
if (chunk.getMaxX() > maxX) {
maxX = chunk.getMaxX();
}
if (chunk.getMaxY() > maxY) {
maxY = chunk.getMaxY();
}
if (chunk.getMaxZ() > maxZ) {
maxZ = chunk.getMaxZ();
}
// compute size of material in bytes
final var material = chunk.getMaterial();
// boolean indicating availability
// of material
var materialSizeInBytes = 1;
if (material != null) {
// material id (int)
materialSizeInBytes += Integer.SIZE / 8;
// ambient color (RGB) -> 3 bytes
//boolean indicating availability
materialSizeInBytes += 1;
if (material.isAmbientColorAvailable()) {
// RGB components
materialSizeInBytes += 3;
}
// diffuse color
// boolean indicating availability
materialSizeInBytes += 1;
if (material.isDiffuseColorAvailable()) {
// RGB components
materialSizeInBytes += 3;
}
// specular color
// boolean indicating availability
materialSizeInBytes += 1;
if (material.isSpecularColorAvailable()) {
// RGB components
materialSizeInBytes += 3;
}
// specular coefficient (float)
// boolean indicating availability
materialSizeInBytes += 1;
if (material.isSpecularCoefficientAvailable()) {
materialSizeInBytes += Float.SIZE / 8;
}
// ambient texture map
// boolean indicating availability
materialSizeInBytes += 1;
if (material.isAmbientTextureMapAvailable()) {
// id of texture (int)
materialSizeInBytes += Integer.SIZE / 8;
// texture width (int)
materialSizeInBytes += Integer.SIZE / 8;
// texture height (int)
materialSizeInBytes += Integer.SIZE / 8;
}
// diffuse texture map
// boolean indicating availability
materialSizeInBytes += 1;
if (material.isDiffuseTextureMapAvailable()) {
// id of texture (int)
materialSizeInBytes += Integer.SIZE / 8;
// texture width (int)
materialSizeInBytes += Integer.SIZE / 8;
// texture height (int)
materialSizeInBytes += Integer.SIZE / 8;
}
// specular texture map
// boolean indicating availability
materialSizeInBytes += 1;
if (material.isSpecularTextureMapAvailable()) {
// id of texture (int)
materialSizeInBytes += Integer.SIZE / 8;
// texture width (int)
materialSizeInBytes += Integer.SIZE / 8;
// texture height (int)
materialSizeInBytes += Integer.SIZE / 8;
}
// alpha texture map
// boolean indicating availability
materialSizeInBytes += 1;
if (material.isAlphaTextureMapAvailable()) {
// id of texture (int)
materialSizeInBytes += Integer.SIZE / 8;
// texture width (int)
materialSizeInBytes += Integer.SIZE / 8;
// texture height (int)
materialSizeInBytes += Integer.SIZE / 8;
}
// bump texture map
// boolean indicating availability
materialSizeInBytes += 1;
if (material.isBumpTextureMapAvailable()) {
// id of texture (int)
materialSizeInBytes += Integer.SIZE / 8;
// texture width (int)
materialSizeInBytes += Integer.SIZE / 8;
// texture height (int)
materialSizeInBytes += Integer.SIZE / 8;
}
// transparency
// availability
materialSizeInBytes += 1;
if (material.isTransparencyAvailable()) {
// one byte 0-255 of
// transparency
materialSizeInBytes += 1;
}
// enum of illumination(int)
// boolean containing availability
materialSizeInBytes += 1;
if (material.isIlluminationAvailable()) {
materialSizeInBytes += Integer.SIZE / 8;
}
}
// compute size of chunk data in bytes
var coordsSizeInBytes = 0;
var colorsSizeInBytes = 0;
var indicesSizeInBytes = 0;
var textureCoordsSizeInBytes = 0;
var normalsSizeInBytes = 0;
if (coordsAvailable) {
coordsSizeInBytes = coords.length * Float.SIZE / 8;
}
if (colorsAvailable) {
colorsSizeInBytes = colors.length;
}
if (indicesAvailable) {
indicesSizeInBytes = indices.length * Short.SIZE / 8;
}
if (textureCoordsAvailable) {
textureCoordsSizeInBytes = textureCoords.length * Float.SIZE / 8;
}
if (normalsAvailable) {
normalsSizeInBytes = normals.length * Float.SIZE / 8;
}
int chunkSize = materialSizeInBytes + coordsSizeInBytes + colorsSizeInBytes + indicesSizeInBytes
+ textureCoordsSizeInBytes + normalsSizeInBytes
+ (5 * Integer.SIZE / 8) + // sizes
(6 * Float.SIZE / 8); // min/max values
if (colorsAvailable) {
// bytes for number of color components
chunkSize += Integer.SIZE / 8;
}
// indicate that no more textures follow
if (!ignoreTextureValidation) {
// byte below is written only once after all textures and
// before all vertex data
dataStream.writeBoolean(false);
// so that no more textures are
// written into stream
ignoreTextureValidation = true;
}
// write total chunk size
dataStream.writeInt(chunkSize);
// indicate material availability
dataStream.writeBoolean(material != null);
if (material != null) {
// material id
dataStream.writeInt(material.getId());
// ambient color
dataStream.writeBoolean(material.isAmbientColorAvailable());
if (material.isAmbientColorAvailable()) {
var b = (byte) (material.getAmbientRedColor() & 0x00ff);
dataStream.writeByte(b);
b = (byte) (material.getAmbientGreenColor() & 0x00ff);
dataStream.writeByte(b);
b = (byte) (material.getAmbientBlueColor() & 0x00ff);
dataStream.writeByte(b);
}
// diffuse color
dataStream.writeBoolean(material.isDiffuseColorAvailable());
if (material.isDiffuseColorAvailable()) {
var b = (byte) (material.getDiffuseRedColor() & 0x00ff);
dataStream.writeByte(b);
b = (byte) (material.getDiffuseGreenColor() & 0x00ff);
dataStream.writeByte(b);
b = (byte) (material.getDiffuseBlueColor() & 0x00ff);
dataStream.writeByte(b);
}
// specular color
dataStream.writeBoolean(material.isSpecularColorAvailable());
if (material.isSpecularColorAvailable()) {
var b = (byte) (material.getSpecularRedColor() & 0x00ff);
dataStream.writeByte(b);
b = (byte) (material.getSpecularGreenColor() & 0x00ff);
dataStream.writeByte(b);
b = (byte) (material.getSpecularBlueColor() & 0x00ff);
dataStream.writeByte(b);
}
// specular coefficient (float)
dataStream.writeBoolean(material.isSpecularCoefficientAvailable());
if (material.isSpecularCoefficientAvailable()) {
dataStream.writeFloat(material.getSpecularCoefficient());
}
// ambient texture map
dataStream.writeBoolean(material.isAmbientTextureMapAvailable());
if (material.isAmbientTextureMapAvailable()) {
final var tex = material.getAmbientTextureMap();
// texture id
dataStream.writeInt(tex.getId());
// texture width
dataStream.writeInt(tex.getWidth());
// texture height
dataStream.writeInt(tex.getHeight());
}
// diffuse texture map
dataStream.writeBoolean(material.isDiffuseTextureMapAvailable());
if (material.isDiffuseTextureMapAvailable()) {
final var tex = material.getDiffuseTextureMap();
// texture id
dataStream.writeInt(tex.getId());
// texture width
dataStream.writeInt(tex.getWidth());
// texture height
dataStream.writeInt(tex.getHeight());
}
// specular texture map
dataStream.writeBoolean(material.isSpecularTextureMapAvailable());
if (material.isSpecularTextureMapAvailable()) {
final var tex = material.getSpecularTextureMap();
// texture id
dataStream.writeInt(tex.getId());
// texture width
dataStream.writeInt(tex.getWidth());
// texture height
dataStream.writeInt(tex.getHeight());
}
// alpha texture map
dataStream.writeBoolean(material.isAlphaTextureMapAvailable());
if (material.isAlphaTextureMapAvailable()) {
final var tex = material.getAlphaTextureMap();
// texture id
dataStream.writeInt(tex.getId());
// texture width
dataStream.writeInt(tex.getWidth());
// texture height
dataStream.writeInt(tex.getHeight());
}
// bump texture map
dataStream.writeBoolean(material.isBumpTextureMapAvailable());
if (material.isBumpTextureMapAvailable()) {
final var tex = material.getBumpTextureMap();
// texture id
dataStream.writeInt(tex.getId());
// texture width
dataStream.writeInt(tex.getWidth());
// texture height
dataStream.writeInt(tex.getHeight());
}
dataStream.writeBoolean(material.isTransparencyAvailable());
if (material.isTransparencyAvailable()) {
final var b = (byte) (material.getTransparency() & 0x00ff);
dataStream.writeByte(b);
}
dataStream.writeBoolean(material.isIlluminationAvailable());
if (material.isIlluminationAvailable()) {
dataStream.writeInt(material.getIllumination().value());
}
}
// write coords size
dataStream.writeInt(coordsSizeInBytes);
// write coords
if (coordsAvailable) {
for (final var coord : coords) {
dataStream.writeFloat(coord);
}
}
// write colors size
dataStream.writeInt(colorsSizeInBytes);
// write colors
if (colorsAvailable) {
var i = 0;
while (i < colors.length) {
final var b = (byte) (colors[i] & 0x00ff);
dataStream.writeByte(b);
i++;
}
dataStream.writeInt(colorComponents);
}
// write indices size
dataStream.writeInt(indicesSizeInBytes);
// write indices
if (indicesAvailable) {
for (final var index : indices) {
final var s = (short) (index & 0x0000ffff);
dataStream.writeShort(s);
}
}
// write texture coords
dataStream.writeInt(textureCoordsSizeInBytes);
// write texture coords
if (textureCoordsAvailable) {
for (final var textureCoord : textureCoords) {
dataStream.writeFloat(textureCoord);
}
}
// write normals
dataStream.writeInt(normalsSizeInBytes);
// write normals
if (normalsAvailable) {
for (final var normal : normals) {
dataStream.writeFloat(normal);
}
}
// write min/max x, y, z
dataStream.writeFloat(chunk.getMinX());
dataStream.writeFloat(chunk.getMinY());
dataStream.writeFloat(chunk.getMinZ());
dataStream.writeFloat(chunk.getMaxX());
dataStream.writeFloat(chunk.getMaxY());
dataStream.writeFloat(chunk.getMaxZ());
dataStream.flush();
}
if (listener != null) {
listener.onWriteEnd(this);
}
locked = false;
} catch (final LoaderException | IOException e) {
throw e;
} catch (final Exception e) {
throw new LoaderException(e);
}
}
/**
* Processes texture file. By reading provided texture file that has been
* created in a temporal location and embedding it into resulting output
* stream.
*
* @param texture reference to texture that uses texture image.
* @param textureFile file containing texture image. File will usually be
* created in a temporal location.
* @throws IOException if an I/O error occurs.
*/
@Override
protected void processTextureFile(final Texture texture, final File textureFile) throws IOException {
if (!textureFile.exists()) {
return;
}
// write boolean to true indicating that a texture follows
dataStream.writeBoolean(true);
// write int containing texture id
final var textureId = texture.getId();
dataStream.writeInt(textureId);
// write int containing image width
final var width = texture.getWidth();
dataStream.writeInt(width);
// write int containing image height
final var height = texture.getHeight();
dataStream.writeInt(height);
// write length of texture file in bytes
final var length = textureFile.length();
dataStream.writeLong(length);
// write file data
try (final var textureStream = Files.newInputStream(textureFile.toPath())) {
final var buffer = new byte[BUFFER_SIZE];
int n;
while ((n = textureStream.read(buffer)) > 0) {
dataStream.write(buffer, 0, n);
}
dataStream.flush();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy