/*
* Copyright (C) 2014 MillerV
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package aspect.resources;
import aspect.render.Material;
import aspect.render.ViewModel;
import aspect.audio.Music;
import aspect.audio.Sound2D;
import aspect.audio.AudioClip;
import static aspect.core.AspectRenderer.GeometryType.*;
import aspect.render.LineModel;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.logging.Logger;
import java.io.IOException;
import java.util.HashMap;
import java.util.logging.Level;
import javax.imageio.ImageIO;
import javax.sound.sampled.UnsupportedAudioFileException;
import aspect.util.Vector2;
import aspect.util.Vector3;
import aspect.render.MultiModel;
import aspect.render.Texture;
import aspect.render.Mesh;
import aspect.render.shader.Shader;
import aspect.render.shader.ShaderProgram;
import aspect.resources.ArcFile.ArcEntry;
import aspect.resources.modeling.Vertex;
import aspect.util.Angles;
import aspect.util.Color;
import aspect.util.Matrix4x4;
import aspect.util.Trig;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import org.lwjgl.LWJGLException;
import org.lwjgl.util.WaveData;
/**
* A utility class for loading, storing, and retrieving assets such as audio,
* textures, and models.
*
* @author MillerV
*/
public class Resources {
public static final HashMap<String, Sound2D> AUDIO_2D = new HashMap<>();
public static final HashMap<String, AudioClip> AUDIO_3D = new HashMap<>();
public static final HashMap<String, Music> MUSIC = new HashMap<>();
public static final HashMap<String, Texture> TEXTURES = new HashMap();
public static final HashMap<String, Material> MATERIALS = new HashMap<>();
public static final HashMap<String, BufferedImage> IMAGES = new HashMap<>();
public static final HashMap<String, ViewModel> MODELS = new HashMap<>();
public static final HashMap<String, Shader> SHADERS = new HashMap<>();
public static ArcFile archive = null;
public static long audioMaxSize = (long) 1e6;
public static final Sound2D UNLOADED_AUDIO2D;
public static final Music UNLOADED_MUSIC;
public static final BufferedImage UNLOADED_IMAGE;
public static final Shader UNLOADED_SHADER;
public static final int VERTEX_DATA_QUAD = 4 * 3;
public static final int TEXCOORD_DATA_QUAD = 4 * 2;
public static final int VERTEX_DATA_BOX = VERTEX_DATA_QUAD * 6;
public static final int TEXCOORD_DATA_BOX = TEXCOORD_DATA_QUAD * 6;
public static final int VERTEX_DATA_TRIANGLE = 3 * 3;
public static final int TEXCOORD_DATA_TRIANGLE = 3 * 2;
static {
BufferedImage ui;
try {
ui = ImageIO.read(Resources.class.getResource("materials/unloaded.jpg"));
} catch (IOException ex) {
ui = null;
Logger.getLogger(Material.class.getName()).log(Level.SEVERE, "Could not load default texture: ", ex);
}
UNLOADED_AUDIO2D = new Sound2D();
UNLOADED_MUSIC = new Music();
UNLOADED_IMAGE = ui;
UNLOADED_SHADER = new Shader(0);
}
public static InputStream getStream(File file) throws IOException {
if (archive == null) {
return new FileInputStream(file);
} else {
return archive.getStream(file);
}
}
public static Sound2D loadSound2D(File source) {
try {
return loadSound2D(getStream(source));
} catch (IOException ex) {
Logger.getLogger(Resources.class.getName()).log(Level.SEVERE, null, ex);
return UNLOADED_AUDIO2D;
}
}
public static Sound2D loadSound2D(InputStream source) {
try {
Sound2D snd = new Sound2D(source);
return snd;
} catch (UnsupportedAudioFileException | IOException ex) {
Logger.getLogger(Resources.class.getName()).log(Level.SEVERE, "Sound could not be loaded: ", ex);
return UNLOADED_AUDIO2D;
}
}
public static Sound2D loadSound2D(String name, File source) {
if (AUDIO_2D.containsKey(name)) {
return AUDIO_2D.get(name);
}
Sound2D snd = loadSound2D(source);
addSound2D(name, snd);
return snd;
}
public static Sound2D loadSound2D(String name, InputStream source) {
if (AUDIO_2D.containsKey(name)) {
return AUDIO_2D.get(name);
}
Sound2D snd = loadSound2D(source);
addSound2D(name, snd);
return snd;
}
public static void addSound2D(String name, Sound2D snd) {
AUDIO_2D.put(name, snd);
}
public static Sound2D getSound2D(String name) {
return AUDIO_2D.get(name);
}
public static AudioClip loadSound3D(File source) {
try {
return loadSound3D(getStream(source));
} catch (IOException ex) {
Logger.getLogger(Resources.class.getName()).log(Level.SEVERE, null, ex);
return null;
}
}
public static AudioClip loadSound3D(InputStream source) {
try {
WaveData data = WaveData.create(new BufferedInputStream(source));
AudioClip clip = new AudioClip(data);
return clip;
} catch (LWJGLException ex) {
Logger.getLogger(Resources.class.getName()).log(Level.SEVERE, "Sound could not be loaded: ", ex);
return null;
}
}
public static AudioClip loadSound3D(String name, File source) {
if (AUDIO_3D.containsKey(name)) {
return AUDIO_3D.get(name);
}
AudioClip clip = loadSound3D(source);
addSound3D(name, clip);
return clip;
}
public static AudioClip loadSound3D(String name, InputStream source) {
if (AUDIO_3D.containsKey(name)) {
return AUDIO_3D.get(name);
}
AudioClip clip = loadSound3D(source);
addSound3D(name, clip);
return clip;
}
public static void addSound3D(String name, AudioClip clip) {
AUDIO_3D.put(name, clip);
}
public static AudioClip getSound3D(String name) {
return AUDIO_3D.get(name);
}
public static Music loadMusic(File file) {
try {
return loadMusic(getStream(file));
} catch (IOException ex) {
Logger.getLogger(Resources.class.getName()).log(Level.SEVERE, null, ex);
return new Music();
}
}
public static Music loadMusic(InputStream source) {
return new Music(source);
}
public static Music loadMusic(String name, File file) {
if (MUSIC.containsKey(name)) {
return MUSIC.get(name);
}
Music mus = loadMusic(file);
addMusic(name, mus);
return mus;
}
public static Music loadMusic(String name, InputStream source) {
if (MUSIC.containsKey(name)) {
return MUSIC.get(name);
}
Music mus = loadMusic(source);
addMusic(name, mus);
return mus;
}
public static void addMusic(String name, Music mus) {
MUSIC.put(name, mus);
}
public static Music getMusic(String name) {
return MUSIC.get(name);
}
public static Texture loadTexture(File file) {
try {
return loadTexture(getStream(file));
} catch (IOException ex) {
Logger.getLogger(Resources.class.getName()).log(Level.SEVERE, "Material could not be loaded: ", ex);
return Texture.create(UNLOADED_IMAGE);
}
}
public static Texture loadTexture(InputStream source) {
try {
Texture texture = Texture.create(source);
return texture;
} catch (IOException ex) {
Logger.getLogger(Resources.class.getName()).log(Level.SEVERE, "Material could not be loaded: ", ex);
return Texture.create(UNLOADED_IMAGE);
}
}
public static Texture loadTexture(String name, File file) {
if (TEXTURES.containsKey(name)) {
return TEXTURES.get(name);
}
Texture texture = loadTexture(file);
addTexture(name, texture);
return texture;
}
public static Texture loadTexture(String name, InputStream source) {
if (TEXTURES.containsKey(name)) {
return TEXTURES.get(name);
}
Texture texture = loadTexture(source);
addTexture(name, texture);
return texture;
}
public static void addTexture(String name, Texture texture) {
TEXTURES.put(name, texture);
}
public static Texture getTexture(String name) {
return TEXTURES.get(name);
}
public static Shader loadShader(File file, Shader.Type type) {
return new Shader(file, type);
}
public static Shader loadShader(InputStream stream, Shader.Type type) {
return new Shader(loadTextFile(stream), type);
}
public static Shader loadShader(String name, File file, Shader.Type type) {
if (SHADERS.containsKey(name)) {
return SHADERS.get(name);
}
Shader shader = loadShader(file, type);
addShader(name, shader);
return shader;
}
public static Shader loadShader(String name, InputStream stream, Shader.Type type) {
if (SHADERS.containsKey(name)) {
return SHADERS.get(name);
}
Shader shader = loadShader(stream, type);
addShader(name, shader);
return shader;
}
public static void addShader(String name, Shader shader) {
SHADERS.put(name, shader);
}
public static Shader getShader(String name) {
return SHADERS.get(name);
}
public static Material loadMaterial(File source) {
try {
return loadMaterial(getStream(source));
} catch (IOException ex) {
Logger.getLogger(Resources.class.getName()).log(Level.SEVERE, null, ex);
return new Material();
}
}
// When loading a Material from an InputStream, make sure to load the Shader
// and Texture first.
public static Material loadMaterial(InputStream source) {
String s = loadTextFile(source);
Material m = new Material();
for (String line : s.trim().split("\n")) {
line = line.trim();
String[] args = line.split(" ");
if (line.startsWith("#") || line.isEmpty()) {
continue;
}
switch (args[0]) {
case "texture:": {
m.texture = loadTexture(args[1], new File(args[1]));
break;
}
case "ambient:": {
m.ambient = new Color(Float.parseFloat(args[1]), Float.parseFloat(args[2]), Float.parseFloat(args[3]));
break;
}
case "diffuse:": {
m.diffuse = new Color(Float.parseFloat(args[1]), Float.parseFloat(args[2]), Float.parseFloat(args[3]));
break;
}
case "specular:": {
m.specular = new Color(Float.parseFloat(args[1]), Float.parseFloat(args[2]), Float.parseFloat(args[3]));
break;
}
case "emissive:": {
m.emissive = new Color(Float.parseFloat(args[1]), Float.parseFloat(args[2]), Float.parseFloat(args[3]));
}
case "shininess:": {
m.shininess = Float.parseFloat(args[1]);
break;
}
case "shader:": {
if (args[1].equals("prebuilt")) {
m.shader = ShaderProgram.loadPrebuilt(args[2]);
} else {
Shader vert = loadShader(args[1], new File(args[1]), Shader.Type.VERTEX);
Shader frag = loadShader(args[2], new File(args[2]), Shader.Type.FRAGMENT);
ShaderProgram shader = new ShaderProgram(vert, frag);
m.shader = shader;
}
break;
}
case "cull:": {
m.cull = Boolean.parseBoolean(args[1]);
break;
}
default: {
Logger.getLogger(Resources.class.getName()).log(Level.SEVERE, "Could not parse line of amp file: {0}", line);
}
}
}
return m;
}
public static Material loadMaterial(String name, File source) {
if (MATERIALS.containsKey(name)) {
return MATERIALS.get(name);
}
Material material = loadMaterial(source);
addMaterial(name, material);
return material;
}
public static Material loadMaterial(String name, InputStream source) {
if (MATERIALS.containsKey(name)) {
return MATERIALS.get(name);
}
Material material = loadMaterial(source);
addMaterial(name, material);
return material;
}
public static void addMaterial(String name, Material mtl) {
MATERIALS.put(name, mtl);
}
public static Material getMaterial(String name) {
return MATERIALS.get(name);
}
public static BufferedImage loadImage(File file) {
try {
return loadImage(getStream(file));
} catch (IOException ex) {
Logger.getLogger(Resources.class.getName()).log(Level.SEVERE, null, ex);
return UNLOADED_IMAGE;
}
}
public static BufferedImage loadImage(InputStream stream) {
try {
BufferedImage img = ImageIO.read(stream);
return img;
} catch (IOException ex) {
Logger.getLogger(Resources.class.getName()).log(Level.SEVERE, "Image could not be loaded: ", ex);
return UNLOADED_IMAGE;
}
}
public static BufferedImage loadImage(String name, File file) {
if (IMAGES.containsKey(name)) {
return IMAGES.get(name);
}
BufferedImage img = loadImage(file);
addImage(name, img);
return img;
}
public static BufferedImage loadImage(String name, InputStream stream) {
if (IMAGES.containsKey(name)) {
return IMAGES.get(name);
}
BufferedImage img = loadImage(stream);
addImage(name, img);
return img;
}
public static void addImage(String name, BufferedImage img) {
IMAGES.put(name, img);
}
public static BufferedImage getImage(String name) {
return IMAGES.get(name);
}
public static BufferedImage createImage(int width, int height) {
GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
return gc.createCompatibleImage(width, height, java.awt.Color.BITMASK);
}
public static String loadTextFile(File file) {
try {
return loadTextFile(getStream(file));
} catch (IOException ex) {
Logger.getLogger(Resources.class.getName()).log(Level.SEVERE, null, ex);
return new String();
}
}
public static String loadTextFile(InputStream stream) {
try {
return Shader.readIntoString(stream);
} catch (IOException ex) {
Logger.getLogger(Resources.class.getName()).log(Level.SEVERE, null, ex);
return new String();
}
}
public static ViewModel loadObjModel(File file, Vector3 scale) {
try {
String text = loadTextFile(file);
return loadObj(text, scale);
} catch (IOException ex) {
Logger.getLogger(Resources.class.getName()).log(Level.SEVERE, "Model could not be loaded: ", ex);
return new ViewModel() {
@Override
public void renderModel() {
Logger.getLogger(Resources.class.getName()).log(Level.SEVERE, "Model not loaded.");
}
};
}
}
public static ViewModel loadObjModel(InputStream stream, Vector3 scale) {
try {
String text = loadTextFile(stream);
return loadObj(text, scale);
} catch (IOException ex) {
Logger.getLogger(Resources.class.getName()).log(Level.SEVERE, "Model could not be loaded: ", ex);
return new ViewModel() {
@Override
public void renderModel() {
Logger.getLogger(Resources.class.getName()).log(Level.SEVERE, "Model not loaded.");
}
};
}
}
public static ViewModel loadObjModel(String name, File file, Vector3 scale) {
if (MODELS.containsKey(name)) {
return MODELS.get(name);
}
ViewModel mdl = loadObjModel(file, scale);
addModel(name, mdl);
return mdl;
}
public static ViewModel loadObjModel(String name, InputStream stream, Vector3 scale) {
if (MODELS.containsKey(name)) {
return MODELS.get(name);
}
ViewModel mdl = loadObjModel(stream, scale);
addModel(name, mdl);
return mdl;
}
public static void addModel(String name, ViewModel model) {
MODELS.put(name, model);
}
public static ViewModel getModel(String name) {
return MODELS.get(name);
}
public static int vertexDataPipe(int lod) {
return lod * 4 * 3;
}
public static int texCoordDataPipe(int lod) {
return lod * 4 * 2;
}
public static int vertexDataSphere(int lod) {
return lod * (lod / 2) * 4 * 3;
}
public static int texCoordDataSphere(int lod) {
return lod * (lod / 2) * 4 * 2;
}
public static int vertexDataPolygon(int npoints) {
return npoints * 3;
}
public static int texCoordDataPolygon(int npoints) {
return npoints * 2;
}
public static float[] polygonTexCoords(float hRepeat, float vRepeat, Vector3... points) {
float[] f = new float[texCoordDataPolygon(points.length)];
polygonTexCoords(hRepeat, vRepeat, points, f, 0);
return f;
}
public static void polygonTexCoords(float hRepeat, float vRepeat, Vector3[] points, float[] dest, int start) {
float minx = Float.POSITIVE_INFINITY;
float miny = Float.POSITIVE_INFINITY;
float minz = Float.POSITIVE_INFINITY;
float maxx = Float.NEGATIVE_INFINITY;
float maxy = Float.NEGATIVE_INFINITY;
float maxz = Float.NEGATIVE_INFINITY;
for (Vector3 point : points) {
float x = point.x;
float y = point.y;
float z = point.z;
minx = Math.min(minx, x);
miny = Math.min(miny, y);
minz = Math.min(minz, z);
maxx = Math.max(maxx, x);
maxy = Math.max(maxy, y);
maxz = Math.max(maxz, z);
}
float w = new Vector2(maxx - minx, maxz - minz).mag();
for (int i = 0; i < points.length; i++) {
float x = (new Vector2(points[i].x - minx, points[i].z - minz).mag() / w) * hRepeat;
float y = ((points[i].y - miny) / (maxy - miny)) * vRepeat;
dest[i * 2 + start + 0] = x;
dest[i * 2 + start + 1] = y;
}
}
public static ViewModel loadObjLines(String obj, float scale) throws IOException {
ArrayList<Vector3> allVertices = new ArrayList<>(100);
LinkedList<Float> triangleVerts = new LinkedList<>();
LinkedList<Float> lineVerts = new LinkedList<>();
for (String s : obj.trim().split("\n")) {
s = s.trim();
String[] parts = s.split(" ");
if (s.startsWith("#") || s.isEmpty()) {
continue;
}
switch (parts[0]) {
case "v": {
float x = Float.parseFloat(parts[1]);
float y = Float.parseFloat(parts[2]);
float z = Float.parseFloat(parts[3]);
allVertices.add(new Vector3(x, y, z).times(scale));
break;
}
case "f": {
for (int i = 0; i < 3; i++) {
String[] info = parts[i + 1].split("/");
int vertex = Integer.parseInt(info[0]);
if (vertex < 0) {
vertex = allVertices.size() + vertex;
} else {
vertex--;
}
Vector3 v = allVertices.get(vertex);
triangleVerts.add(v.x);
triangleVerts.add(v.y);
triangleVerts.add(v.z);
}
break;
}
case "l": {
for (int i = 0; i < 2; i++) {
int vertex = Integer.parseInt(parts[i + 1]);
if (vertex < 0) {
vertex = allVertices.size() + vertex;
} else {
vertex--;
}
Vector3 v = allVertices.get(vertex);
lineVerts.add(v.x);
lineVerts.add(v.y);
lineVerts.add(v.z);
}
break;
}
}
}
float[] triangles = new float[triangleVerts.size()];
float[] lines = new float[lineVerts.size()];
int i = 0;
for (float f : triangleVerts) {
triangles[i++] = f;
}
i = 0;
for (float f : lineVerts) {
lines[i++] = f;
}
Mesh triangleVBO = new Mesh(TRIANGLES, triangles, new float[triangles.length / 3]);
Mesh lineVBO = new Mesh(LINES, lines, new float[lines.length / 3]);
return new LineModel(triangleVBO, lineVBO);
}
public static ViewModel loadObj(String obj, Vector3 scale) throws IOException {
ArrayList<Vector3> allVertices = new ArrayList<>(100);
ArrayList<Vector2> allTexCoords = new ArrayList<>(100);
ArrayList<Vector3> allNormals = new ArrayList<>(100);
Material currentMaterial = null;
//LinkedList<ViewModel> polygons = new LinkedList<>();
HashMap<Material, LinkedList<Vertex>> triangles = new HashMap<>();
for (String s : obj.trim().split("\n")) {
s = s.trim();
String[] parts = s.split(" ");
if (s.startsWith("#") || s.isEmpty()) {
continue;
}
switch (parts[0]) {
case "mtllib": {
loadMtl(loadTextFile(new File("models/" + parts[1])));
break;
}
case "usemtl": {
currentMaterial = getMaterial(parts[1]);
break;
}
case "v": {
float x = Float.parseFloat(parts[1]);
float y = Float.parseFloat(parts[2]);
float z = Float.parseFloat(parts[3]);
allVertices.add(new Vector3(x, y, z).component(scale));
break;
}
case "vt": {
float x = Float.parseFloat(parts[1]);
float y = Float.parseFloat(parts[2]);
allTexCoords.add(new Vector2(x, y));
break;
}
case "vn": {
float x = Float.parseFloat(parts[1]);
float y = Float.parseFloat(parts[2]);
float z = Float.parseFloat(parts[3]);
allNormals.add(new Vector3(x / scale.x, y / scale.y, z / scale.z).normalize());
break;
}
case "f": {
LinkedList<Vertex> thisMtl = triangles.get(currentMaterial);
if (thisMtl == null) {
thisMtl = new LinkedList<>();
triangles.put(currentMaterial, thisMtl);
}
for (int i = 0; i < 3; i++) {
String[] info = parts[i + 1].split("/");
int vertex = Integer.parseInt(info[0]);
int texCoord = Integer.parseInt(info[1]);
int normal = Integer.parseInt(info[2]);
if (vertex < 0) {
vertex = allVertices.size() + vertex;
} else {
vertex--;
}
if (texCoord < 0) {
texCoord = allTexCoords.size() + texCoord;
} else {
texCoord--;
}
if (normal < 0) {
normal = allNormals.size() + normal;
} else {
normal--;
}
thisMtl.add(new Vertex(allVertices.get(vertex), allNormals.get(normal), allTexCoords.get(texCoord)));
}
}
}
}
LinkedList<ViewModel> polygons = new LinkedList<>();
for (Material m : triangles.keySet()) {
LinkedList<Vertex> vertices = triangles.get(m);
float[] positions = new float[vertices.size() * 3];
float[] texcoords = new float[vertices.size() * 2];
float[] normals = new float[vertices.size() * 3];
int i = 0;
for (Vertex v : vertices) {
positions[i * 3 + 0] = v.position.x;
positions[i * 3 + 1] = v.position.y;
positions[i * 3 + 2] = v.position.z;
texcoords[i * 2 + 0] = v.texcoord.x;
texcoords[i * 2 + 1] = v.texcoord.y;
normals[i * 3 + 0] = v.normal.x;
normals[i * 3 + 1] = v.normal.y;
normals[i * 3 + 2] = v.normal.z;
i++;
}
polygons.add(new Mesh(TRIANGLES, positions, normals, texcoords, m));
}
return new MultiModel(polygons);
}
public static void loadMtl(String mtl) throws IOException {
HashMap<String, Color> colors = new HashMap<>();
Material current = null;
for (String s : mtl.trim().split("\n")) {
s = s.trim();
String[] parts = s.split(" ");
switch (parts[0]) {
case "newmtl": {
current = new Material();
current.shader = ShaderProgram.PHONG;
current.cull = false;
addMaterial(parts[1], current);
break;
}
case "Ka": {
float red = Float.parseFloat(parts[1]);
float green = Float.parseFloat(parts[2]);
float blue = Float.parseFloat(parts[3]);
current.emissive = new Color(red, green, blue);
break;
}
case "Kd": {
float red = Float.parseFloat(parts[1]);
float green = Float.parseFloat(parts[2]);
float blue = Float.parseFloat(parts[3]);
current.diffuse = new Color(red, green, blue);
current.ambient = new Color(red, green, blue);
break;
}
case "Ks": {/*
float red = Float.parseFloat(parts[1]);
float green = Float.parseFloat(parts[2]);
float blue = Float.parseFloat(parts[3]);
current.specular = new Color(red, green, blue);*/
break;
}
case "map_Kd": {
String path = "models/" + parts[1];
current.texture = loadTexture(path, getStream(new File(path)));
break;
}
}
}
}
public static ViewModel rect(Material texture, float width, float height) {
return rect(texture, 1, 1, width, height);
}
public static ViewModel rect(Material texture, float hRepeat, float vRepeat, float width, float height) {
float[] vertices = rectVertices(width, height);
float[] texCoords = rectTexCoords(hRepeat, vRepeat);
return new Mesh(QUADS, vertices, copyVector3(Vector3.zAxis(), 4), texCoords, texture);
}
public static ViewModel rect(Color color, float width, float height) {
return rect(new Material(color), width, height);
}
public static ViewModel sprite(Material image, float xscl, float yscl) {
Texture tex = image.getTexture();
return rect(image, tex.getWidth() * xscl, tex.getHeight() * yscl);
}
public static ViewModel ellipse(Material texture, float width, float height, int npoints) {
float[] vertices = ellipseVertices(width, height, npoints);
float[] texCoords = ellipseTexCoords(npoints);
return new Mesh(POLYGON, vertices, copyVector3(Vector3.zAxis(), npoints), texCoords, texture);
}
public static ViewModel ellipse(Color color, float width, float height, int npoints) {
return ellipse(new Material(color), width, height, npoints);
}
public static float[] pipeVertices(float radius, float length, int lod) {
float[] f = new float[vertexDataPipe(lod)];
pipeVertices(radius, length, lod, f, 0);
return f;
}
public static int pipeVertices(float radius, float length, int lod, float[] dest, int start) {
length /= 2;
float angleStep = (Trig.FULL_CIRCLE / lod);
int index = start;
for (int i = 0; i < lod; i++) {
float angle = i * angleStep;
float x1 = Trig.cos(angle) * radius;
float z1 = Trig.sin(angle) * radius;
float x2 = Trig.cos(angle + angleStep) * radius;
float z2 = Trig.sin(angle + angleStep) * radius;
Vector3 vert1 = new Vector3(x1, -length, z1);
Vector3 vert2 = new Vector3(x1, length, z1);
Vector3 vert3 = new Vector3(x2, length, z2);
Vector3 vert4 = new Vector3(x2, -length, z2);
index += copyVector3(vert1, dest, index, 1);
index += copyVector3(vert2, dest, index, 1);
index += copyVector3(vert3, dest, index, 1);
index += copyVector3(vert4, dest, index, 1);
}
return vertexDataPipe(lod);
}
public static float[] pipeTexCoords(float hRepeat, float vRepeat, int lod) {
float[] f = new float[texCoordDataPipe(lod)];
pipeTexCoords(hRepeat, vRepeat, lod, f, 0);
return f;
}
public static int pipeTexCoords(float hRepeat, float vRepeat, int lod, float[] dest, int start) {
float texStep = (1.0f / lod);
int index = start;
for (int i = 0; i < lod; i++) {
float tex = i * texStep;
Vector2 tex1 = new Vector2(tex * hRepeat, vRepeat);
Vector2 tex2 = new Vector2(tex * hRepeat, 0);
Vector2 tex3 = new Vector2((tex + texStep) * hRepeat, 0);
Vector2 tex4 = new Vector2((tex + texStep) * hRepeat, vRepeat);
index += copyVector2(tex1, dest, index, 1);
index += copyVector2(tex2, dest, index, 1);
index += copyVector2(tex3, dest, index, 1);
index += copyVector2(tex4, dest, index, 1);
}
return texCoordDataPipe(lod);
}
public static float[] pipeNormals(int lod) {
float[] f = new float[vertexDataPipe(lod)];
pipeNormals(lod, f, 0);
return f;
}
public static int pipeNormals(int lod, float[] dest, int start) {
return pipeVertices(1.0f, 0.0f, lod, dest, start);
}
public static ViewModel pipe(Material sides, float hRepeat, float vRepeat, float radius, float length, int lod) {
return new Mesh(QUADS, pipeVertices(radius, length, lod), pipeNormals(lod), pipeTexCoords(hRepeat, vRepeat, lod), sides);
}
public static int sphereVertices(float radius, int lod, float[] dest, int start) {
float step = Trig.FULL_CIRCLE / lod;
int vlod = lod / 2;
int index = start;
for (int i = 0; i < lod; i++) {
float lon = i * step;
for (int j = 0; j < vlod; j++) {
float lat = j * step - 90.0f;
Vector3 vert1 = Vector3.fromAngles(new Angles(lat, lon, 0.0f), radius);
Vector3 vert2 = Vector3.fromAngles(new Angles(lat, lon + step, 0.0f), radius);
Vector3 vert3 = Vector3.fromAngles(new Angles(lat + step, lon + step, 0.0f), radius);
Vector3 vert4 = Vector3.fromAngles(new Angles(lat + step, lon, 0.0f), radius);
index += copyVector3(vert1, dest, index, 1);
index += copyVector3(vert2, dest, index, 1);
index += copyVector3(vert3, dest, index, 1);
index += copyVector3(vert4, dest, index, 1);
}
}
return vertexDataSphere(lod);
}
public static float[] sphereVertices(float radius, int lod) {
float[] f = new float[vertexDataSphere(lod)];
sphereVertices(radius, lod, f, 0);
return f;
}
public static int sphereTexCoords(int lod, float[] dest, int start) {
float texstep = 1.0f / lod;
int vlod = lod / 2;
int index = start;
for (int i = lod; i > 0; i--) {
float u = i * texstep;
for (int j = 0; j < vlod; j++) {
float v = j * texstep * 2;
Vector2 tex1 = new Vector2(u + texstep, v);
Vector2 tex2 = new Vector2(u, v);
Vector2 tex3 = new Vector2(u, v + texstep * 2);
Vector2 tex4 = new Vector2(u + texstep, v + texstep * 2);
index += copyVector2(tex1, dest, index, 1);
index += copyVector2(tex2, dest, index, 1);
index += copyVector2(tex3, dest, index, 1);
index += copyVector2(tex4, dest, index, 1);
}
}
return texCoordDataSphere(lod);
}
public static float[] sphereTexCoords(int lod) {
float[] f = new float[texCoordDataSphere(lod)];
sphereTexCoords(lod, f, 0);
return f;
}
public static int sphereNormals(int lod, float[] dest, int start) {
return sphereVertices(1.0f, lod, dest, start);
}
public static float[] sphereNormals(int lod) {
return sphereVertices(1.0f, lod);
}
public static ViewModel sphere(float radius, int lod, Material mtl) {
return new Mesh(QUADS, sphereVertices(radius, lod), sphereNormals(lod), sphereTexCoords(lod), mtl);
}
public static float[] rectVertices(float width, float height) {
width /= 2;
height /= 2;
return new float[]{
width, -height, 0,
width, height, 0,
-width, height, 0,
-width, -height, 0
};
}
public static int rectVertices(float width, float height, float[] dest, int start) {
System.arraycopy(rectVertices(width, height), 0, dest, start, 12);
return VERTEX_DATA_QUAD;
}
public static float[] ellipseTexCoords(int lod) {
float[] f = new float[texCoordDataPolygon(lod)];
ellipseTexCoords(lod, f, 0);
return f;
}
public static int ellipseTexCoords(int lod, float[] dest, int start) {
float step = (float) (2 * Math.PI / lod);
for (int i = 0; i < lod; i++) {
dest[i * 2 + start + 0] = -(float) (0.49 * Math.cos(i * step) + 0.5);
dest[i * 2 + start + 1] = (float) (0.49 * Math.sin(i * step) + 0.5);
}
return texCoordDataPolygon(lod);
}
public static float[] ellipseVertices(float width, float height, int lod) {
float[] f = new float[vertexDataPolygon(lod)];
ellipseVertices(width, height, lod, f, 0);
return f;
}
public static int ellipseVertices(float width, float height, int lod, float[] dest, int start) {
width /= 2;
height /= 2;
float step = Trig.FULL_CIRCLE / lod;
for (int i = 0; i < lod; i++) {
dest[i * 3 + start + 0] = width * Trig.cos(i * step);
dest[i * 3 + start + 1] = height * Trig.sin(i * step);
dest[i * 3 + start + 2] = 0.0f;
}
return vertexDataPolygon(lod);
}
public static float[] rectTexCoords(float hRepeat, float vRepeat) {
float[] f = new float[TEXCOORD_DATA_QUAD];
rectTexCoords(hRepeat, vRepeat, f, 0);
return f;
}
public static int rectTexCoords(float hRepeat, float vRepeat, float[] dest, int start) {
dest[start + 0] = 0;
dest[start + 1] = 0;
dest[start + 2] = 0;
dest[start + 3] = vRepeat;
dest[start + 4] = hRepeat;
dest[start + 5] = vRepeat;
dest[start + 6] = hRepeat;
dest[start + 7] = 0;
return TEXCOORD_DATA_BOX;
}
public static ViewModel box(Material texture, float width, float height, float depth) {
return box(texture, 1, 1, 1, width, height, depth);
}
public static ViewModel box(Material texture, float xRepeat, float yRepeat, float zRepeat, float width, float height, float depth) {
float[] vertices = boxVertices(width, height, depth);
float[] texCoords = boxTexCoords(xRepeat, yRepeat, zRepeat);
return new Mesh(QUADS, vertices, boxNormals(), texCoords, texture);
}
public static ViewModel box(Color color, float width, float height, float depth) {
return box(new Material(color), width, height, depth);
}
public static float[] boxVertices(float width, float height, float depth) {
width /= 2;
height /= 2;
depth /= 2;
return new float[]{
// front (-z)
-width, height, depth,
-width, -height, depth,
width, -height, depth,
width, height, depth,
// left (-x)
-width, height, -depth,
-width, -height, -depth,
-width, -height, depth,
-width, height, depth,
// back (+z)
width, height, -depth,
width, -height, -depth,
-width, -height, -depth,
-width, height, -depth,
// right (+x)
width, height, depth,
width, -height, depth,
width, -height, -depth,
width, height, -depth,
// top (+y)
width, height, -depth,
-width, height, -depth,
-width, height, depth,
width, height, depth,
// bottom (-y)
width, -height, depth,
-width, -height, depth,
-width, -height, -depth,
width, -height, -depth
};
}
public static int boxVertices(float width, float height, float depth, float[] dest, int start) {
System.arraycopy(boxVertices(width, height, depth), 0, dest, start, 6 * 4 * 3);
return VERTEX_DATA_BOX;
}
public static float[] boxNormals() {
float[] f = new float[VERTEX_DATA_BOX];
boxNormals(f, 0);
return f;
}
public static int boxNormals(float[] dest, int start) {
copyVector3(Vector3.zAxis(), dest, start + 0 * 3, 4);
copyVector3(Vector3.xAxis().negate(), dest, start + 4 * 3, 4);
copyVector3(Vector3.zAxis().negate(), dest, start + 8 * 3, 4);
copyVector3(Vector3.xAxis(), dest, start + 12 * 3, 4);
copyVector3(Vector3.yAxis(), dest, start + 16 * 3, 4);
copyVector3(Vector3.yAxis().negate(), dest, start + 20 * 3, 4);
return VERTEX_DATA_BOX;
}
public static float[] polygonVertices(Vector3... points) {
float[] f = new float[vertexDataPolygon(points.length)];
polygonVertices(points, f, 0);
return f;
}
public static int polygonVertices(Vector3[] points, float[] dest, int start) {
for (int i = 0; i < points.length; i++) {
copyVector3(points[i], dest, start + i * 3, 1);
}
return vertexDataPolygon(points.length);
}
public static float[] boxTexCoords(float xRepeat, float yRepeat, float zRepeat) {
float[] f = new float[TEXCOORD_DATA_BOX];
boxTexCoords(xRepeat, yRepeat, zRepeat, f, 0);
return f;
}
public static int boxTexCoords(float xRepeat, float yRepeat, float zRepeat, float[] dest, int start) {
float[] hRepeats = {xRepeat, zRepeat, xRepeat, zRepeat, zRepeat, zRepeat};
float[] vRepeats = {yRepeat, yRepeat, yRepeat, yRepeat, xRepeat, xRepeat};
for (int i = 0; i < 6; i++) {
dest[i * 8 + start + 0] = hRepeats[i];
dest[i * 8 + start + 1] = 0;
dest[i * 8 + start + 2] = hRepeats[i];
dest[i * 8 + start + 3] = vRepeats[i];
dest[i * 8 + start + 4] = 0;
dest[i * 8 + start + 5] = vRepeats[i];
dest[i * 8 + start + 6] = 0;
dest[i * 8 + start + 7] = 0;
}
return TEXCOORD_DATA_BOX;
}
public static int copyVector2(Vector2 vector, float[] dest, int start, int num) {
for (int i = 0; i < num; i++) {
dest[i * 2 + start + 0] = vector.x;
dest[i * 2 + start + 1] = vector.y;
}
return num * 2;
}
public static float[] copyVector2(Vector2 vector, int num) {
float[] f = new float[num * 2];
copyVector2(vector, f, 0, num);
return f;
}
public static int copyVector3(Vector3 vector, float[] dest, int start, int num) {
for (int i = 0; i < num; i++) {
dest[i * 3 + start + 0] = vector.x;
dest[i * 3 + start + 1] = vector.y;
dest[i * 3 + start + 2] = vector.z;
}
return num * 3;
}
public static float[] copyVector3(Vector3 vector, int num) {
float[] f = new float[num * 3];
copyVector3(vector, f, 0, num);
return f;
}
public static int copyColorRGB(Color color, float[] dest, int start, int num) {
for (int i = 0; i < num; i++) {
dest[i * 3 + start + 0] = color.red;
dest[i * 3 + start + 1] = color.green;
dest[i * 3 + start + 2] = color.blue;
}
return num * 3;
}
public static float[] copyColorRGB(Color color, int num) {
float[] f = new float[num * 3];
copyColorRGB(color, f, 0, num);
return f;
}
public static int copyColorRGBA(Color color, float[] dest, int start, int num) {
for (int i = 0; i < num; i++) {
dest[i * 4 + start + 0] = color.red;
dest[i * 4 + start + 1] = color.green;
dest[i * 4 + start + 2] = color.blue;
dest[i * 4 + start + 3] = color.alpha;
}
return num * 4;
}
public static float[] copyColorRGBA(Color color, int num) {
float[] f = new float[num * 4];
copyColorRGBA(color, f, 0, num);
return f;
}
public static void transformVectors(float[] vectors, Matrix4x4 matrix, int start, int num) {
for (int i = start; i < start + num; i+= 3) {
Vector3 vec = new Vector3(vectors[i], vectors[i + 1], vectors[i + 2]);
Vector3 transformed = matrix.transformVector(vec);
copyVector3(transformed, vectors, i, 1);
}
}
public static void transformPoints(float[] vectors, Matrix4x4 matrix, int start, int num) {
for (int i = start; i < start + num; i+= 3) {
Vector3 vec = new Vector3(vectors[i], vectors[i + 1], vectors[i + 2]);
Vector3 transformed = matrix.transformPoint(vec);
copyVector3(transformed, vectors, i, 1);
}
}
public static Vector3 normal(Vector3 v1, Vector3 v2, Vector3 v3) {
Vector3 u = Vector3.subtract(v2, v1);
Vector3 v = Vector3.subtract(v3, v1);
Vector3 n = Vector3.cross(u, v).normalize();
return n;
}
public static float[] faceNormals(Vector3... vertices) {
float[] normals = new float[vertexDataPolygon(vertices.length)];
faceNormals(vertices, normals, 0, vertices.length);
return normals;
}
public static int faceNormals(Vector3[] vertices, float[] dest, int index, int num) {
return copyVector3(normal(vertices[0], vertices[1], vertices[2]), dest, index, num);
}
public static ViewModel polygon(Material texture, Vector3... points) {
return polygon(texture, 1, 1, points);
}
public static ViewModel polygon(Material texture, float hRepeat, float vRepeat, Vector3... points) {
float[] vertices = polygonVertices(points);
float[] texCoords = polygonTexCoords(hRepeat, vRepeat, points);
return new Mesh(POLYGON, vertices, copyVector3(Vector3.zAxis(), points.length), texCoords, texture);
}
public static ViewModel polygon(Color color, Vector3... points) {
return polygon(new Material(color), points);
}
}