/**
* Copyright 2012 Jason Sorensen (sorensenj@smert.net)
*
* 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 net.smert.frameworkgl.opengl.mesh;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.smert.frameworkgl.math.AABB;
import net.smert.frameworkgl.opengl.GL;
import net.smert.frameworkgl.opengl.TextureType;
import net.smert.frameworkgl.opengl.renderable.Renderable;
import net.smert.frameworkgl.opengl.renderable.RenderableConfiguration;
import net.smert.frameworkgl.utils.Color;
import net.smert.frameworkgl.utils.HashMapIntGeneric;
/**
*
* @author Jason Sorensen <sorensenj@smert.net>
*/
public class Mesh {
private boolean hasColors;
private boolean hasIndexes;
private boolean hasNormals;
private boolean hasTexCoords;
private boolean hasVertices;
private int renderableConfigID;
private int totalVertices;
private int[] indexes;
private final AABB aabb;
private final List<Segment> segments;
private MeshMaterial material;
public Mesh() {
aabb = new AABB();
segments = new ArrayList<>();
material = GL.meshFactory.createMeshMaterial();
reset();
}
public void addSegment(Segment segment) {
// Check arguments
if (segments.contains(segment)) {
throw new IllegalArgumentException("The segment already exists");
}
float[] colors = segment.getData(SegmentDataType.COLOR);
float[] normals = segment.getData(SegmentDataType.NORMAL);
float[] texCoords = segment.getData(SegmentDataType.TEX_COORD0);
float[] vertices = segment.getData(SegmentDataType.VERTEX);
int elementCount = segment.getElementCount();
RenderableConfiguration config = Renderable.configPool.get(renderableConfigID);
if ((elementCount == 0) && !segment.hasDrawCommands()) {
throw new IllegalArgumentException("The segment must contain at least 1 vertex or have draw commands");
}
if ((colors != null) && (colors.length != (elementCount * config.getColorSize()))) {
throw new IllegalArgumentException(
"There needs to be one color for every element in the segment. Colors: "
+ colors.length + " Expected: " + (elementCount * config.getColorSize()));
}
if ((normals != null) && (normals.length != (elementCount * config.getNormalSize()))) {
throw new IllegalArgumentException(
"There needs to be one normal for every element in the segment. Normals: "
+ normals.length + " Expected: " + (elementCount * config.getNormalSize()));
}
if ((texCoords != null) && (texCoords.length != (elementCount * config.getTexCoordSize()))) {
throw new IllegalArgumentException(
"There needs to be one texture coord for every element in the segment. Texture Coords: "
+ texCoords.length + " Expected: " + (elementCount * config.getTexCoordSize()));
}
if ((vertices != null) && (vertices.length != (elementCount * config.getVertexSize()))) {
throw new IllegalArgumentException(
"There needs to be one vertex for every element in the segment. Vertices: "
+ vertices.length + " Expected: " + (elementCount * config.getVertexSize()));
}
// Add segment and update totals
segments.add(segment);
totalVertices += segment.getElementCount();
// Update booleans
updateHasBooleansFromSegment();
}
public Mesh cloneWithSharedSegmentData() {
Mesh newMesh = GL.meshFactory.createMesh();
Mesh oldMesh = this;
// Copy mesh data
newMesh.setRenderableConfigID(oldMesh.getRenderableConfigID());
newMesh.setIndexes(oldMesh.getIndexes());
newMesh.getAabb().set(oldMesh.getAabb());
newMesh.getMaterial().setShaderName(oldMesh.getMaterial().getShaderName());
for (int i = 0; i < oldMesh.getTotalSegments(); i++) {
Segment newSegment = GL.meshFactory.createSegment();
Segment oldSegment = oldMesh.getSegment(i);
// Copy segment data
newSegment.setElementCount(oldSegment.getElementCount());
newSegment.setMaxIndex(oldSegment.getMaxIndex());
newSegment.setMinIndex(oldSegment.getMinIndex());
newSegment.setPrimitiveMode(oldSegment.getPrimitiveMode());
if (oldSegment.hasDrawCommands()) {
newSegment.setDrawCommands(oldSegment.getDrawCommands());
}
newSegment.setName(oldSegment.getName());
// Copy segment data
if (oldSegment.getData().size() > 0) {
Iterator<HashMapIntGeneric.Entry<float[]>> iterator = oldSegment.getData().entrySet().iterator();
while (iterator.hasNext()) {
HashMapIntGeneric.Entry<float[]> entry = iterator.next();
int segmentDataType = entry.getKey();
float[] data = entry.getValue();
newSegment.setData(segmentDataType, data);
}
}
// Copy segment material
if (oldSegment.getMaterial() != null) {
SegmentMaterial newMaterial = GL.meshFactory.createSegmentMaterial();
SegmentMaterial oldMaterial = oldSegment.getMaterial();
newMaterial.setColor(oldMaterial.getColor());
newMaterial.setMaterialLightName(oldMaterial.getMaterialLightName());
// Copy segment material textures
if (oldMaterial.getTextures().size() > 0) {
Iterator<Map.Entry<TextureType, String>> iterator = oldMaterial.getTextures().entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<TextureType, String> entry = iterator.next();
TextureType textureType = entry.getKey();
String filename = entry.getValue();
newMaterial.setTexture(textureType, filename);
}
}
// Add segment material to segment
newSegment.setMaterial(newMaterial);
}
// Add segment to mesh
newMesh.addSegment(newSegment);
}
return newMesh;
}
public int getRenderableConfigID() {
return renderableConfigID;
}
public void setRenderableConfigID(int renderableConfigID) {
this.renderableConfigID = renderableConfigID;
}
public int getTotalSegments() {
return segments.size();
}
public int getTotalVerticies() {
return totalVertices;
}
public int[] getIndexes() {
return indexes;
}
public void setIndexes(int[] indexes) {
hasIndexes = (indexes != null);
this.indexes = indexes;
}
public AABB getAabb() {
return aabb;
}
public MeshMaterial getMaterial() {
return material;
}
public void setMaterial(MeshMaterial material) {
this.material = material;
}
/**
* Get the unique list of textures found in all segments.
*
* @return
*/
public List<String> getTextures() {
List<String> newTextures = new ArrayList<>();
for (Segment segment : segments) {
SegmentMaterial segmentMaterial = segment.getMaterial();
if (segmentMaterial == null) {
continue;
}
Map<TextureType, String> textureTypeToFilename = segmentMaterial.getTextures();
Iterator<String> iterator = textureTypeToFilename.values().iterator();
while (iterator.hasNext()) {
String texture = iterator.next();
if (newTextures.contains(texture)) {
continue;
}
newTextures.add(texture);
}
}
return newTextures;
}
public Segment getSegment(int index) {
return segments.get(index);
}
public Segment getSegmentByName(String name) {
for (Segment segment : segments) {
if (segment.getName().equals(name)) {
return segment;
}
}
return null;
}
public boolean hasColors() {
return hasColors;
}
public boolean hasIndexes() {
return hasIndexes;
}
public boolean hasNormals() {
return hasNormals;
}
public boolean hasTexCoords() {
return hasTexCoords;
}
public boolean hasVertices() {
return hasVertices;
}
public void removeSegment(Segment segment) {
// Check arguments
if (!segments.remove(segment)) {
throw new IllegalArgumentException("The segment was not found");
}
// Update totals
totalVertices -= segment.getElementCount();
}
public final void reset() {
hasColors = false;
hasIndexes = false;
hasNormals = false;
hasTexCoords = false;
hasVertices = false;
renderableConfigID = -1;
totalVertices = 0;
indexes = null;
segments.clear();
}
public void setAllColors(float r, float g, float b, float a) {
for (int i = 0; i < segments.size(); i++) {
setAllColors(i, r, g, b, a);
}
}
public void setAllColors(int segmentIndex, float r, float g, float b, float a) {
// Check arguments
if ((segmentIndex < 0) || (segmentIndex >= segments.size())) {
throw new IllegalArgumentException("Invalid segment index: " + segmentIndex);
}
// Get configuration from the pool
RenderableConfiguration config = Renderable.configPool.get(renderableConfigID);
int colorSize = config.getColorSize();
// Change colors for the segment
Segment segment = segments.get(segmentIndex);
int elementCount = segment.getElementCount();
int totalColorSize = elementCount * colorSize;
float[] colors = new float[totalColorSize];
for (int i = 0; i < elementCount; i++) {
int offset = i * colorSize;
colors[offset + 0] = r;
colors[offset + 1] = g;
colors[offset + 2] = b;
if (colorSize == 4) {
colors[offset + 3] = a;
}
}
// Set color data
segment.setData(SegmentDataType.COLOR, colors);
}
public void setAllMaterialColors(float r, float g, float b, float a) {
for (int i = 0; i < segments.size(); i++) {
setAllMaterialColors(i, r, g, b, a);
}
}
public void setAllMaterialColors(int segmentIndex, float r, float g, float b, float a) {
// Check arguments
if ((segmentIndex < 0) || (segmentIndex >= segments.size())) {
throw new IllegalArgumentException("Invalid segment index: " + segmentIndex);
}
// Change colors for the segment material
Segment segment = segments.get(segmentIndex);
SegmentMaterial segmentMaterial = segment.getMaterial();
if (segmentMaterial != null) {
if (segmentMaterial.getColor() == null) {
segmentMaterial.setColor(new Color(r, g, b, a));
} else {
segmentMaterial.getColor().set(r, g, b, a);
}
}
}
public void updateHasBooleansFromSegment() {
hasColors = false;
hasIndexes = false;
hasNormals = false;
hasTexCoords = false;
hasVertices = false;
// Update booleans
for (Segment segment : segments) {
float[] colors = segment.getData(SegmentDataType.COLOR);
float[] normals = segment.getData(SegmentDataType.NORMAL);
float[] texCoords = segment.getData(SegmentDataType.TEX_COORD0);
float[] vertices = segment.getData(SegmentDataType.VERTEX);
hasColors |= (colors != null);
hasNormals |= (normals != null);
hasTexCoords |= (texCoords != null);
hasVertices |= (vertices != null);
hasIndexes |= ((getIndexes() != null) && (getIndexes().length > 0));
}
}
}