diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/discord.xml b/.idea/discord.xml new file mode 100644 index 0000000..30bab2a --- /dev/null +++ b/.idea/discord.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..0548357 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..08a35a7 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..797acea --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/MCLWJGL3.iml b/MCLWJGL3.iml new file mode 100644 index 0000000..c90834f --- /dev/null +++ b/MCLWJGL3.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/de/verschwiegener/lwjgl3/BufferUtils.java b/src/de/verschwiegener/lwjgl3/BufferUtils.java new file mode 100644 index 0000000..b0dfdf2 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/BufferUtils.java @@ -0,0 +1,160 @@ +package de.verschwiegener.lwjgl3; + +import org.lwjgl.PointerBuffer; + +import java.nio.*; + +public final class BufferUtils { + + /** + * Construct a direct native-ordered bytebuffer with the specified size. + * @param size The size, in bytes + * @return a ByteBuffer + */ + public static ByteBuffer createByteBuffer(int size) { + return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()); + } + + /** + * Construct a direct native-order shortbuffer with the specified number + * of elements. + * @param size The size, in shorts + * @return a ShortBuffer + */ + public static ShortBuffer createShortBuffer(int size) { + return createByteBuffer(size << 1).asShortBuffer(); + } + + /** + * Construct a direct native-order charbuffer with the specified number + * of elements. + * @param size The size, in chars + * @return an CharBuffer + */ + public static CharBuffer createCharBuffer(int size) { + return createByteBuffer(size << 1).asCharBuffer(); + } + + /** + * Construct a direct native-order intbuffer with the specified number + * of elements. + * @param size The size, in ints + * @return an IntBuffer + */ + public static IntBuffer createIntBuffer(int size) { + return createByteBuffer(size << 2).asIntBuffer(); + } + + /** + * Construct a direct native-order longbuffer with the specified number + * of elements. + * @param size The size, in longs + * @return an LongBuffer + */ + public static LongBuffer createLongBuffer(int size) { + return createByteBuffer(size << 3).asLongBuffer(); + } + + /** + * Construct a direct native-order floatbuffer with the specified number + * of elements. + * @param size The size, in floats + * @return a FloatBuffer + */ + public static FloatBuffer createFloatBuffer(int size) { + return createByteBuffer(size << 2).asFloatBuffer(); + } + + /** + * Construct a direct native-order doublebuffer with the specified number + * of elements. + * @param size The size, in floats + * @return a FloatBuffer + */ + public static DoubleBuffer createDoubleBuffer(int size) { + return createByteBuffer(size << 3).asDoubleBuffer(); + } + + /** + * Construct a PointerBuffer with the specified number + * of elements. + * @param size The size, in memory addresses + * @return a PointerBuffer + */ + public static PointerBuffer createPointerBuffer(int size) { + return PointerBuffer.allocateDirect(size); + } + + /** + * @return n, where buffer_element_size=2^n. + */ + public static int getElementSizeExponent(Buffer buf) { + if (buf instanceof ByteBuffer) + return 0; + else if (buf instanceof ShortBuffer || buf instanceof CharBuffer) + return 1; + else if (buf instanceof FloatBuffer || buf instanceof IntBuffer) + return 2; + else if (buf instanceof LongBuffer || buf instanceof DoubleBuffer) + return 3; + else + throw new IllegalStateException("Unsupported buffer type: " + buf); + } + + /** + * A helper function which is used to get the byte offset in an arbitrary buffer + * based on its position + * @return the position of the buffer, in BYTES + */ + public static int getOffset(Buffer buffer) { + return buffer.position() << getElementSizeExponent(buffer); + } + + /** Fill buffer with zeros from position to remaining */ + public static void zeroBuffer(ByteBuffer b) { + zeroBuffer0(b, b.position(), b.remaining()); + } + + /** Fill buffer with zeros from position to remaining */ + public static void zeroBuffer(ShortBuffer b) { + zeroBuffer0(b, b.position()*2L, b.remaining()*2L); + } + + /** Fill buffer with zeros from position to remaining */ + public static void zeroBuffer(CharBuffer b) { + zeroBuffer0(b, b.position()*2L, b.remaining()*2L); + } + + /** Fill buffer with zeros from position to remaining */ + public static void zeroBuffer(IntBuffer b) { + zeroBuffer0(b, b.position()*4L, b.remaining()*4L); + } + + /** Fill buffer with zeros from position to remaining */ + public static void zeroBuffer(FloatBuffer b) { + zeroBuffer0(b, b.position()*4L, b.remaining()*4L); + } + + /** Fill buffer with zeros from position to remaining */ + public static void zeroBuffer(LongBuffer b) { + zeroBuffer0(b, b.position()*8L, b.remaining()*8L); + } + + /** Fill buffer with zeros from position to remaining */ + public static void zeroBuffer(DoubleBuffer b) { + zeroBuffer0(b, b.position()*8L, b.remaining()*8L); + } + + /** Fill buffer with zeros from position to remaining */ + private static native void zeroBuffer0(Buffer b, long off, long size); + + /** + * Returns the memory address of the specified buffer. + * + * @param buffer the buffer + * + * @return the memory address + */ + static native long getBufferAddress(Buffer buffer); + +} diff --git a/src/de/verschwiegener/lwjgl3/LWJGLUtil.java b/src/de/verschwiegener/lwjgl3/LWJGLUtil.java new file mode 100644 index 0000000..836b441 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/LWJGLUtil.java @@ -0,0 +1,52 @@ +package de.verschwiegener.lwjgl3; + + +import org.lwjgl.opengl.GL; + +import java.security.AccessController; +import java.security.PrivilegedAction; + +import static org.lwjgl.opengl.ARBImaging.*; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL30.*; + +public class LWJGLUtil { + + public static final boolean DEBUG = getPrivilegedBoolean("org.lwjgl.util.Debug"); + + public static boolean getPrivilegedBoolean(final String property_name) { + return AccessController.doPrivileged(new PrivilegedAction() { + public Boolean run() { + return Boolean.getBoolean(property_name); + } + }); + } + public static String translateGLErrorString(int error_code) { + switch (error_code) { + case GL_NO_ERROR: + return "No error"; + case GL_INVALID_ENUM: + return "Invalid enum"; + case GL_INVALID_VALUE: + return "Invalid value"; + case GL_INVALID_OPERATION: + return "Invalid operation"; + case GL_STACK_OVERFLOW: + return "Stack overflow"; + case GL_STACK_UNDERFLOW: + return "Stack underflow"; + case GL_OUT_OF_MEMORY: + return "Out of memory"; + case GL_TABLE_TOO_LARGE: + return "Table too large"; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "Invalid framebuffer operation"; + default: + return null; + } + } + public static void log(String log){ + System.out.println(log); + } + +} diff --git a/src/de/verschwiegener/lwjgl3/OpenGLException.java b/src/de/verschwiegener/lwjgl3/OpenGLException.java new file mode 100644 index 0000000..96d29ae --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/OpenGLException.java @@ -0,0 +1,39 @@ +package de.verschwiegener.lwjgl3; + +public class OpenGLException extends Exception { + + private static final long serialVersionUID = 1L; + + /** + * Plain c'tor + */ + public OpenGLException() { + super(); + } + + /** + * Creates a new OpenGLException + * + * @param msg + * String identifier for exception + */ + public OpenGLException(String msg) { + super(msg); + } + + /** + * @param message + * @param cause + */ + public OpenGLException(String message, Throwable cause) { + super(message, cause); + } + + /** + * @param cause + */ + public OpenGLException(Throwable cause) { + super(cause); + } + +} diff --git a/src/de/verschwiegener/lwjgl3/Sys.java b/src/de/verschwiegener/lwjgl3/Sys.java new file mode 100644 index 0000000..945c165 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/Sys.java @@ -0,0 +1,53 @@ +package de.verschwiegener.lwjgl3; + +import org.lwjgl.Version; +import org.lwjgl.glfw.GLFW; +import org.lwjgl.openal.AL; + +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; + +public class Sys { + + public static long getTime(){ + return (long) GLFW.glfwGetTime(); + } + public static long getTimerResolution() { + return GLFW.glfwGetTimerFrequency(); + } + public static boolean openURL(String url) { + // Attempt to use Webstart if we have it available + try { + // Lookup the javax.jnlp.BasicService object + final Class serviceManagerClass = Class.forName("javax.jnlp.ServiceManager"); + Method lookupMethod = AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Method run() throws Exception { + return serviceManagerClass.getMethod("lookup", String.class); + } + }); + Object basicService = lookupMethod.invoke(serviceManagerClass, new Object[] {"javax.jnlp.BasicService"}); + final Class basicServiceClass = Class.forName("javax.jnlp.BasicService"); + Method showDocumentMethod = AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Method run() throws Exception { + return basicServiceClass.getMethod("showDocument", URL.class); + } + }); + try { + Boolean ret = (Boolean)showDocumentMethod.invoke(basicService, new URL(url)); + return ret; + } catch (MalformedURLException e) { + e.printStackTrace(System.err); + return false; + } + } catch (Exception ue) { + ue.printStackTrace(); + return false; + } + } + public static String getVersion() { + return "LWJGL3 Port by Juli15 Version 2.0 stable, based on LWJGL" + Version.getVersion(); + } +} diff --git a/src/de/verschwiegener/lwjgl3/display/ContextAttribs.java b/src/de/verschwiegener/lwjgl3/display/ContextAttribs.java new file mode 100644 index 0000000..db74152 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/display/ContextAttribs.java @@ -0,0 +1,38 @@ +package de.verschwiegener.lwjgl3.display; + +public class ContextAttribs { + + private int version_major, version_minor; + private boolean profileCore, debug; + + public ContextAttribs(int version_major, int version_minor){ + this.version_major = version_major; + this.version_minor = version_minor; + } + public ContextAttribs(){ + this.version_major = 0; + this.version_minor = 0; + } + + public ContextAttribs withProfileCore(boolean profileCore){ + this.profileCore = profileCore; + return this; + } + + public int getVersion_major() { + return version_major; + } + + public int getVersion_minor() { + return version_minor; + } + + public boolean isProfileCore() { + return profileCore; + } + + public ContextAttribs withDebug(boolean b) { + this.debug = debug; + return this; + } +} diff --git a/src/de/verschwiegener/lwjgl3/display/Display.java b/src/de/verschwiegener/lwjgl3/display/Display.java new file mode 100644 index 0000000..55758b4 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/display/Display.java @@ -0,0 +1,253 @@ +package de.verschwiegener.lwjgl3.display; + +import de.verschwiegener.lwjgl3.util.IOUtil; +import org.lwjgl.LWJGLException; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; +import net.minecraft.util.ResourceLocation; +import org.lwjgl.glfw.*; +import org.lwjgl.opengl.GL; +import org.lwjgl.stb.STBImage; +import org.lwjgl.system.MemoryStack; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.file.Paths; +import java.util.ArrayList; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.glfw.GLFW.glfwGetPrimaryMonitor; +import static org.lwjgl.system.MemoryUtil.*; + +public class Display { + + private static final DisplayMode initial_mode; + private static DisplayMode current_mode; + + private static String title = "Minecraft 1.8.9"; + private static final long window; + + private static boolean created; + private static boolean resized; + private static boolean focused; + private static boolean vsync; + + + static{ + glfwSetErrorCallback(GLFWErrorCallback.createPrint(System.err)); + + if ( !glfwInit() ) + throw new RuntimeException("Failed to initialize GLFW"); + + GLFWVidMode vidMode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + int bpp = vidMode.redBits() + vidMode.greenBits() + vidMode.blueBits(); + current_mode = initial_mode = new DisplayMode(vidMode.width(), vidMode.height(), bpp, vidMode.refreshRate()); + + //INITIAL SETUP + glfwDefaultWindowHints(); + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_FALSE); + glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); + + + window = glfwCreateWindow(current_mode.getWidth(), current_mode.getHeight(), title, NULL, NULL); + if (window == NULL) + throw new RuntimeException("Failed to create the GLFW window"); + + glfwMakeContextCurrent(window); + + GL.createCapabilities(); + } + + public static void setDisplayMode(DisplayMode displayMode) { + current_mode = displayMode; + glfwSetWindowSize(window, displayMode.getWidth(), displayMode.getHeight()); + glfwSetWindowPos( + window, + (initial_mode.getWidth() - displayMode.getWidth()) / 2, + (initial_mode.getHeight() - displayMode.getHeight()) / 2 + ); + } + + public static DisplayMode getDisplayMode() { + return current_mode; + } + + public static void setFullscreen(boolean fullscreen) { + if(fullscreen) glfwSetWindowSize(window, initial_mode.getWidth(), initial_mode.getHeight()); + else glfwSetWindowSize(window, current_mode.getWidth(), current_mode.getHeight()); + glfwWindowHint(GLFW_DECORATED, fullscreen ? GLFW_FALSE : GLFW_TRUE); + glfwWindowHint(GLFW_RESIZABLE, fullscreen ? GLFW_FALSE : GLFW_TRUE); + } + + public static void setResizable(boolean resizable){ + glfwWindowHint(GLFW_RESIZABLE, resizable ? 1 : 0); + } + + public static void setTitle(String titleIn) { + title = titleIn; + glfwSetWindowTitle(window, titleIn); + } + + public static void setIcon(ResourceLocation[] path){ + + GLFWImage.Buffer imagebf = GLFWImage.malloc(path.length); + for(int i = 0; i < path.length;i++){ + String fileLocation; + ByteBuffer byteBuffer; + try { + fileLocation = Paths.get(Display.class.getResource("/assets/" + path[i].getResourceDomain() + "/" + path[i].getResourcePath()).toURI()).toString(); + byteBuffer = IOUtil.ioResourceToByteBuffer(fileLocation, 1024); + } catch (URISyntaxException | IOException | NullPointerException e) { + System.err.println("could not load the icons"); + e.printStackTrace(); + return; + } + ByteBuffer image; + int width, heigh; + try (MemoryStack stack = MemoryStack.stackPush()) { + IntBuffer comp = stack.mallocInt(1); + IntBuffer w = stack.mallocInt(1); + IntBuffer h = stack.mallocInt(1); + + image = STBImage.stbi_load_from_memory(byteBuffer, w, h, comp, 4); + if (image == null) { + throw new IOException(STBImage.stbi_failure_reason()); + } + width = w.get(); + heigh = h.get(); + + GLFWImage imagegl = GLFWImage.malloc(); + imagegl.set(width, heigh, image); + imagebf.put(i, imagegl); + } catch (IOException e) { + e.printStackTrace(); + } + } + glfwSetWindowIcon(window, imagebf); + } + + public static void setVSyncEnabled(boolean vsyncIn) { + if(glfwGetCurrentContext() != 0){ + glfwSwapInterval(vsyncIn ? GLFW_TRUE : GLFW_FALSE); + } + vsync = vsyncIn; + } + public static void destroy(){ + glfwDestroyWindow(window); + created = false; + } + public static DisplayMode[] getAvailableDisplayModes(){ + GLFWVidMode.Buffer m = glfwGetVideoModes(glfwGetPrimaryMonitor()); + ArrayList modes = new ArrayList<>(); + while(m.hasRemaining()){ + GLFWVidMode vidMode = m.get(); + int bpp = vidMode.redBits() + vidMode.greenBits() + vidMode.blueBits(); + modes.add(new DisplayMode(vidMode.width(), vidMode.height(), bpp, vidMode.refreshRate())); + } + return modes.toArray(new DisplayMode[modes.size()]); + } + public static DisplayMode getDesktopDisplayMode(){ + GLFWVidMode vidMode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + int bpp = vidMode.redBits() + vidMode.greenBits() + vidMode.blueBits(); + return new DisplayMode(vidMode.width(), vidMode.height(), bpp, vidMode.refreshRate()); + } + public static boolean isCreated(){ + return created; + } + public static boolean isCloseRequested(){ + return glfwWindowShouldClose(window); + } + public static void sync(int fps){ + Sync.sync(fps); + } + public static void update(){ + if(glfwGetWindowAttrib(window, GLFW_VISIBLE) == 1) { + resized = false; + glfwSwapBuffers(window); + Mouse.poll_scrollY = 0; + glfwPollEvents(); + Mouse.createEvent(); + } + } + public static boolean wasResized(){ + return resized; + } + public static int getWidth(){ + return current_mode.getWidth(); + } + public static int getHeight(){ + return current_mode.getHeight(); + } + public static boolean isActive(){ + return focused; + } + + public static long getWindow() { + return window; + } + + public static void releaseContext() { + glfwSetWindowShouldClose(window, true); + } + + public static void create() throws LWJGLException { + create(new PixelFormat(), new ContextAttribs(3, 3).withProfileCore(true)); + } + + public static void create(PixelFormat pf) throws LWJGLException { + create(pf, new ContextAttribs(3, 3).withProfileCore(true)); + } + + public static void create(PixelFormat pf, ContextAttribs attributes) throws LWJGLException { + created = true; + try ( MemoryStack stack = MemoryStack.stackPush()) { + IntBuffer pWidth = stack.mallocInt(1); // int* + IntBuffer pHeight = stack.mallocInt(1); // int* + + // Get the window size passed to glfwCreateWindow + glfwGetWindowSize(window, pWidth, pHeight); + + // Center the window + glfwSetWindowPos( + window, + (initial_mode.getWidth() - pWidth.get(0)) / 2, + (initial_mode.getHeight() - pHeight.get(0)) / 2 + ); + } + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, attributes.getVersion_major()); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, attributes.getVersion_minor()); + if(attributes.isProfileCore()) glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + glfwSetWindowSizeCallback(window, new GLFWWindowSizeCallback() { + @Override + public void invoke(long window, int width, int height) { + current_mode = new DisplayMode(width, height); + resized = true; + } + }); + glfwSetWindowFocusCallback(window, new GLFWWindowFocusCallback() { + @Override + public void invoke(long l, boolean focused) { + Display.focused = focused; + Mouse.poll_xPos = Mouse.last_x; + Mouse.poll_yPos = Mouse.last_y; + } + }); + + Keyboard.create(); + Keyboard.pollGLFW(); + Mouse.create(); + Mouse.pollGLFW(); + + //Use raw input, better for 3D camera + if (glfwRawMouseMotionSupported()) + glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE); + + glfwSwapInterval(vsync ? GLFW_TRUE : GLFW_FALSE); + glfwShowWindow(window); + } +} diff --git a/src/de/verschwiegener/lwjgl3/display/DisplayMode.java b/src/de/verschwiegener/lwjgl3/display/DisplayMode.java new file mode 100644 index 0000000..876e48f --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/display/DisplayMode.java @@ -0,0 +1,41 @@ +package de.verschwiegener.lwjgl3.display; + +public class DisplayMode { + + private int width, height, bpp, freq; + + private final boolean fullscreen; + + public DisplayMode(int width, int height) { + this(width, height, 0, 0, false); + } + + DisplayMode(int width, int height, int bpp, int freq) { + this(width, height, bpp, freq, true); + } + + private DisplayMode(int width, int height, int bpp, int freq, boolean fullscreen) { + this.width = width; + this.height = height; + this.bpp = bpp; + this.freq = freq; + this.fullscreen = fullscreen; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public int getBitsPerPixel() { + return bpp; + } + + public int getFrequency() { + return freq; + } + +} diff --git a/src/de/verschwiegener/lwjgl3/display/GlobalLock.java b/src/de/verschwiegener/lwjgl3/display/GlobalLock.java new file mode 100644 index 0000000..b2d3ed0 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/display/GlobalLock.java @@ -0,0 +1,5 @@ +package de.verschwiegener.lwjgl3.display; + +final class GlobalLock { + static final Object lock = new Object(); +} diff --git a/src/de/verschwiegener/lwjgl3/display/PixelFormat.java b/src/de/verschwiegener/lwjgl3/display/PixelFormat.java new file mode 100644 index 0000000..52fee73 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/display/PixelFormat.java @@ -0,0 +1,381 @@ +package de.verschwiegener.lwjgl3.display; + +public class PixelFormat { + + /** + * We probybly dont even need this class but so it is drop in place + */ + + /** + * The number of bits per pixel, exluding alpha. + * This parameter is ignored in Display.create(). + */ + private int bpp; + /** The number of alpha bits. */ + private int alpha; + /** The number of depth buffer bits */ + private int depth; + /** The number of stencil bits */ + private int stencil; + /** + * The number of samples to use in anti-aliasing. + * 0 means that anti-aliasing is disabled. + */ + private int samples; + /** + * The number of COLOR_SAMPLES_NV to use for Coverage Sample Anti-aliasing (CSAA). + * When this number is greater than 0, the {@code samples} property will be treated + * as if it were the COVERAGE_SAMPLES_NV property. + *

+ * This property is currently a no-op for the MacOS implementation. + */ + private int colorSamples; + /** The number of auxiliary buffers */ + private int num_aux_buffers; + /** The number of bits per pixel in the accumulation buffer */ + private int accum_bpp; + /** The number of alpha bits in the accumulation buffer */ + private int accum_alpha; + /** Whether this format requires a stereo buffer */ + private boolean stereo; + /** Whether this format specifies a floating point format */ + private boolean floating_point; + /** + * Whether this format specifies a packed floating point format (32 bit unsigned - R11F_G11F_B10F) + * This property is currently a no-op for the MacOS implementation. + */ + private boolean floating_point_packed; + /** + * Whether this format specifies an sRGB format + * This property is currently a no-op for the MacOS implementation. + */ + private boolean sRGB; + + /** + * Default pixel format is minimum 8 bits depth, and no alpha + * nor stencil requirements. + */ + public PixelFormat() { + this(0, 8, 0); + } + + public PixelFormat(int alpha, int depth, int stencil) { + this(alpha, depth, stencil, 0); + } + + public PixelFormat(int alpha, int depth, int stencil, int samples) { + this(0, alpha, depth, stencil, samples); + } + + public PixelFormat(int bpp, int alpha, int depth, int stencil, int samples) { + this(bpp, alpha, depth, stencil, samples, 0, 0, 0, false); + } + + public PixelFormat(int bpp, int alpha, int depth, int stencil, int samples, int num_aux_buffers, int accum_bpp, int accum_alpha, boolean stereo) { + this(bpp, alpha, depth, stencil, samples, num_aux_buffers, accum_bpp, accum_alpha, stereo, false); + } + + public PixelFormat(int bpp, int alpha, int depth, int stencil, int samples, int num_aux_buffers, int accum_bpp, int accum_alpha, boolean stereo, boolean floating_point) { + this.bpp = bpp; + this.alpha = alpha; + this.depth = depth; + this.stencil = stencil; + + this.samples = samples; + + this.num_aux_buffers = num_aux_buffers; + + this.accum_bpp = accum_bpp; + this.accum_alpha = accum_alpha; + + this.stereo = stereo; + + this.floating_point = floating_point; + this.floating_point_packed = false; + this.sRGB = false; + } + + private PixelFormat(final PixelFormat pf) { + this.bpp = pf.bpp; + this.alpha = pf.alpha; + this.depth = pf.depth; + this.stencil = pf.stencil; + + this.samples = pf.samples; + this.colorSamples = pf.colorSamples; + + this.num_aux_buffers = pf.num_aux_buffers; + + this.accum_bpp = pf.accum_bpp; + this.accum_alpha = pf.accum_alpha; + + this.stereo = pf.stereo; + + this.floating_point = pf.floating_point; + this.floating_point_packed = pf.floating_point_packed; + this.sRGB = pf.sRGB; + } + + public int getBitsPerPixel() { + return bpp; + } + + /** + * Returns a new PixelFormat object with the same properties as this PixelFormat and the new bits per pixel value. + * + * @param bpp the new bits per pixel value. + * + * @return the new PixelFormat + */ + public PixelFormat withBitsPerPixel(final int bpp) { + if ( bpp < 0 ) + throw new IllegalArgumentException("Invalid number of bits per pixel specified: " + bpp); + + final PixelFormat pf = new PixelFormat(this); + pf.bpp = bpp; + return pf; + } + + public int getAlphaBits() { + return alpha; + } + + /** + * Returns a new PixelFormat object with the same properties as this PixelFormat and the new alpha bits value. + * + * @param alpha the new alpha bits value. + * + * @return the new PixelFormat + */ + public PixelFormat withAlphaBits(final int alpha) { + if ( alpha < 0 ) + throw new IllegalArgumentException("Invalid number of alpha bits specified: " + alpha); + + final PixelFormat pf = new PixelFormat(this); + pf.alpha = alpha; + return pf; + } + + public int getDepthBits() { + return depth; + } + + /** + * Returns a new PixelFormat object with the same properties as this PixelFormat and the new depth bits value. + * + * @param depth the new depth bits value. + * + * @return the new PixelFormat + */ + public PixelFormat withDepthBits(final int depth) { + if ( depth < 0 ) + throw new IllegalArgumentException("Invalid number of depth bits specified: " + depth); + + final PixelFormat pf = new PixelFormat(this); + pf.depth = depth; + return pf; + } + + public int getStencilBits() { + return stencil; + } + + /** + * Returns a new PixelFormat object with the same properties as this PixelFormat and the new stencil bits value. + * + * @param stencil the new stencil bits value. + * + * @return the new PixelFormat + */ + public PixelFormat withStencilBits(final int stencil) { + if ( stencil < 0 ) + throw new IllegalArgumentException("Invalid number of stencil bits specified: " + stencil); + + final PixelFormat pf = new PixelFormat(this); + pf.stencil = stencil; + return pf; + } + + public int getSamples() { + return samples; + } + + /** + * Returns a new PixelFormat object with the same properties as this PixelFormat and the new samples value. + * + * @param samples the new samples value. + * + * @return the new PixelFormat + */ + public PixelFormat withSamples(final int samples) { + if ( samples < 0 ) + throw new IllegalArgumentException("Invalid number of samples specified: " + samples); + + final PixelFormat pf = new PixelFormat(this); + pf.samples = samples; + return pf; + } + + /** + * Returns a new PixelFormat object with the same properties as this PixelFormat and the new color samples values. + * A value greater than 0 is valid only if the {@code samples} property is also greater than 0. Additionally, the + * color samples value needs to be lower than or equal to the {@code samples} property. + * + * @param colorSamples the new color samples value. + * + * @return the new PixelFormat + */ + public PixelFormat withCoverageSamples(final int colorSamples) { + return withCoverageSamples(colorSamples, samples); + } + + /** + * Returns a new PixelFormat object with the same properties as this PixelFormat and the new color samples + * and coverage samples values. + * + * @param colorSamples the new color samples value. This value must be lower than or equal to the coverage samples value. + * @param coverageSamples the new coverage samples value. + * + * @return the new PixelFormat + */ + public PixelFormat withCoverageSamples(final int colorSamples, final int coverageSamples) { + if ( coverageSamples < 0 || colorSamples < 0 || (coverageSamples == 0 && 0 < colorSamples) || coverageSamples < colorSamples ) + throw new IllegalArgumentException("Invalid number of coverage samples specified: " + coverageSamples + " - " + colorSamples); + + final PixelFormat pf = new PixelFormat(this); + pf.samples = coverageSamples; + pf.colorSamples = colorSamples; + return pf; + } + + public int getAuxBuffers() { + return num_aux_buffers; + } + + /** + * Returns a new PixelFormat object with the same properties as this PixelFormat and the new auxiliary buffers value. + * + * @param num_aux_buffers the new auxiliary buffers value. + * + * @return the new PixelFormat + */ + public PixelFormat withAuxBuffers(final int num_aux_buffers) { + if ( num_aux_buffers < 0 ) + throw new IllegalArgumentException("Invalid number of auxiliary buffers specified: " + num_aux_buffers); + + final PixelFormat pf = new PixelFormat(this); + pf.num_aux_buffers = num_aux_buffers; + return pf; + } + + public int getAccumulationBitsPerPixel() { + return accum_bpp; + } + + /** + * Returns a new PixelFormat object with the same properties as this PixelFormat and the new bits per pixel in the accumulation buffer value. + * + * @param accum_bpp the new bits per pixel in the accumulation buffer value. + * + * @return the new PixelFormat + */ + public PixelFormat withAccumulationBitsPerPixel(final int accum_bpp) { + if ( accum_bpp < 0 ) + throw new IllegalArgumentException("Invalid number of bits per pixel in the accumulation buffer specified: " + accum_bpp); + + final PixelFormat pf = new PixelFormat(this); + pf.accum_bpp = accum_bpp; + return pf; + } + + public int getAccumulationAlpha() { + return accum_alpha; + } + + /** + * Returns a new PixelFormat object with the same properties as this PixelFormat and the new alpha bits in the accumulation buffer value. + * + * @param accum_alpha the new alpha bits in the accumulation buffer value. + * + * @return the new PixelFormat + */ + public PixelFormat withAccumulationAlpha(final int accum_alpha) { + if ( accum_alpha < 0 ) + throw new IllegalArgumentException("Invalid number of alpha bits in the accumulation buffer specified: " + accum_alpha); + + final PixelFormat pf = new PixelFormat(this); + pf.accum_alpha = accum_alpha; + return pf; + } + + public boolean isStereo() { + return stereo; + } + + /** + * Returns a new PixelFormat object with the same properties as this PixelFormat and the new stereo value. + * + * @param stereo the new stereo value. + * + * @return the new PixelFormat + */ + public PixelFormat withStereo(final boolean stereo) { + final PixelFormat pf = new PixelFormat(this); + pf.stereo = stereo; + return pf; + } + + public boolean isFloatingPoint() { + return floating_point; + } + + /** + * Returns a new PixelFormat object with the same properties as this PixelFormat and the new floating point value. + * If floating_point is true, floating_point_packed will be reset to false. + * + * @param floating_point the new floating point value. + * + * @return the new PixelFormat + */ + public PixelFormat withFloatingPoint(final boolean floating_point) { + final PixelFormat pf = new PixelFormat(this); + pf.floating_point = floating_point; + if ( floating_point ) + pf.floating_point_packed = false; + return pf; + } + + /** + * Returns a new PixelFormat object with the same properties as this PixelFormat and the new packed floating point value. + * If floating_point_packed is true, floating_point will be reset to false. + * + * @param floating_point_packed the new packed floating point value. + * + * @return the new PixelFormat + */ + public PixelFormat withFloatingPointPacked(final boolean floating_point_packed) { + final PixelFormat pf = new PixelFormat(this); + pf.floating_point_packed = floating_point_packed; + if ( floating_point_packed ) + pf.floating_point = false; + return pf; + } + + public boolean isSRGB() { + return sRGB; + } + + /** + * Returns a new PixelFormat object with the same properties as this PixelFormat and the new sRGB value. + * + * @param sRGB the new floating point value. + * + * @return the new PixelFormat + */ + public PixelFormat withSRGB(final boolean sRGB) { + final PixelFormat pf = new PixelFormat(this); + pf.sRGB = sRGB; + return pf; + } + +} diff --git a/src/de/verschwiegener/lwjgl3/display/Sync.java b/src/de/verschwiegener/lwjgl3/display/Sync.java new file mode 100644 index 0000000..d916160 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/display/Sync.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2002-2012 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.display; + +import org.lwjgl.glfw.GLFW; + +/** + * A highly accurate sync method that continually adapts to the system + * it runs on to provide reliable results. + * + * @author Riven + * @author kappaOne + */ +class Sync { + + /** number of nano seconds in a second */ + private static final long NANOS_IN_SECOND = 1000L * 1000L * 1000L; + + /** The time to sleep/yield until the next frame */ + private static long nextFrame = 0; + + /** whether the initialisation code has run */ + private static boolean initialised = false; + + /** for calculating the averages the previous sleep/yield times are stored */ + private static RunningAvg sleepDurations = new RunningAvg(10); + private static RunningAvg yieldDurations = new RunningAvg(10); + + + /** + * An accurate sync method that will attempt to run at a constant frame rate. + * It should be called once every frame. + * + * @param fps - the desired frame rate, in frames per second + */ + public static void sync(int fps) { + if (fps <= 0) return; + if (!initialised) initialise(); + try { + // sleep until the average sleep time is greater than the time remaining till nextFrame + for (long t0 = getTime(), t1; (nextFrame - t0) > sleepDurations.avg(); t0 = t1) { + Thread.sleep(1); + sleepDurations.add((t1 = getTime()) - t0); // update average sleep time + } + + // slowly dampen sleep average if too high to avoid yielding too much + sleepDurations.dampenForLowResTicker(); + + // yield until the average yield time is greater than the time remaining till nextFrame + for (long t0 = getTime(), t1; (nextFrame - t0) > yieldDurations.avg(); t0 = t1) { + Thread.yield(); + yieldDurations.add((t1 = getTime()) - t0); // update average yield time + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + + // schedule next frame, drop frame(s) if already too late for next frame + nextFrame = Math.max(nextFrame + NANOS_IN_SECOND / fps, getTime()); + } + + /** + * This method will initialise the sync method by setting initial + * values for sleepDurations/yieldDurations and nextFrame. + * + * If running on windows it will start the sleep timer fix. + */ + private static void initialise() { + initialised = true; + + sleepDurations.init(1000 * 1000); + yieldDurations.init((int) (-(getTime() - getTime()) * 1.333)); + + nextFrame = getTime(); + + String osName = System.getProperty("os.name"); + + if (osName.startsWith("Win")) { + // On windows the sleep functions can be highly inaccurate by + // over 10ms making in unusable. However it can be forced to + // be a bit more accurate by running a separate sleeping daemon + // thread. + Thread timerAccuracyThread = new Thread(new Runnable() { + public void run() { + try { + Thread.sleep(Long.MAX_VALUE); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + timerAccuracyThread.setName("LWJGL Timer"); + timerAccuracyThread.setDaemon(true); + timerAccuracyThread.start(); + } + } + + /** + * Get the system time in nano seconds + * + * @return will return the current time in nano's + */ + private static long getTime() { + return (long) ((GLFW.glfwGetTime() * NANOS_IN_SECOND)); + } + + private static class RunningAvg { + private final long[] slots; + private int offset; + + private static final long DAMPEN_THRESHOLD = 10 * 1000L * 1000L; // 10ms + private static final float DAMPEN_FACTOR = 0.9f; // don't change: 0.9f is exactly right! + + public RunningAvg(int slotCount) { + this.slots = new long[slotCount]; + this.offset = 0; + } + + public void init(long value) { + while (this.offset < this.slots.length) { + this.slots[this.offset++] = value; + } + } + + public void add(long value) { + this.slots[this.offset++ % this.slots.length] = value; + this.offset %= this.slots.length; + } + + public long avg() { + long sum = 0; + for (int i = 0; i < this.slots.length; i++) { + sum += this.slots[i]; + } + return sum / this.slots.length; + } + + public void dampenForLowResTicker() { + if (this.avg() > DAMPEN_THRESHOLD) { + for (int i = 0; i < this.slots.length; i++) { + this.slots[i] *= DAMPEN_FACTOR; + } + } + } + } +} \ No newline at end of file diff --git a/src/de/verschwiegener/lwjgl3/opengl/ARBDebugOutputCallback.java b/src/de/verschwiegener/lwjgl3/opengl/ARBDebugOutputCallback.java new file mode 100644 index 0000000..a09d4a8 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/opengl/ARBDebugOutputCallback.java @@ -0,0 +1,158 @@ +package de.verschwiegener.lwjgl3.opengl; + +public class ARBDebugOutputCallback extends PointerWrapperAbstract { + /** Severity levels. */ + private static final int + GL_DEBUG_SEVERITY_HIGH_ARB = 0x9146, + GL_DEBUG_SEVERITY_MEDIUM_ARB = 0x9147, + GL_DEBUG_SEVERITY_LOW_ARB = 0x9148; + + /** Sources. */ + private static final int + GL_DEBUG_SOURCE_API_ARB = 0x8246, + GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB = 0x8247, + GL_DEBUG_SOURCE_SHADER_COMPILER_ARB = 0x8248, + GL_DEBUG_SOURCE_THIRD_PARTY_ARB = 0x8249, + GL_DEBUG_SOURCE_APPLICATION_ARB = 0x824A, + GL_DEBUG_SOURCE_OTHER_ARB = 0x824B; + + /** Types. */ + private static final int + GL_DEBUG_TYPE_ERROR_ARB = 0x824C, + GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB = 0x824D, + GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB = 0x824E, + GL_DEBUG_TYPE_PORTABILITY_ARB = 0x824F, + GL_DEBUG_TYPE_PERFORMANCE_ARB = 0x8250, + GL_DEBUG_TYPE_OTHER_ARB = 0x8251; + + private static final long CALLBACK_POINTER; + + static { + long pointer = 0; + try { + // Call reflectively so that we can compile this class for the Generator. + pointer = (Long)Class.forName("org.lwjgl.opengl.CallbackUtil").getDeclaredMethod("getDebugOutputCallbackARB").invoke(null); + } catch (Exception e) { + // ignore + } + CALLBACK_POINTER = pointer; + } + + private final Handler handler; + + /** + * Creates an ARBDebugOutputCallback with a default callback handler. + * The default handler will simply print the message on System.err. + */ + public ARBDebugOutputCallback() { + this(new Handler() { + public void handleMessage(final int source, final int type, final int id, final int severity, final String message) { + System.err.println("[LWJGL] ARB_debug_output message"); + System.err.println("\tID: " + id); + + String description; + switch ( source ) { + case GL_DEBUG_SOURCE_API_ARB: + description = "API"; + break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: + description = "WINDOW SYSTEM"; + break; + case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: + description = "SHADER COMPILER"; + break; + case GL_DEBUG_SOURCE_THIRD_PARTY_ARB: + description = "THIRD PARTY"; + break; + case GL_DEBUG_SOURCE_APPLICATION_ARB: + description = "APPLICATION"; + break; + case GL_DEBUG_SOURCE_OTHER_ARB: + description = "OTHER"; + break; + default: + description = printUnknownToken(source); + } + System.err.println("\tSource: " + description); + + switch ( type ) { + case GL_DEBUG_TYPE_ERROR_ARB: + description = "ERROR"; + break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: + description = "DEPRECATED BEHAVIOR"; + break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: + description = "UNDEFINED BEHAVIOR"; + break; + case GL_DEBUG_TYPE_PORTABILITY_ARB: + description = "PORTABILITY"; + break; + case GL_DEBUG_TYPE_PERFORMANCE_ARB: + description = "PERFORMANCE"; + break; + case GL_DEBUG_TYPE_OTHER_ARB: + description = "OTHER"; + break; + default: + description = printUnknownToken(type); + } + System.err.println("\tType: " + description); + + switch ( severity ) { + case GL_DEBUG_SEVERITY_HIGH_ARB: + description = "HIGH"; + break; + case GL_DEBUG_SEVERITY_MEDIUM_ARB: + description = "MEDIUM"; + break; + case GL_DEBUG_SEVERITY_LOW_ARB: + description = "LOW"; + break; + default: + description = printUnknownToken(severity); + } + System.err.println("\tSeverity: " + description); + + System.err.println("\tMessage: " + message); + } + + private String printUnknownToken(final int token) { + return "Unknown (0x" + Integer.toHexString(token).toUpperCase() + ")"; + } + }); + } + + /** + * Creates an ARBDebugOutputCallback with the specified callback handler. + * The handler's {@code handleMessage} method will be called whenever + * debug output is generated by the GL. + * + * @param handler the callback handler + */ + public ARBDebugOutputCallback(final Handler handler) { + super(CALLBACK_POINTER); + + this.handler = handler; + } + + Handler getHandler() { + return handler; + } + + /** Implementations of this interface can be used to receive ARB_debug_output notifications. */ + public interface Handler { + + /** + * This method will be called when an ARB_debug_output message is generated. + * + * @param source the message source + * @param type the message type + * @param id the message ID + * @param severity the message severity + * @param message the string representation of the message. + */ + void handleMessage(int source, int type, int id, int severity, String message); + + } +} diff --git a/src/de/verschwiegener/lwjgl3/opengl/GLContext.java b/src/de/verschwiegener/lwjgl3/opengl/GLContext.java new file mode 100644 index 0000000..2b5153b --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/opengl/GLContext.java @@ -0,0 +1,11 @@ +package de.verschwiegener.lwjgl3.opengl; + +import org.lwjgl.opengl.GL; +import org.lwjgl.opengl.GLCapabilities; + +public class GLContext { + + public static GLCapabilities getCapabilities(){ + return GL.getCapabilities(); + } +} diff --git a/src/de/verschwiegener/lwjgl3/opengl/PointerWrapper.java b/src/de/verschwiegener/lwjgl3/opengl/PointerWrapper.java new file mode 100644 index 0000000..7e937ff --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/opengl/PointerWrapper.java @@ -0,0 +1,5 @@ +package de.verschwiegener.lwjgl3.opengl; + +public interface PointerWrapper { + long getPointer(); +} diff --git a/src/de/verschwiegener/lwjgl3/opengl/PointerWrapperAbstract.java b/src/de/verschwiegener/lwjgl3/opengl/PointerWrapperAbstract.java new file mode 100644 index 0000000..e99b701 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/opengl/PointerWrapperAbstract.java @@ -0,0 +1,58 @@ +package de.verschwiegener.lwjgl3.opengl; + + +import de.verschwiegener.lwjgl3.LWJGLUtil; + +public class PointerWrapperAbstract implements PointerWrapper { + protected final long pointer; + + protected PointerWrapperAbstract(final long pointer) { + this.pointer = pointer; + } + + /** + * Returns true if this object represents a valid pointer. + * The pointer might be invalid because it is NULL or because + * some other action has deleted the object that this pointer + * represents. + * + * @return true if the pointer is valid + */ + public boolean isValid() { + return pointer != 0; + } + + /** + * Checks if the pointer is valid and throws an IllegalStateException if + * it is not. This method is a NO-OP, unless the org.lwjgl.util.Debug + * property has been set to true. + */ + public final void checkValid() { + if ( LWJGLUtil.DEBUG && !isValid() ) + throw new IllegalStateException("This " + getClass().getSimpleName() + " pointer is not valid."); + } + + public final long getPointer() { + checkValid(); + return pointer; + } + + public boolean equals(final Object o) { + if ( this == o ) return true; + if ( !(o instanceof PointerWrapperAbstract) ) return false; + + final PointerWrapperAbstract that = (PointerWrapperAbstract)o; + + if ( pointer != that.pointer ) return false; + + return true; + } + + public int hashCode() { + return (int)(pointer ^ (pointer >>> 32)); + } + + public String toString() { + return getClass().getSimpleName() + " pointer (0x" + Long.toHexString(pointer).toUpperCase() + ")"; + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/Color.java b/src/de/verschwiegener/lwjgl3/util/Color.java new file mode 100644 index 0000000..a807d01 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/Color.java @@ -0,0 +1,494 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util; +import java.io.Serializable; +import java.nio.ByteBuffer; + +/** + * A mutable Color class + * @author $Author$ + * @version $Revision$ + * $Id$ + */ +public final class Color implements ReadableColor, Serializable, WritableColor { + + static final long serialVersionUID = 1L; + + /** Color components, publicly accessible */ + private byte red, green, blue, alpha; + + /** + * Constructor for Color. + */ + public Color() { + this(0, 0, 0, 255); + } + + /** + * Constructor for Color. Alpha defaults to 255. + */ + public Color(int r, int g, int b) { + this(r, g, b, 255); + } + + /** + * Constructor for Color. Alpha defaults to 255. + */ + public Color(byte r, byte g, byte b) { + this(r, g, b, (byte) 255); + } + + /** + * Constructor for Color. + */ + public Color(int r, int g, int b, int a) { + set(r, g, b, a); + } + + /** + * Constructor for Color. + */ + public Color(byte r, byte g, byte b, byte a) { + set(r, g, b, a); + } + + /** + * Constructor for Color + */ + public Color(ReadableColor c) { + setColor(c); + } + + /** + * Set a color + */ + public void set(int r, int g, int b, int a) { + red = (byte) r; + green = (byte) g; + blue = (byte) b; + alpha = (byte) a; + } + + /** + * Set a color + */ + public void set(byte r, byte g, byte b, byte a) { + this.red = r; + this.green = g; + this.blue = b; + this.alpha = a; + } + + /** + * Set a color + */ + public void set(int r, int g, int b) { + set(r, g, b, 255); + } + + /** + * Set a color + */ + public void set(byte r, byte g, byte b) { + set(r, g, b, (byte) 255); + } + + /** + * Accessor + */ + public int getRed() { + return red & 0xFF; + } + + /** + * Accessor + */ + public int getGreen() { + return green & 0xFF; + } + + /** + * Accessor + */ + public int getBlue() { + return blue & 0xFF; + } + + /** + * Accessor + */ + public int getAlpha() { + return alpha & 0xFF; + } + + /** + * Set the Red component + */ + public void setRed(int red) { + this.red = (byte) red; + } + + /** + * Set the Green component + */ + public void setGreen(int green) { + this.green = (byte) green; + } + + /** + * Set the Blue component + */ + public void setBlue(int blue) { + this.blue = (byte) blue; + } + + /** + * Set the Alpha component + */ + public void setAlpha(int alpha) { + this.alpha = (byte) alpha; + } + + /** + * Set the Red component + */ + public void setRed(byte red) { + this.red = red; + } + + /** + * Set the Green component + */ + public void setGreen(byte green) { + this.green = green; + } + + /** + * Set the Blue component + */ + public void setBlue(byte blue) { + this.blue = blue; + } + + /** + * Set the Alpha component + */ + public void setAlpha(byte alpha) { + this.alpha = alpha; + } + + /** + * Stringify + */ + public String toString() { + return "Color [" + getRed() + ", " + getGreen() + ", " + getBlue() + ", " + getAlpha() + "]"; + } + + /** + * Equals + */ + public boolean equals(Object o) { + return (o != null) + && (o instanceof ReadableColor) + && (((ReadableColor) o).getRed() == this.getRed()) + && (((ReadableColor) o).getGreen() == this.getGreen()) + && (((ReadableColor) o).getBlue() == this.getBlue()) + && (((ReadableColor) o).getAlpha() == this.getAlpha()); + } + + /** + * Hashcode + */ + public int hashCode() { + return (red << 24) | (green << 16) | (blue << 8) | alpha; + } + + /* (Overrides) + * @see com.shavenpuppy.jglib.ReadableColor#getAlphaByte() + */ + public byte getAlphaByte() { + return alpha; + } + + /* (Overrides) + * @see com.shavenpuppy.jglib.ReadableColor#getBlueByte() + */ + public byte getBlueByte() { + return blue; + } + + /* (Overrides) + * @see com.shavenpuppy.jglib.ReadableColor#getGreenByte() + */ + public byte getGreenByte() { + return green; + } + + /* (Overrides) + * @see com.shavenpuppy.jglib.ReadableColor#getRedByte() + */ + public byte getRedByte() { + return red; + } + + /* (Overrides) + * @see com.shavenpuppy.jglib.ReadableColor#writeRGBA(java.nio.ByteBuffer) + */ + public void writeRGBA(ByteBuffer dest) { + dest.put(red); + dest.put(green); + dest.put(blue); + dest.put(alpha); + } + + /* (Overrides) + * @see com.shavenpuppy.jglib.ReadableColor#writeRGB(java.nio.ByteBuffer) + */ + public void writeRGB(ByteBuffer dest) { + dest.put(red); + dest.put(green); + dest.put(blue); + } + + /* (Overrides) + * @see com.shavenpuppy.jglib.ReadableColor#writeABGR(java.nio.ByteBuffer) + */ + public void writeABGR(ByteBuffer dest) { + dest.put(alpha); + dest.put(blue); + dest.put(green); + dest.put(red); + } + + /* (Overrides) + * @see com.shavenpuppy.jglib.ReadableColor#writeARGB(java.nio.ByteBuffer) + */ + public void writeARGB(ByteBuffer dest) { + dest.put(alpha); + dest.put(red); + dest.put(green); + dest.put(blue); + } + + /* (Overrides) + * @see com.shavenpuppy.jglib.ReadableColor#writeBGR(java.nio.ByteBuffer) + */ + public void writeBGR(ByteBuffer dest) { + dest.put(blue); + dest.put(green); + dest.put(red); + } + + /* (Overrides) + * @see com.shavenpuppy.jglib.ReadableColor#writeBGRA(java.nio.ByteBuffer) + */ + public void writeBGRA(ByteBuffer dest) { + dest.put(blue); + dest.put(green); + dest.put(red); + dest.put(alpha); + } + + /** + * Read a color from a byte buffer + * @param src The source buffer + */ + public void readRGBA(ByteBuffer src) { + red = src.get(); + green = src.get(); + blue = src.get(); + alpha = src.get(); + } + + /** + * Read a color from a byte buffer + * @param src The source buffer + */ + public void readRGB(ByteBuffer src) { + red = src.get(); + green = src.get(); + blue = src.get(); + } + + /** + * Read a color from a byte buffer + * @param src The source buffer + */ + public void readARGB(ByteBuffer src) { + alpha = src.get(); + red = src.get(); + green = src.get(); + blue = src.get(); + } + + /** + * Read a color from a byte buffer + * @param src The source buffer + */ + public void readBGRA(ByteBuffer src) { + blue = src.get(); + green = src.get(); + red = src.get(); + alpha = src.get(); + } + + /** + * Read a color from a byte buffer + * @param src The source buffer + */ + public void readBGR(ByteBuffer src) { + blue = src.get(); + green = src.get(); + red = src.get(); + } + + /** + * Read a color from a byte buffer + * @param src The source buffer + */ + public void readABGR(ByteBuffer src) { + alpha = src.get(); + blue = src.get(); + green = src.get(); + red = src.get(); + } + + /** + * Set this color's color by copying another color + * @param src The source color + */ + public void setColor(ReadableColor src) { + red = src.getRedByte(); + green = src.getGreenByte(); + blue = src.getBlueByte(); + alpha = src.getAlphaByte(); + } + + /** + * HSB to RGB conversion, pinched from java.awt.Color. + * @param hue (0..1.0f) + * @param saturation (0..1.0f) + * @param brightness (0..1.0f) + */ + public void fromHSB(float hue, float saturation, float brightness) { + if (saturation == 0.0F) { + red = green = blue = (byte) (brightness * 255F + 0.5F); + } else { + float f3 = (hue - (float) Math.floor(hue)) * 6F; + float f4 = f3 - (float) Math.floor(f3); + float f5 = brightness * (1.0F - saturation); + float f6 = brightness * (1.0F - saturation * f4); + float f7 = brightness * (1.0F - saturation * (1.0F - f4)); + switch ((int) f3) { + case 0 : + red = (byte) (brightness * 255F + 0.5F); + green = (byte) (f7 * 255F + 0.5F); + blue = (byte) (f5 * 255F + 0.5F); + break; + case 1 : + red = (byte) (f6 * 255F + 0.5F); + green = (byte) (brightness * 255F + 0.5F); + blue = (byte) (f5 * 255F + 0.5F); + break; + case 2 : + red = (byte) (f5 * 255F + 0.5F); + green = (byte) (brightness * 255F + 0.5F); + blue = (byte) (f7 * 255F + 0.5F); + break; + case 3 : + red = (byte) (f5 * 255F + 0.5F); + green = (byte) (f6 * 255F + 0.5F); + blue = (byte) (brightness * 255F + 0.5F); + break; + case 4 : + red = (byte) (f7 * 255F + 0.5F); + green = (byte) (f5 * 255F + 0.5F); + blue = (byte) (brightness * 255F + 0.5F); + break; + case 5 : + red = (byte) (brightness * 255F + 0.5F); + green = (byte) (f5 * 255F + 0.5F); + blue = (byte) (f6 * 255F + 0.5F); + break; + } + } + } + + /** + * RGB to HSB conversion, pinched from java.awt.Color. + * The HSB value is returned in dest[] if dest[] is supplied. + * Values range from 0..1 + * @param dest Destination floats, or null + * @return dest, or a new float array + */ + public float[] toHSB(float dest[]) { + int r = getRed(); + int g = getGreen(); + int b = getBlue(); + if (dest == null) + dest = new float[3]; + int l = r <= g ? g : r; + if (b > l) + l = b; + int i1 = r >= g ? g : r; + if (b < i1) + i1 = b; + float brightness = l / 255F; + float saturation; + if (l != 0) + saturation = (float) (l - i1) / (float) l; + else + saturation = 0.0F; + float hue; + if (saturation == 0.0F) { + hue = 0.0F; + } else { + float f3 = (float) (l - r) / (float) (l - i1); + float f4 = (float) (l - g) / (float) (l - i1); + float f5 = (float) (l - b) / (float) (l - i1); + if (r == l) + hue = f5 - f4; + else if (g == l) + hue = (2.0F + f3) - f5; + else + hue = (4F + f4) - f3; + hue /= 6F; + if (hue < 0.0F) + hue++; + } + dest[0] = hue; + dest[1] = saturation; + dest[2] = brightness; + return dest; + } + +} diff --git a/src/de/verschwiegener/lwjgl3/util/Dimension.java b/src/de/verschwiegener/lwjgl3/util/Dimension.java new file mode 100644 index 0000000..7e8b7f3 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/Dimension.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util; + +import java.io.Serializable; + +/** + * A 2D integer Dimension class, which looks remarkably like an AWT one. + * @author $Author$ + * @version $Revision$ + * $Id$ + */ +public final class Dimension implements Serializable, ReadableDimension, WritableDimension { + + static final long serialVersionUID = 1L; + + /** The dimensions! */ + private int width, height; + + /** + * Constructor for Dimension. + */ + public Dimension() { + super(); + } + + /** + * Constructor for Dimension. + */ + public Dimension(int w, int h) { + this.width = w; + this.height = h; + } + + /** + * Constructor for Dimension. + */ + public Dimension(ReadableDimension d) { + setSize(d); + } + + public void setSize(int w, int h) { + this.width = w; + this.height = h; + } + + public void setSize(ReadableDimension d) { + this.width = d.getWidth(); + this.height = d.getHeight(); + } + + /* (Overrides) + * @see com.shavenpuppy.jglib.ReadableDimension#getSize(com.shavenpuppy.jglib.Dimension) + */ + public void getSize(WritableDimension dest) { + dest.setSize(this); + } + + /** + * Checks whether two dimension objects have equal values. + */ + public boolean equals(Object obj) { + if (obj instanceof ReadableDimension) { + ReadableDimension d = (ReadableDimension) obj; + return (width == d.getWidth()) && (height == d.getHeight()); + } + return false; + } + + /** + * Returns the hash code for this Dimension. + * + * @return a hash code for this Dimension + */ + public int hashCode() { + int sum = width + height; + return sum * (sum + 1) / 2 + width; + } + + /** + * Returns a string representation of the values of this + * Dimension object's height and + * width fields. This method is intended to be used only + * for debugging purposes, and the content and format of the returned + * string may vary between implementations. The returned string may be + * empty but may not be null. + * + * @return a string representation of this Dimension + * object + */ + public String toString() { + return getClass().getName() + "[width=" + width + ",height=" + height + "]"; + } + + /** + * Gets the height. + * @return Returns a int + */ + public int getHeight() { + return height; + } + + /** + * Sets the height. + * @param height The height to set + */ + public void setHeight(int height) { + this.height = height; + } + + /** + * Gets the width. + * @return Returns a int + */ + public int getWidth() { + return width; + } + + /** + * Sets the width. + * @param width The width to set + */ + public void setWidth(int width) { + this.width = width; + } + +} diff --git a/src/de/verschwiegener/lwjgl3/util/IOUtil.java b/src/de/verschwiegener/lwjgl3/util/IOUtil.java new file mode 100644 index 0000000..1f2a08d --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/IOUtil.java @@ -0,0 +1,68 @@ +package de.verschwiegener.lwjgl3.util; + +import org.lwjgl.*; + +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import java.nio.file.*; + +import static org.lwjgl.BufferUtils.*; +import static org.lwjgl.system.MemoryUtil.*; + +public class IOUtil { + + private IOUtil() { + } + + private static ByteBuffer resizeBuffer(ByteBuffer buffer, int newCapacity) { + ByteBuffer newBuffer = BufferUtils.createByteBuffer(newCapacity); + buffer.flip(); + newBuffer.put(buffer); + return newBuffer; + } + + /** + * Reads the specified resource and returns the raw data as a ByteBuffer. + * + * @param resource the resource to read + * @param bufferSize the initial buffer size + * + * @return the resource data + * + * @throws IOException if an IO error occurs + */ + public static ByteBuffer ioResourceToByteBuffer(String resource, int bufferSize) throws IOException { + ByteBuffer buffer; + + Path path = Paths.get(resource); + if (Files.isReadable(path)) { + try (SeekableByteChannel fc = Files.newByteChannel(path)) { + buffer = BufferUtils.createByteBuffer((int)fc.size() + 1); + while (fc.read(buffer) != -1) { + ; + } + } + } else { + try ( + InputStream source = IOUtil.class.getClassLoader().getResourceAsStream(resource); + ReadableByteChannel rbc = Channels.newChannel(source) + ) { + buffer = createByteBuffer(bufferSize); + + while (true) { + int bytes = rbc.read(buffer); + if (bytes == -1) { + break; + } + if (buffer.remaining() == 0) { + buffer = resizeBuffer(buffer, buffer.capacity() * 3 / 2); // 50% + } + } + } + } + + buffer.flip(); + return memSlice(buffer); + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/Point.java b/src/de/verschwiegener/lwjgl3/util/Point.java new file mode 100644 index 0000000..0c8bb29 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/Point.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util; + +import java.io.Serializable; + +/** + * A 2D integer point class, which looks remarkably like an AWT one. + * @author $Author$ + * @version $Revision$ + * $Id$ + */ +public final class Point implements ReadablePoint, WritablePoint, Serializable { + + static final long serialVersionUID = 1L; + + /** The location */ + private int x, y; + + /** + * Constructor for Point. + */ + public Point() { + super(); + } + + /** + * Constructor for Point. + */ + public Point(int x, int y) { + setLocation(x, y); + } + + /** + * Constructor for Point. + */ + public Point(ReadablePoint p) { + setLocation(p); + } + + public void setLocation(int x, int y) { + this.x = x; + this.y = y; + } + + public void setLocation(ReadablePoint p) { + this.x = p.getX(); + this.y = p.getY(); + } + + public void setX(int x) { + this.x = x; + } + + public void setY(int y) { + this.y = y; + } + + /** + * Translate a point. + * @param dx The translation to apply + * @param dy The translation to apply + */ + public void translate(int dx, int dy) { + this.x += dx; + this.y += dy; + } + + /** + * Translate a point. + * @param p The translation to apply + */ + public void translate(ReadablePoint p) { + this.x += p.getX(); + this.y += p.getY(); + } + + /** + * Un-translate a point. + * @param p The translation to apply + */ + public void untranslate(ReadablePoint p) { + this.x -= p.getX(); + this.y -= p.getY(); + } + + /** + * Determines whether an instance of Point2D is equal + * to this point. Two instances of Point2D are equal if + * the values of their x and y member + * fields, representing their position in the coordinate space, are + * the same. + * @param obj an object to be compared with this point + * @return true if the object to be compared is + * an instance of Point and has + * the same values; false otherwise + */ + public boolean equals(Object obj) { + if (obj instanceof Point) { + Point pt = (Point) obj; + return (x == pt.x) && (y == pt.y); + } + return super.equals(obj); + } + + /** + * Returns a string representation of this point and its location + * in the (xy) coordinate space. This method is + * intended to be used only for debugging purposes, and the content + * and format of the returned string may vary between implementations. + * The returned string may be empty but may not be null. + * + * @return a string representation of this point + */ + public String toString() { + return getClass().getName() + "[x=" + x + ",y=" + y + "]"; + } + + /** + * Returns the hash code for this Point. + * + * @return a hash code for this Point + */ + public int hashCode() { + int sum = x + y; + return sum * (sum + 1) / 2 + x; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public void getLocation(WritablePoint dest) { + dest.setLocation(x, y); + } + +} diff --git a/src/de/verschwiegener/lwjgl3/util/ReadableColor.java b/src/de/verschwiegener/lwjgl3/util/ReadableColor.java new file mode 100644 index 0000000..8da4930 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/ReadableColor.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util; + +import java.nio.ByteBuffer; + +/** + * Readonly interface for Colors + * @author $Author$ + * @version $Revision$ + * $Id$ + */ +public interface ReadableColor { + + /** + * Return the red component (0..255) + * @return int + */ + int getRed(); + + /** + * Return the red component (0..255) + * @return int + */ + int getGreen(); + + /** + * Return the red component (0..255) + * @return int + */ + int getBlue(); + + /** + * Return the red component (0..255) + * @return int + */ + int getAlpha(); + + /** + * Return the red component + * @return int + */ + byte getRedByte(); + + /** + * Return the red component + * @return int + */ + byte getGreenByte(); + + /** + * Return the red component + * @return int + */ + byte getBlueByte(); + + /** + * Return the red component + * @return int + */ + byte getAlphaByte(); + + /** + * Write the RGBA color directly out to a ByteBuffer + * @param dest the buffer to write to + */ + void writeRGBA(ByteBuffer dest); + + /** + * Write the RGB color directly out to a ByteBuffer + * @param dest the buffer to write to + */ + void writeRGB(ByteBuffer dest); + + /** + * Write the ABGR color directly out to a ByteBuffer + * @param dest the buffer to write to + */ + void writeABGR(ByteBuffer dest); + + /** + * Write the BGR color directly out to a ByteBuffer + * @param dest the buffer to write to + */ + void writeBGR(ByteBuffer dest); + + /** + * Write the BGRA color directly out to a ByteBuffer + * @param dest the buffer to write to + */ + void writeBGRA(ByteBuffer dest); + + /** + * Write the ARGB color directly out to a ByteBuffer + * @param dest the buffer to write to + */ + void writeARGB(ByteBuffer dest); + + /* + * Some standard colors + */ + ReadableColor RED = new Color(255, 0, 0); + ReadableColor ORANGE = new Color(255, 128, 0); + ReadableColor YELLOW = new Color(255, 255, 0); + ReadableColor GREEN = new Color(0, 255, 0); + ReadableColor CYAN = new Color(0, 255, 255); + ReadableColor BLUE = new Color(0, 0, 255); + ReadableColor PURPLE = new Color(255, 0, 255); + ReadableColor WHITE = new Color(255, 255, 255); + ReadableColor BLACK = new Color(0, 0, 0); + ReadableColor LTGREY = new Color(192, 192, 192); + ReadableColor DKGREY = new Color(64, 64, 64); + ReadableColor GREY = new Color(128, 128, 128); + + + +} diff --git a/src/de/verschwiegener/lwjgl3/util/ReadableDimension.java b/src/de/verschwiegener/lwjgl3/util/ReadableDimension.java new file mode 100644 index 0000000..1978006 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/ReadableDimension.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util; + +/** + * Readonly interface for Dimensions + * @author $Author$ + * @version $Revision$ + * $Id$ + */ +public interface ReadableDimension { + + /** + * Get the width + * @return int + */ + int getWidth(); + + /** + * Get the height + * @return int + */ + int getHeight(); + + /** + * Copy this ReadableDimension into a destination Dimension + * @param dest The destination + */ + void getSize(WritableDimension dest); + +} diff --git a/src/de/verschwiegener/lwjgl3/util/ReadablePoint.java b/src/de/verschwiegener/lwjgl3/util/ReadablePoint.java new file mode 100644 index 0000000..a09f00e --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/ReadablePoint.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util; + +/** + * Readonly interface for Points + * @author $Author$ + * @version $Revision$ + * $Id$ + */ +public interface ReadablePoint { + + /** + * @return int + */ + int getX(); + + /** + * @return int + */ + int getY(); + + /** + * Copy this ReadablePoint into a destination Point + * @param dest The destination Point, or null, to create a new Point + */ + void getLocation(WritablePoint dest); +} diff --git a/src/de/verschwiegener/lwjgl3/util/ReadableRectangle.java b/src/de/verschwiegener/lwjgl3/util/ReadableRectangle.java new file mode 100644 index 0000000..7e52d1f --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/ReadableRectangle.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util; + +/** + * Readonly interface for Rectangles + * @author $Author$ + * @version $Revision$ + * $Id$ + */ +public interface ReadableRectangle extends ReadableDimension, ReadablePoint { + + /** + * Copy this readable rectangle's bounds into a destination Rectangle + * @param dest The destination Rectangle, or null, to create a new Rectangle + */ + void getBounds(WritableRectangle dest); + +} diff --git a/src/de/verschwiegener/lwjgl3/util/Rectangle.java b/src/de/verschwiegener/lwjgl3/util/Rectangle.java new file mode 100644 index 0000000..2ff576b --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/Rectangle.java @@ -0,0 +1,581 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util; + +import java.io.Serializable; + +/** + * A 2D integer Rectangle class which looks remarkably like an AWT one. + * @author $Author$ + * @version $Revision$ + * $Id$ + */ +public final class Rectangle implements ReadableRectangle, WritableRectangle, Serializable { + + static final long serialVersionUID = 1L; + + /** Rectangle's bounds */ + private int x, y, width, height; + + /** + * Constructor for Rectangle. + */ + public Rectangle() { + super(); + } + /** + * Constructor for Rectangle. + */ + public Rectangle(int x, int y, int w, int h) { + this.x = x; + this.y = y; + this.width = w; + this.height = h; + } + /** + * Constructor for Rectangle. + */ + public Rectangle(ReadablePoint p, ReadableDimension d) { + x = p.getX(); + y = p.getY(); + width = d.getWidth(); + height = d.getHeight(); + } + /** + * Constructor for Rectangle. + */ + public Rectangle(ReadableRectangle r) { + x = r.getX(); + y = r.getY(); + width = r.getWidth(); + height = r.getHeight(); + } + + public void setLocation(int x, int y) { + this.x = x; + this.y = y; + } + + public void setLocation(ReadablePoint p) { + this.x = p.getX(); + this.y = p.getY(); + } + + public void setSize(int w, int h) { + this.width = w; + this.height = h; + } + + public void setSize(ReadableDimension d) { + this.width = d.getWidth(); + this.height = d.getHeight(); + } + + public void setBounds(int x, int y, int w, int h) { + this.x = x; + this.y = y; + this.width = w; + this.height = h; + } + + public void setBounds(ReadablePoint p, ReadableDimension d) { + x = p.getX(); + y = p.getY(); + width = d.getWidth(); + height = d.getHeight(); + } + + public void setBounds(ReadableRectangle r) { + x = r.getX(); + y = r.getY(); + width = r.getWidth(); + height = r.getHeight(); + } + + /* (Overrides) + * @see com.shavenpuppy.jglib.ReadableRectangle#getBounds(com.shavenpuppy.jglib.Rectangle) + */ + public void getBounds(WritableRectangle dest) { + dest.setBounds(x, y, width, height); + } + + /* (Overrides) + * @see com.shavenpuppy.jglib.ReadablePoint#getLocation(com.shavenpuppy.jglib.Point) + */ + public void getLocation(WritablePoint dest) { + dest.setLocation(x, y); + } + + /* (Overrides) + * @see com.shavenpuppy.jglib.ReadableDimension#getSize(com.shavenpuppy.jglib.Dimension) + */ + public void getSize(WritableDimension dest) { + dest.setSize(width, height); + } + + /** + * Translate the rectangle by an amount. + * @param x The translation amount on the x axis + * @param y The translation amount on the y axis + */ + public void translate(int x, int y) { + this.x += x; + this.y += y; + } + + /** + * Translate the rectangle by an amount. + * @param point The translation amount + */ + public void translate(ReadablePoint point) { + this.x += point.getX(); + this.y += point.getY(); + } + + /** + * Un-translate the rectangle by an amount. + * @param point The translation amount + */ + public void untranslate(ReadablePoint point) { + this.x -= point.getX(); + this.y -= point.getY(); + } + + /** + * Checks whether or not this Rectangle contains the + * specified Point. + * @param p the Point to test + * @return true if the Point + * (xy) is inside this + * Rectangle; + * false otherwise. + */ + public boolean contains(ReadablePoint p) { + return contains(p.getX(), p.getY()); + } + + /** + * Checks whether or not this Rectangle contains the + * point at the specified location + * (xy). + * @param X the specified x coordinate + * @param Y the specified y coordinate + * @return true if the point + * (xy) is inside this + * Rectangle; + * false otherwise. + */ + public boolean contains(int X, int Y) { + int w = this.width; + int h = this.height; + if ((w | h) < 0) { + // At least one of the dimensions is negative... + return false; + } + // Note: if either dimension is zero, tests below must return false... + int x = this.x; + int y = this.y; + if (X < x || Y < y) { + return false; + } + w += x; + h += y; + // overflow || intersect + return ((w < x || w > X) && (h < y || h > Y)); + } + + /** + * Checks whether or not this Rectangle entirely contains + * the specified Rectangle. + * @param r the specified Rectangle + * @return true if the Rectangle + * is contained entirely inside this Rectangle; + * false otherwise. + */ + public boolean contains(ReadableRectangle r) { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Checks whether this Rectangle entirely contains + * the Rectangle + * at the specified location (XY) with the + * specified dimensions (WH). + * @param X the specified x coordinate + * @param Y the specified y coordinate + * @param W the width of the Rectangle + * @param H the height of the Rectangle + * @return true if the Rectangle specified by + * (XYWH) + * is entirely enclosed inside this Rectangle; + * false otherwise. + */ + public boolean contains(int X, int Y, int W, int H) { + int w = this.width; + int h = this.height; + if ((w | h | W | H) < 0) { + // At least one of the dimensions is negative... + return false; + } + // Note: if any dimension is zero, tests below must return false... + int x = this.x; + int y = this.y; + if (X < x || Y < y) { + return false; + } + w += x; + W += X; + if (W <= X) { + // X+W overflowed or W was zero, return false if... + // either original w or W was zero or + // x+w did not overflow or + // the overflowed x+w is smaller than the overflowed X+W + if (w >= x || W > w) + return false; + } else { + // X+W did not overflow and W was not zero, return false if... + // original w was zero or + // x+w did not overflow and x+w is smaller than X+W + if (w >= x && W > w) + return false; + } + h += y; + H += Y; + if (H <= Y) { + if (h >= y || H > h) + return false; + } else { + if (h >= y && H > h) + return false; + } + return true; + } + + /** + * Determines whether or not this Rectangle and the specified + * Rectangle intersect. Two rectangles intersect if + * their intersection is nonempty. + * + * @param r the specified Rectangle + * @return true if the specified Rectangle + * and this Rectangle intersect; + * false otherwise. + */ + public boolean intersects(ReadableRectangle r) { + int tw = this.width; + int th = this.height; + int rw = r.getWidth(); + int rh = r.getHeight(); + if (rw <= 0 || rh <= 0 || tw <= 0 || th <= 0) { + return false; + } + int tx = this.x; + int ty = this.y; + int rx = r.getX(); + int ry = r.getY(); + rw += rx; + rh += ry; + tw += tx; + th += ty; + // overflow || intersect + return ((rw < rx || rw > tx) && (rh < ry || rh > ty) && (tw < tx || tw > rx) && (th < ty || th > ry)); + } + + /** + * Computes the intersection of this Rectangle with the + * specified Rectangle. Returns a new Rectangle + * that represents the intersection of the two rectangles. + * If the two rectangles do not intersect, the result will be + * an empty rectangle. + * + * @param r the specified Rectangle + * @return the largest Rectangle contained in both the + * specified Rectangle and in + * this Rectangle; or if the rectangles + * do not intersect, an empty rectangle. + */ + public Rectangle intersection(ReadableRectangle r, Rectangle dest) { + int tx1 = this.x; + int ty1 = this.y; + int rx1 = r.getX(); + int ry1 = r.getY(); + long tx2 = tx1; + tx2 += this.width; + long ty2 = ty1; + ty2 += this.height; + long rx2 = rx1; + rx2 += r.getWidth(); + long ry2 = ry1; + ry2 += r.getHeight(); + if (tx1 < rx1) + tx1 = rx1; + if (ty1 < ry1) + ty1 = ry1; + if (tx2 > rx2) + tx2 = rx2; + if (ty2 > ry2) + ty2 = ry2; + tx2 -= tx1; + ty2 -= ty1; + // tx2,ty2 will never overflow (they will never be + // larger than the smallest of the two source w,h) + // they might underflow, though... + if (tx2 < Integer.MIN_VALUE) + tx2 = Integer.MIN_VALUE; + if (ty2 < Integer.MIN_VALUE) + ty2 = Integer.MIN_VALUE; + if (dest == null) + dest = new Rectangle(tx1, ty1, (int) tx2, (int) ty2); + else + dest.setBounds(tx1, ty1, (int) tx2, (int) ty2); + return dest; + + } + + /** + * Computes the union of this Rectangle with the + * specified Rectangle. Returns a new + * Rectangle that + * represents the union of the two rectangles + * @param r the specified Rectangle + * @return the smallest Rectangle containing both + * the specified Rectangle and this + * Rectangle. + */ + public WritableRectangle union(ReadableRectangle r, WritableRectangle dest) { + int x1 = Math.min(x, r.getX()); + int x2 = Math.max(x + width, r.getX() + r.getWidth()); + int y1 = Math.min(y, r.getY()); + int y2 = Math.max(y + height, r.getY() + r.getHeight()); + dest.setBounds(x1, y1, x2 - x1, y2 - y1); + return dest; + } + + /** + * Adds a point, specified by the integer arguments newx + * and newy, to this Rectangle. The + * resulting Rectangle is + * the smallest Rectangle that contains both the + * original Rectangle and the specified point. + *

+ * After adding a point, a call to contains with the + * added point as an argument does not necessarily return + * true. The contains method does not + * return true for points on the right or bottom + * edges of a Rectangle. Therefore, if the added point + * falls on the right or bottom edge of the enlarged + * Rectangle, contains returns + * false for that point. + * @param newx the x coordinates of the new point + * @param newy the y coordinates of the new point + */ + public void add(int newx, int newy) { + int x1 = Math.min(x, newx); + int x2 = Math.max(x + width, newx); + int y1 = Math.min(y, newy); + int y2 = Math.max(y + height, newy); + x = x1; + y = y1; + width = x2 - x1; + height = y2 - y1; + } + + /** + * Adds the specified Point to this + * Rectangle. The resulting Rectangle + * is the smallest Rectangle that contains both the + * original Rectangle and the specified + * Point. + *

+ * After adding a Point, a call to contains + * with the added Point as an argument does not + * necessarily return true. The contains + * method does not return true for points on the right + * or bottom edges of a Rectangle. Therefore if the added + * Point falls on the right or bottom edge of the + * enlarged Rectangle, contains returns + * false for that Point. + * @param pt the new Point to add to this + * Rectangle + */ + public void add(ReadablePoint pt) { + add(pt.getX(), pt.getY()); + } + + /** + * Adds a Rectangle to this Rectangle. + * The resulting Rectangle is the union of the two + * rectangles. + * @param r the specified Rectangle + */ + public void add(ReadableRectangle r) { + int x1 = Math.min(x, r.getX()); + int x2 = Math.max(x + width, r.getX() + r.getWidth()); + int y1 = Math.min(y, r.getY()); + int y2 = Math.max(y + height, r.getY() + r.getHeight()); + x = x1; + y = y1; + width = x2 - x1; + height = y2 - y1; + } + + /** + * Resizes the Rectangle both horizontally and vertically. + *

+ * This method modifies the Rectangle so that it is + * h units larger on both the left and right side, + * and v units larger at both the top and bottom. + *

+ * The new Rectangle has (x - h, + * y - v) as its top-left corner, a + * width of + * width + 2h, + * and a height of + * height + 2v. + *

+ * If negative values are supplied for h and + * v, the size of the Rectangle + * decreases accordingly. + * The grow method does not check whether the resulting + * values of width and height are + * non-negative. + * @param h the horizontal expansion + * @param v the vertical expansion + */ + public void grow(int h, int v) { + x -= h; + y -= v; + width += h * 2; + height += v * 2; + } + + /** + * Determines whether or not this Rectangle is empty. A + * Rectangle is empty if its width or its height is less + * than or equal to zero. + * @return true if this Rectangle is empty; + * false otherwise. + */ + public boolean isEmpty() { + return (width <= 0) || (height <= 0); + } + /** + * Checks whether two rectangles are equal. + *

+ * The result is true if and only if the argument is not + * null and is a Rectangle object that has the + * same top-left corner, width, and height as this Rectangle. + * @param obj the Object to compare with + * this Rectangle + * @return true if the objects are equal; + * false otherwise. + */ + public boolean equals(Object obj) { + if (obj instanceof Rectangle) { + Rectangle r = (Rectangle) obj; + return ((x == r.x) && (y == r.y) && (width == r.width) && (height == r.height)); + } + return super.equals(obj); + } + + /** + * Debugging + * @return a String + */ + public String toString() { + return getClass().getName() + "[x=" + x + ",y=" + y + ",width=" + width + ",height=" + height + "]"; + } + /** + * Gets the height. + * @return Returns a int + */ + public int getHeight() { + return height; + } + + /** + * Sets the height. + * @param height The height to set + */ + public void setHeight(int height) { + this.height = height; + } + + /** + * Gets the width. + * @return Returns a int + */ + public int getWidth() { + return width; + } + + /** + * Sets the width. + * @param width The width to set + */ + public void setWidth(int width) { + this.width = width; + } + + /** + * Gets the x. + * @return Returns a int + */ + public int getX() { + return x; + } + + /** + * Sets the x. + * @param x The x to set + */ + public void setX(int x) { + this.x = x; + } + + /** + * Gets the y. + * @return Returns a int + */ + public int getY() { + return y; + } + + /** + * Sets the y. + * @param y The y to set + */ + public void setY(int y) { + this.y = y; + } + +} diff --git a/src/de/verschwiegener/lwjgl3/util/Renderable.java b/src/de/verschwiegener/lwjgl3/util/Renderable.java new file mode 100644 index 0000000..18dad98 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/Renderable.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util; + +/** + * + * Simple interface to things that can be Rendered. + * + * @author $Author$ + * @version $Revision$ + * $Id$ + */ +public interface Renderable { + + /** + * "Render" this thing. This will involve calls to the GL. + */ + void render(); + +} diff --git a/src/de/verschwiegener/lwjgl3/util/Timer.java b/src/de/verschwiegener/lwjgl3/util/Timer.java new file mode 100644 index 0000000..b1f713a --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/Timer.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util; + +import de.verschwiegener.lwjgl3.Sys; + +/** + * + * A hires timer. This measures time in seconds as floating point values. + * All Timers created are updated simultaneously by calling the static method + * tick(). This ensures that within a single iteration of a game loop that + * all timers are updated consistently with each other. + * + * @author cix_foo + * @version $Revision$ + * $Id$ + */ +public class Timer { + + // Record the timer resolution on classload + private static long resolution = Sys.getTimerResolution(); + + // Every so often we will re-query the timer resolution + private static final int QUERY_INTERVAL = 50; // in calls to tick() + private static int queryCount; + + // Globally keeps track of time for all instances of Timer + private static long currentTime; + + // When the timer was started + private long startTime; + + // The last time recorded by getTime() + private long lastTime; + + // Whether the timer is paused + private boolean paused; + + static { + tick(); + } + + /** + * Constructs a timer. The timer will be reset to 0.0 and resumed immediately. + */ + public Timer() { + reset(); + resume(); + } + + /** + * @return the time in seconds, as a float + */ + public float getTime() { + if (!paused) { + lastTime = currentTime - startTime; + } + + return (float) ((double) lastTime / (double) resolution); + } + /** + * @return whether this timer is paused + */ + public boolean isPaused() { + return paused; + } + + /** + * Pause the timer. Whilst paused the time will not change for this timer + * when tick() is called. + * + * @see #resume() + */ + public void pause() { + paused = true; + } + + /** + * Reset the timer. Equivalent to set(0.0f); + * @see #set(float) + */ + public void reset() { + set(0.0f); + } + + /** + * Resume the timer. + * @see #pause() + */ + public void resume() { + paused = false; + startTime = currentTime - lastTime; + } + + /** + * Set the time of this timer + * @param newTime the new time, in seconds + */ + public void set(float newTime) { + long newTimeInTicks = (long) ((double) newTime * (double) resolution); + startTime = currentTime - newTimeInTicks; + lastTime = newTimeInTicks; + } + + /** + * Get the next time update from the system's hires timer. This method should + * be called once per main loop iteration; all timers are updated simultaneously + * from it. + */ + public static void tick() { + currentTime = Sys.getTime(); + + // Periodically refresh the timer resolution: + queryCount ++; + if (queryCount > QUERY_INTERVAL) { + queryCount = 0; + resolution = Sys.getTimerResolution(); + } + } + + /** + * Debug output. + */ + public String toString() { + return "Timer[Time=" + getTime() + ", Paused=" + paused + "]"; + } +} \ No newline at end of file diff --git a/src/de/verschwiegener/lwjgl3/util/WritableColor.java b/src/de/verschwiegener/lwjgl3/util/WritableColor.java new file mode 100644 index 0000000..4615532 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/WritableColor.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util; + +import java.nio.ByteBuffer; + +/** + * Write interface for Colors + * @author $Author$ + * @version $Revision$ + * $Id$ + */ +public interface WritableColor { + /** + * Set a color + */ + void set(int r, int g, int b, int a); + /** + * Set a color + */ + void set(byte r, byte g, byte b, byte a); + /** + * Set a color + */ + void set(int r, int g, int b); + /** + * Set a color + */ + void set(byte r, byte g, byte b); + /** + * Set the Red component + */ + void setRed(int red); + /** + * Set the Green component + */ + void setGreen(int green); + /** + * Set the Blue component + */ + void setBlue(int blue); + /** + * Set the Alpha component + */ + void setAlpha(int alpha); + /** + * Set the Red component + */ + void setRed(byte red); + /** + * Set the Green component + */ + void setGreen(byte green); + /** + * Set the Blue component + */ + void setBlue(byte blue); + /** + * Set the Alpha component + */ + void setAlpha(byte alpha); + /** + * Read a color from a byte buffer + * @param src The source buffer + */ + void readRGBA(ByteBuffer src); + /** + * Read a color from a byte buffer + * @param src The source buffer + */ + void readRGB(ByteBuffer src); + /** + * Read a color from a byte buffer + * @param src The source buffer + */ + void readARGB(ByteBuffer src); + /** + * Read a color from a byte buffer + * @param src The source buffer + */ + void readBGRA(ByteBuffer src); + /** + * Read a color from a byte buffer + * @param src The source buffer + */ + void readBGR(ByteBuffer src); + /** + * Read a color from a byte buffer + * @param src The source buffer + */ + void readABGR(ByteBuffer src); + /** + * Set this color's color by copying another color + * @param src The source color + */ + void setColor(ReadableColor src); +} \ No newline at end of file diff --git a/src/de/verschwiegener/lwjgl3/util/WritableDimension.java b/src/de/verschwiegener/lwjgl3/util/WritableDimension.java new file mode 100644 index 0000000..d69c24b --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/WritableDimension.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util; + +/** + * Write interface for Dimensions + * @author $Author$ + * @version $Revision$ + * $Id$ + + */ +public interface WritableDimension { + void setSize(int w, int h); + void setSize(ReadableDimension d); + /** + * Sets the height. + * @param height The height to set + */ + void setHeight(int height); + /** + * Sets the width. + * @param width The width to set + */ + void setWidth(int width); +} \ No newline at end of file diff --git a/src/de/verschwiegener/lwjgl3/util/WritablePoint.java b/src/de/verschwiegener/lwjgl3/util/WritablePoint.java new file mode 100644 index 0000000..795f4e2 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/WritablePoint.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util; + +/** + * Write interface for Points + * @author $Author$ + * @version $Revision$ + * $Id$ + */ +public interface WritablePoint { + void setLocation(int x, int y); + void setLocation(ReadablePoint p); + void setX(int x); + void setY(int y); +} \ No newline at end of file diff --git a/src/de/verschwiegener/lwjgl3/util/WritableRectangle.java b/src/de/verschwiegener/lwjgl3/util/WritableRectangle.java new file mode 100644 index 0000000..e311598 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/WritableRectangle.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util; + +/** + * Write interface for Rectangles + * @author $Author$ + * @version $Revision$ + * $Id$ + */ +public interface WritableRectangle extends WritablePoint, WritableDimension { + + /** + * Sets the bounds of the rectangle + * @param x Position of rectangle on x axis + * @param y Position of rectangle on y axis + * @param width Width of rectangle + * @param height Height of rectangle + */ + void setBounds(int x, int y, int width, int height); + + /** + * Sets the bounds of the rectangle + * @param location + * @param size + */ + void setBounds(ReadablePoint location, ReadableDimension size); + + /** + * Sets the bounds of the rectangle + * @param src + */ + void setBounds(ReadableRectangle src); +} diff --git a/src/de/verschwiegener/lwjgl3/util/XPMFile.java b/src/de/verschwiegener/lwjgl3/util/XPMFile.java new file mode 100644 index 0000000..60f46e5 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/XPMFile.java @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.util.HashMap; +import java.util.StringTokenizer; + +/** + *

+ * NOTE: This simple XPM reader does not support extensions nor hotspots + *

+ * + * @author Brian Matzon + * @author Jos Hirth + * @version $Revision$ + * $Id$ + */ + +public class XPMFile { + + /** Array of bytes (RGBA) */ + private byte bytes[]; + + private static final int WIDTH = 0; + + private static final int HEIGHT = 1; + + private static final int NUMBER_OF_COLORS = 2; + + private static final int CHARACTERS_PER_PIXEL = 3; + + private static int[] format = new int[4]; + + /* + * Private constructor, use load(String filename) + */ + private XPMFile() { + } + + /** + * Loads the XPM file + * + * @param file + * path to file + * @return XPMFile loaded, or exception + * @throws IOException + * If any IO exceptions occurs while reading file + */ + public static XPMFile load(String file) throws IOException { + return load(new FileInputStream(new File(file))); + } + + /** + * Loads the XPM file + * + * @param is + * InputStream to read file from + * @return XPMFile loaded, or exception + */ + public static XPMFile load(InputStream is) { + XPMFile xFile = new XPMFile(); + xFile.readImage(is); + return xFile; + } + + /** + * @return the height of the image. + */ + public int getHeight() { + return format[HEIGHT]; + } + + /** + * @return the width of the image. + */ + public int getWidth() { + return format[WIDTH]; + } + + /** + * @return The data of the image. + */ + public byte[] getBytes() { + return bytes; + } + + /** + * Read the image from the specified file. + */ + private void readImage(InputStream is) { + try { + LineNumberReader reader = new LineNumberReader( + new InputStreamReader(is)); + HashMap colors = new HashMap(); + + format = parseFormat(nextLineOfInterest(reader)); + + // setup color mapping + for (int i = 0; i < format[NUMBER_OF_COLORS]; i++) { + Object[] colorDefinition = parseColor(nextLineOfInterest(reader)); + colors.put((String)colorDefinition[0], (Integer)colorDefinition[1]); + } + + // read actual image (convert to RGBA) + bytes = new byte[format[WIDTH] * format[HEIGHT] * 4]; + for (int i = 0; i < format[HEIGHT]; i++) { + parseImageLine(nextLineOfInterest(reader), format, colors, i); + } + } catch (Exception e) { + e.printStackTrace(); + throw new IllegalArgumentException("Unable to parse XPM File"); + } + } + + /** + * Finds the next interesting line of text. + * + * @param reader + * The LineNumberReader to read from + * @return The next interesting String (with stripped quotes) + * @throws IOException + * If any IO exceptions occurs while reading file + */ + private static String nextLineOfInterest(LineNumberReader reader) + throws IOException { + String ret; + do { + ret = reader.readLine(); + } while (!ret.startsWith("\"")); + // lacks sanity check + return ret.substring(1, ret.lastIndexOf('\"')); + } + + /** + * Parses the format of the xpm file given a format string + * + * @param format + * String to parse + * @return Array specifying width, height, colors, characters per pixel + */ + private static int[] parseFormat(String format) { + // format should look like this: + // 16 16 122 2 + + // tokenize it + StringTokenizer st = new StringTokenizer(format); + + return new int[] { Integer.parseInt(st.nextToken()), /* width */ + Integer.parseInt(st.nextToken()), /* height */ + Integer.parseInt(st.nextToken()), /* colors */ + Integer.parseInt(st.nextToken()) /* chars per pixel */ + }; + } + + /** + * Given a line defining a color/pixel, parses this into an array containing + * a key and a color + * + * @param line + * Line to parse + * @return Array containing a key (String) and a color (Integer) + */ + private static Object[] parseColor(String line) { + // line should look like this: + // # c #0A0A0A + + // NOTE: will break if the color is something like "black" or "gray50" + // etc (instead of #rrggbb). + + String key = line.substring(0, format[CHARACTERS_PER_PIXEL]); + // since we always assume color as type we dont need to read it + // String type = line.substring(format[CHARACTERS_PER_PIXEL] + 1, + // format[CHARACTERS_PER_PIXEL] + 2); + String color = line.substring(format[CHARACTERS_PER_PIXEL] + 4); + + // we always assume type is color, and supplied as # + return new Object[] { key, Integer.parseInt(color, 16) }; + } + + /** + * Parses an Image line into its byte values + * + * @param line + * Line of chars to parse + * @param format + * Format to expext it in + * @param colors + * Colors to lookup + * @param index + * current index into lines, we've reached + */ + private void parseImageLine(String line, int[] format, HashMap colors, + int index) { + // offset for next line + int offset = index * 4 * format[WIDTH]; + + // read characters times, + // each iteration equals one pixel + for (int i = 0; i < format[WIDTH]; i++) { + String key = line + .substring( + i * format[CHARACTERS_PER_PIXEL], + (i * format[CHARACTERS_PER_PIXEL] + format[CHARACTERS_PER_PIXEL])); + int color = colors.get(key); + bytes[offset + (i * 4)] = (byte) ((color & 0x00ff0000) >> 16); + bytes[offset + ((i * 4) + 1)] = (byte) ((color & 0x0000ff00) >> 8); + bytes[offset + ((i * 4) + 2)] = (byte) ((color & 0x000000ff) >> 0); // looks + // better + // :) + bytes[offset + ((i * 4) + 3)] = (byte) 0xff; // always 0xff alpha + } + } + + /** + * @param args + */ + public static void main(String[] args) { + if (args.length != 1) { + System.out.println("usage:\nXPMFile "); + } + + try { + String out = args[0].substring(0, args[0].indexOf(".")) + ".raw"; + XPMFile file = XPMFile.load(args[0]); + BufferedOutputStream bos = new BufferedOutputStream( + new FileOutputStream(new File(out))); + bos.write(file.getBytes()); + bos.close(); + + // showResult(file.getBytes()); + } catch (Exception e) { + e.printStackTrace(); + } + } + /* + private static void showResult(byte[] bytes) { + final BufferedImage i = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); + int c = 0; + for (int y = 0; y < 16; y++) { + for (int x = 0; x < 16; x++) { + i.setRGB(x, y, (bytes[c] << 16) + (bytes[c + 1] << 8) + (bytes[c + 2] << 0) + (bytes[c + 3] << 24));//+(128<<24));// + c += 4; + } + } + + final Frame frame = new Frame("XPM Result"); + frame.add(new Canvas() { + + public void paint(Graphics g) { + g.drawImage(i, 0, 0, frame); + } + }); + + frame.addWindowListener(new WindowAdapter() { + + public void windowClosing(WindowEvent e) { + frame.dispose(); + } + + }); + + frame.setSize(100, 100); + frame.setVisible(true); + }*/ +} \ No newline at end of file diff --git a/src/de/verschwiegener/lwjgl3/util/glu/Cylinder.java b/src/de/verschwiegener/lwjgl3/util/glu/Cylinder.java new file mode 100644 index 0000000..e1666bd --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/Cylinder.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.glu; + +import static org.lwjgl.opengl.GL11.*; +import static de.verschwiegener.lwjgl3.util.glu.GLU.*; + +/** + * Cylinder.java + * + * + * Created 23-dec-2003 + * @author Erik Duijs + */ +public class Cylinder extends Quadric { + + /** + * Constructor for Cylinder. + */ + public Cylinder() { + super(); + } + + /** + * draws a cylinder oriented along the z axis. The base of the + * cylinder is placed at z = 0, and the top at z=height. Like a sphere, a + * cylinder is subdivided around the z axis into slices, and along the z axis + * into stacks. + * + * Note that if topRadius is set to zero, then this routine will generate a + * cone. + * + * If the orientation is set to GLU.OUTSIDE (with glu.quadricOrientation), then + * any generated normals point away from the z axis. Otherwise, they point + * toward the z axis. + * + * If texturing is turned on (with glu.quadricTexture), then texture + * coordinates are generated so that t ranges linearly from 0.0 at z = 0 to + * 1.0 at z = height, and s ranges from 0.0 at the +y axis, to 0.25 at the +x + * axis, to 0.5 at the -y axis, to 0.75 at the -x axis, and back to 1.0 at the + * +y axis. + * + * @param baseRadius Specifies the radius of the cylinder at z = 0. + * @param topRadius Specifies the radius of the cylinder at z = height. + * @param height Specifies the height of the cylinder. + * @param slices Specifies the number of subdivisions around the z axis. + * @param stacks Specifies the number of subdivisions along the z axis. + */ + public void draw(float baseRadius, float topRadius, float height, int slices, int stacks) { + + float da, r, dr, dz; + float x, y, z, nz, nsign; + int i, j; + + if (super.orientation == GLU_INSIDE) { + nsign = -1.0f; + } else { + nsign = 1.0f; + } + + da = 2.0f * PI / slices; + dr = (topRadius - baseRadius) / stacks; + dz = height / stacks; + nz = (baseRadius - topRadius) / height; + // Z component of normal vectors + + if (super.drawStyle == GLU_POINT) { + glBegin(GL_POINTS); + for (i = 0; i < slices; i++) { + x = cos((i * da)); + y = sin((i * da)); + normal3f(x * nsign, y * nsign, nz * nsign); + + z = 0.0f; + r = baseRadius; + for (j = 0; j <= stacks; j++) { + glVertex3f((x * r), (y * r), z); + z += dz; + r += dr; + } + } + glEnd(); + } else if (super.drawStyle == GLU_LINE || super.drawStyle == GLU_SILHOUETTE) { + // Draw rings + if (super.drawStyle == GLU_LINE) { + z = 0.0f; + r = baseRadius; + for (j = 0; j <= stacks; j++) { + glBegin(GL_LINE_LOOP); + for (i = 0; i < slices; i++) { + x = cos((i * da)); + y = sin((i * da)); + normal3f(x * nsign, y * nsign, nz * nsign); + glVertex3f((x * r), (y * r), z); + } + glEnd(); + z += dz; + r += dr; + } + } else { + // draw one ring at each end + if (baseRadius != 0.0) { + glBegin(GL_LINE_LOOP); + for (i = 0; i < slices; i++) { + x = cos((i * da)); + y = sin((i * da)); + normal3f(x * nsign, y * nsign, nz * nsign); + glVertex3f((x * baseRadius), (y * baseRadius), 0.0f); + } + glEnd(); + glBegin(GL_LINE_LOOP); + for (i = 0; i < slices; i++) { + x = cos((i * da)); + y = sin((i * da)); + normal3f(x * nsign, y * nsign, nz * nsign); + glVertex3f((x * topRadius), (y * topRadius), height); + } + glEnd(); + } + } + // draw length lines + glBegin(GL_LINES); + for (i = 0; i < slices; i++) { + x = cos((i * da)); + y = sin((i * da)); + normal3f(x * nsign, y * nsign, nz * nsign); + glVertex3f((x * baseRadius), (y * baseRadius), 0.0f); + glVertex3f((x * topRadius), (y * topRadius), (height)); + } + glEnd(); + } else if (super.drawStyle == GLU_FILL) { + float ds = 1.0f / slices; + float dt = 1.0f / stacks; + float t = 0.0f; + z = 0.0f; + r = baseRadius; + for (j = 0; j < stacks; j++) { + float s = 0.0f; + glBegin(GL_QUAD_STRIP); + for (i = 0; i <= slices; i++) { + if (i == slices) { + x = sin(0.0f); + y = cos(0.0f); + } else { + x = sin((i * da)); + y = cos((i * da)); + } + if (nsign == 1.0f) { + normal3f((x * nsign), (y * nsign), (nz * nsign)); + TXTR_COORD(s, t); + glVertex3f((x * r), (y * r), z); + normal3f((x * nsign), (y * nsign), (nz * nsign)); + TXTR_COORD(s, t + dt); + glVertex3f((x * (r + dr)), (y * (r + dr)), (z + dz)); + } else { + normal3f(x * nsign, y * nsign, nz * nsign); + TXTR_COORD(s, t); + glVertex3f((x * r), (y * r), z); + normal3f(x * nsign, y * nsign, nz * nsign); + TXTR_COORD(s, t + dt); + glVertex3f((x * (r + dr)), (y * (r + dr)), (z + dz)); + } + s += ds; + } // for slices + glEnd(); + r += dr; + t += dt; + z += dz; + } // for stacks + } + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/Disk.java b/src/de/verschwiegener/lwjgl3/util/glu/Disk.java new file mode 100644 index 0000000..7093762 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/Disk.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.glu; + +import static org.lwjgl.opengl.GL11.*; +import static de.verschwiegener.lwjgl3.util.glu.GLU.*; + +/** + * Disk.java + * + * + * Created 23-dec-2003 + * @author Erik Duijs + */ +public class Disk extends Quadric { + + /** + * Constructor for Disk. + */ + public Disk() { + super(); + } + + /** + * renders a disk on the z = 0 plane. The disk has a radius of + * outerRadius, and contains a concentric circular hole with a radius of + * innerRadius. If innerRadius is 0, then no hole is generated. The disk is + * subdivided around the z axis into slices (like pizza slices), and also + * about the z axis into rings (as specified by slices and loops, + * respectively). + * + * With respect to orientation, the +z side of the disk is considered to be + * "outside" (see glu.quadricOrientation). This means that if the orientation + * is set to GLU.OUTSIDE, then any normals generated point along the +z axis. + * Otherwise, they point along the -z axis. + * + * If texturing is turned on (with glu.quadricTexture), texture coordinates are + * generated linearly such that where r=outerRadius, the value at (r, 0, 0) is + * (1, 0.5), at (0, r, 0) it is (0.5, 1), at (-r, 0, 0) it is (0, 0.5), and at + * (0, -r, 0) it is (0.5, 0). + */ + public void draw(float innerRadius, float outerRadius, int slices, int loops) + { + float da, dr; + + /* Normal vectors */ + if (super.normals != GLU_NONE) { + if (super.orientation == GLU_OUTSIDE) { + glNormal3f(0.0f, 0.0f, +1.0f); + } + else { + glNormal3f(0.0f, 0.0f, -1.0f); + } + } + + da = 2.0f * PI / slices; + dr = (outerRadius - innerRadius) / loops; + + switch (super.drawStyle) { + case GLU_FILL: + { + /* texture of a gluDisk is a cut out of the texture unit square + * x, y in [-outerRadius, +outerRadius]; s, t in [0, 1] + * (linear mapping) + */ + float dtc = 2.0f * outerRadius; + float sa, ca; + float r1 = innerRadius; + int l; + for (l = 0; l < loops; l++) { + float r2 = r1 + dr; + if (super.orientation == GLU_OUTSIDE) { + int s; + glBegin(GL_QUAD_STRIP); + for (s = 0; s <= slices; s++) { + float a; + if (s == slices) + a = 0.0f; + else + a = s * da; + sa = sin(a); + ca = cos(a); + TXTR_COORD(0.5f + sa * r2 / dtc, 0.5f + ca * r2 / dtc); + glVertex2f(r2 * sa, r2 * ca); + TXTR_COORD(0.5f + sa * r1 / dtc, 0.5f + ca * r1 / dtc); + glVertex2f(r1 * sa, r1 * ca); + } + glEnd(); + } + else { + int s; + glBegin(GL_QUAD_STRIP); + for (s = slices; s >= 0; s--) { + float a; + if (s == slices) + a = 0.0f; + else + a = s * da; + sa = sin(a); + ca = cos(a); + TXTR_COORD(0.5f - sa * r2 / dtc, 0.5f + ca * r2 / dtc); + glVertex2f(r2 * sa, r2 * ca); + TXTR_COORD(0.5f - sa * r1 / dtc, 0.5f + ca * r1 / dtc); + glVertex2f(r1 * sa, r1 * ca); + } + glEnd(); + } + r1 = r2; + } + break; + } + case GLU_LINE: + { + int l, s; + /* draw loops */ + for (l = 0; l <= loops; l++) { + float r = innerRadius + l * dr; + glBegin(GL_LINE_LOOP); + for (s = 0; s < slices; s++) { + float a = s * da; + glVertex2f(r * sin(a), r * cos(a)); + } + glEnd(); + } + /* draw spokes */ + for (s = 0; s < slices; s++) { + float a = s * da; + float x = sin(a); + float y = cos(a); + glBegin(GL_LINE_STRIP); + for (l = 0; l <= loops; l++) { + float r = innerRadius + l * dr; + glVertex2f(r * x, r * y); + } + glEnd(); + } + break; + } + case GLU_POINT: + { + int s; + glBegin(GL_POINTS); + for (s = 0; s < slices; s++) { + float a = s * da; + float x = sin(a); + float y = cos(a); + int l; + for (l = 0; l <= loops; l++) { + float r = innerRadius * l * dr; + glVertex2f(r * x, r * y); + } + } + glEnd(); + break; + } + case GLU_SILHOUETTE: + { + if (innerRadius != 0.0) { + float a; + glBegin(GL_LINE_LOOP); + for (a = 0.0f; a < 2.0 * PI; a += da) { + float x = innerRadius * sin(a); + float y = innerRadius * cos(a); + glVertex2f(x, y); + } + glEnd(); + } + { + float a; + glBegin(GL_LINE_LOOP); + for (a = 0; a < 2.0f * PI; a += da) { + float x = outerRadius * sin(a); + float y = outerRadius * cos(a); + glVertex2f(x, y); + } + glEnd(); + } + break; + } + default: + return; + } + } + +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/GLU.java b/src/de/verschwiegener/lwjgl3/util/glu/GLU.java new file mode 100644 index 0000000..c463139 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/GLU.java @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.glu; + +import de.verschwiegener.lwjgl3.LWJGLUtil; +import de.verschwiegener.lwjgl3.util.glu.tessellation.GLUtessellatorImpl; + +import static de.verschwiegener.lwjgl3.util.glu.GLU.*; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + + +import static org.lwjgl.opengl.GL11.*; + +/** + * GLU.java + * + * + * Created 23-dec-2003 + * @author Erik Duijs + */ +public class GLU { + static final float PI = (float)Math.PI; + + /* Errors: (return value 0 = no error) */ + public static final int GLU_INVALID_ENUM = 100900; + public static final int GLU_INVALID_VALUE = 100901; + public static final int GLU_OUT_OF_MEMORY = 100902; + public static final int GLU_INCOMPATIBLE_GL_VERSION = 100903; + + /* StringName */ + public static final int GLU_VERSION = 100800; + public static final int GLU_EXTENSIONS = 100801; + + /* Boolean */ + public static final boolean GLU_TRUE = true; + public static final boolean GLU_FALSE = false; + + + /**** Quadric constants ****/ + + /* QuadricNormal */ + public static final int GLU_SMOOTH = 100000; + public static final int GLU_FLAT = 100001; + public static final int GLU_NONE = 100002; + + /* QuadricDrawStyle */ + public static final int GLU_POINT = 100010; + public static final int GLU_LINE = 100011; + public static final int GLU_FILL = 100012; + public static final int GLU_SILHOUETTE = 100013; + + /* QuadricOrientation */ + public static final int GLU_OUTSIDE = 100020; + public static final int GLU_INSIDE = 100021; + + /* Callback types: */ + /* ERROR = 100103 */ + + + /**** Tesselation constants ****/ + + public static final double GLU_TESS_MAX_COORD = 1.0e150; + public static final double TESS_MAX_COORD = 1.0e150; + + /* TessProperty */ + public static final int GLU_TESS_WINDING_RULE = 100140; + public static final int GLU_TESS_BOUNDARY_ONLY = 100141; + public static final int GLU_TESS_TOLERANCE = 100142; + + /* TessWinding */ + public static final int GLU_TESS_WINDING_ODD = 100130; + public static final int GLU_TESS_WINDING_NONZERO = 100131; + public static final int GLU_TESS_WINDING_POSITIVE = 100132; + public static final int GLU_TESS_WINDING_NEGATIVE = 100133; + public static final int GLU_TESS_WINDING_ABS_GEQ_TWO = 100134; + + /* TessCallback */ + public static final int GLU_TESS_BEGIN = 100100; /* void (CALLBACK*)(GLenum type) */ + public static final int GLU_TESS_VERTEX = 100101; /* void (CALLBACK*)(void *data) */ + public static final int GLU_TESS_END = 100102; /* void (CALLBACK*)(void) */ + public static final int GLU_TESS_ERROR = 100103; /* void (CALLBACK*)(GLenum errno) */ + public static final int GLU_TESS_EDGE_FLAG = 100104; /* void (CALLBACK*)(GLboolean boundaryEdge) */ + public static final int GLU_TESS_COMBINE = 100105; /* void (CALLBACK*)(GLdouble coords[3], + void *data[4], + GLfloat weight[4], + void **dataOut) */ + public static final int GLU_TESS_BEGIN_DATA = 100106; /* void (CALLBACK*)(GLenum type, + void *polygon_data) */ + public static final int GLU_TESS_VERTEX_DATA = 100107; /* void (CALLBACK*)(void *data, + void *polygon_data) */ + public static final int GLU_TESS_END_DATA = 100108; /* void (CALLBACK*)(void *polygon_data) */ + public static final int GLU_TESS_ERROR_DATA = 100109; /* void (CALLBACK*)(GLenum errno, + void *polygon_data) */ + public static final int GLU_TESS_EDGE_FLAG_DATA = 100110; /* void (CALLBACK*)(GLboolean boundaryEdge, + void *polygon_data) */ + public static final int GLU_TESS_COMBINE_DATA = 100111; /* void (CALLBACK*)(GLdouble coords[3], + void *data[4], + GLfloat weight[4], + void **dataOut, + void *polygon_data) */ + + /* TessError */ + public static final int GLU_TESS_ERROR1 = 100151; + public static final int GLU_TESS_ERROR2 = 100152; + public static final int GLU_TESS_ERROR3 = 100153; + public static final int GLU_TESS_ERROR4 = 100154; + public static final int GLU_TESS_ERROR5 = 100155; + public static final int GLU_TESS_ERROR6 = 100156; + public static final int GLU_TESS_ERROR7 = 100157; + public static final int GLU_TESS_ERROR8 = 100158; + + public static final int GLU_TESS_MISSING_BEGIN_POLYGON = GLU_TESS_ERROR1; + public static final int GLU_TESS_MISSING_BEGIN_CONTOUR = GLU_TESS_ERROR2; + public static final int GLU_TESS_MISSING_END_POLYGON = GLU_TESS_ERROR3; + public static final int GLU_TESS_MISSING_END_CONTOUR = GLU_TESS_ERROR4; + public static final int GLU_TESS_COORD_TOO_LARGE = GLU_TESS_ERROR5; + public static final int GLU_TESS_NEED_COMBINE_CALLBACK = GLU_TESS_ERROR6; + + /**** NURBS constants ****/ + + /* NurbsProperty */ + public static final int GLU_AUTO_LOAD_MATRIX = 100200; + public static final int GLU_CULLING = 100201; + public static final int GLU_SAMPLING_TOLERANCE = 100203; + public static final int GLU_DISPLAY_MODE = 100204; + public static final int GLU_PARAMETRIC_TOLERANCE = 100202; + public static final int GLU_SAMPLING_METHOD = 100205; + public static final int GLU_U_STEP = 100206; + public static final int GLU_V_STEP = 100207; + + /* NurbsSampling */ + public static final int GLU_PATH_LENGTH = 100215; + public static final int GLU_PARAMETRIC_ERROR = 100216; + public static final int GLU_DOMAIN_DISTANCE = 100217; + + + /* NurbsTrim */ + public static final int GLU_MAP1_TRIM_2 = 100210; + public static final int GLU_MAP1_TRIM_3 = 100211; + + /* NurbsDisplay */ + /* FILL = 100012 */ + public static final int GLU_OUTLINE_POLYGON = 100240; + public static final int GLU_OUTLINE_PATCH = 100241; + + /* NurbsCallback */ + /* ERROR = 100103 */ + + /* NurbsErrors */ + public static final int GLU_NURBS_ERROR1 = 100251; + public static final int GLU_NURBS_ERROR2 = 100252; + public static final int GLU_NURBS_ERROR3 = 100253; + public static final int GLU_NURBS_ERROR4 = 100254; + public static final int GLU_NURBS_ERROR5 = 100255; + public static final int GLU_NURBS_ERROR6 = 100256; + public static final int GLU_NURBS_ERROR7 = 100257; + public static final int GLU_NURBS_ERROR8 = 100258; + public static final int GLU_NURBS_ERROR9 = 100259; + public static final int GLU_NURBS_ERROR10 = 100260; + public static final int GLU_NURBS_ERROR11 = 100261; + public static final int GLU_NURBS_ERROR12 = 100262; + public static final int GLU_NURBS_ERROR13 = 100263; + public static final int GLU_NURBS_ERROR14 = 100264; + public static final int GLU_NURBS_ERROR15 = 100265; + public static final int GLU_NURBS_ERROR16 = 100266; + public static final int GLU_NURBS_ERROR17 = 100267; + public static final int GLU_NURBS_ERROR18 = 100268; + public static final int GLU_NURBS_ERROR19 = 100269; + public static final int GLU_NURBS_ERROR20 = 100270; + public static final int GLU_NURBS_ERROR21 = 100271; + public static final int GLU_NURBS_ERROR22 = 100272; + public static final int GLU_NURBS_ERROR23 = 100273; + public static final int GLU_NURBS_ERROR24 = 100274; + public static final int GLU_NURBS_ERROR25 = 100275; + public static final int GLU_NURBS_ERROR26 = 100276; + public static final int GLU_NURBS_ERROR27 = 100277; + public static final int GLU_NURBS_ERROR28 = 100278; + public static final int GLU_NURBS_ERROR29 = 100279; + public static final int GLU_NURBS_ERROR30 = 100280; + public static final int GLU_NURBS_ERROR31 = 100281; + public static final int GLU_NURBS_ERROR32 = 100282; + public static final int GLU_NURBS_ERROR33 = 100283; + public static final int GLU_NURBS_ERROR34 = 100284; + public static final int GLU_NURBS_ERROR35 = 100285; + public static final int GLU_NURBS_ERROR36 = 100286; + public static final int GLU_NURBS_ERROR37 = 100287; + + /* Contours types -- obsolete! */ + public static final int GLU_CW = 100120; + public static final int GLU_CCW = 100121; + public static final int GLU_INTERIOR = 100122; + public static final int GLU_EXTERIOR = 100123; + public static final int GLU_UNKNOWN = 100124; + + /* Names without "TESS_" prefix */ + public static final int GLU_BEGIN = GLU_TESS_BEGIN; + public static final int GLU_VERTEX = GLU_TESS_VERTEX; + public static final int GLU_END = GLU_TESS_END; + public static final int GLU_ERROR = GLU_TESS_ERROR; + public static final int GLU_EDGE_FLAG = GLU_TESS_EDGE_FLAG; + + /** + * Method gluLookAt + * @param eyex + * @param eyey + * @param eyez + * @param centerx + * @param centery + * @param centerz + * @param upx + * @param upy + * @param upz + */ + public static void gluLookAt( + float eyex, + float eyey, + float eyez, + float centerx, + float centery, + float centerz, + float upx, + float upy, + float upz) { + + Project.gluLookAt(eyex, eyey, eyez, centerx, centery, centerz, upx, upy, upz); + } + + /** + * Method gluOrtho2D + * @param left + * @param right + * @param bottom + * @param top + */ + public static void gluOrtho2D( + float left, + float right, + float bottom, + float top) { + + glOrtho(left, right, bottom, top, -1.0, 1.0); + } + + /** + * Method gluPerspective + * @param fovy + * @param aspect + * @param zNear + * @param zFar + */ + public static void gluPerspective( + float fovy, + float aspect, + float zNear, + float zFar) { + + Project.gluPerspective(fovy, aspect, zNear, zFar); + } + + /** + * Method gluProject + * @param objx + * @param objy + * @param objz + * @param modelMatrix + * @param projMatrix + * @param viewport + * @param win_pos + */ + public static boolean gluProject(float objx, float objy, float objz, + FloatBuffer modelMatrix, + FloatBuffer projMatrix, + IntBuffer viewport, + FloatBuffer win_pos) + { + return Project.gluProject(objx, objy, objz, modelMatrix, projMatrix, viewport, win_pos); + } + + /** + * Method gluUnproject + * @param winx + * @param winy + * @param winz + * @param modelMatrix + * @param projMatrix + * @param viewport + * @param obj_pos + */ + public static boolean gluUnProject(float winx, float winy, float winz, + FloatBuffer modelMatrix, + FloatBuffer projMatrix, + IntBuffer viewport, + FloatBuffer obj_pos) + { + return Project.gluUnProject(winx, winy, winz, modelMatrix, projMatrix, viewport, obj_pos); + } + + /** + * Method gluPickMatrix + * @param x + * @param y + * @param width + * @param height + * @param viewport + */ + public static void gluPickMatrix( + float x, + float y, + float width, + float height, + IntBuffer viewport) { + + Project.gluPickMatrix(x, y, width, height, viewport); + } + + /** + * Method gluGetString. + * @param name + * @return String + */ + public static String gluGetString(int name) { + return Registry.gluGetString(name); + } + + /** + * Method gluCheckExtension. + * @param extName + * @param extString + * @return boolean + */ + public static boolean gluCheckExtension(String extName, String extString) { + return Registry.gluCheckExtension(extName, extString); + } + + /** + * Method gluBuild2DMipmaps + * @param target + * @param components + * @param width + * @param height + * @param format + * @param type + * @param data + * @return int + */ + public static int gluBuild2DMipmaps( + int target, + int components, + int width, + int height, + int format, + int type, + ByteBuffer data) { + + return MipMap.gluBuild2DMipmaps(target, components, width, height, format, type, data); + } + + /** + * Method gluScaleImage. + * @param format + * @param widthIn + * @param heightIn + * @param typeIn + * @param dataIn + * @param widthOut + * @param heightOut + * @param typeOut + * @param dataOut + * @return int + */ + public static int gluScaleImage( + int format, + int widthIn, + int heightIn, + int typeIn, + ByteBuffer dataIn, + int widthOut, + int heightOut, + int typeOut, + ByteBuffer dataOut) { + + return MipMap.gluScaleImage(format, widthIn, heightIn, typeIn, dataIn, widthOut, heightOut, typeOut, dataOut); + } + + public static String gluErrorString(int error_code) { + switch (error_code) { + case GLU_INVALID_ENUM: + return "Invalid enum (glu)"; + case GLU_INVALID_VALUE: + return "Invalid value (glu)"; + case GLU_OUT_OF_MEMORY: + return "Out of memory (glu)"; + default: + return LWJGLUtil.translateGLErrorString(error_code); + } + } + + public static GLUtessellator gluNewTess() { + return new GLUtessellatorImpl(); + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/GLUtessellator.java b/src/de/verschwiegener/lwjgl3/util/glu/GLUtessellator.java new file mode 100644 index 0000000..666c260 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/GLUtessellator.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package de.verschwiegener.lwjgl3.util.glu; + +public interface GLUtessellator { + + void gluDeleteTess(); + + void gluTessProperty(int which, double value); + + /* Returns tessellator property */ + void gluGetTessProperty(int which, double[] value, + int value_offset); /* gluGetTessProperty() */ + + void gluTessNormal(double x, double y, double z); + + void gluTessCallback(int which, + GLUtessellatorCallback aCallback); + + void gluTessVertex(double[] coords, int coords_offset, + Object vertexData); + + void gluTessBeginPolygon(Object data); + + void gluTessBeginContour(); + + void gluTessEndContour(); + + void gluTessEndPolygon(); + + /*******************************************************/ + + /* Obsolete calls -- for backward compatibility */ + + void gluBeginPolygon(); + + /*ARGSUSED*/ + void gluNextContour(int type); + + void gluEndPolygon(); + +} \ No newline at end of file diff --git a/src/de/verschwiegener/lwjgl3/util/glu/GLUtessellatorCallback.java b/src/de/verschwiegener/lwjgl3/util/glu/GLUtessellatorCallback.java new file mode 100644 index 0000000..3a78193 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/GLUtessellatorCallback.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu; + +/** + * GLUtessellatorCallback interface provides methods that the user will + * override to define the callbacks for a tessellation object. + * + * @author Eric Veach, July 1994 + * @author Java Port: Pepijn Van Eeckhoudt, July 2003 + * @author Java Port: Nathan Parker Burg, August 2003 + */ +public interface GLUtessellatorCallback { + + void begin(int type); + + + void beginData(int type, Object polygonData); + + + + void edgeFlag(boolean boundaryEdge); + + + + void edgeFlagData(boolean boundaryEdge, Object polygonData); + + + + void vertex(Object vertexData); + + + + void vertexData(Object vertexData, Object polygonData); + + + + void end(); + + + + void endData(Object polygonData); + + + + void combine(double[] coords, Object[] data, + float[] weight, Object[] outData); + + + + void combineData(double[] coords, Object[] data, + float[] weight, Object[] outData, + Object polygonData); + + + + + void error(int errnum); + + + void errorData(int errnum, Object polygonData); + + //void mesh(com.sun.opengl.impl.tessellator.GLUmesh mesh); +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/GLUtessellatorCallbackAdapter.java b/src/de/verschwiegener/lwjgl3/util/glu/GLUtessellatorCallbackAdapter.java new file mode 100644 index 0000000..059116a --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/GLUtessellatorCallbackAdapter.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu; + +/** + * The GLUtessellatorCallbackAdapter provides a default implementation of + * {@link GLUtessellatorCallback GLUtessellatorCallback} + * with empty callback methods. This class can be extended to provide user + * defined callback methods. + * + * @author Eric Veach, July 1994 + * @author Java Port: Pepijn Van Eechhoudt, July 2003 + * @author Java Port: Nathan Parker Burg, August 2003 + */ + +public class GLUtessellatorCallbackAdapter implements GLUtessellatorCallback { + public void begin(int type) {} + public void edgeFlag(boolean boundaryEdge) {} + public void vertex(Object vertexData) {} + public void end() {} +// public void mesh(com.sun.opengl.impl.tessellator.GLUmesh mesh) {} + public void error(int errnum) {} + public void combine(double[] coords, Object[] data, + float[] weight, Object[] outData) {} + public void beginData(int type, Object polygonData) {} + public void edgeFlagData(boolean boundaryEdge, + Object polygonData) {} + public void vertexData(Object vertexData, Object polygonData) {} + public void endData(Object polygonData) {} + public void errorData(int errnum, Object polygonData) {} + public void combineData(double[] coords, Object[] data, + float[] weight, Object[] outData, + Object polygonData) {} +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/MipMap.java b/src/de/verschwiegener/lwjgl3/util/glu/MipMap.java new file mode 100644 index 0000000..6d57e96 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/MipMap.java @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.glu; + +import java.nio.ByteBuffer; + +import org.lwjgl.BufferUtils; + +import static org.lwjgl.opengl.GL11.*; +import static de.verschwiegener.lwjgl3.util.glu.GLU.*; + +/** + * MipMap.java + * + * + * Created 11-jan-2004 + * @author Erik Duijs + */ +public class MipMap extends Util { + + /** + * Method gluBuild2DMipmaps + * + * @param target + * @param components + * @param width + * @param height + * @param format + * @param type + * @param data + * @return int + */ + public static int gluBuild2DMipmaps(final int target, + final int components, final int width, final int height, + final int format, final int type, final ByteBuffer data) { + if ( width < 1 || height < 1 ) return GLU_INVALID_VALUE; + + final int bpp = bytesPerPixel(format, type); + if ( bpp == 0 ) + return GLU_INVALID_ENUM; + + final int maxSize = glGetIntegerv(GL_MAX_TEXTURE_SIZE); + + int w = nearestPower(width); + if ( w > maxSize ) + w = maxSize; + + int h = nearestPower(height); + if ( h > maxSize ) + h = maxSize; + + // Get current glPixelStore state + PixelStoreState pss = new PixelStoreState(); + + // set pixel packing + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_SKIP_ROWS, 0); + glPixelStorei(GL_PACK_SKIP_PIXELS, 0); + + ByteBuffer image; + int retVal = 0; + boolean done = false; + + if ( w != width || h != height ) { + // must rescale image to get "top" mipmap texture image + image = BufferUtils.createByteBuffer((w + 4) * h * bpp); + int error = gluScaleImage(format, width, height, type, data, w, h, type, image); + if ( error != 0 ) { + retVal = error; + done = true; + } + + /* set pixel unpacking */ + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + } else { + image = data; + } + + ByteBuffer bufferA = null; + ByteBuffer bufferB = null; + + int level = 0; + while ( !done ) { + if (image != data) { + /* set pixel unpacking */ + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + } + + glTexImage2D(target, level, components, w, h, 0, format, type, image); + + if ( w == 1 && h == 1 ) + break; + + final int newW = (w < 2) ? 1 : w >> 1; + final int newH = (h < 2) ? 1 : h >> 1; + + final ByteBuffer newImage; + + if ( bufferA == null ) + newImage = (bufferA = BufferUtils.createByteBuffer((newW + 4) * newH * bpp)); + else if ( bufferB == null ) + newImage = (bufferB = BufferUtils.createByteBuffer((newW + 4) * newH * bpp)); + else + newImage = bufferB; + + int error = gluScaleImage(format, w, h, type, image, newW, newH, type, newImage); + if ( error != 0 ) { + retVal = error; + done = true; + } + + image = newImage; + if ( bufferB != null ) + bufferB = bufferA; + + w = newW; + h = newH; + level++; + } + + // Restore original glPixelStore state + pss.save(); + + return retVal; + } + + /** + * Method gluScaleImage. + * @param format + * @param widthIn + * @param heightIn + * @param typein + * @param dataIn + * @param widthOut + * @param heightOut + * @param typeOut + * @param dataOut + * @return int + */ + public static int gluScaleImage(int format, + int widthIn, int heightIn, int typein, ByteBuffer dataIn, + int widthOut, int heightOut, int typeOut, ByteBuffer dataOut) { + + final int components = compPerPix(format); + if ( components == -1 ) + return GLU_INVALID_ENUM; + + int i, j, k; + float[] tempIn, tempOut; + float sx, sy; + int sizein, sizeout; + int rowstride, rowlen; + + // temp image data + tempIn = new float[widthIn * heightIn * components]; + tempOut = new float[widthOut * heightOut * components]; + + // Determine bytes per input type + switch ( typein ) { + case GL_UNSIGNED_BYTE: + sizein = 1; + break; + case GL_FLOAT: + sizein = 4; + break; + default: + return GL_INVALID_ENUM; + } + + // Determine bytes per output type + switch ( typeOut ) { + case GL_UNSIGNED_BYTE: + sizeout = 1; + break; + case GL_FLOAT: + sizeout = 4; + break; + default: + return GL_INVALID_ENUM; + } + + // Get glPixelStore state + PixelStoreState pss = new PixelStoreState(); + + //Unpack the pixel data and convert to floating point + if ( pss.unpackRowLength > 0 ) + rowlen = pss.unpackRowLength; + else + rowlen = widthIn; + + if ( sizein >= pss.unpackAlignment ) + rowstride = components * rowlen; + else + rowstride = pss.unpackAlignment / sizein * ceil(components * rowlen * sizein, pss.unpackAlignment); + + switch ( typein ) { + case GL_UNSIGNED_BYTE: + k = 0; + dataIn.rewind(); + for ( i = 0; i < heightIn; i++ ) { + int ubptr = i * rowstride + pss.unpackSkipRows * rowstride + pss.unpackSkipPixels * components; + for ( j = 0; j < widthIn * components; j++ ) { + tempIn[k++] = dataIn.get(ubptr++) & 0xff; + } + } + break; + case GL_FLOAT: + k = 0; + dataIn.rewind(); + for ( i = 0; i < heightIn; i++ ) + { + int fptr = 4 * (i * rowstride + pss.unpackSkipRows * rowstride + pss.unpackSkipPixels * components); + for ( j = 0; j < widthIn * components; j++ ) + { + tempIn[k++] = dataIn.getFloat(fptr); + fptr += 4; + } + } + break; + default: + return GLU_INVALID_ENUM; + } + + // Do scaling + sx = (float)widthIn / (float)widthOut; + sy = (float)heightIn / (float)heightOut; + + float[] c = new float[components]; + int src, dst; + + for ( int iy = 0; iy < heightOut; iy++ ) { + for ( int ix = 0; ix < widthOut; ix++ ) { + int x0 = (int)(ix * sx); + int x1 = (int)((ix + 1) * sx); + int y0 = (int)(iy * sy); + int y1 = (int)((iy + 1) * sy); + + int readPix = 0; + + // reset weighted pixel + for ( int ic = 0; ic < components; ic++ ) { + c[ic] = 0; + } + + // create weighted pixel + for ( int ix0 = x0; ix0 < x1; ix0++ ) { + for ( int iy0 = y0; iy0 < y1; iy0++ ) { + + src = (iy0 * widthIn + ix0) * components; + + for ( int ic = 0; ic < components; ic++ ) { + c[ic] += tempIn[src + ic]; + } + + readPix++; + } + } + + // store weighted pixel + dst = (iy * widthOut + ix) * components; + + if ( readPix == 0 ) { + // Image is sized up, caused by non power of two texture as input + src = (y0 * widthIn + x0) * components; + for ( int ic = 0; ic < components; ic++ ) { + tempOut[dst++] = tempIn[src + ic]; + } + } else { + // sized down + for ( k = 0; k < components; k++ ) { + tempOut[dst++] = c[k] / readPix; + } + } + } + } + + + // Convert temp output + if ( pss.packRowLength > 0 ) + rowlen = pss.packRowLength; + else + rowlen = widthOut; + + if ( sizeout >= pss.packAlignment ) + rowstride = components * rowlen; + else + rowstride = pss.packAlignment / sizeout * ceil(components * rowlen * sizeout, pss.packAlignment); + + switch ( typeOut ) { + case GL_UNSIGNED_BYTE: + k = 0; + for ( i = 0; i < heightOut; i++ ) { + int ubptr = i * rowstride + pss.packSkipRows * rowstride + pss.packSkipPixels * components; + + for ( j = 0; j < widthOut * components; j++ ) { + dataOut.put(ubptr++, (byte)tempOut[k++]); + } + } + break; + case GL_FLOAT: + k = 0; + for ( i = 0; i < heightOut; i++ ) { + int fptr = 4 * (i * rowstride + pss.unpackSkipRows * rowstride + pss.unpackSkipPixels * components); + + for ( j = 0; j < widthOut * components; j++ ) { + dataOut.putFloat(fptr, tempOut[k++]); + fptr += 4; + } + } + break; + default: + return GLU_INVALID_ENUM; + } + + return 0; + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/PartialDisk.java b/src/de/verschwiegener/lwjgl3/util/glu/PartialDisk.java new file mode 100644 index 0000000..ff7f8de --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/PartialDisk.java @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.glu; + +import static org.lwjgl.opengl.GL11.*; +import static de.verschwiegener.lwjgl3.util.glu.GLU.*; + +/** + * PartialDisk.java + * + * + * Created 23-dec-2003 + * + * @author Erik Duijs + */ +public class PartialDisk extends Quadric { + + private static final int CACHE_SIZE = 240; + + /** + * Constructor for PartialDisk. + */ + public PartialDisk() { + super(); + } + + /** + * renders a partial disk on the z=0 plane. A partial disk is similar to a + * full disk, except that only the subset of the disk from startAngle + * through startAngle + sweepAngle is included (where 0 degrees is along + * the +y axis, 90 degrees along the +x axis, 180 along the -y axis, and + * 270 along the -x axis). + * + * The partial disk has a radius of outerRadius, and contains a concentric + * circular hole with a radius of innerRadius. If innerRadius is zero, then + * no hole is generated. The partial disk is subdivided around the z axis + * into slices (like pizza slices), and also about the z axis into rings + * (as specified by slices and loops, respectively). + * + * With respect to orientation, the +z side of the partial disk is + * considered to be outside (see gluQuadricOrientation). This means that if + * the orientation is set to GLU.GLU_OUTSIDE, then any normals generated point + * along the +z axis. Otherwise, they point along the -z axis. + * + * If texturing is turned on (with gluQuadricTexture), texture coordinates + * are generated linearly such that where r=outerRadius, the value at (r, 0, 0) + * is (1, 0.5), at (0, r, 0) it is (0.5, 1), at (-r, 0, 0) it is (0, 0.5), + * and at (0, -r, 0) it is (0.5, 0). + */ + public void draw( + float innerRadius, + float outerRadius, + int slices, + int loops, + float startAngle, + float sweepAngle) { + + int i, j; + float[] sinCache = new float[CACHE_SIZE]; + float[] cosCache = new float[CACHE_SIZE]; + float angle; + float sintemp, costemp; + float deltaRadius; + float radiusLow, radiusHigh; + float texLow = 0, texHigh = 0; + float angleOffset; + int slices2; + int finish; + + if (slices >= CACHE_SIZE) + slices = CACHE_SIZE - 1; + if (slices < 2 + || loops < 1 + || outerRadius <= 0.0f + || innerRadius < 0.0f + || innerRadius > outerRadius) { + //gluQuadricError(qobj, GLU.GLU_INVALID_VALUE); + System.err.println("PartialDisk: GLU_INVALID_VALUE"); + return; + } + + if (sweepAngle < -360.0f) + sweepAngle = 360.0f; + if (sweepAngle > 360.0f) + sweepAngle = 360.0f; + if (sweepAngle < 0) { + startAngle += sweepAngle; + sweepAngle = -sweepAngle; + } + + if (sweepAngle == 360.0f) { + slices2 = slices; + } else { + slices2 = slices + 1; + } + + /* Compute length (needed for normal calculations) */ + deltaRadius = outerRadius - innerRadius; + + /* Cache is the vertex locations cache */ + + angleOffset = startAngle / 180.0f * PI; + for (i = 0; i <= slices; i++) { + angle = angleOffset + ((PI * sweepAngle) / 180.0f) * i / slices; + sinCache[i] = sin(angle); + cosCache[i] = cos(angle); + } + + if (sweepAngle == 360.0f) { + sinCache[slices] = sinCache[0]; + cosCache[slices] = cosCache[0]; + } + + switch (super.normals) { + case GLU_FLAT : + case GLU_SMOOTH : + if (super.orientation == GLU_OUTSIDE) { + glNormal3f(0.0f, 0.0f, 1.0f); + } else { + glNormal3f(0.0f, 0.0f, -1.0f); + } + break; + default : + case GLU_NONE : + break; + } + + switch (super.drawStyle) { + case GLU_FILL : + if (innerRadius == .0f) { + finish = loops - 1; + /* Triangle strip for inner polygons */ + glBegin(GL_TRIANGLE_FAN); + if (super.textureFlag) { + glTexCoord2f(0.5f, 0.5f); + } + glVertex3f(0.0f, 0.0f, 0.0f); + radiusLow = outerRadius - deltaRadius * ((float) (loops - 1) / loops); + if (super.textureFlag) { + texLow = radiusLow / outerRadius / 2; + } + + if (super.orientation == GLU_OUTSIDE) { + for (i = slices; i >= 0; i--) { + if (super.textureFlag) { + glTexCoord2f( + texLow * sinCache[i] + 0.5f, + texLow * cosCache[i] + 0.5f); + } + glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f); + } + } else { + for (i = 0; i <= slices; i++) { + if (super.textureFlag) { + glTexCoord2f( + texLow * sinCache[i] + 0.5f, + texLow * cosCache[i] + 0.5f); + } + glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f); + } + } + glEnd(); + } else { + finish = loops; + } + for (j = 0; j < finish; j++) { + radiusLow = outerRadius - deltaRadius * ((float) j / loops); + radiusHigh = outerRadius - deltaRadius * ((float) (j + 1) / loops); + if (super.textureFlag) { + texLow = radiusLow / outerRadius / 2; + texHigh = radiusHigh / outerRadius / 2; + } + + glBegin(GL_QUAD_STRIP); + for (i = 0; i <= slices; i++) { + if (super.orientation == GLU_OUTSIDE) { + if (super.textureFlag) { + glTexCoord2f( + texLow * sinCache[i] + 0.5f, + texLow * cosCache[i] + 0.5f); + } + glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f); + + if (super.textureFlag) { + glTexCoord2f( + texHigh * sinCache[i] + 0.5f, + texHigh * cosCache[i] + 0.5f); + } + glVertex3f( + radiusHigh * sinCache[i], + radiusHigh * cosCache[i], + 0.0f); + } else { + if (super.textureFlag) { + glTexCoord2f( + texHigh * sinCache[i] + 0.5f, + texHigh * cosCache[i] + 0.5f); + } + glVertex3f( + radiusHigh * sinCache[i], + radiusHigh * cosCache[i], + 0.0f); + + if (super.textureFlag) { + glTexCoord2f( + texLow * sinCache[i] + 0.5f, + texLow * cosCache[i] + 0.5f); + } + glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f); + } + } + glEnd(); + } + break; + case GLU_POINT : + glBegin(GL_POINTS); + for (i = 0; i < slices2; i++) { + sintemp = sinCache[i]; + costemp = cosCache[i]; + for (j = 0; j <= loops; j++) { + radiusLow = outerRadius - deltaRadius * ((float) j / loops); + + if (super.textureFlag) { + texLow = radiusLow / outerRadius / 2; + + glTexCoord2f( + texLow * sinCache[i] + 0.5f, + texLow * cosCache[i] + 0.5f); + } + glVertex3f(radiusLow * sintemp, radiusLow * costemp, 0.0f); + } + } + glEnd(); + break; + case GLU_LINE : + if (innerRadius == outerRadius) { + glBegin(GL_LINE_STRIP); + + for (i = 0; i <= slices; i++) { + if (super.textureFlag) { + glTexCoord2f(sinCache[i] / 2 + 0.5f, cosCache[i] / 2 + 0.5f); + } + glVertex3f(innerRadius * sinCache[i], innerRadius * cosCache[i], 0.0f); + } + glEnd(); + break; + } + for (j = 0; j <= loops; j++) { + radiusLow = outerRadius - deltaRadius * ((float) j / loops); + if (super.textureFlag) { + texLow = radiusLow / outerRadius / 2; + } + + glBegin(GL_LINE_STRIP); + for (i = 0; i <= slices; i++) { + if (super.textureFlag) { + glTexCoord2f( + texLow * sinCache[i] + 0.5f, + texLow * cosCache[i] + 0.5f); + } + glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f); + } + glEnd(); + } + for (i = 0; i < slices2; i++) { + sintemp = sinCache[i]; + costemp = cosCache[i]; + glBegin(GL_LINE_STRIP); + for (j = 0; j <= loops; j++) { + radiusLow = outerRadius - deltaRadius * ((float) j / loops); + if (super.textureFlag) { + texLow = radiusLow / outerRadius / 2; + } + + if (super.textureFlag) { + glTexCoord2f( + texLow * sinCache[i] + 0.5f, + texLow * cosCache[i] + 0.5f); + } + glVertex3f(radiusLow * sintemp, radiusLow * costemp, 0.0f); + } + glEnd(); + } + break; + case GLU_SILHOUETTE : + if (sweepAngle < 360.0f) { + for (i = 0; i <= slices; i += slices) { + sintemp = sinCache[i]; + costemp = cosCache[i]; + glBegin(GL_LINE_STRIP); + for (j = 0; j <= loops; j++) { + radiusLow = outerRadius - deltaRadius * ((float) j / loops); + + if (super.textureFlag) { + texLow = radiusLow / outerRadius / 2; + glTexCoord2f( + texLow * sinCache[i] + 0.5f, + texLow * cosCache[i] + 0.5f); + } + glVertex3f(radiusLow * sintemp, radiusLow * costemp, 0.0f); + } + glEnd(); + } + } + for (j = 0; j <= loops; j += loops) { + radiusLow = outerRadius - deltaRadius * ((float) j / loops); + if (super.textureFlag) { + texLow = radiusLow / outerRadius / 2; + } + + glBegin(GL_LINE_STRIP); + for (i = 0; i <= slices; i++) { + if (super.textureFlag) { + glTexCoord2f( + texLow * sinCache[i] + 0.5f, + texLow * cosCache[i] + 0.5f); + } + glVertex3f(radiusLow * sinCache[i], radiusLow * cosCache[i], 0.0f); + } + glEnd(); + if (innerRadius == outerRadius) + break; + } + break; + default : + break; + } + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/PixelStoreState.java b/src/de/verschwiegener/lwjgl3/util/glu/PixelStoreState.java new file mode 100644 index 0000000..4c34530 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/PixelStoreState.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.glu; + +import static org.lwjgl.opengl.GL11.*; + +/** + * PixelStoreState.java + * + * + * Created 11-jan-2004 + * @author Erik Duijs + */ +class PixelStoreState extends Util { + + public int unpackRowLength; + public int unpackAlignment; + public int unpackSkipRows; + public int unpackSkipPixels; + public int packRowLength; + public int packAlignment; + public int packSkipRows; + public int packSkipPixels; + + /** + * Constructor for PixelStoreState. + */ + PixelStoreState() { + super(); + load(); + } + + public void load() { + unpackRowLength = glGetIntegerv(GL_UNPACK_ROW_LENGTH); + unpackAlignment = glGetIntegerv(GL_UNPACK_ALIGNMENT); + unpackSkipRows = glGetIntegerv(GL_UNPACK_SKIP_ROWS); + unpackSkipPixels = glGetIntegerv(GL_UNPACK_SKIP_PIXELS); + packRowLength = glGetIntegerv(GL_PACK_ROW_LENGTH); + packAlignment = glGetIntegerv(GL_PACK_ALIGNMENT); + packSkipRows = glGetIntegerv(GL_PACK_SKIP_ROWS); + packSkipPixels = glGetIntegerv(GL_PACK_SKIP_PIXELS); + } + + public void save() { + glPixelStorei(GL_UNPACK_ROW_LENGTH, unpackRowLength); + glPixelStorei(GL_UNPACK_ALIGNMENT, unpackAlignment); + glPixelStorei(GL_UNPACK_SKIP_ROWS, unpackSkipRows); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, unpackSkipPixels); + glPixelStorei(GL_PACK_ROW_LENGTH, packRowLength); + glPixelStorei(GL_PACK_ALIGNMENT, packAlignment); + glPixelStorei(GL_PACK_SKIP_ROWS, packSkipRows); + glPixelStorei(GL_PACK_SKIP_PIXELS, packSkipPixels); + } + +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/Project.java b/src/de/verschwiegener/lwjgl3/util/glu/Project.java new file mode 100644 index 0000000..2cd2956 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/Project.java @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.glu; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GLUtil; + +import static org.lwjgl.opengl.GL11.*; + +/** + * Project.java + *

+ *

+ * Created 11-jan-2004 + * + * @author Erik Duijs + */ +public class Project extends Util { + + private static final float[] IDENTITY_MATRIX = + new float[] { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f }; + + private static final FloatBuffer matrix = BufferUtils.createFloatBuffer(16); + private static final FloatBuffer finalMatrix = BufferUtils.createFloatBuffer(16); + + private static final FloatBuffer tempMatrix = BufferUtils.createFloatBuffer(16); + private static final float[] in = new float[4]; + private static final float[] out = new float[4]; + + private static final float[] forward = new float[3]; + private static final float[] side = new float[3]; + private static final float[] up = new float[3]; + + /** + * Make matrix an identity matrix + */ + private static void __gluMakeIdentityf(FloatBuffer m) { + int oldPos = m.position(); + m.put(IDENTITY_MATRIX); + m.position(oldPos); + } + + /** + * Method __gluMultMatrixVecf + * + * @param m + * @param in + * @param out + */ + private static void __gluMultMatrixVecf(FloatBuffer m, float[] in, float[] out) { + for (int i = 0; i < 4; i++) { + out[i] = + in[0] * m.get(m.position() + 0*4 + i) + + in[1] * m.get(m.position() + 1*4 + i) + + in[2] * m.get(m.position() + 2*4 + i) + + in[3] * m.get(m.position() + 3*4 + i); + + } + } + + /** + * @param src + * @param inverse + * + * @return true if the matrix was succesfully inverted + */ + private static boolean __gluInvertMatrixf(FloatBuffer src, FloatBuffer inverse) { + int i, j, k, swap; + float t; + FloatBuffer temp = Project.tempMatrix; + + + for (i = 0; i < 16; i++) { + temp.put(i, src.get(i + src.position())); + } + __gluMakeIdentityf(inverse); + + for (i = 0; i < 4; i++) { + /* + * * Look for largest element in column + */ + swap = i; + for (j = i + 1; j < 4; j++) { + /* + * if (fabs(temp[j][i]) > fabs(temp[i][i])) { swap = j; + */ + if (Math.abs(temp.get(j*4 + i)) > Math.abs(temp.get(i* 4 + i))) { + swap = j; + } + } + + if (swap != i) { + /* + * * Swap rows. + */ + for (k = 0; k < 4; k++) { + t = temp.get(i*4 + k); + temp.put(i*4 + k, temp.get(swap*4 + k)); + temp.put(swap*4 + k, t); + + t = inverse.get(i*4 + k); + inverse.put(i*4 + k, inverse.get(swap*4 + k)); + //inverse.put((i << 2) + k, inverse.get((swap << 2) + k)); + inverse.put(swap*4 + k, t); + //inverse.put((swap << 2) + k, t); + } + } + + if (temp.get(i*4 + i) == 0) { + /* + * * No non-zero pivot. The matrix is singular, which shouldn't * + * happen. This means the user gave us a bad matrix. + */ + return false; + } + + t = temp.get(i*4 + i); + for (k = 0; k < 4; k++) { + temp.put(i*4 + k, temp.get(i*4 + k)/t); + inverse.put(i*4 + k, inverse.get(i*4 + k)/t); + } + for (j = 0; j < 4; j++) { + if (j != i) { + t = temp.get(j*4 + i); + for (k = 0; k < 4; k++) { + temp.put(j*4 + k, temp.get(j*4 + k) - temp.get(i*4 + k) * t); + inverse.put(j*4 + k, inverse.get(j*4 + k) - inverse.get(i*4 + k) * t); + /*inverse.put( + (j << 2) + k, + inverse.get((j << 2) + k) - inverse.get((i << 2) + k) * t);*/ + } + } + } + } + return true; + } + + /** + * @param a + * @param b + * @param r + */ + private static void __gluMultMatricesf(FloatBuffer a, FloatBuffer b, FloatBuffer r) { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + r.put(r.position() + i*4 + j, + a.get(a.position() + i*4 + 0) * b.get(b.position() + 0*4 + j) + a.get(a.position() + i*4 + 1) * b.get(b.position() + 1*4 + j) + a.get(a.position() + i*4 + 2) * b.get(b.position() + 2*4 + j) + a.get(a.position() + i*4 + 3) * b.get(b.position() + 3*4 + j)); + } + } + } + + /** + * Method gluPerspective. + * + * @param fovy + * @param aspect + * @param zNear + * @param zFar + */ + public static void gluPerspective(float fovy, float aspect, float zNear, float zFar) { + float sine, cotangent, deltaZ; + float radians = fovy / 2 * GLU.PI / 180; + + deltaZ = zFar - zNear; + sine = (float) Math.sin(radians); + + if ((deltaZ == 0) || (sine == 0) || (aspect == 0)) { + return; + } + + cotangent = (float) Math.cos(radians) / sine; + + __gluMakeIdentityf(matrix); + + matrix.put(0 * 4 + 0, cotangent / aspect); + matrix.put(1 * 4 + 1, cotangent); + matrix.put(2 * 4 + 2, - (zFar + zNear) / deltaZ); + matrix.put(2 * 4 + 3, -1); + matrix.put(3 * 4 + 2, -2 * zNear * zFar / deltaZ); + matrix.put(3 * 4 + 3, 0); + + glMultMatrixf(matrix); + } + + /** + * Method gluLookAt + * + * @param eyex + * @param eyey + * @param eyez + * @param centerx + * @param centery + * @param centerz + * @param upx + * @param upy + * @param upz + */ + public static void gluLookAt( + float eyex, + float eyey, + float eyez, + float centerx, + float centery, + float centerz, + float upx, + float upy, + float upz) { + float[] forward = Project.forward; + float[] side = Project.side; + float[] up = Project.up; + + forward[0] = centerx - eyex; + forward[1] = centery - eyey; + forward[2] = centerz - eyez; + + up[0] = upx; + up[1] = upy; + up[2] = upz; + + normalize(forward); + + /* Side = forward x up */ + cross(forward, up, side); + normalize(side); + + /* Recompute up as: up = side x forward */ + cross(side, forward, up); + + __gluMakeIdentityf(matrix); + matrix.put(0 * 4 + 0, side[0]); + matrix.put(1 * 4 + 0, side[1]); + matrix.put(2 * 4 + 0, side[2]); + + matrix.put(0 * 4 + 1, up[0]); + matrix.put(1 * 4 + 1, up[1]); + matrix.put(2 * 4 + 1, up[2]); + + matrix.put(0 * 4 + 2, -forward[0]); + matrix.put(1 * 4 + 2, -forward[1]); + matrix.put(2 * 4 + 2, -forward[2]); + + glMultMatrixf(matrix); + glTranslatef(-eyex, -eyey, -eyez); + } + + /** + * Method gluProject + * + * @param objx + * @param objy + * @param objz + * @param modelMatrix + * @param projMatrix + * @param viewport + * @param win_pos + */ + public static boolean gluProject( + float objx, + float objy, + float objz, + FloatBuffer modelMatrix, + FloatBuffer projMatrix, + IntBuffer viewport, + FloatBuffer win_pos) { + + float[] in = Project.in; + float[] out = Project.out; + + in[0] = objx; + in[1] = objy; + in[2] = objz; + in[3] = 1.0f; + + __gluMultMatrixVecf(modelMatrix, in, out); + __gluMultMatrixVecf(projMatrix, out, in); + + if (in[3] == 0.0) + return false; + + in[3] = (1.0f / in[3]) * 0.5f; + + // Map x, y and z to range 0-1 + in[0] = in[0] * in[3] + 0.5f; + in[1] = in[1] * in[3] + 0.5f; + in[2] = in[2] * in[3] + 0.5f; + + // Map x,y to viewport + win_pos.put(0, in[0] * viewport.get(viewport.position() + 2) + viewport.get(viewport.position() + 0)); + win_pos.put(1, in[1] * viewport.get(viewport.position() + 3) + viewport.get(viewport.position() + 1)); + win_pos.put(2, in[2]); + + return true; + } + + /** + * Method gluUnproject + * + * @param winx + * @param winy + * @param winz + * @param modelMatrix + * @param projMatrix + * @param viewport + * @param obj_pos + */ + public static boolean gluUnProject( + float winx, + float winy, + float winz, + FloatBuffer modelMatrix, + FloatBuffer projMatrix, + IntBuffer viewport, + FloatBuffer obj_pos) { + float[] in = Project.in; + float[] out = Project.out; + + __gluMultMatricesf(modelMatrix, projMatrix, finalMatrix); + + if (!__gluInvertMatrixf(finalMatrix, finalMatrix)) + return false; + + in[0] = winx; + in[1] = winy; + in[2] = winz; + in[3] = 1.0f; + + // Map x and y from window coordinates + in[0] = (in[0] - viewport.get(viewport.position() + 0)) / viewport.get(viewport.position() + 2); + in[1] = (in[1] - viewport.get(viewport.position() + 1)) / viewport.get(viewport.position() + 3); + + // Map to range -1 to 1 + in[0] = in[0] * 2 - 1; + in[1] = in[1] * 2 - 1; + in[2] = in[2] * 2 - 1; + + __gluMultMatrixVecf(finalMatrix, in, out); + + if (out[3] == 0.0) + return false; + + out[3] = 1.0f / out[3]; + + obj_pos.put(obj_pos.position() + 0, out[0] * out[3]); + obj_pos.put(obj_pos.position() + 1, out[1] * out[3]); + obj_pos.put(obj_pos.position() + 2, out[2] * out[3]); + + return true; + } + + /** + * Method gluPickMatrix + * + * @param x + * @param y + * @param deltaX + * @param deltaY + * @param viewport + */ + public static void gluPickMatrix( + float x, + float y, + float deltaX, + float deltaY, + IntBuffer viewport) { + if (deltaX <= 0 || deltaY <= 0) { + return; + } + + /* Translate and scale the picked region to the entire window */ + glTranslatef( + (viewport.get(viewport.position() + 2) - 2 * (x - viewport.get(viewport.position() + 0))) / deltaX, + (viewport.get(viewport.position() + 3) - 2 * (y - viewport.get(viewport.position() + 1))) / deltaY, + 0); + glScalef(viewport.get(viewport.position() + 2) / deltaX, viewport.get(viewport.position() + 3) / deltaY, 1.0f); + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/Quadric.java b/src/de/verschwiegener/lwjgl3/util/glu/Quadric.java new file mode 100644 index 0000000..779f98e --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/Quadric.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.glu; + +import static org.lwjgl.opengl.GL11.*; +import static de.verschwiegener.lwjgl3.util.glu.GLU.*; + +/** + * Quadric.java + * + * + * Created 22-dec-2003 + * @author Erik Duijs + */ +public class Quadric { + + protected int drawStyle; + protected int orientation; + protected boolean textureFlag; + protected int normals; + + /** + * Constructor for Quadric. + */ + public Quadric() { + super(); + + drawStyle = GLU_FILL; + orientation = GLU_OUTSIDE; + textureFlag = false; + normals = GLU_SMOOTH; + } + + /** + * Call glNormal3f after scaling normal to unit length. + * + * @param x + * @param y + * @param z + */ + protected void normal3f(float x, float y, float z) { + float mag; + + mag = (float)Math.sqrt(x * x + y * y + z * z); + if (mag > 0.00001F) { + x /= mag; + y /= mag; + z /= mag; + } + glNormal3f(x, y, z); + } + + /** + * specifies the draw style for quadrics. + * + * The legal values are as follows: + * + * GLU.FILL: Quadrics are rendered with polygon primitives. The polygons + * are drawn in a counterclockwise fashion with respect to + * their normals (as defined with glu.quadricOrientation). + * + * GLU.LINE: Quadrics are rendered as a set of lines. + * + * GLU.SILHOUETTE: Quadrics are rendered as a set of lines, except that edges + * separating coplanar faces will not be drawn. + * + * GLU.POINT: Quadrics are rendered as a set of points. + * + * @param drawStyle The drawStyle to set + */ + public void setDrawStyle(int drawStyle) { + this.drawStyle = drawStyle; + } + + /** + * specifies what kind of normals are desired for quadrics. + * The legal values are as follows: + * + * GLU.NONE: No normals are generated. + * + * GLU.FLAT: One normal is generated for every facet of a quadric. + * + * GLU.SMOOTH: One normal is generated for every vertex of a quadric. This + * is the default. + * + * @param normals The normals to set + */ + public void setNormals(int normals) { + this.normals = normals; + } + + /** + * specifies what kind of orientation is desired for. + * The orientation values are as follows: + * + * GLU.OUTSIDE: Quadrics are drawn with normals pointing outward. + * + * GLU.INSIDE: Normals point inward. The default is GLU.OUTSIDE. + * + * Note that the interpretation of outward and inward depends on the quadric + * being drawn. + * + * @param orientation The orientation to set + */ + public void setOrientation(int orientation) { + this.orientation = orientation; + } + + /** + * specifies if texture coordinates should be generated for + * quadrics rendered with qobj. If the value of textureCoords is true, + * then texture coordinates are generated, and if textureCoords is false, + * they are not.. The default is false. + * + * The manner in which texture coordinates are generated depends upon the + * specific quadric rendered. + * + * @param textureFlag The textureFlag to set + */ + public void setTextureFlag(boolean textureFlag) { + this.textureFlag = textureFlag; + } + + + /** + * Returns the drawStyle. + * @return int + */ + public int getDrawStyle() { + return drawStyle; + } + + /** + * Returns the normals. + * @return int + */ + public int getNormals() { + return normals; + } + + /** + * Returns the orientation. + * @return int + */ + public int getOrientation() { + return orientation; + } + + /** + * Returns the textureFlag. + * @return boolean + */ + public boolean getTextureFlag() { + return textureFlag; + } + + protected void TXTR_COORD(float x, float y) { + if (textureFlag) glTexCoord2f(x,y); + } + + + protected float sin(float r) { + return (float)Math.sin(r); + } + + protected float cos(float r) { + return (float)Math.cos(r); + } + +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/Registry.java b/src/de/verschwiegener/lwjgl3/util/glu/Registry.java new file mode 100644 index 0000000..0ec70fa --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/Registry.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.glu; + +import static de.verschwiegener.lwjgl3.util.glu.GLU.*; + +/** + * Registry.java + * + * + * Created 11-jan-2004 + * @author Erik Duijs + */ +public class Registry extends Util { + + private static final String versionString = "1.3"; + private static final String extensionString = + "GLU_EXT_nurbs_tessellator " + "GLU_EXT_object_space_tess "; + + /** + * Method gluGetString + * @param name + * @return String + */ + public static String gluGetString(int name) { + + if (name == GLU_VERSION) { + return versionString; + } else if (name == GLU_EXTENSIONS) { + return extensionString; + } + return null; + } + + /** + * Method gluCheckExtension + * + * @param extName is an extension name. + * @param extString is a string of extensions separated by blank(s). There may or + * may not be leading or trailing blank(s) in extString. + * This works in cases of extensions being prefixes of another like + * GL_EXT_texture and GL_EXT_texture3D. + * @return boolean true if extName is found otherwise it returns false. + */ + public static boolean gluCheckExtension(String extName, String extString) { + if (extString == null || extName == null) + return false; + + return extString.indexOf(extName) != -1; + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/Sphere.java b/src/de/verschwiegener/lwjgl3/util/glu/Sphere.java new file mode 100644 index 0000000..12afee4 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/Sphere.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.glu; + +import static org.lwjgl.opengl.GL11.*; +import static de.verschwiegener.lwjgl3.util.glu.GLU.*; + +/** + * Sphere.java + * + * + * Created 23-dec-2003 + * @author Erik Duijs + */ +public class Sphere extends Quadric { + + /** + * Constructor + */ + public Sphere() { + super(); + } + + /** + * draws a sphere of the given radius centered around the origin. + * The sphere is subdivided around the z axis into slices and along the z axis + * into stacks (similar to lines of longitude and latitude). + * + * If the orientation is set to GLU.OUTSIDE (with glu.quadricOrientation), then + * any normals generated point away from the center of the sphere. Otherwise, + * they point toward the center of the sphere. + + * If texturing is turned on (with glu.quadricTexture), then texture + * coordinates are generated so that t ranges from 0.0 at z=-radius to 1.0 at + * z=radius (t increases linearly along longitudinal lines), and s ranges from + * 0.0 at the +y axis, to 0.25 at the +x axis, to 0.5 at the -y axis, to 0.75 + * at the -x axis, and back to 1.0 at the +y axis. + */ + public void draw(float radius, int slices, int stacks) { + + float rho, drho, theta, dtheta; + float x, y, z; + float s, t, ds, dt; + int i, j, imin, imax; + boolean normals; + float nsign; + + normals = super.normals != GLU_NONE; + + if (super.orientation == GLU_INSIDE) { + nsign = -1.0f; + } else { + nsign = 1.0f; + } + + drho = PI / stacks; + dtheta = 2.0f * PI / slices; + + if (super.drawStyle == GLU_FILL) { + if (!super.textureFlag) { + // draw +Z end as a triangle fan + glBegin(GL_TRIANGLE_FAN); + glNormal3f(0.0f, 0.0f, 1.0f); + glVertex3f(0.0f, 0.0f, nsign * radius); + for (j = 0; j <= slices; j++) { + theta = (j == slices) ? 0.0f : j * dtheta; + x = -sin(theta) * sin(drho); + y = cos(theta) * sin(drho); + z = nsign * cos(drho); + if (normals) { + glNormal3f(x * nsign, y * nsign, z * nsign); + } + glVertex3f(x * radius, y * radius, z * radius); + } + glEnd(); + } + + ds = 1.0f / slices; + dt = 1.0f / stacks; + t = 1.0f; // because loop now runs from 0 + if (super.textureFlag) { + imin = 0; + imax = stacks; + } else { + imin = 1; + imax = stacks - 1; + } + + // draw intermediate stacks as quad strips + for (i = imin; i < imax; i++) { + rho = i * drho; + glBegin(GL_QUAD_STRIP); + s = 0.0f; + for (j = 0; j <= slices; j++) { + theta = (j == slices) ? 0.0f : j * dtheta; + x = -sin(theta) * sin(rho); + y = cos(theta) * sin(rho); + z = nsign * cos(rho); + if (normals) { + glNormal3f(x * nsign, y * nsign, z * nsign); + } + TXTR_COORD(s, t); + glVertex3f(x * radius, y * radius, z * radius); + x = -sin(theta) * sin(rho + drho); + y = cos(theta) * sin(rho + drho); + z = nsign * cos(rho + drho); + if (normals) { + glNormal3f(x * nsign, y * nsign, z * nsign); + } + TXTR_COORD(s, t - dt); + s += ds; + glVertex3f(x * radius, y * radius, z * radius); + } + glEnd(); + t -= dt; + } + + if (!super.textureFlag) { + // draw -Z end as a triangle fan + glBegin(GL_TRIANGLE_FAN); + glNormal3f(0.0f, 0.0f, -1.0f); + glVertex3f(0.0f, 0.0f, -radius * nsign); + rho = PI - drho; + s = 1.0f; + for (j = slices; j >= 0; j--) { + theta = (j == slices) ? 0.0f : j * dtheta; + x = -sin(theta) * sin(rho); + y = cos(theta) * sin(rho); + z = nsign * cos(rho); + if (normals) + glNormal3f(x * nsign, y * nsign, z * nsign); + s -= ds; + glVertex3f(x * radius, y * radius, z * radius); + } + glEnd(); + } + } else if ( + super.drawStyle == GLU_LINE + || super.drawStyle == GLU_SILHOUETTE) { + // draw stack lines + for (i = 1; + i < stacks; + i++) { // stack line at i==stacks-1 was missing here + rho = i * drho; + glBegin(GL_LINE_LOOP); + for (j = 0; j < slices; j++) { + theta = j * dtheta; + x = cos(theta) * sin(rho); + y = sin(theta) * sin(rho); + z = cos(rho); + if (normals) + glNormal3f(x * nsign, y * nsign, z * nsign); + glVertex3f(x * radius, y * radius, z * radius); + } + glEnd(); + } + // draw slice lines + for (j = 0; j < slices; j++) { + theta = j * dtheta; + glBegin(GL_LINE_STRIP); + for (i = 0; i <= stacks; i++) { + rho = i * drho; + x = cos(theta) * sin(rho); + y = sin(theta) * sin(rho); + z = cos(rho); + if (normals) + glNormal3f(x * nsign, y * nsign, z * nsign); + glVertex3f(x * radius, y * radius, z * radius); + } + glEnd(); + } + } else if (super.drawStyle == GLU_POINT) { + // top and bottom-most points + glBegin(GL_POINTS); + if (normals) + glNormal3f(0.0f, 0.0f, nsign); + glVertex3f(0.0f, 0.0f, radius); + if (normals) + glNormal3f(0.0f, 0.0f, -nsign); + glVertex3f(0.0f, 0.0f, -radius); + + // loop over stacks + for (i = 1; i < stacks - 1; i++) { + rho = i * drho; + for (j = 0; j < slices; j++) { + theta = j * dtheta; + x = cos(theta) * sin(rho); + y = sin(theta) * sin(rho); + z = cos(rho); + if (normals) + glNormal3f(x * nsign, y * nsign, z * nsign); + glVertex3f(x * radius, y * radius, z * radius); + } + } + glEnd(); + } + } + +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/Util.java b/src/de/verschwiegener/lwjgl3/util/glu/Util.java new file mode 100644 index 0000000..202995d --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/Util.java @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.glu; + +import java.nio.IntBuffer; + +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL11; + +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.opengl.GL12.*; + +/** + * Util.java + *

+ *

+ * Created 7-jan-2004 + * + * @author Erik Duijs + */ +public class Util { + + /** + * temp IntBuffer of one for getting an int from some GL functions + */ + private static IntBuffer scratch = BufferUtils.createIntBuffer(16); + + /** + * Return ceiling of integer division + * + * @param a + * @param b + * + * @return int + */ + protected static int ceil(int a, int b) { + return (a % b == 0 ? a / b : a / b + 1); + } + + /** + * Normalize vector + * + * @param v + * + * @return float[] + */ + protected static float[] normalize(float[] v) { + float r; + + r = (float)Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + if ( r == 0.0 ) + return v; + + r = 1.0f / r; + + v[0] *= r; + v[1] *= r; + v[2] *= r; + + return v; + } + + /** + * Calculate cross-product + * + * @param v1 + * @param v2 + * @param result + */ + protected static void cross(float[] v1, float[] v2, float[] result) { + result[0] = v1[1] * v2[2] - v1[2] * v2[1]; + result[1] = v1[2] * v2[0] - v1[0] * v2[2]; + result[2] = v1[0] * v2[1] - v1[1] * v2[0]; + } + + /** + * Method compPerPix. + * + * @param format + * + * @return int + */ + protected static int compPerPix(int format) { + /* Determine number of components per pixel */ + switch ( format ) { + case GL_COLOR_INDEX: + case GL_STENCIL_INDEX: + case GL_DEPTH_COMPONENT: + case GL_RED: + case GL_GREEN: + case GL_BLUE: + case GL_ALPHA: + case GL_LUMINANCE: + return 1; + case GL_LUMINANCE_ALPHA: + return 2; + case GL_RGB: + case GL_BGR: + return 3; + case GL_RGBA: + case GL_BGRA: + return 4; + default : + return -1; + } + } + + /** + * Method nearestPower. + *

+ * Compute the nearest power of 2 number. This algorithm is a little strange, but it works quite well. + * + * @param value + * + * @return int + */ + protected static int nearestPower(int value) { + int i; + + i = 1; + + /* Error! */ + if ( value == 0 ) + return -1; + + for ( ; ; ) { + if ( value == 1 ) { + return i; + } else if ( value == 3 ) { + return i << 2; + } + value >>= 1; + i <<= 1; + } + } + + /** + * Method bytesPerPixel. + * + * @param format + * @param type + * + * @return int + */ + protected static int bytesPerPixel(int format, int type) { + int n, m; + + switch ( format ) { + case GL_COLOR_INDEX: + case GL_STENCIL_INDEX: + case GL_DEPTH_COMPONENT: + case GL_RED: + case GL_GREEN: + case GL_BLUE: + case GL_ALPHA: + case GL_LUMINANCE: + n = 1; + break; + case GL_LUMINANCE_ALPHA: + n = 2; + break; + case GL_RGB: + case GL_BGR: + n = 3; + break; + case GL_RGBA: + case GL_BGRA: + n = 4; + break; + default : + n = 0; + } + + switch ( type ) { + case GL_UNSIGNED_BYTE: + m = 1; + break; + case GL_BYTE: + m = 1; + break; + case GL_BITMAP: + m = 1; + break; + case GL_UNSIGNED_SHORT: + m = 2; + break; + case GL_SHORT: + m = 2; + break; + case GL_UNSIGNED_INT: + m = 4; + break; + case GL_INT: + m = 4; + break; + case GL_FLOAT: + m = 4; + break; + default : + m = 0; + } + + return n * m; + } + + /** + * Convenience method for returning an int, rather than getting it out of a buffer yourself. + * + * @param what + * + * @return int + */ + protected static int glGetIntegerv(int what) { + scratch.rewind(); + GL11.glGetIntegerv(what, scratch); + return scratch.get(); + } + +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/ActiveRegion.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/ActiveRegion.java new file mode 100644 index 0000000..981088b --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/ActiveRegion.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + +class ActiveRegion { + GLUhalfEdge eUp; /* upper edge, directed right to left */ + DictNode nodeUp; /* dictionary node corresponding to eUp */ + int windingNumber; /* used to determine which regions are + * inside the polygon */ + boolean inside; /* is this region inside the polygon? */ + boolean sentinel; /* marks fake edges at t = +/-infinity */ + boolean dirty; /* marks regions where the upper or lower + * edge has changed, but we haven't checked + * whether they intersect yet */ + boolean fixUpperEdge; /* marks temporary edges introduced when + * we process a "right vertex" (one without + * any edges leaving to the right) */ +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/CachedVertex.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/CachedVertex.java new file mode 100644 index 0000000..1fc75aa --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/CachedVertex.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + +class CachedVertex { + public double[] coords = new double[3]; + public Object data; +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/Dict.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/Dict.java new file mode 100644 index 0000000..1e6f806 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/Dict.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + +class Dict { + DictNode head; + Object frame; + DictLeq leq; + + private Dict() { + } + + static Dict dictNewDict(Object frame, DictLeq leq) { + Dict dict = new Dict(); + dict.head = new DictNode(); + + dict.head.key = null; + dict.head.next = dict.head; + dict.head.prev = dict.head; + + dict.frame = frame; + dict.leq = leq; + + return dict; + } + + static void dictDeleteDict(Dict dict) { + dict.head = null; + dict.frame = null; + dict.leq = null; + } + + static DictNode dictInsert(Dict dict, Object key) { + return dictInsertBefore(dict, dict.head, key); + } + + static DictNode dictInsertBefore(Dict dict, DictNode node, Object key) { + do { + node = node.prev; + } while (node.key != null && !dict.leq.leq(dict.frame, node.key, key)); + + DictNode newNode = new DictNode(); + newNode.key = key; + newNode.next = node.next; + node.next.prev = newNode; + newNode.prev = node; + node.next = newNode; + + return newNode; + } + + static Object dictKey(DictNode aNode) { + return aNode.key; + } + + static DictNode dictSucc(DictNode aNode) { + return aNode.next; + } + + static DictNode dictPred(DictNode aNode) { + return aNode.prev; + } + + static DictNode dictMin(Dict aDict) { + return aDict.head.next; + } + + static DictNode dictMax(Dict aDict) { + return aDict.head.prev; + } + + static void dictDelete(Dict dict, DictNode node) { + node.next.prev = node.prev; + node.prev.next = node.next; + } + + static DictNode dictSearch(Dict dict, Object key) { + DictNode node = dict.head; + + do { + node = node.next; + } while (node.key != null && !(dict.leq.leq(dict.frame, key, node.key))); + + return node; + } + + public interface DictLeq { + boolean leq(Object frame, Object key1, Object key2); + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/DictNode.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/DictNode.java new file mode 100644 index 0000000..9d40123 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/DictNode.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + +class DictNode { + Object key; + DictNode next; + DictNode prev; +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/GLUface.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/GLUface.java new file mode 100644 index 0000000..9b8beb3 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/GLUface.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + +class GLUface { + public GLUface next; /* next face (never NULL) */ + public GLUface prev; /* previous face (never NULL) */ + public GLUhalfEdge anEdge; /* a half edge with this left face */ + public Object data; /* room for client's data */ + + /* Internal data (keep hidden) */ + public GLUface trail; /* "stack" for conversion to strips */ + public boolean marked; /* flag for conversion to strips */ + public boolean inside; /* this face is in the polygon interior */ +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/GLUhalfEdge.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/GLUhalfEdge.java new file mode 100644 index 0000000..7e1fb87 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/GLUhalfEdge.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + + + +class GLUhalfEdge { + public GLUhalfEdge next; /* doubly-linked list (prev==Sym->next) */ + public GLUhalfEdge Sym; /* same edge, opposite direction */ + public GLUhalfEdge Onext; /* next edge CCW around origin */ + public GLUhalfEdge Lnext; /* next edge CCW around left face */ + public GLUvertex Org; /* origin vertex (Overtex too long) */ + public GLUface Lface; /* left face */ + + /* Internal data (keep hidden) */ + public ActiveRegion activeRegion; /* a region with this upper edge (sweep.c) */ + public int winding; /* change in winding number when crossing */ + public boolean first; + + GLUhalfEdge(boolean first) { + this.first = first; + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/GLUmesh.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/GLUmesh.java new file mode 100644 index 0000000..790b8f3 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/GLUmesh.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + + + +class GLUmesh { + GLUvertex vHead = new GLUvertex(); /* dummy header for vertex list */ + GLUface fHead = new GLUface(); /* dummy header for face list */ + GLUhalfEdge eHead = new GLUhalfEdge(true); /* dummy header for edge list */ + GLUhalfEdge eHeadSym = new GLUhalfEdge(false); /* and its symmetric counterpart */ +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/GLUtessellatorImpl.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/GLUtessellatorImpl.java new file mode 100644 index 0000000..fd1d19e --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/GLUtessellatorImpl.java @@ -0,0 +1,670 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + + +import de.verschwiegener.lwjgl3.util.glu.GLUtessellator; +import de.verschwiegener.lwjgl3.util.glu.GLUtessellatorCallback; +import de.verschwiegener.lwjgl3.util.glu.GLUtessellatorCallbackAdapter; + +import static de.verschwiegener.lwjgl3.util.glu.GLU.*; + +public class GLUtessellatorImpl implements GLUtessellator { + public static final int TESS_MAX_CACHE = 100; + + private int state; /* what begin/end calls have we seen? */ + + private GLUhalfEdge lastEdge; /* lastEdge->Org is the most recent vertex */ + GLUmesh mesh; /* stores the input contours, and eventually + the tessellation itself */ + + /*** state needed for projecting onto the sweep plane ***/ + + double[] normal = new double[3]; /* user-specified normal (if provided) */ + double[] sUnit = new double[3]; /* unit vector in s-direction (debugging) */ + double[] tUnit = new double[3]; /* unit vector in t-direction (debugging) */ + + /*** state needed for the line sweep ***/ + + private double relTolerance; /* tolerance for merging features */ + int windingRule; /* rule for determining polygon interior */ + boolean fatalError; /* fatal error: needed combine callback */ + + Dict dict; /* edge dictionary for sweep line */ + PriorityQ pq; /* priority queue of vertex events */ + GLUvertex event; /* current sweep event being processed */ + + /*** state needed for rendering callbacks (see render.c) ***/ + + boolean flagBoundary; /* mark boundary edges (use EdgeFlag) */ + boolean boundaryOnly; /* Extract contours, not triangles */ + GLUface lonelyTriList; + /* list of triangles which could not be rendered as strips or fans */ + + + + /*** state needed to cache single-contour polygons for renderCache() */ + + private boolean flushCacheOnNextVertex; /* empty cache on next vertex() call */ + int cacheCount; /* number of cached vertices */ + CachedVertex[] cache = new CachedVertex[TESS_MAX_CACHE]; /* the vertex data */ + + /*** rendering callbacks that also pass polygon data ***/ + private Object polygonData; /* client data for current polygon */ + + private GLUtessellatorCallback callBegin; + private GLUtessellatorCallback callEdgeFlag; + private GLUtessellatorCallback callVertex; + private GLUtessellatorCallback callEnd; +// private GLUtessellatorCallback callMesh; + private GLUtessellatorCallback callError; + private GLUtessellatorCallback callCombine; + + private GLUtessellatorCallback callBeginData; + private GLUtessellatorCallback callEdgeFlagData; + private GLUtessellatorCallback callVertexData; + private GLUtessellatorCallback callEndData; +// private GLUtessellatorCallback callMeshData; + private GLUtessellatorCallback callErrorData; + private GLUtessellatorCallback callCombineData; + + private static final double GLU_TESS_DEFAULT_TOLERANCE = 0.0; +// private static final int GLU_TESS_MESH = 100112; /* void (*)(GLUmesh *mesh) */ + private static GLUtessellatorCallback NULL_CB = new GLUtessellatorCallbackAdapter(); + +// #define MAX_FAST_ALLOC (MAX(sizeof(EdgePair), \ +// MAX(sizeof(GLUvertex),sizeof(GLUface)))) + + public GLUtessellatorImpl() { + state = TessState.T_DORMANT; + + normal[0] = 0; + normal[1] = 0; + normal[2] = 0; + + relTolerance = GLU_TESS_DEFAULT_TOLERANCE; + windingRule = GLU_TESS_WINDING_ODD; + flagBoundary = false; + boundaryOnly = false; + + callBegin = NULL_CB; + callEdgeFlag = NULL_CB; + callVertex = NULL_CB; + callEnd = NULL_CB; + callError = NULL_CB; + callCombine = NULL_CB; +// callMesh = NULL_CB; + + callBeginData = NULL_CB; + callEdgeFlagData = NULL_CB; + callVertexData = NULL_CB; + callEndData = NULL_CB; + callErrorData = NULL_CB; + callCombineData = NULL_CB; + + polygonData = null; + + for (int i = 0; i < cache.length; i++) { + cache[i] = new CachedVertex(); + } + } + + public static GLUtessellator gluNewTess() + { + return new GLUtessellatorImpl(); + } + + + private void makeDormant() { + /* Return the tessellator to its original dormant state. */ + + if (mesh != null) { + Mesh.__gl_meshDeleteMesh(mesh); + } + state = TessState.T_DORMANT; + lastEdge = null; + mesh = null; + } + + private void requireState(int newState) { + if (state != newState) gotoState(newState); + } + + private void gotoState(int newState) { + while (state != newState) { + /* We change the current state one level at a time, to get to + * the desired state. + */ + if (state < newState) { + if (state == TessState.T_DORMANT) { + callErrorOrErrorData(GLU_TESS_MISSING_BEGIN_POLYGON); + gluTessBeginPolygon(null); + } else if (state == TessState.T_IN_POLYGON) { + callErrorOrErrorData(GLU_TESS_MISSING_BEGIN_CONTOUR); + gluTessBeginContour(); + } + } else { + if (state == TessState.T_IN_CONTOUR) { + callErrorOrErrorData(GLU_TESS_MISSING_END_CONTOUR); + gluTessEndContour(); + } else if (state == TessState.T_IN_POLYGON) { + callErrorOrErrorData(GLU_TESS_MISSING_END_POLYGON); + /* gluTessEndPolygon( tess ) is too much work! */ + makeDormant(); + } + } + } + } + + public void gluDeleteTess() { + requireState(TessState.T_DORMANT); + } + + public void gluTessProperty(int which, double value) { + switch (which) { + case GLU_TESS_TOLERANCE: + if (value < 0.0 || value > 1.0) break; + relTolerance = value; + return; + + case GLU_TESS_WINDING_RULE: + int windingRule = (int) value; + if (windingRule != value) break; /* not an integer */ + + switch (windingRule) { + case GLU_TESS_WINDING_ODD: + case GLU_TESS_WINDING_NONZERO: + case GLU_TESS_WINDING_POSITIVE: + case GLU_TESS_WINDING_NEGATIVE: + case GLU_TESS_WINDING_ABS_GEQ_TWO: + this.windingRule = windingRule; + return; + default: + break; + } + + case GLU_TESS_BOUNDARY_ONLY: + boundaryOnly = (value != 0); + return; + + default: + callErrorOrErrorData(GLU_INVALID_ENUM); + return; + } + callErrorOrErrorData(GLU_INVALID_VALUE); + } + +/* Returns tessellator property */ + public void gluGetTessProperty(int which, double[] value, int value_offset) { + switch (which) { + case GLU_TESS_TOLERANCE: +/* tolerance should be in range [0..1] */ + assert (0.0 <= relTolerance && relTolerance <= 1.0); + value[value_offset] = relTolerance; + break; + case GLU_TESS_WINDING_RULE: + assert (windingRule == GLU_TESS_WINDING_ODD || + windingRule == GLU_TESS_WINDING_NONZERO || + windingRule == GLU_TESS_WINDING_POSITIVE || + windingRule == GLU_TESS_WINDING_NEGATIVE || + windingRule == GLU_TESS_WINDING_ABS_GEQ_TWO); + value[value_offset] = windingRule; + break; + case GLU_TESS_BOUNDARY_ONLY: + assert (boundaryOnly == true || boundaryOnly == false); + value[value_offset] = boundaryOnly ? 1 : 0; + break; + default: + value[value_offset] = 0.0; + callErrorOrErrorData(GLU_INVALID_ENUM); + break; + } + } /* gluGetTessProperty() */ + + public void gluTessNormal(double x, double y, double z) { + normal[0] = x; + normal[1] = y; + normal[2] = z; + } + + public void gluTessCallback(int which, GLUtessellatorCallback aCallback) { + switch (which) { + case GLU_TESS_BEGIN: + callBegin = aCallback == null ? NULL_CB : aCallback; + return; + case GLU_TESS_BEGIN_DATA: + callBeginData = aCallback == null ? NULL_CB : aCallback; + return; + case GLU_TESS_EDGE_FLAG: + callEdgeFlag = aCallback == null ? NULL_CB : aCallback; +/* If the client wants boundary edges to be flagged, + * we render everything as separate triangles (no strips or fans). + */ + flagBoundary = aCallback != null; + return; + case GLU_TESS_EDGE_FLAG_DATA: + callEdgeFlagData = callBegin = aCallback == null ? NULL_CB : aCallback; +/* If the client wants boundary edges to be flagged, + * we render everything as separate triangles (no strips or fans). + */ + flagBoundary = (aCallback != null); + return; + case GLU_TESS_VERTEX: + callVertex = aCallback == null ? NULL_CB : aCallback; + return; + case GLU_TESS_VERTEX_DATA: + callVertexData = aCallback == null ? NULL_CB : aCallback; + return; + case GLU_TESS_END: + callEnd = aCallback == null ? NULL_CB : aCallback; + return; + case GLU_TESS_END_DATA: + callEndData = aCallback == null ? NULL_CB : aCallback; + return; + case GLU_TESS_ERROR: + callError = aCallback == null ? NULL_CB : aCallback; + return; + case GLU_TESS_ERROR_DATA: + callErrorData = aCallback == null ? NULL_CB : aCallback; + return; + case GLU_TESS_COMBINE: + callCombine = aCallback == null ? NULL_CB : aCallback; + return; + case GLU_TESS_COMBINE_DATA: + callCombineData = aCallback == null ? NULL_CB : aCallback; + return; +// case GLU_TESS_MESH: +// callMesh = aCallback == null ? NULL_CB : aCallback; +// return; + default: + callErrorOrErrorData(GLU_INVALID_ENUM); + return; + } + } + + private boolean addVertex(double[] coords, Object vertexData) { + GLUhalfEdge e; + + e = lastEdge; + if (e == null) { +/* Make a self-loop (one vertex, one edge). */ + + e = Mesh.__gl_meshMakeEdge(mesh); + if (e == null) return false; + if (!Mesh.__gl_meshSplice(e, e.Sym)) return false; + } else { +/* Create a new vertex and edge which immediately follow e + * in the ordering around the left face. + */ + if (Mesh.__gl_meshSplitEdge(e) == null) return false; + e = e.Lnext; + } + +/* The new vertex is now e.Org. */ + e.Org.data = vertexData; + e.Org.coords[0] = coords[0]; + e.Org.coords[1] = coords[1]; + e.Org.coords[2] = coords[2]; + +/* The winding of an edge says how the winding number changes as we + * cross from the edge''s right face to its left face. We add the + * vertices in such an order that a CCW contour will add +1 to + * the winding number of the region inside the contour. + */ + e.winding = 1; + e.Sym.winding = -1; + + lastEdge = e; + + return true; + } + + private void cacheVertex(double[] coords, Object vertexData) { + if (cache[cacheCount] == null) { + cache[cacheCount] = new CachedVertex(); + } + + CachedVertex v = cache[cacheCount]; + + v.data = vertexData; + v.coords[0] = coords[0]; + v.coords[1] = coords[1]; + v.coords[2] = coords[2]; + ++cacheCount; + } + + + private boolean flushCache() { + CachedVertex[] v = cache; + + mesh = Mesh.__gl_meshNewMesh(); + if (mesh == null) return false; + + for (int i = 0; i < cacheCount; i++) { + CachedVertex vertex = v[i]; + if (!addVertex(vertex.coords, vertex.data)) return false; + } + cacheCount = 0; + flushCacheOnNextVertex = false; + + return true; + } + + public void gluTessVertex(double[] coords, int coords_offset, Object vertexData) { + int i; + boolean tooLarge = false; + double x; + double[] clamped = new double[3]; + + requireState(TessState.T_IN_CONTOUR); + + if (flushCacheOnNextVertex) { + if (!flushCache()) { + callErrorOrErrorData(GLU_OUT_OF_MEMORY); + return; + } + lastEdge = null; + } + for (i = 0; i < 3; ++i) { + x = coords[i+coords_offset]; + if (x < -GLU_TESS_MAX_COORD) { + x = -GLU_TESS_MAX_COORD; + tooLarge = true; + } + if (x > GLU_TESS_MAX_COORD) { + x = GLU_TESS_MAX_COORD; + tooLarge = true; + } + clamped[i] = x; + } + if (tooLarge) { + callErrorOrErrorData(GLU_TESS_COORD_TOO_LARGE); + } + + if (mesh == null) { + if (cacheCount < TESS_MAX_CACHE) { + cacheVertex(clamped, vertexData); + return; + } + if (!flushCache()) { + callErrorOrErrorData(GLU_OUT_OF_MEMORY); + return; + } + } + + if (!addVertex(clamped, vertexData)) { + callErrorOrErrorData(GLU_OUT_OF_MEMORY); + } + } + + + public void gluTessBeginPolygon(Object data) { + requireState(TessState.T_DORMANT); + + state = TessState.T_IN_POLYGON; + cacheCount = 0; + flushCacheOnNextVertex = false; + mesh = null; + + polygonData = data; + } + + + public void gluTessBeginContour() { + requireState(TessState.T_IN_POLYGON); + + state = TessState.T_IN_CONTOUR; + lastEdge = null; + if (cacheCount > 0) { +/* Just set a flag so we don't get confused by empty contours + * -- these can be generated accidentally with the obsolete + * NextContour() interface. + */ + flushCacheOnNextVertex = true; + } + } + + + public void gluTessEndContour() { + requireState(TessState.T_IN_CONTOUR); + state = TessState.T_IN_POLYGON; + } + + public void gluTessEndPolygon() { + GLUmesh mesh; + + try { + requireState(TessState.T_IN_POLYGON); + state = TessState.T_DORMANT; + + if (this.mesh == null) { + if (!flagBoundary /*&& callMesh == NULL_CB*/) { + +/* Try some special code to make the easy cases go quickly + * (eg. convex polygons). This code does NOT handle multiple contours, + * intersections, edge flags, and of course it does not generate + * an explicit mesh either. + */ + if (Render.__gl_renderCache(this)) { + polygonData = null; + return; + } + } + if (!flushCache()) throw new RuntimeException(); /* could've used a label*/ + } + +/* Determine the polygon normal and project vertices onto the plane + * of the polygon. + */ + Normal.__gl_projectPolygon(this); + +/* __gl_computeInterior( tess ) computes the planar arrangement specified + * by the given contours, and further subdivides this arrangement + * into regions. Each region is marked "inside" if it belongs + * to the polygon, according to the rule given by windingRule. + * Each interior region is guaranteed be monotone. + */ + if (!Sweep.__gl_computeInterior(this)) { + throw new RuntimeException(); /* could've used a label */ + } + + mesh = this.mesh; + if (!fatalError) { + boolean rc = true; + +/* If the user wants only the boundary contours, we throw away all edges + * except those which separate the interior from the exterior. + * Otherwise we tessellate all the regions marked "inside". + */ + if (boundaryOnly) { + rc = TessMono.__gl_meshSetWindingNumber(mesh, 1, true); + } else { + rc = TessMono.__gl_meshTessellateInterior(mesh); + } + if (!rc) throw new RuntimeException(); /* could've used a label */ + + Mesh.__gl_meshCheckMesh(mesh); + + if (callBegin != NULL_CB || callEnd != NULL_CB + || callVertex != NULL_CB || callEdgeFlag != NULL_CB + || callBeginData != NULL_CB + || callEndData != NULL_CB + || callVertexData != NULL_CB + || callEdgeFlagData != NULL_CB) { + if (boundaryOnly) { + Render.__gl_renderBoundary(this, mesh); /* output boundary contours */ + } else { + Render.__gl_renderMesh(this, mesh); /* output strips and fans */ + } + } +// if (callMesh != NULL_CB) { +// +///* Throw away the exterior faces, so that all faces are interior. +// * This way the user doesn't have to check the "inside" flag, +// * and we don't need to even reveal its existence. It also leaves +// * the freedom for an implementation to not generate the exterior +// * faces in the first place. +// */ +// TessMono.__gl_meshDiscardExterior(mesh); +// callMesh.mesh(mesh); /* user wants the mesh itself */ +// mesh = null; +// polygonData = null; +// return; +// } + } + Mesh.__gl_meshDeleteMesh(mesh); + polygonData = null; + mesh = null; + } catch (Exception e) { + e.printStackTrace(); + callErrorOrErrorData(GLU_OUT_OF_MEMORY); + } + } + + /*******************************************************/ + +/* Obsolete calls -- for backward compatibility */ + + public void gluBeginPolygon() { + gluTessBeginPolygon(null); + gluTessBeginContour(); + } + + +/*ARGSUSED*/ + public void gluNextContour(int type) { + gluTessEndContour(); + gluTessBeginContour(); + } + + + public void gluEndPolygon() { + gluTessEndContour(); + gluTessEndPolygon(); + } + + void callBeginOrBeginData(int a) { + if (callBeginData != NULL_CB) + callBeginData.beginData(a, polygonData); + else + callBegin.begin(a); + } + + void callVertexOrVertexData(Object a) { + if (callVertexData != NULL_CB) + callVertexData.vertexData(a, polygonData); + else + callVertex.vertex(a); + } + + void callEdgeFlagOrEdgeFlagData(boolean a) { + if (callEdgeFlagData != NULL_CB) + callEdgeFlagData.edgeFlagData(a, polygonData); + else + callEdgeFlag.edgeFlag(a); + } + + void callEndOrEndData() { + if (callEndData != NULL_CB) + callEndData.endData(polygonData); + else + callEnd.end(); + } + + void callCombineOrCombineData(double[] coords, Object[] vertexData, float[] weights, Object[] outData) { + if (callCombineData != NULL_CB) + callCombineData.combineData(coords, vertexData, weights, outData, polygonData); + else + callCombine.combine(coords, vertexData, weights, outData); + } + + void callErrorOrErrorData(int a) { + if (callErrorData != NULL_CB) + callErrorData.errorData(a, polygonData); + else + callError.error(a); + } + +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/GLUvertex.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/GLUvertex.java new file mode 100644 index 0000000..8793b76 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/GLUvertex.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + +class GLUvertex { + public GLUvertex next; /* next vertex (never NULL) */ + public GLUvertex prev; /* previous vertex (never NULL) */ + public GLUhalfEdge anEdge; /* a half-edge with this origin */ + public Object data; /* client's data */ + + /* Internal data (keep hidden) */ + public double[] coords = new double[3]; /* vertex location in 3D */ + public double s, t; /* projection onto the sweep plane */ + public int pqHandle; /* to allow deletion from priority queue */ +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/Geom.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/Geom.java new file mode 100644 index 0000000..4aafab8 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/Geom.java @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + +class Geom { + private Geom() { + } + + /* Given three vertices u,v,w such that VertLeq(u,v) && VertLeq(v,w), + * evaluates the t-coord of the edge uw at the s-coord of the vertex v. + * Returns v->t - (uw)(v->s), ie. the signed distance from uw to v. + * If uw is vertical (and thus passes thru v), the result is zero. + * + * The calculation is extremely accurate and stable, even when v + * is very close to u or w. In particular if we set v->t = 0 and + * let r be the negated result (this evaluates (uw)(v->s)), then + * r is guaranteed to satisfy MIN(u->t,w->t) <= r <= MAX(u->t,w->t). + */ + static double EdgeEval(GLUvertex u, GLUvertex v, GLUvertex w) { + double gapL, gapR; + + assert (VertLeq(u, v) && VertLeq(v, w)); + + gapL = v.s - u.s; + gapR = w.s - v.s; + + if (gapL + gapR > 0) { + if (gapL < gapR) { + return (v.t - u.t) + (u.t - w.t) * (gapL / (gapL + gapR)); + } else { + return (v.t - w.t) + (w.t - u.t) * (gapR / (gapL + gapR)); + } + } + /* vertical line */ + return 0; + } + + static double EdgeSign(GLUvertex u, GLUvertex v, GLUvertex w) { + double gapL, gapR; + + assert (VertLeq(u, v) && VertLeq(v, w)); + + gapL = v.s - u.s; + gapR = w.s - v.s; + + if (gapL + gapR > 0) { + return (v.t - w.t) * gapL + (v.t - u.t) * gapR; + } + /* vertical line */ + return 0; + } + + + /*********************************************************************** + * Define versions of EdgeSign, EdgeEval with s and t transposed. + */ + + static double TransEval(GLUvertex u, GLUvertex v, GLUvertex w) { + /* Given three vertices u,v,w such that TransLeq(u,v) && TransLeq(v,w), + * evaluates the t-coord of the edge uw at the s-coord of the vertex v. + * Returns v->s - (uw)(v->t), ie. the signed distance from uw to v. + * If uw is vertical (and thus passes thru v), the result is zero. + * + * The calculation is extremely accurate and stable, even when v + * is very close to u or w. In particular if we set v->s = 0 and + * let r be the negated result (this evaluates (uw)(v->t)), then + * r is guaranteed to satisfy MIN(u->s,w->s) <= r <= MAX(u->s,w->s). + */ + double gapL, gapR; + + assert (TransLeq(u, v) && TransLeq(v, w)); + + gapL = v.t - u.t; + gapR = w.t - v.t; + + if (gapL + gapR > 0) { + if (gapL < gapR) { + return (v.s - u.s) + (u.s - w.s) * (gapL / (gapL + gapR)); + } else { + return (v.s - w.s) + (w.s - u.s) * (gapR / (gapL + gapR)); + } + } + /* vertical line */ + return 0; + } + + static double TransSign(GLUvertex u, GLUvertex v, GLUvertex w) { + /* Returns a number whose sign matches TransEval(u,v,w) but which + * is cheaper to evaluate. Returns > 0, == 0 , or < 0 + * as v is above, on, or below the edge uw. + */ + double gapL, gapR; + + assert (TransLeq(u, v) && TransLeq(v, w)); + + gapL = v.t - u.t; + gapR = w.t - v.t; + + if (gapL + gapR > 0) { + return (v.s - w.s) * gapL + (v.s - u.s) * gapR; + } + /* vertical line */ + return 0; + } + + + static boolean VertCCW(GLUvertex u, GLUvertex v, GLUvertex w) { + /* For almost-degenerate situations, the results are not reliable. + * Unless the floating-point arithmetic can be performed without + * rounding errors, *any* implementation will give incorrect results + * on some degenerate inputs, so the client must have some way to + * handle this situation. + */ + return (u.s * (v.t - w.t) + v.s * (w.t - u.t) + w.s * (u.t - v.t)) >= 0; + } + +/* Given parameters a,x,b,y returns the value (b*x+a*y)/(a+b), + * or (x+y)/2 if a==b==0. It requires that a,b >= 0, and enforces + * this in the rare case that one argument is slightly negative. + * The implementation is extremely stable numerically. + * In particular it guarantees that the result r satisfies + * MIN(x,y) <= r <= MAX(x,y), and the results are very accurate + * even when a and b differ greatly in magnitude. + */ + static double Interpolate(double a, double x, double b, double y) { + a = (a < 0) ? 0 : a; + b = (b < 0) ? 0 : b; + if (a <= b) { + if (b == 0) { + return (x + y) / 2.0; + } else { + return (x + (y - x) * (a / (a + b))); + } + } else { + return (y + (x - y) * (b / (a + b))); + } + } + + static void EdgeIntersect(GLUvertex o1, GLUvertex d1, + GLUvertex o2, GLUvertex d2, + GLUvertex v) +/* Given edges (o1,d1) and (o2,d2), compute their point of intersection. + * The computed point is guaranteed to lie in the intersection of the + * bounding rectangles defined by each edge. + */ { + double z1, z2; + + /* This is certainly not the most efficient way to find the intersection + * of two line segments, but it is very numerically stable. + * + * Strategy: find the two middle vertices in the VertLeq ordering, + * and interpolate the intersection s-value from these. Then repeat + * using the TransLeq ordering to find the intersection t-value. + */ + + if (!VertLeq(o1, d1)) { + GLUvertex temp = o1; + o1 = d1; + d1 = temp; + } + if (!VertLeq(o2, d2)) { + GLUvertex temp = o2; + o2 = d2; + d2 = temp; + } + if (!VertLeq(o1, o2)) { + GLUvertex temp = o1; + o1 = o2; + o2 = temp; + temp = d1; + d1 = d2; + d2 = temp; + } + + if (!VertLeq(o2, d1)) { + /* Technically, no intersection -- do our best */ + v.s = (o2.s + d1.s) / 2.0; + } else if (VertLeq(d1, d2)) { + /* Interpolate between o2 and d1 */ + z1 = EdgeEval(o1, o2, d1); + z2 = EdgeEval(o2, d1, d2); + if (z1 + z2 < 0) { + z1 = -z1; + z2 = -z2; + } + v.s = Interpolate(z1, o2.s, z2, d1.s); + } else { + /* Interpolate between o2 and d2 */ + z1 = EdgeSign(o1, o2, d1); + z2 = -EdgeSign(o1, d2, d1); + if (z1 + z2 < 0) { + z1 = -z1; + z2 = -z2; + } + v.s = Interpolate(z1, o2.s, z2, d2.s); + } + + /* Now repeat the process for t */ + + if (!TransLeq(o1, d1)) { + GLUvertex temp = o1; + o1 = d1; + d1 = temp; + } + if (!TransLeq(o2, d2)) { + GLUvertex temp = o2; + o2 = d2; + d2 = temp; + } + if (!TransLeq(o1, o2)) { + GLUvertex temp = o2; + o2 = o1; + o1 = temp; + temp = d2; + d2 = d1; + d1 = temp; + } + + if (!TransLeq(o2, d1)) { + /* Technically, no intersection -- do our best */ + v.t = (o2.t + d1.t) / 2.0; + } else if (TransLeq(d1, d2)) { + /* Interpolate between o2 and d1 */ + z1 = TransEval(o1, o2, d1); + z2 = TransEval(o2, d1, d2); + if (z1 + z2 < 0) { + z1 = -z1; + z2 = -z2; + } + v.t = Interpolate(z1, o2.t, z2, d1.t); + } else { + /* Interpolate between o2 and d2 */ + z1 = TransSign(o1, o2, d1); + z2 = -TransSign(o1, d2, d1); + if (z1 + z2 < 0) { + z1 = -z1; + z2 = -z2; + } + v.t = Interpolate(z1, o2.t, z2, d2.t); + } + } + + static boolean VertEq(GLUvertex u, GLUvertex v) { + return u.s == v.s && u.t == v.t; + } + + static boolean VertLeq(GLUvertex u, GLUvertex v) { + return u.s < v.s || (u.s == v.s && u.t <= v.t); + } + +/* Versions of VertLeq, EdgeSign, EdgeEval with s and t transposed. */ + + static boolean TransLeq(GLUvertex u, GLUvertex v) { + return u.t < v.t || (u.t == v.t && u.s <= v.s); + } + + static boolean EdgeGoesLeft(GLUhalfEdge e) { + return VertLeq(e.Sym.Org, e.Org); + } + + static boolean EdgeGoesRight(GLUhalfEdge e) { + return VertLeq(e.Org, e.Sym.Org); + } + + static double VertL1dist(GLUvertex u, GLUvertex v) { + return Math.abs(u.s - v.s) + Math.abs(u.t - v.t); + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/Mesh.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/Mesh.java new file mode 100644 index 0000000..e6ad874 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/Mesh.java @@ -0,0 +1,766 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + +class Mesh { + private Mesh() { + } + + /************************ Utility Routines ************************/ +/* MakeEdge creates a new pair of half-edges which form their own loop. + * No vertex or face structures are allocated, but these must be assigned + * before the current edge operation is completed. + */ + static GLUhalfEdge MakeEdge(GLUhalfEdge eNext) { + GLUhalfEdge e; + GLUhalfEdge eSym; + GLUhalfEdge ePrev; + +// EdgePair * pair = (EdgePair *) +// memAlloc(sizeof(EdgePair)); +// if (pair == NULL) return NULL; +// +// e = &pair - > e; + e = new GLUhalfEdge(true); +// eSym = &pair - > eSym; + eSym = new GLUhalfEdge(false); + + + /* Make sure eNext points to the first edge of the edge pair */ + if (!eNext.first) { + eNext = eNext.Sym; + } + + /* Insert in circular doubly-linked list before eNext. + * Note that the prev pointer is stored in Sym->next. + */ + ePrev = eNext.Sym.next; + eSym.next = ePrev; + ePrev.Sym.next = e; + e.next = eNext; + eNext.Sym.next = eSym; + + e.Sym = eSym; + e.Onext = e; + e.Lnext = eSym; + e.Org = null; + e.Lface = null; + e.winding = 0; + e.activeRegion = null; + + eSym.Sym = e; + eSym.Onext = eSym; + eSym.Lnext = e; + eSym.Org = null; + eSym.Lface = null; + eSym.winding = 0; + eSym.activeRegion = null; + + return e; + } + +/* Splice( a, b ) is best described by the Guibas/Stolfi paper or the + * CS348a notes (see mesh.h). Basically it modifies the mesh so that + * a->Onext and b->Onext are exchanged. This can have various effects + * depending on whether a and b belong to different face or vertex rings. + * For more explanation see __gl_meshSplice() below. + */ + static void Splice(GLUhalfEdge a, GLUhalfEdge b) { + GLUhalfEdge aOnext = a.Onext; + GLUhalfEdge bOnext = b.Onext; + + aOnext.Sym.Lnext = b; + bOnext.Sym.Lnext = a; + a.Onext = bOnext; + b.Onext = aOnext; + } + +/* MakeVertex( newVertex, eOrig, vNext ) attaches a new vertex and makes it the + * origin of all edges in the vertex loop to which eOrig belongs. "vNext" gives + * a place to insert the new vertex in the global vertex list. We insert + * the new vertex *before* vNext so that algorithms which walk the vertex + * list will not see the newly created vertices. + */ + static void MakeVertex(GLUvertex newVertex, + GLUhalfEdge eOrig, GLUvertex vNext) { + GLUhalfEdge e; + GLUvertex vPrev; + GLUvertex vNew = newVertex; + + assert (vNew != null); + + /* insert in circular doubly-linked list before vNext */ + vPrev = vNext.prev; + vNew.prev = vPrev; + vPrev.next = vNew; + vNew.next = vNext; + vNext.prev = vNew; + + vNew.anEdge = eOrig; + vNew.data = null; + /* leave coords, s, t undefined */ + + /* fix other edges on this vertex loop */ + e = eOrig; + do { + e.Org = vNew; + e = e.Onext; + } while (e != eOrig); + } + +/* MakeFace( newFace, eOrig, fNext ) attaches a new face and makes it the left + * face of all edges in the face loop to which eOrig belongs. "fNext" gives + * a place to insert the new face in the global face list. We insert + * the new face *before* fNext so that algorithms which walk the face + * list will not see the newly created faces. + */ + static void MakeFace(GLUface newFace, GLUhalfEdge eOrig, GLUface fNext) { + GLUhalfEdge e; + GLUface fPrev; + GLUface fNew = newFace; + + assert (fNew != null); + + /* insert in circular doubly-linked list before fNext */ + fPrev = fNext.prev; + fNew.prev = fPrev; + fPrev.next = fNew; + fNew.next = fNext; + fNext.prev = fNew; + + fNew.anEdge = eOrig; + fNew.data = null; + fNew.trail = null; + fNew.marked = false; + + /* The new face is marked "inside" if the old one was. This is a + * convenience for the common case where a face has been split in two. + */ + fNew.inside = fNext.inside; + + /* fix other edges on this face loop */ + e = eOrig; + do { + e.Lface = fNew; + e = e.Lnext; + } while (e != eOrig); + } + +/* KillEdge( eDel ) destroys an edge (the half-edges eDel and eDel->Sym), + * and removes from the global edge list. + */ + static void KillEdge(GLUhalfEdge eDel) { + GLUhalfEdge ePrev, eNext; + + /* Half-edges are allocated in pairs, see EdgePair above */ + if (!eDel.first) { + eDel = eDel.Sym; + } + + /* delete from circular doubly-linked list */ + eNext = eDel.next; + ePrev = eDel.Sym.next; + eNext.Sym.next = ePrev; + ePrev.Sym.next = eNext; + } + + +/* KillVertex( vDel ) destroys a vertex and removes it from the global + * vertex list. It updates the vertex loop to point to a given new vertex. + */ + static void KillVertex(GLUvertex vDel, GLUvertex newOrg) { + GLUhalfEdge e, eStart = vDel.anEdge; + GLUvertex vPrev, vNext; + + /* change the origin of all affected edges */ + e = eStart; + do { + e.Org = newOrg; + e = e.Onext; + } while (e != eStart); + + /* delete from circular doubly-linked list */ + vPrev = vDel.prev; + vNext = vDel.next; + vNext.prev = vPrev; + vPrev.next = vNext; + } + +/* KillFace( fDel ) destroys a face and removes it from the global face + * list. It updates the face loop to point to a given new face. + */ + static void KillFace(GLUface fDel, GLUface newLface) { + GLUhalfEdge e, eStart = fDel.anEdge; + GLUface fPrev, fNext; + + /* change the left face of all affected edges */ + e = eStart; + do { + e.Lface = newLface; + e = e.Lnext; + } while (e != eStart); + + /* delete from circular doubly-linked list */ + fPrev = fDel.prev; + fNext = fDel.next; + fNext.prev = fPrev; + fPrev.next = fNext; + } + + + /****************** Basic Edge Operations **********************/ + +/* __gl_meshMakeEdge creates one edge, two vertices, and a loop (face). + * The loop consists of the two new half-edges. + */ + public static GLUhalfEdge __gl_meshMakeEdge(GLUmesh mesh) { + GLUvertex newVertex1 = new GLUvertex(); + GLUvertex newVertex2 = new GLUvertex(); + GLUface newFace = new GLUface(); + GLUhalfEdge e; + + e = MakeEdge(mesh.eHead); + if (e == null) return null; + + MakeVertex(newVertex1, e, mesh.vHead); + MakeVertex(newVertex2, e.Sym, mesh.vHead); + MakeFace(newFace, e, mesh.fHead); + return e; + } + + +/* __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the + * mesh connectivity and topology. It changes the mesh so that + * eOrg->Onext <- OLD( eDst->Onext ) + * eDst->Onext <- OLD( eOrg->Onext ) + * where OLD(...) means the value before the meshSplice operation. + * + * This can have two effects on the vertex structure: + * - if eOrg->Org != eDst->Org, the two vertices are merged together + * - if eOrg->Org == eDst->Org, the origin is split into two vertices + * In both cases, eDst->Org is changed and eOrg->Org is untouched. + * + * Similarly (and independently) for the face structure, + * - if eOrg->Lface == eDst->Lface, one loop is split into two + * - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one + * In both cases, eDst->Lface is changed and eOrg->Lface is unaffected. + * + * Some special cases: + * If eDst == eOrg, the operation has no effect. + * If eDst == eOrg->Lnext, the new face will have a single edge. + * If eDst == eOrg->Lprev, the old face will have a single edge. + * If eDst == eOrg->Onext, the new vertex will have a single edge. + * If eDst == eOrg->Oprev, the old vertex will have a single edge. + */ + public static boolean __gl_meshSplice(GLUhalfEdge eOrg, GLUhalfEdge eDst) { + boolean joiningLoops = false; + boolean joiningVertices = false; + + if (eOrg == eDst) return true; + + if (eDst.Org != eOrg.Org) { + /* We are merging two disjoint vertices -- destroy eDst->Org */ + joiningVertices = true; + KillVertex(eDst.Org, eOrg.Org); + } + if (eDst.Lface != eOrg.Lface) { + /* We are connecting two disjoint loops -- destroy eDst.Lface */ + joiningLoops = true; + KillFace(eDst.Lface, eOrg.Lface); + } + + /* Change the edge structure */ + Splice(eDst, eOrg); + + if (!joiningVertices) { + GLUvertex newVertex = new GLUvertex(); + + /* We split one vertex into two -- the new vertex is eDst.Org. + * Make sure the old vertex points to a valid half-edge. + */ + MakeVertex(newVertex, eDst, eOrg.Org); + eOrg.Org.anEdge = eOrg; + } + if (!joiningLoops) { + GLUface newFace = new GLUface(); + + /* We split one loop into two -- the new loop is eDst.Lface. + * Make sure the old face points to a valid half-edge. + */ + MakeFace(newFace, eDst, eOrg.Lface); + eOrg.Lface.anEdge = eOrg; + } + + return true; + } + + +/* __gl_meshDelete( eDel ) removes the edge eDel. There are several cases: + * if (eDel.Lface != eDel.Rface), we join two loops into one; the loop + * eDel.Lface is deleted. Otherwise, we are splitting one loop into two; + * the newly created loop will contain eDel.Dst. If the deletion of eDel + * would create isolated vertices, those are deleted as well. + * + * This function could be implemented as two calls to __gl_meshSplice + * plus a few calls to memFree, but this would allocate and delete + * unnecessary vertices and faces. + */ + static boolean __gl_meshDelete(GLUhalfEdge eDel) { + GLUhalfEdge eDelSym = eDel.Sym; + boolean joiningLoops = false; + + /* First step: disconnect the origin vertex eDel.Org. We make all + * changes to get a consistent mesh in this "intermediate" state. + */ + if (eDel.Lface != eDel.Sym.Lface) { + /* We are joining two loops into one -- remove the left face */ + joiningLoops = true; + KillFace(eDel.Lface, eDel.Sym.Lface); + } + + if (eDel.Onext == eDel) { + KillVertex(eDel.Org, null); + } else { + /* Make sure that eDel.Org and eDel.Sym.Lface point to valid half-edges */ + eDel.Sym.Lface.anEdge = eDel.Sym.Lnext; + eDel.Org.anEdge = eDel.Onext; + + Splice(eDel, eDel.Sym.Lnext); + if (!joiningLoops) { + GLUface newFace = new GLUface(); + + /* We are splitting one loop into two -- create a new loop for eDel. */ + MakeFace(newFace, eDel, eDel.Lface); + } + } + + /* Claim: the mesh is now in a consistent state, except that eDel.Org + * may have been deleted. Now we disconnect eDel.Dst. + */ + if (eDelSym.Onext == eDelSym) { + KillVertex(eDelSym.Org, null); + KillFace(eDelSym.Lface, null); + } else { + /* Make sure that eDel.Dst and eDel.Lface point to valid half-edges */ + eDel.Lface.anEdge = eDelSym.Sym.Lnext; + eDelSym.Org.anEdge = eDelSym.Onext; + Splice(eDelSym, eDelSym.Sym.Lnext); + } + + /* Any isolated vertices or faces have already been freed. */ + KillEdge(eDel); + + return true; + } + + + /******************** Other Edge Operations **********************/ + +/* All these routines can be implemented with the basic edge + * operations above. They are provided for convenience and efficiency. + */ + + +/* __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that + * eNew == eOrg.Lnext, and eNew.Dst is a newly created vertex. + * eOrg and eNew will have the same left face. + */ + static GLUhalfEdge __gl_meshAddEdgeVertex(GLUhalfEdge eOrg) { + GLUhalfEdge eNewSym; + GLUhalfEdge eNew = MakeEdge(eOrg); + + eNewSym = eNew.Sym; + + /* Connect the new edge appropriately */ + Splice(eNew, eOrg.Lnext); + + /* Set the vertex and face information */ + eNew.Org = eOrg.Sym.Org; + { + GLUvertex newVertex = new GLUvertex(); + + MakeVertex(newVertex, eNewSym, eNew.Org); + } + eNew.Lface = eNewSym.Lface = eOrg.Lface; + + return eNew; + } + + +/* __gl_meshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew, + * such that eNew == eOrg.Lnext. The new vertex is eOrg.Sym.Org == eNew.Org. + * eOrg and eNew will have the same left face. + */ + public static GLUhalfEdge __gl_meshSplitEdge(GLUhalfEdge eOrg) { + GLUhalfEdge eNew; + GLUhalfEdge tempHalfEdge = __gl_meshAddEdgeVertex(eOrg); + + eNew = tempHalfEdge.Sym; + + /* Disconnect eOrg from eOrg.Sym.Org and connect it to eNew.Org */ + Splice(eOrg.Sym, eOrg.Sym.Sym.Lnext); + Splice(eOrg.Sym, eNew); + + /* Set the vertex and face information */ + eOrg.Sym.Org = eNew.Org; + eNew.Sym.Org.anEdge = eNew.Sym; /* may have pointed to eOrg.Sym */ + eNew.Sym.Lface = eOrg.Sym.Lface; + eNew.winding = eOrg.winding; /* copy old winding information */ + eNew.Sym.winding = eOrg.Sym.winding; + + return eNew; + } + + +/* __gl_meshConnect( eOrg, eDst ) creates a new edge from eOrg.Sym.Org + * to eDst.Org, and returns the corresponding half-edge eNew. + * If eOrg.Lface == eDst.Lface, this splits one loop into two, + * and the newly created loop is eNew.Lface. Otherwise, two disjoint + * loops are merged into one, and the loop eDst.Lface is destroyed. + * + * If (eOrg == eDst), the new face will have only two edges. + * If (eOrg.Lnext == eDst), the old face is reduced to a single edge. + * If (eOrg.Lnext.Lnext == eDst), the old face is reduced to two edges. + */ + static GLUhalfEdge __gl_meshConnect(GLUhalfEdge eOrg, GLUhalfEdge eDst) { + GLUhalfEdge eNewSym; + boolean joiningLoops = false; + GLUhalfEdge eNew = MakeEdge(eOrg); + + eNewSym = eNew.Sym; + + if (eDst.Lface != eOrg.Lface) { + /* We are connecting two disjoint loops -- destroy eDst.Lface */ + joiningLoops = true; + KillFace(eDst.Lface, eOrg.Lface); + } + + /* Connect the new edge appropriately */ + Splice(eNew, eOrg.Lnext); + Splice(eNewSym, eDst); + + /* Set the vertex and face information */ + eNew.Org = eOrg.Sym.Org; + eNewSym.Org = eDst.Org; + eNew.Lface = eNewSym.Lface = eOrg.Lface; + + /* Make sure the old face points to a valid half-edge */ + eOrg.Lface.anEdge = eNewSym; + + if (!joiningLoops) { + GLUface newFace = new GLUface(); + + /* We split one loop into two -- the new loop is eNew.Lface */ + MakeFace(newFace, eNew, eOrg.Lface); + } + return eNew; + } + + + /******************** Other Operations **********************/ + +/* __gl_meshZapFace( fZap ) destroys a face and removes it from the + * global face list. All edges of fZap will have a null pointer as their + * left face. Any edges which also have a null pointer as their right face + * are deleted entirely (along with any isolated vertices this produces). + * An entire mesh can be deleted by zapping its faces, one at a time, + * in any order. Zapped faces cannot be used in further mesh operations! + */ + static void __gl_meshZapFace(GLUface fZap) { + GLUhalfEdge eStart = fZap.anEdge; + GLUhalfEdge e, eNext, eSym; + GLUface fPrev, fNext; + + /* walk around face, deleting edges whose right face is also null */ + eNext = eStart.Lnext; + do { + e = eNext; + eNext = e.Lnext; + + e.Lface = null; + if (e.Sym.Lface == null) { + /* delete the edge -- see __gl_MeshDelete above */ + + if (e.Onext == e) { + KillVertex(e.Org, null); + } else { + /* Make sure that e.Org points to a valid half-edge */ + e.Org.anEdge = e.Onext; + Splice(e, e.Sym.Lnext); + } + eSym = e.Sym; + if (eSym.Onext == eSym) { + KillVertex(eSym.Org, null); + } else { + /* Make sure that eSym.Org points to a valid half-edge */ + eSym.Org.anEdge = eSym.Onext; + Splice(eSym, eSym.Sym.Lnext); + } + KillEdge(e); + } + } while (e != eStart); + + /* delete from circular doubly-linked list */ + fPrev = fZap.prev; + fNext = fZap.next; + fNext.prev = fPrev; + fPrev.next = fNext; + } + + +/* __gl_meshNewMesh() creates a new mesh with no edges, no vertices, + * and no loops (what we usually call a "face"). + */ + public static GLUmesh __gl_meshNewMesh() { + GLUvertex v; + GLUface f; + GLUhalfEdge e; + GLUhalfEdge eSym; + GLUmesh mesh = new GLUmesh(); + + v = mesh.vHead; + f = mesh.fHead; + e = mesh.eHead; + eSym = mesh.eHeadSym; + + v.next = v.prev = v; + v.anEdge = null; + v.data = null; + + f.next = f.prev = f; + f.anEdge = null; + f.data = null; + f.trail = null; + f.marked = false; + f.inside = false; + + e.next = e; + e.Sym = eSym; + e.Onext = null; + e.Lnext = null; + e.Org = null; + e.Lface = null; + e.winding = 0; + e.activeRegion = null; + + eSym.next = eSym; + eSym.Sym = e; + eSym.Onext = null; + eSym.Lnext = null; + eSym.Org = null; + eSym.Lface = null; + eSym.winding = 0; + eSym.activeRegion = null; + + return mesh; + } + + +/* __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in + * both meshes, and returns the new mesh (the old meshes are destroyed). + */ + static GLUmesh __gl_meshUnion(GLUmesh mesh1, GLUmesh mesh2) { + GLUface f1 = mesh1.fHead; + GLUvertex v1 = mesh1.vHead; + GLUhalfEdge e1 = mesh1.eHead; + GLUface f2 = mesh2.fHead; + GLUvertex v2 = mesh2.vHead; + GLUhalfEdge e2 = mesh2.eHead; + + /* Add the faces, vertices, and edges of mesh2 to those of mesh1 */ + if (f2.next != f2) { + f1.prev.next = f2.next; + f2.next.prev = f1.prev; + f2.prev.next = f1; + f1.prev = f2.prev; + } + + if (v2.next != v2) { + v1.prev.next = v2.next; + v2.next.prev = v1.prev; + v2.prev.next = v1; + v1.prev = v2.prev; + } + + if (e2.next != e2) { + e1.Sym.next.Sym.next = e2.next; + e2.next.Sym.next = e1.Sym.next; + e2.Sym.next.Sym.next = e1; + e1.Sym.next = e2.Sym.next; + } + + return mesh1; + } + + +/* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh. + */ + static void __gl_meshDeleteMeshZap(GLUmesh mesh) { + GLUface fHead = mesh.fHead; + + while (fHead.next != fHead) { + __gl_meshZapFace(fHead.next); + } + assert (mesh.vHead.next == mesh.vHead); + } + +/* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh. + */ + public static void __gl_meshDeleteMesh(GLUmesh mesh) { + GLUface f, fNext; + GLUvertex v, vNext; + GLUhalfEdge e, eNext; + + for (f = mesh.fHead.next; f != mesh.fHead; f = fNext) { + fNext = f.next; + } + + for (v = mesh.vHead.next; v != mesh.vHead; v = vNext) { + vNext = v.next; + } + + for (e = mesh.eHead.next; e != mesh.eHead; e = eNext) { + /* One call frees both e and e.Sym (see EdgePair above) */ + eNext = e.next; + } + } + +/* __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency. + */ + public static void __gl_meshCheckMesh(GLUmesh mesh) { + GLUface fHead = mesh.fHead; + GLUvertex vHead = mesh.vHead; + GLUhalfEdge eHead = mesh.eHead; + GLUface f, fPrev; + GLUvertex v, vPrev; + GLUhalfEdge e, ePrev; + + fPrev = fHead; + for (fPrev = fHead; (f = fPrev.next) != fHead; fPrev = f) { + assert (f.prev == fPrev); + e = f.anEdge; + do { + assert (e.Sym != e); + assert (e.Sym.Sym == e); + assert (e.Lnext.Onext.Sym == e); + assert (e.Onext.Sym.Lnext == e); + assert (e.Lface == f); + e = e.Lnext; + } while (e != f.anEdge); + } + assert (f.prev == fPrev && f.anEdge == null && f.data == null); + + vPrev = vHead; + for (vPrev = vHead; (v = vPrev.next) != vHead; vPrev = v) { + assert (v.prev == vPrev); + e = v.anEdge; + do { + assert (e.Sym != e); + assert (e.Sym.Sym == e); + assert (e.Lnext.Onext.Sym == e); + assert (e.Onext.Sym.Lnext == e); + assert (e.Org == v); + e = e.Onext; + } while (e != v.anEdge); + } + assert (v.prev == vPrev && v.anEdge == null && v.data == null); + + ePrev = eHead; + for (ePrev = eHead; (e = ePrev.next) != eHead; ePrev = e) { + assert (e.Sym.next == ePrev.Sym); + assert (e.Sym != e); + assert (e.Sym.Sym == e); + assert (e.Org != null); + assert (e.Sym.Org != null); + assert (e.Lnext.Onext.Sym == e); + assert (e.Onext.Sym.Lnext == e); + } + assert (e.Sym.next == ePrev.Sym + && e.Sym == mesh.eHeadSym + && e.Sym.Sym == e + && e.Org == null && e.Sym.Org == null + && e.Lface == null && e.Sym.Lface == null); + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/Normal.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/Normal.java new file mode 100644 index 0000000..3a1e6d2 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/Normal.java @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + +import de.verschwiegener.lwjgl3.util.glu.GLU; + +class Normal { + private Normal() { + } + + static boolean SLANTED_SWEEP; + static double S_UNIT_X; /* Pre-normalized */ + static double S_UNIT_Y; + private static final boolean TRUE_PROJECT = false; + + static { + if (SLANTED_SWEEP) { +/* The "feature merging" is not intended to be complete. There are + * special cases where edges are nearly parallel to the sweep line + * which are not implemented. The algorithm should still behave + * robustly (ie. produce a reasonable tesselation) in the presence + * of such edges, however it may miss features which could have been + * merged. We could minimize this effect by choosing the sweep line + * direction to be something unusual (ie. not parallel to one of the + * coordinate axes). + */ + S_UNIT_X = 0.50941539564955385; /* Pre-normalized */ + S_UNIT_Y = 0.86052074622010633; + } else { + S_UNIT_X = 1.0; + S_UNIT_Y = 0.0; + } + } + + private static double Dot(double[] u, double[] v) { + return (u[0] * v[0] + u[1] * v[1] + u[2] * v[2]); + } + + static void Normalize(double[] v) { + double len = v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; + + assert (len > 0); + len = Math.sqrt(len); + v[0] /= len; + v[1] /= len; + v[2] /= len; + } + + static int LongAxis(double[] v) { + int i = 0; + + if (Math.abs(v[1]) > Math.abs(v[0])) { + i = 1; + } + if (Math.abs(v[2]) > Math.abs(v[i])) { + i = 2; + } + return i; + } + + static void ComputeNormal(GLUtessellatorImpl tess, double[] norm) { + GLUvertex v, v1, v2; + double c, tLen2, maxLen2; + double[] maxVal, minVal, d1, d2, tNorm; + GLUvertex[] maxVert, minVert; + GLUvertex vHead = tess.mesh.vHead; + int i; + + maxVal = new double[3]; + minVal = new double[3]; + minVert = new GLUvertex[3]; + maxVert = new GLUvertex[3]; + d1 = new double[3]; + d2 = new double[3]; + tNorm = new double[3]; + + maxVal[0] = maxVal[1] = maxVal[2] = -2 * GLU.TESS_MAX_COORD; + minVal[0] = minVal[1] = minVal[2] = 2 * GLU.TESS_MAX_COORD; + + for (v = vHead.next; v != vHead; v = v.next) { + for (i = 0; i < 3; ++i) { + c = v.coords[i]; + if (c < minVal[i]) { + minVal[i] = c; + minVert[i] = v; + } + if (c > maxVal[i]) { + maxVal[i] = c; + maxVert[i] = v; + } + } + } + +/* Find two vertices separated by at least 1/sqrt(3) of the maximum + * distance between any two vertices + */ + i = 0; + if (maxVal[1] - minVal[1] > maxVal[0] - minVal[0]) { + i = 1; + } + if (maxVal[2] - minVal[2] > maxVal[i] - minVal[i]) { + i = 2; + } + if (minVal[i] >= maxVal[i]) { +/* All vertices are the same -- normal doesn't matter */ + norm[0] = 0; + norm[1] = 0; + norm[2] = 1; + return; + } + +/* Look for a third vertex which forms the triangle with maximum area + * (Length of normal == twice the triangle area) + */ + maxLen2 = 0; + v1 = minVert[i]; + v2 = maxVert[i]; + d1[0] = v1.coords[0] - v2.coords[0]; + d1[1] = v1.coords[1] - v2.coords[1]; + d1[2] = v1.coords[2] - v2.coords[2]; + for (v = vHead.next; v != vHead; v = v.next) { + d2[0] = v.coords[0] - v2.coords[0]; + d2[1] = v.coords[1] - v2.coords[1]; + d2[2] = v.coords[2] - v2.coords[2]; + tNorm[0] = d1[1] * d2[2] - d1[2] * d2[1]; + tNorm[1] = d1[2] * d2[0] - d1[0] * d2[2]; + tNorm[2] = d1[0] * d2[1] - d1[1] * d2[0]; + tLen2 = tNorm[0] * tNorm[0] + tNorm[1] * tNorm[1] + tNorm[2] * tNorm[2]; + if (tLen2 > maxLen2) { + maxLen2 = tLen2; + norm[0] = tNorm[0]; + norm[1] = tNorm[1]; + norm[2] = tNorm[2]; + } + } + + if (maxLen2 <= 0) { +/* All points lie on a single line -- any decent normal will do */ + norm[0] = norm[1] = norm[2] = 0; + norm[LongAxis(d1)] = 1; + } + } + + static void CheckOrientation(GLUtessellatorImpl tess) { + double area; + GLUface f, fHead = tess.mesh.fHead; + GLUvertex v, vHead = tess.mesh.vHead; + GLUhalfEdge e; + +/* When we compute the normal automatically, we choose the orientation + * so that the the sum of the signed areas of all contours is non-negative. + */ + area = 0; + for (f = fHead.next; f != fHead; f = f.next) { + e = f.anEdge; + if (e.winding <= 0) continue; + do { + area += (e.Org.s - e.Sym.Org.s) * (e.Org.t + e.Sym.Org.t); + e = e.Lnext; + } while (e != f.anEdge); + } + if (area < 0) { +/* Reverse the orientation by flipping all the t-coordinates */ + for (v = vHead.next; v != vHead; v = v.next) { + v.t = -v.t; + } + tess.tUnit[0] = -tess.tUnit[0]; + tess.tUnit[1] = -tess.tUnit[1]; + tess.tUnit[2] = -tess.tUnit[2]; + } + } + +/* Determine the polygon normal and project vertices onto the plane + * of the polygon. + */ + public static void __gl_projectPolygon(GLUtessellatorImpl tess) { + GLUvertex v, vHead = tess.mesh.vHead; + double w; + double[] norm = new double[3]; + double[] sUnit, tUnit; + int i; + boolean computedNormal = false; + + norm[0] = tess.normal[0]; + norm[1] = tess.normal[1]; + norm[2] = tess.normal[2]; + if (norm[0] == 0 && norm[1] == 0 && norm[2] == 0) { + ComputeNormal(tess, norm); + computedNormal = true; + } + sUnit = tess.sUnit; + tUnit = tess.tUnit; + i = LongAxis(norm); + + if (TRUE_PROJECT) { +/* Choose the initial sUnit vector to be approximately perpendicular + * to the normal. + */ + Normalize(norm); + + sUnit[i] = 0; + sUnit[(i + 1) % 3] = S_UNIT_X; + sUnit[(i + 2) % 3] = S_UNIT_Y; + +/* Now make it exactly perpendicular */ + w = Dot(sUnit, norm); + sUnit[0] -= w * norm[0]; + sUnit[1] -= w * norm[1]; + sUnit[2] -= w * norm[2]; + Normalize(sUnit); + +/* Choose tUnit so that (sUnit,tUnit,norm) form a right-handed frame */ + tUnit[0] = norm[1] * sUnit[2] - norm[2] * sUnit[1]; + tUnit[1] = norm[2] * sUnit[0] - norm[0] * sUnit[2]; + tUnit[2] = norm[0] * sUnit[1] - norm[1] * sUnit[0]; + Normalize(tUnit); + } else { +/* Project perpendicular to a coordinate axis -- better numerically */ + sUnit[i] = 0; + sUnit[(i + 1) % 3] = S_UNIT_X; + sUnit[(i + 2) % 3] = S_UNIT_Y; + + tUnit[i] = 0; + tUnit[(i + 1) % 3] = (norm[i] > 0) ? -S_UNIT_Y : S_UNIT_Y; + tUnit[(i + 2) % 3] = (norm[i] > 0) ? S_UNIT_X : -S_UNIT_X; + } + +/* Project the vertices onto the sweep plane */ + for (v = vHead.next; v != vHead; v = v.next) { + v.s = Dot(v.coords, sUnit); + v.t = Dot(v.coords, tUnit); + } + if (computedNormal) { + CheckOrientation(tess); + } + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/PriorityQ.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/PriorityQ.java new file mode 100644 index 0000000..c66ef3b --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/PriorityQ.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + +abstract class PriorityQ { + public static final int INIT_SIZE = 32; + + public static class PQnode { + int handle; + } + + public static class PQhandleElem { + Object key; + int node; + } + + public interface Leq { + boolean leq(Object key1, Object key2); + } + + // #ifdef FOR_TRITE_TEST_PROGRAM +// private static boolean LEQ(PriorityQCommon.Leq leq, Object x,Object y) { +// return pq.leq.leq(x,y); +// } +// #else +/* Violates modularity, but a little faster */ +// #include "geom.h" + public static boolean LEQ(Leq leq, Object x, Object y) { + return Geom.VertLeq((GLUvertex) x, (GLUvertex) y); + } + + static PriorityQ pqNewPriorityQ(Leq leq) { + return new PriorityQSort(leq); + } + + abstract void pqDeletePriorityQ(); + + abstract boolean pqInit(); + + abstract int pqInsert(Object keyNew); + + abstract Object pqExtractMin(); + + abstract void pqDelete(int hCurr); + + abstract Object pqMinimum(); + + abstract boolean pqIsEmpty(); +// #endif +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/PriorityQHeap.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/PriorityQHeap.java new file mode 100644 index 0000000..43698bb --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/PriorityQHeap.java @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + + + +class PriorityQHeap extends PriorityQ { + PQnode[] nodes; + PQhandleElem[] handles; + int size, max; + int freeList; + boolean initialized; + Leq leq; + +/* really __gl_pqHeapNewPriorityQ */ +PriorityQHeap(Leq leq) { + size = 0; + max = PriorityQ.INIT_SIZE; + nodes = new PQnode[PriorityQ.INIT_SIZE + 1]; + for (int i = 0; i < nodes.length; i++) { + nodes[i] = new PQnode(); + } + handles = new PQhandleElem[PriorityQ.INIT_SIZE + 1]; + for (int i = 0; i < handles.length; i++) { + handles[i] = new PQhandleElem(); + } + initialized = false; + freeList = 0; + this.leq = leq; + + nodes[1].handle = 1; /* so that Minimum() returns NULL */ + handles[1].key = null; + } + +/* really __gl_pqHeapDeletePriorityQ */ + void pqDeletePriorityQ() { + handles = null; + nodes = null; + } + + void FloatDown(int curr) { + PQnode[] n = nodes; + PQhandleElem[] h = handles; + int hCurr, hChild; + int child; + + hCurr = n[curr].handle; + for (; ;) { + child = curr << 1; + if (child < size && LEQ(leq, h[n[child + 1].handle].key, + h[n[child].handle].key)) { + ++child; + } + + assert (child <= max); + + hChild = n[child].handle; + if (child > size || LEQ(leq, h[hCurr].key, h[hChild].key)) { + n[curr].handle = hCurr; + h[hCurr].node = curr; + break; + } + n[curr].handle = hChild; + h[hChild].node = curr; + curr = child; + } + } + + + void FloatUp(int curr) { + PQnode[] n = nodes; + PQhandleElem[] h = handles; + int hCurr, hParent; + int parent; + + hCurr = n[curr].handle; + for (; ;) { + parent = curr >> 1; + hParent = n[parent].handle; + if (parent == 0 || LEQ(leq, h[hParent].key, h[hCurr].key)) { + n[curr].handle = hCurr; + h[hCurr].node = curr; + break; + } + n[curr].handle = hParent; + h[hParent].node = curr; + curr = parent; + } + } + +/* really __gl_pqHeapInit */ + boolean pqInit() { + int i; + + /* This method of building a heap is O(n), rather than O(n lg n). */ + + for (i = size; i >= 1; --i) { + FloatDown(i); + } + initialized = true; + + return true; + } + +/* really __gl_pqHeapInsert */ +/* returns LONG_MAX iff out of memory */ + int pqInsert(Object keyNew) { + int curr; + int free; + + curr = ++size; + if ((curr * 2) > max) { + PQnode[] saveNodes = nodes; + PQhandleElem[] saveHandles = handles; + + /* If the heap overflows, double its size. */ + max <<= 1; +// pq->nodes = (PQnode *)memRealloc( pq->nodes, (size_t) ((pq->max + 1) * sizeof( pq->nodes[0] ))); + PQnode[] pqNodes = new PQnode[max + 1]; + System.arraycopy( nodes, 0, pqNodes, 0, nodes.length ); + for (int i = nodes.length; i < pqNodes.length; i++) { + pqNodes[i] = new PQnode(); + } + nodes = pqNodes; + if (nodes == null) { + nodes = saveNodes; /* restore ptr to free upon return */ + return Integer.MAX_VALUE; + } + +// pq->handles = (PQhandleElem *)memRealloc( pq->handles,(size_t)((pq->max + 1) * sizeof( pq->handles[0] ))); + PQhandleElem[] pqHandles = new PQhandleElem[max + 1]; + System.arraycopy( handles, 0, pqHandles, 0, handles.length ); + for (int i = handles.length; i < pqHandles.length; i++) { + pqHandles[i] = new PQhandleElem(); + } + handles = pqHandles; + if (handles == null) { + handles = saveHandles; /* restore ptr to free upon return */ + return Integer.MAX_VALUE; + } + } + + if (freeList == 0) { + free = curr; + } else { + free = freeList; + freeList = handles[free].node; + } + + nodes[curr].handle = free; + handles[free].node = curr; + handles[free].key = keyNew; + + if (initialized) { + FloatUp(curr); + } + assert (free != Integer.MAX_VALUE); + return free; + } + +/* really __gl_pqHeapExtractMin */ + Object pqExtractMin() { + PQnode[] n = nodes; + PQhandleElem[] h = handles; + int hMin = n[1].handle; + Object min = h[hMin].key; + + if (size > 0) { + n[1].handle = n[size].handle; + h[n[1].handle].node = 1; + + h[hMin].key = null; + h[hMin].node = freeList; + freeList = hMin; + + if (--size > 0) { + FloatDown(1); + } + } + return min; + } + +/* really __gl_pqHeapDelete */ + void pqDelete(int hCurr) { + PQnode[] n = nodes; + PQhandleElem[] h = handles; + int curr; + + assert (hCurr >= 1 && hCurr <= max && h[hCurr].key != null); + + curr = h[hCurr].node; + n[curr].handle = n[size].handle; + h[n[curr].handle].node = curr; + + if (curr <= --size) { + if (curr <= 1 || LEQ(leq, h[n[curr >> 1].handle].key, h[n[curr].handle].key)) { + FloatDown(curr); + } else { + FloatUp(curr); + } + } + h[hCurr].key = null; + h[hCurr].node = freeList; + freeList = hCurr; + } + + Object pqMinimum() { + return handles[nodes[1].handle].key; + } + + boolean pqIsEmpty() { + return size == 0; + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/PriorityQSort.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/PriorityQSort.java new file mode 100644 index 0000000..d040c8f --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/PriorityQSort.java @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + + + +class PriorityQSort extends PriorityQ { + PriorityQHeap heap; + Object[] keys; + + // JAVA: 'order' contains indices into the keys array. + // This simulates the indirect pointers used in the original C code + // (from Frank Suykens, Luciad.com). + int[] order; + int size, max; + boolean initialized; + Leq leq; + + PriorityQSort(Leq leq) { + heap = new PriorityQHeap(leq); + + keys = new Object[PriorityQ.INIT_SIZE]; + + size = 0; + max = PriorityQ.INIT_SIZE; + initialized = false; + this.leq = leq; + } + +/* really __gl_pqSortDeletePriorityQ */ + void pqDeletePriorityQ() { + if (heap != null) heap.pqDeletePriorityQ(); + order = null; + keys = null; + } + + private static boolean LT(Leq leq, Object x, Object y) { + return (!PriorityQHeap.LEQ(leq, y, x)); + } + + private static boolean GT(Leq leq, Object x, Object y) { + return (!PriorityQHeap.LEQ(leq, x, y)); + } + + private static void Swap(int[] array, int a, int b) { + if (true) { + int tmp = array[a]; + array[a] = array[b]; + array[b] = tmp; + } else { + + } + } + + private static class Stack { + int p, r; + } + +/* really __gl_pqSortInit */ + boolean pqInit() { + int p, r, i, j; + int piv; + Stack[] stack = new Stack[50]; + for (int k = 0; k < stack.length; k++) { + stack[k] = new Stack(); + } + int top = 0; + + int seed = 2016473283; + + /* Create an array of indirect pointers to the keys, so that we + * the handles we have returned are still valid. + */ + order = new int[size + 1]; +/* the previous line is a patch to compensate for the fact that IBM */ +/* machines return a null on a malloc of zero bytes (unlike SGI), */ +/* so we have to put in this defense to guard against a memory */ +/* fault four lines down. from fossum@austin.ibm.com. */ + p = 0; + r = size - 1; + for (piv = 0, i = p; i <= r; ++piv, ++i) { + // indirect pointers: keep an index into the keys array, not a direct pointer to its contents + order[i] = piv; + } + + /* Sort the indirect pointers in descending order, + * using randomized Quicksort + */ + stack[top].p = p; + stack[top].r = r; + ++top; + while (--top >= 0) { + p = stack[top].p; + r = stack[top].r; + while (r > p + 10) { + seed = Math.abs( seed * 1539415821 + 1 ); + i = p + seed % (r - p + 1); + piv = order[i]; + order[i] = order[p]; + order[p] = piv; + i = p - 1; + j = r + 1; + do { + do { + ++i; + } while (GT(leq, keys[order[i]], keys[piv])); + do { + --j; + } while (LT(leq, keys[order[j]], keys[piv])); + Swap(order, i, j); + } while (i < j); + Swap(order, i, j); /* Undo last swap */ + if (i - p < r - j) { + stack[top].p = j + 1; + stack[top].r = r; + ++top; + r = i - 1; + } else { + stack[top].p = p; + stack[top].r = i - 1; + ++top; + p = j + 1; + } + } + /* Insertion sort small lists */ + for (i = p + 1; i <= r; ++i) { + piv = order[i]; + for (j = i; j > p && LT(leq, keys[order[j - 1]], keys[piv]); --j) { + order[j] = order[j - 1]; + } + order[j] = piv; + } + } + max = size; + initialized = true; + heap.pqInit(); /* always succeeds */ + +/* #ifndef NDEBUG + p = order; + r = p + size - 1; + for (i = p; i < r; ++i) { + Assertion.doAssert(LEQ( * * (i + 1), **i )); + } + #endif*/ + + return true; + } + +/* really __gl_pqSortInsert */ +/* returns LONG_MAX iff out of memory */ + int pqInsert(Object keyNew) { + int curr; + + if (initialized) { + return heap.pqInsert(keyNew); + } + curr = size; + if (++size >= max) { + Object[] saveKey = keys; + + /* If the heap overflows, double its size. */ + max <<= 1; +// pq->keys = (PQHeapKey *)memRealloc( pq->keys,(size_t)(pq->max * sizeof( pq->keys[0] ))); + Object[] pqKeys = new Object[max]; + System.arraycopy( keys, 0, pqKeys, 0, keys.length ); + keys = pqKeys; + if (keys == null) { + keys = saveKey; /* restore ptr to free upon return */ + return Integer.MAX_VALUE; + } + } + assert curr != Integer.MAX_VALUE; + keys[curr] = keyNew; + + /* Negative handles index the sorted array. */ + return -(curr + 1); + } + +/* really __gl_pqSortExtractMin */ + Object pqExtractMin() { + Object sortMin, heapMin; + + if (size == 0) { + return heap.pqExtractMin(); + } + sortMin = keys[order[size - 1]]; + if (!heap.pqIsEmpty()) { + heapMin = heap.pqMinimum(); + if (LEQ(leq, heapMin, sortMin)) { + return heap.pqExtractMin(); + } + } + do { + --size; + } while (size > 0 && keys[order[size - 1]] == null); + return sortMin; + } + +/* really __gl_pqSortMinimum */ + Object pqMinimum() { + Object sortMin, heapMin; + + if (size == 0) { + return heap.pqMinimum(); + } + sortMin = keys[order[size - 1]]; + if (!heap.pqIsEmpty()) { + heapMin = heap.pqMinimum(); + if (PriorityQHeap.LEQ(leq, heapMin, sortMin)) { + return heapMin; + } + } + return sortMin; + } + +/* really __gl_pqSortIsEmpty */ + boolean pqIsEmpty() { + return (size == 0) && heap.pqIsEmpty(); + } + +/* really __gl_pqSortDelete */ + void pqDelete(int curr) { + if (curr >= 0) { + heap.pqDelete(curr); + return; + } + curr = -(curr + 1); + assert curr < max && keys[curr] != null; + + keys[curr] = null; + while (size > 0 && keys[order[size - 1]] == null) { + --size; + } + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/Render.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/Render.java new file mode 100644 index 0000000..759c75b --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/Render.java @@ -0,0 +1,589 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + +import static org.lwjgl.opengl.GL11.*; +import static de.verschwiegener.lwjgl3.util.glu.GLU.*; + +class Render { + private static final boolean USE_OPTIMIZED_CODE_PATH = false; + + private Render() { + } + + private static final RenderFan renderFan = new RenderFan(); + private static final RenderStrip renderStrip = new RenderStrip(); + private static final RenderTriangle renderTriangle = new RenderTriangle(); + +/* This structure remembers the information we need about a primitive + * to be able to render it later, once we have determined which + * primitive is able to use the most triangles. + */ + private static class FaceCount { + private FaceCount() { + } + + private FaceCount(long size, GLUhalfEdge eStart, renderCallBack render) { + this.size = size; + this.eStart = eStart; + this.render = render; + } + + long size; /* number of triangles used */ + GLUhalfEdge eStart; /* edge where this primitive starts */ + renderCallBack render; + }; + + private interface renderCallBack { + void render(GLUtessellatorImpl tess, GLUhalfEdge e, long size); + } + + /************************ Strips and Fans decomposition ******************/ + +/* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle + * fans, strips, and separate triangles. A substantial effort is made + * to use as few rendering primitives as possible (ie. to make the fans + * and strips as large as possible). + * + * The rendering output is provided as callbacks (see the api). + */ + public static void __gl_renderMesh(GLUtessellatorImpl tess, GLUmesh mesh) { + GLUface f; + + /* Make a list of separate triangles so we can render them all at once */ + tess.lonelyTriList = null; + + for (f = mesh.fHead.next; f != mesh.fHead; f = f.next) { + f.marked = false; + } + for (f = mesh.fHead.next; f != mesh.fHead; f = f.next) { + + /* We examine all faces in an arbitrary order. Whenever we find + * an unprocessed face F, we output a group of faces including F + * whose size is maximum. + */ + if (f.inside && !f.marked) { + RenderMaximumFaceGroup(tess, f); + assert (f.marked); + } + } + if (tess.lonelyTriList != null) { + RenderLonelyTriangles(tess, tess.lonelyTriList); + tess.lonelyTriList = null; + } + } + + + static void RenderMaximumFaceGroup(GLUtessellatorImpl tess, GLUface fOrig) { + /* We want to find the largest triangle fan or strip of unmarked faces + * which includes the given face fOrig. There are 3 possible fans + * passing through fOrig (one centered at each vertex), and 3 possible + * strips (one for each CCW permutation of the vertices). Our strategy + * is to try all of these, and take the primitive which uses the most + * triangles (a greedy approach). + */ + GLUhalfEdge e = fOrig.anEdge; + FaceCount max = new FaceCount(); + FaceCount newFace; + + max.size = 1; + max.eStart = e; + max.render = renderTriangle; + + if (!tess.flagBoundary) { + newFace = MaximumFan(e); + if (newFace.size > max.size) { + max = newFace; + } + newFace = MaximumFan(e.Lnext); + if (newFace.size > max.size) { + max = newFace; + } + newFace = MaximumFan(e.Onext.Sym); + if (newFace.size > max.size) { + max = newFace; + } + + newFace = MaximumStrip(e); + if (newFace.size > max.size) { + max = newFace; + } + newFace = MaximumStrip(e.Lnext); + if (newFace.size > max.size) { + max = newFace; + } + newFace = MaximumStrip(e.Onext.Sym); + if (newFace.size > max.size) { + max = newFace; + } + } + max.render.render(tess, max.eStart, max.size); + } + + +/* Macros which keep track of faces we have marked temporarily, and allow + * us to backtrack when necessary. With triangle fans, this is not + * really necessary, since the only awkward case is a loop of triangles + * around a single origin vertex. However with strips the situation is + * more complicated, and we need a general tracking method like the + * one here. + */ + private static boolean Marked(GLUface f) { + return !f.inside || f.marked; + } + + private static GLUface AddToTrail(GLUface f, GLUface t) { + f.trail = t; + f.marked = true; + return f; + } + + private static void FreeTrail(GLUface t) { + if (true) { + while (t != null) { + t.marked = false; + t = t.trail; + } + } else { + /* absorb trailing semicolon */ + } + } + + static FaceCount MaximumFan(GLUhalfEdge eOrig) { + /* eOrig.Lface is the face we want to render. We want to find the size + * of a maximal fan around eOrig.Org. To do this we just walk around + * the origin vertex as far as possible in both directions. + */ + FaceCount newFace = new FaceCount(0, null, renderFan); + GLUface trail = null; + GLUhalfEdge e; + + for (e = eOrig; !Marked(e.Lface); e = e.Onext) { + trail = AddToTrail(e.Lface, trail); + ++newFace.size; + } + for (e = eOrig; !Marked(e.Sym.Lface); e = e.Sym.Lnext) { + trail = AddToTrail(e.Sym.Lface, trail); + ++newFace.size; + } + newFace.eStart = e; + /*LINTED*/ + FreeTrail(trail); + return newFace; + } + + + private static boolean IsEven(long n) { + return (n & 0x1L) == 0; + } + + static FaceCount MaximumStrip(GLUhalfEdge eOrig) { + /* Here we are looking for a maximal strip that contains the vertices + * eOrig.Org, eOrig.Dst, eOrig.Lnext.Dst (in that order or the + * reverse, such that all triangles are oriented CCW). + * + * Again we walk forward and backward as far as possible. However for + * strips there is a twist: to get CCW orientations, there must be + * an *even* number of triangles in the strip on one side of eOrig. + * We walk the strip starting on a side with an even number of triangles; + * if both side have an odd number, we are forced to shorten one side. + */ + FaceCount newFace = new FaceCount(0, null, renderStrip); + long headSize = 0, tailSize = 0; + GLUface trail = null; + GLUhalfEdge e, eTail, eHead; + + for (e = eOrig; !Marked(e.Lface); ++tailSize, e = e.Onext) { + trail = AddToTrail(e.Lface, trail); + ++tailSize; + e = e.Lnext.Sym; + if (Marked(e.Lface)) break; + trail = AddToTrail(e.Lface, trail); + } + eTail = e; + + for (e = eOrig; !Marked(e.Sym.Lface); ++headSize, e = e.Sym.Onext.Sym) { + trail = AddToTrail(e.Sym.Lface, trail); + ++headSize; + e = e.Sym.Lnext; + if (Marked(e.Sym.Lface)) break; + trail = AddToTrail(e.Sym.Lface, trail); + } + eHead = e; + + newFace.size = tailSize + headSize; + if (IsEven(tailSize)) { + newFace.eStart = eTail.Sym; + } else if (IsEven(headSize)) { + newFace.eStart = eHead; + } else { + /* Both sides have odd length, we must shorten one of them. In fact, + * we must start from eHead to guarantee inclusion of eOrig.Lface. + */ + --newFace.size; + newFace.eStart = eHead.Onext; + } + /*LINTED*/ + FreeTrail(trail); + return newFace; + } + + private static class RenderTriangle implements renderCallBack { + public void render(GLUtessellatorImpl tess, GLUhalfEdge e, long size) { + /* Just add the triangle to a triangle list, so we can render all + * the separate triangles at once. + */ + assert (size == 1); + tess.lonelyTriList = AddToTrail(e.Lface, tess.lonelyTriList); + } + } + + + static void RenderLonelyTriangles(GLUtessellatorImpl tess, GLUface f) { + /* Now we render all the separate triangles which could not be + * grouped into a triangle fan or strip. + */ + GLUhalfEdge e; + int newState; + int edgeState = -1; /* force edge state output for first vertex */ + + tess.callBeginOrBeginData(GL_TRIANGLES); + + for (; f != null; f = f.trail) { + /* Loop once for each edge (there will always be 3 edges) */ + + e = f.anEdge; + do { + if (tess.flagBoundary) { + /* Set the "edge state" to true just before we output the + * first vertex of each edge on the polygon boundary. + */ + newState = (!e.Sym.Lface.inside) ? 1 : 0; + if (edgeState != newState) { + edgeState = newState; + tess.callEdgeFlagOrEdgeFlagData( edgeState != 0); + } + } + tess.callVertexOrVertexData( e.Org.data); + + e = e.Lnext; + } while (e != f.anEdge); + } + tess.callEndOrEndData(); + } + + private static class RenderFan implements renderCallBack { + public void render(GLUtessellatorImpl tess, GLUhalfEdge e, long size) { + /* Render as many CCW triangles as possible in a fan starting from + * edge "e". The fan *should* contain exactly "size" triangles + * (otherwise we've goofed up somewhere). + */ + tess.callBeginOrBeginData(GL_TRIANGLE_FAN); + tess.callVertexOrVertexData( e.Org.data); + tess.callVertexOrVertexData( e.Sym.Org.data); + + while (!Marked(e.Lface)) { + e.Lface.marked = true; + --size; + e = e.Onext; + tess.callVertexOrVertexData( e.Sym.Org.data); + } + + assert (size == 0); + tess.callEndOrEndData(); + } + } + + private static class RenderStrip implements renderCallBack { + public void render(GLUtessellatorImpl tess, GLUhalfEdge e, long size) { + /* Render as many CCW triangles as possible in a strip starting from + * edge "e". The strip *should* contain exactly "size" triangles + * (otherwise we've goofed up somewhere). + */ + tess.callBeginOrBeginData(GL_TRIANGLE_STRIP); + tess.callVertexOrVertexData( e.Org.data); + tess.callVertexOrVertexData( e.Sym.Org.data); + + while (!Marked(e.Lface)) { + e.Lface.marked = true; + --size; + e = e.Lnext.Sym; + tess.callVertexOrVertexData( e.Org.data); + if (Marked(e.Lface)) break; + + e.Lface.marked = true; + --size; + e = e.Onext; + tess.callVertexOrVertexData( e.Sym.Org.data); + } + + assert (size == 0); + tess.callEndOrEndData(); + } + } + + /************************ Boundary contour decomposition ******************/ + +/* __gl_renderBoundary( tess, mesh ) takes a mesh, and outputs one + * contour for each face marked "inside". The rendering output is + * provided as callbacks (see the api). + */ + public static void __gl_renderBoundary(GLUtessellatorImpl tess, GLUmesh mesh) { + GLUface f; + GLUhalfEdge e; + + for (f = mesh.fHead.next; f != mesh.fHead; f = f.next) { + if (f.inside) { + tess.callBeginOrBeginData(GL_LINE_LOOP); + e = f.anEdge; + do { + tess.callVertexOrVertexData( e.Org.data); + e = e.Lnext; + } while (e != f.anEdge); + tess.callEndOrEndData(); + } + } + } + + + /************************ Quick-and-dirty decomposition ******************/ + + private static final int SIGN_INCONSISTENT = 2; + + static int ComputeNormal(GLUtessellatorImpl tess, double[] norm, boolean check) +/* + * If check==false, we compute the polygon normal and place it in norm[]. + * If check==true, we check that each triangle in the fan from v0 has a + * consistent orientation with respect to norm[]. If triangles are + * consistently oriented CCW, return 1; if CW, return -1; if all triangles + * are degenerate return 0; otherwise (no consistent orientation) return + * SIGN_INCONSISTENT. + */ { + CachedVertex[] v = tess.cache; +// CachedVertex vn = v0 + tess.cacheCount; + int vn = tess.cacheCount; +// CachedVertex vc; + int vc; + double dot, xc, yc, zc, xp, yp, zp; + double[] n = new double[3]; + int sign = 0; + + /* Find the polygon normal. It is important to get a reasonable + * normal even when the polygon is self-intersecting (eg. a bowtie). + * Otherwise, the computed normal could be very tiny, but perpendicular + * to the true plane of the polygon due to numerical noise. Then all + * the triangles would appear to be degenerate and we would incorrectly + * decompose the polygon as a fan (or simply not render it at all). + * + * We use a sum-of-triangles normal algorithm rather than the more + * efficient sum-of-trapezoids method (used in CheckOrientation() + * in normal.c). This lets us explicitly reverse the signed area + * of some triangles to get a reasonable normal in the self-intersecting + * case. + */ + if (!check) { + norm[0] = norm[1] = norm[2] = 0.0; + } + + vc = 1; + xc = v[vc].coords[0] - v[0].coords[0]; + yc = v[vc].coords[1] - v[0].coords[1]; + zc = v[vc].coords[2] - v[0].coords[2]; + while (++vc < vn) { + xp = xc; + yp = yc; + zp = zc; + xc = v[vc].coords[0] - v[0].coords[0]; + yc = v[vc].coords[1] - v[0].coords[1]; + zc = v[vc].coords[2] - v[0].coords[2]; + + /* Compute (vp - v0) cross (vc - v0) */ + n[0] = yp * zc - zp * yc; + n[1] = zp * xc - xp * zc; + n[2] = xp * yc - yp * xc; + + dot = n[0] * norm[0] + n[1] * norm[1] + n[2] * norm[2]; + if (!check) { + /* Reverse the contribution of back-facing triangles to get + * a reasonable normal for self-intersecting polygons (see above) + */ + if (dot >= 0) { + norm[0] += n[0]; + norm[1] += n[1]; + norm[2] += n[2]; + } else { + norm[0] -= n[0]; + norm[1] -= n[1]; + norm[2] -= n[2]; + } + } else if (dot != 0) { + /* Check the new orientation for consistency with previous triangles */ + if (dot > 0) { + if (sign < 0) return SIGN_INCONSISTENT; + sign = 1; + } else { + if (sign > 0) return SIGN_INCONSISTENT; + sign = -1; + } + } + } + return sign; + } + +/* __gl_renderCache( tess ) takes a single contour and tries to render it + * as a triangle fan. This handles convex polygons, as well as some + * non-convex polygons if we get lucky. + * + * Returns true if the polygon was successfully rendered. The rendering + * output is provided as callbacks (see the api). + */ + public static boolean __gl_renderCache(GLUtessellatorImpl tess) { + CachedVertex[] v = tess.cache; +// CachedVertex vn = v0 + tess.cacheCount; + int vn = tess.cacheCount; +// CachedVertex vc; + int vc; + double[] norm = new double[3]; + int sign; + + if (tess.cacheCount < 3) { + /* Degenerate contour -- no output */ + return true; + } + + norm[0] = tess.normal[0]; + norm[1] = tess.normal[1]; + norm[2] = tess.normal[2]; + if (norm[0] == 0 && norm[1] == 0 && norm[2] == 0) { + ComputeNormal( tess, norm, false); + } + + sign = ComputeNormal( tess, norm, true); + if (sign == SIGN_INCONSISTENT) { + /* Fan triangles did not have a consistent orientation */ + return false; + } + if (sign == 0) { + /* All triangles were degenerate */ + return true; + } + + if ( !USE_OPTIMIZED_CODE_PATH ) { + return false; + } else { + /* Make sure we do the right thing for each winding rule */ + switch (tess.windingRule) { + case GLU_TESS_WINDING_ODD: + case GLU_TESS_WINDING_NONZERO: + break; + case GLU_TESS_WINDING_POSITIVE: + if (sign < 0) return true; + break; + case GLU_TESS_WINDING_NEGATIVE: + if (sign > 0) return true; + break; + case GLU_TESS_WINDING_ABS_GEQ_TWO: + return true; + } + + tess.callBeginOrBeginData( tess.boundaryOnly ? GL_LINE_LOOP + : (tess.cacheCount > 3) ? GL_TRIANGLE_FAN + : GL_TRIANGLES); + + tess.callVertexOrVertexData( v[0].data); + if (sign > 0) { + for (vc = 1; vc < vn; ++vc) { + tess.callVertexOrVertexData( v[vc].data); + } + } else { + for (vc = vn - 1; vc > 0; --vc) { + tess.callVertexOrVertexData( v[vc].data); + } + } + tess.callEndOrEndData(); + return true; + } + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/Sweep.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/Sweep.java new file mode 100644 index 0000000..4be2c54 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/Sweep.java @@ -0,0 +1,1384 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + +import static de.verschwiegener.lwjgl3.util.glu.GLU.*; + +class Sweep { + private Sweep() { + } + +// #ifdef FOR_TRITE_TEST_PROGRAM +// extern void DebugEvent( GLUtessellator *tess ); +// #else + private static void DebugEvent(GLUtessellatorImpl tess) { + + } +// #endif + +/* + * Invariants for the Edge Dictionary. + * - each pair of adjacent edges e2=Succ(e1) satisfies EdgeLeq(e1,e2) + * at any valid location of the sweep event + * - if EdgeLeq(e2,e1) as well (at any valid sweep event), then e1 and e2 + * share a common endpoint + * - for each e, e.Dst has been processed, but not e.Org + * - each edge e satisfies VertLeq(e.Dst,event) && VertLeq(event,e.Org) + * where "event" is the current sweep line event. + * - no edge e has zero length + * + * Invariants for the Mesh (the processed portion). + * - the portion of the mesh left of the sweep line is a planar graph, + * ie. there is *some* way to embed it in the plane + * - no processed edge has zero length + * - no two processed vertices have identical coordinates + * - each "inside" region is monotone, ie. can be broken into two chains + * of monotonically increasing vertices according to VertLeq(v1,v2) + * - a non-invariant: these chains may intersect (very slightly) + * + * Invariants for the Sweep. + * - if none of the edges incident to the event vertex have an activeRegion + * (ie. none of these edges are in the edge dictionary), then the vertex + * has only right-going edges. + * - if an edge is marked "fixUpperEdge" (it is a temporary edge introduced + * by ConnectRightVertex), then it is the only right-going edge from + * its associated vertex. (This says that these edges exist only + * when it is necessary.) + */ + +/* When we merge two edges into one, we need to compute the combined + * winding of the new edge. + */ + private static void AddWinding(GLUhalfEdge eDst, GLUhalfEdge eSrc) { + eDst.winding += eSrc.winding; + eDst.Sym.winding += eSrc.Sym.winding; + } + + + private static ActiveRegion RegionBelow(ActiveRegion r) { + return ((ActiveRegion) Dict.dictKey(Dict.dictPred(r.nodeUp))); + } + + private static ActiveRegion RegionAbove(ActiveRegion r) { + return ((ActiveRegion) Dict.dictKey(Dict.dictSucc(r.nodeUp))); + } + + static boolean EdgeLeq(GLUtessellatorImpl tess, ActiveRegion reg1, ActiveRegion reg2) +/* + * Both edges must be directed from right to left (this is the canonical + * direction for the upper edge of each region). + * + * The strategy is to evaluate a "t" value for each edge at the + * current sweep line position, given by tess.event. The calculations + * are designed to be very stable, but of course they are not perfect. + * + * Special case: if both edge destinations are at the sweep event, + * we sort the edges by slope (they would otherwise compare equally). + */ { + GLUvertex event = tess.event; + GLUhalfEdge e1, e2; + double t1, t2; + + e1 = reg1.eUp; + e2 = reg2.eUp; + + if (e1.Sym.Org == event) { + if (e2.Sym.Org == event) { + /* Two edges right of the sweep line which meet at the sweep event. + * Sort them by slope. + */ + if (Geom.VertLeq(e1.Org, e2.Org)) { + return Geom.EdgeSign(e2.Sym.Org, e1.Org, e2.Org) <= 0; + } + return Geom.EdgeSign(e1.Sym.Org, e2.Org, e1.Org) >= 0; + } + return Geom.EdgeSign(e2.Sym.Org, event, e2.Org) <= 0; + } + if (e2.Sym.Org == event) { + return Geom.EdgeSign(e1.Sym.Org, event, e1.Org) >= 0; + } + + /* General case - compute signed distance *from* e1, e2 to event */ + t1 = Geom.EdgeEval(e1.Sym.Org, event, e1.Org); + t2 = Geom.EdgeEval(e2.Sym.Org, event, e2.Org); + return (t1 >= t2); + } + + + static void DeleteRegion(GLUtessellatorImpl tess, ActiveRegion reg) { + if (reg.fixUpperEdge) { + /* It was created with zero winding number, so it better be + * deleted with zero winding number (ie. it better not get merged + * with a real edge). + */ + assert (reg.eUp.winding == 0); + } + reg.eUp.activeRegion = null; + Dict.dictDelete(tess.dict, reg.nodeUp); /* __gl_dictListDelete */ + } + + + static boolean FixUpperEdge(ActiveRegion reg, GLUhalfEdge newEdge) +/* + * Replace an upper edge which needs fixing (see ConnectRightVertex). + */ { + assert (reg.fixUpperEdge); + if (!Mesh.__gl_meshDelete(reg.eUp)) return false; + reg.fixUpperEdge = false; + reg.eUp = newEdge; + newEdge.activeRegion = reg; + + return true; + } + + static ActiveRegion TopLeftRegion(ActiveRegion reg) { + GLUvertex org = reg.eUp.Org; + GLUhalfEdge e; + + /* Find the region above the uppermost edge with the same origin */ + do { + reg = RegionAbove(reg); + } while (reg.eUp.Org == org); + + /* If the edge above was a temporary edge introduced by ConnectRightVertex, + * now is the time to fix it. + */ + if (reg.fixUpperEdge) { + e = Mesh.__gl_meshConnect(RegionBelow(reg).eUp.Sym, reg.eUp.Lnext); + if (e == null) return null; + if (!FixUpperEdge(reg, e)) return null; + reg = RegionAbove(reg); + } + return reg; + } + + static ActiveRegion TopRightRegion(ActiveRegion reg) { + GLUvertex dst = reg.eUp.Sym.Org; + + /* Find the region above the uppermost edge with the same destination */ + do { + reg = RegionAbove(reg); + } while (reg.eUp.Sym.Org == dst); + return reg; + } + + static ActiveRegion AddRegionBelow(GLUtessellatorImpl tess, + ActiveRegion regAbove, + GLUhalfEdge eNewUp) +/* + * Add a new active region to the sweep line, *somewhere* below "regAbove" + * (according to where the new edge belongs in the sweep-line dictionary). + * The upper edge of the new region will be "eNewUp". + * Winding number and "inside" flag are not updated. + */ { + ActiveRegion regNew = new ActiveRegion(); + if (regNew == null) throw new RuntimeException(); + + regNew.eUp = eNewUp; + /* __gl_dictListInsertBefore */ + regNew.nodeUp = Dict.dictInsertBefore(tess.dict, regAbove.nodeUp, regNew); + if (regNew.nodeUp == null) throw new RuntimeException(); + regNew.fixUpperEdge = false; + regNew.sentinel = false; + regNew.dirty = false; + + eNewUp.activeRegion = regNew; + return regNew; + } + + static boolean IsWindingInside(GLUtessellatorImpl tess, int n) { + switch (tess.windingRule) { + case GLU_TESS_WINDING_ODD: + return (n & 1) != 0; + case GLU_TESS_WINDING_NONZERO: + return (n != 0); + case GLU_TESS_WINDING_POSITIVE: + return (n > 0); + case GLU_TESS_WINDING_NEGATIVE: + return (n < 0); + case GLU_TESS_WINDING_ABS_GEQ_TWO: + return (n >= 2) || (n <= -2); + } + /*LINTED*/ +// assert (false); + throw new InternalError(); + /*NOTREACHED*/ + } + + + static void ComputeWinding(GLUtessellatorImpl tess, ActiveRegion reg) { + reg.windingNumber = RegionAbove(reg).windingNumber + reg.eUp.winding; + reg.inside = IsWindingInside(tess, reg.windingNumber); + } + + + static void FinishRegion(GLUtessellatorImpl tess, ActiveRegion reg) +/* + * Delete a region from the sweep line. This happens when the upper + * and lower chains of a region meet (at a vertex on the sweep line). + * The "inside" flag is copied to the appropriate mesh face (we could + * not do this before -- since the structure of the mesh is always + * changing, this face may not have even existed until now). + */ { + GLUhalfEdge e = reg.eUp; + GLUface f = e.Lface; + + f.inside = reg.inside; + f.anEdge = e; /* optimization for __gl_meshTessellateMonoRegion() */ + DeleteRegion(tess, reg); + } + + + static GLUhalfEdge FinishLeftRegions(GLUtessellatorImpl tess, + ActiveRegion regFirst, ActiveRegion regLast) +/* + * We are given a vertex with one or more left-going edges. All affected + * edges should be in the edge dictionary. Starting at regFirst.eUp, + * we walk down deleting all regions where both edges have the same + * origin vOrg. At the same time we copy the "inside" flag from the + * active region to the face, since at this point each face will belong + * to at most one region (this was not necessarily true until this point + * in the sweep). The walk stops at the region above regLast; if regLast + * is null we walk as far as possible. At the same time we relink the + * mesh if necessary, so that the ordering of edges around vOrg is the + * same as in the dictionary. + */ { + ActiveRegion reg, regPrev; + GLUhalfEdge e, ePrev; + + regPrev = regFirst; + ePrev = regFirst.eUp; + while (regPrev != regLast) { + regPrev.fixUpperEdge = false; /* placement was OK */ + reg = RegionBelow(regPrev); + e = reg.eUp; + if (e.Org != ePrev.Org) { + if (!reg.fixUpperEdge) { + /* Remove the last left-going edge. Even though there are no further + * edges in the dictionary with this origin, there may be further + * such edges in the mesh (if we are adding left edges to a vertex + * that has already been processed). Thus it is important to call + * FinishRegion rather than just DeleteRegion. + */ + FinishRegion(tess, regPrev); + break; + } + /* If the edge below was a temporary edge introduced by + * ConnectRightVertex, now is the time to fix it. + */ + e = Mesh.__gl_meshConnect(ePrev.Onext.Sym, e.Sym); + if (e == null) throw new RuntimeException(); + if (!FixUpperEdge(reg, e)) throw new RuntimeException(); + } + + /* Relink edges so that ePrev.Onext == e */ + if (ePrev.Onext != e) { + if (!Mesh.__gl_meshSplice(e.Sym.Lnext, e)) throw new RuntimeException(); + if (!Mesh.__gl_meshSplice(ePrev, e)) throw new RuntimeException(); + } + FinishRegion(tess, regPrev); /* may change reg.eUp */ + ePrev = reg.eUp; + regPrev = reg; + } + return ePrev; + } + + + static void AddRightEdges(GLUtessellatorImpl tess, ActiveRegion regUp, + GLUhalfEdge eFirst, GLUhalfEdge eLast, GLUhalfEdge eTopLeft, + boolean cleanUp) +/* + * Purpose: insert right-going edges into the edge dictionary, and update + * winding numbers and mesh connectivity appropriately. All right-going + * edges share a common origin vOrg. Edges are inserted CCW starting at + * eFirst; the last edge inserted is eLast.Sym.Lnext. If vOrg has any + * left-going edges already processed, then eTopLeft must be the edge + * such that an imaginary upward vertical segment from vOrg would be + * contained between eTopLeft.Sym.Lnext and eTopLeft; otherwise eTopLeft + * should be null. + */ { + ActiveRegion reg, regPrev; + GLUhalfEdge e, ePrev; + boolean firstTime = true; + + /* Insert the new right-going edges in the dictionary */ + e = eFirst; + do { + assert (Geom.VertLeq(e.Org, e.Sym.Org)); + AddRegionBelow(tess, regUp, e.Sym); + e = e.Onext; + } while (e != eLast); + + /* Walk *all* right-going edges from e.Org, in the dictionary order, + * updating the winding numbers of each region, and re-linking the mesh + * edges to match the dictionary ordering (if necessary). + */ + if (eTopLeft == null) { + eTopLeft = RegionBelow(regUp).eUp.Sym.Onext; + } + regPrev = regUp; + ePrev = eTopLeft; + for (; ;) { + reg = RegionBelow(regPrev); + e = reg.eUp.Sym; + if (e.Org != ePrev.Org) break; + + if (e.Onext != ePrev) { + /* Unlink e from its current position, and relink below ePrev */ + if (!Mesh.__gl_meshSplice(e.Sym.Lnext, e)) throw new RuntimeException(); + if (!Mesh.__gl_meshSplice(ePrev.Sym.Lnext, e)) throw new RuntimeException(); + } + /* Compute the winding number and "inside" flag for the new regions */ + reg.windingNumber = regPrev.windingNumber - e.winding; + reg.inside = IsWindingInside(tess, reg.windingNumber); + + /* Check for two outgoing edges with same slope -- process these + * before any intersection tests (see example in __gl_computeInterior). + */ + regPrev.dirty = true; + if (!firstTime && CheckForRightSplice(tess, regPrev)) { + AddWinding(e, ePrev); + DeleteRegion(tess, regPrev); + if (!Mesh.__gl_meshDelete(ePrev)) throw new RuntimeException(); + } + firstTime = false; + regPrev = reg; + ePrev = e; + } + regPrev.dirty = true; + assert (regPrev.windingNumber - e.winding == reg.windingNumber); + + if (cleanUp) { + /* Check for intersections between newly adjacent edges. */ + WalkDirtyRegions(tess, regPrev); + } + } + + + static void CallCombine(GLUtessellatorImpl tess, GLUvertex isect, + Object[] data, float[] weights, boolean needed) { + double[] coords = new double[3]; + + /* Copy coord data in case the callback changes it. */ + coords[0] = isect.coords[0]; + coords[1] = isect.coords[1]; + coords[2] = isect.coords[2]; + + Object[] outData = new Object[1]; + tess.callCombineOrCombineData(coords, data, weights, outData); + isect.data = outData[0]; + if (isect.data == null) { + if (!needed) { + isect.data = data[0]; + } else if (!tess.fatalError) { + /* The only way fatal error is when two edges are found to intersect, + * but the user has not provided the callback necessary to handle + * generated intersection points. + */ + tess.callErrorOrErrorData(GLU_TESS_NEED_COMBINE_CALLBACK); + tess.fatalError = true; + } + } + } + + static void SpliceMergeVertices(GLUtessellatorImpl tess, GLUhalfEdge e1, + GLUhalfEdge e2) +/* + * Two vertices with idential coordinates are combined into one. + * e1.Org is kept, while e2.Org is discarded. + */ { + Object[] data = new Object[4]; + float[] weights = new float[]{0.5f, 0.5f, 0.0f, 0.0f}; + + data[0] = e1.Org.data; + data[1] = e2.Org.data; + CallCombine(tess, e1.Org, data, weights, false); + if (!Mesh.__gl_meshSplice(e1, e2)) throw new RuntimeException(); + } + + static void VertexWeights(GLUvertex isect, GLUvertex org, GLUvertex dst, + float[] weights) +/* + * Find some weights which describe how the intersection vertex is + * a linear combination of "org" and "dest". Each of the two edges + * which generated "isect" is allocated 50% of the weight; each edge + * splits the weight between its org and dst according to the + * relative distance to "isect". + */ { + double t1 = Geom.VertL1dist(org, isect); + double t2 = Geom.VertL1dist(dst, isect); + + weights[0] = (float) (0.5 * t2 / (t1 + t2)); + weights[1] = (float) (0.5 * t1 / (t1 + t2)); + isect.coords[0] += weights[0] * org.coords[0] + weights[1] * dst.coords[0]; + isect.coords[1] += weights[0] * org.coords[1] + weights[1] * dst.coords[1]; + isect.coords[2] += weights[0] * org.coords[2] + weights[1] * dst.coords[2]; + } + + + static void GetIntersectData(GLUtessellatorImpl tess, GLUvertex isect, + GLUvertex orgUp, GLUvertex dstUp, + GLUvertex orgLo, GLUvertex dstLo) +/* + * We've computed a new intersection point, now we need a "data" pointer + * from the user so that we can refer to this new vertex in the + * rendering callbacks. + */ { + Object[] data = new Object[4]; + float[] weights = new float[4]; + float[] weights1 = new float[2]; + float[] weights2 = new float[2]; + + data[0] = orgUp.data; + data[1] = dstUp.data; + data[2] = orgLo.data; + data[3] = dstLo.data; + + isect.coords[0] = isect.coords[1] = isect.coords[2] = 0; + VertexWeights(isect, orgUp, dstUp, weights1); + VertexWeights(isect, orgLo, dstLo, weights2); + System.arraycopy(weights1, 0, weights, 0, 2); + System.arraycopy(weights2, 0, weights, 2, 2); + + CallCombine(tess, isect, data, weights, true); + } + + static boolean CheckForRightSplice(GLUtessellatorImpl tess, ActiveRegion regUp) +/* + * Check the upper and lower edge of "regUp", to make sure that the + * eUp.Org is above eLo, or eLo.Org is below eUp (depending on which + * origin is leftmost). + * + * The main purpose is to splice right-going edges with the same + * dest vertex and nearly identical slopes (ie. we can't distinguish + * the slopes numerically). However the splicing can also help us + * to recover from numerical errors. For example, suppose at one + * point we checked eUp and eLo, and decided that eUp.Org is barely + * above eLo. Then later, we split eLo into two edges (eg. from + * a splice operation like this one). This can change the result of + * our test so that now eUp.Org is incident to eLo, or barely below it. + * We must correct this condition to maintain the dictionary invariants. + * + * One possibility is to check these edges for intersection again + * (ie. CheckForIntersect). This is what we do if possible. However + * CheckForIntersect requires that tess.event lies between eUp and eLo, + * so that it has something to fall back on when the intersection + * calculation gives us an unusable answer. So, for those cases where + * we can't check for intersection, this routine fixes the problem + * by just splicing the offending vertex into the other edge. + * This is a guaranteed solution, no matter how degenerate things get. + * Basically this is a combinatorial solution to a numerical problem. + */ { + ActiveRegion regLo = RegionBelow(regUp); + GLUhalfEdge eUp = regUp.eUp; + GLUhalfEdge eLo = regLo.eUp; + + if (Geom.VertLeq(eUp.Org, eLo.Org)) { + if (Geom.EdgeSign(eLo.Sym.Org, eUp.Org, eLo.Org) > 0) return false; + + /* eUp.Org appears to be below eLo */ + if (!Geom.VertEq(eUp.Org, eLo.Org)) { + /* Splice eUp.Org into eLo */ + if (Mesh.__gl_meshSplitEdge(eLo.Sym) == null) throw new RuntimeException(); + if (!Mesh.__gl_meshSplice(eUp, eLo.Sym.Lnext)) throw new RuntimeException(); + regUp.dirty = regLo.dirty = true; + + } else if (eUp.Org != eLo.Org) { + /* merge the two vertices, discarding eUp.Org */ + tess.pq.pqDelete(eUp.Org.pqHandle); /* __gl_pqSortDelete */ + SpliceMergeVertices(tess, eLo.Sym.Lnext, eUp); + } + } else { + if (Geom.EdgeSign(eUp.Sym.Org, eLo.Org, eUp.Org) < 0) return false; + + /* eLo.Org appears to be above eUp, so splice eLo.Org into eUp */ + RegionAbove(regUp).dirty = regUp.dirty = true; + if (Mesh.__gl_meshSplitEdge(eUp.Sym) == null) throw new RuntimeException(); + if (!Mesh.__gl_meshSplice(eLo.Sym.Lnext, eUp)) throw new RuntimeException(); + } + return true; + } + + static boolean CheckForLeftSplice(GLUtessellatorImpl tess, ActiveRegion regUp) +/* + * Check the upper and lower edge of "regUp", to make sure that the + * eUp.Sym.Org is above eLo, or eLo.Sym.Org is below eUp (depending on which + * destination is rightmost). + * + * Theoretically, this should always be true. However, splitting an edge + * into two pieces can change the results of previous tests. For example, + * suppose at one point we checked eUp and eLo, and decided that eUp.Sym.Org + * is barely above eLo. Then later, we split eLo into two edges (eg. from + * a splice operation like this one). This can change the result of + * the test so that now eUp.Sym.Org is incident to eLo, or barely below it. + * We must correct this condition to maintain the dictionary invariants + * (otherwise new edges might get inserted in the wrong place in the + * dictionary, and bad stuff will happen). + * + * We fix the problem by just splicing the offending vertex into the + * other edge. + */ { + ActiveRegion regLo = RegionBelow(regUp); + GLUhalfEdge eUp = regUp.eUp; + GLUhalfEdge eLo = regLo.eUp; + GLUhalfEdge e; + + assert (!Geom.VertEq(eUp.Sym.Org, eLo.Sym.Org)); + + if (Geom.VertLeq(eUp.Sym.Org, eLo.Sym.Org)) { + if (Geom.EdgeSign(eUp.Sym.Org, eLo.Sym.Org, eUp.Org) < 0) return false; + + /* eLo.Sym.Org is above eUp, so splice eLo.Sym.Org into eUp */ + RegionAbove(regUp).dirty = regUp.dirty = true; + e = Mesh.__gl_meshSplitEdge(eUp); + if (e == null) throw new RuntimeException(); + if (!Mesh.__gl_meshSplice(eLo.Sym, e)) throw new RuntimeException(); + e.Lface.inside = regUp.inside; + } else { + if (Geom.EdgeSign(eLo.Sym.Org, eUp.Sym.Org, eLo.Org) > 0) return false; + + /* eUp.Sym.Org is below eLo, so splice eUp.Sym.Org into eLo */ + regUp.dirty = regLo.dirty = true; + e = Mesh.__gl_meshSplitEdge(eLo); + if (e == null) throw new RuntimeException(); + if (!Mesh.__gl_meshSplice(eUp.Lnext, eLo.Sym)) throw new RuntimeException(); + e.Sym.Lface.inside = regUp.inside; + } + return true; + } + + + static boolean CheckForIntersect(GLUtessellatorImpl tess, ActiveRegion regUp) +/* + * Check the upper and lower edges of the given region to see if + * they intersect. If so, create the intersection and add it + * to the data structures. + * + * Returns true if adding the new intersection resulted in a recursive + * call to AddRightEdges(); in this case all "dirty" regions have been + * checked for intersections, and possibly regUp has been deleted. + */ { + ActiveRegion regLo = RegionBelow(regUp); + GLUhalfEdge eUp = regUp.eUp; + GLUhalfEdge eLo = regLo.eUp; + GLUvertex orgUp = eUp.Org; + GLUvertex orgLo = eLo.Org; + GLUvertex dstUp = eUp.Sym.Org; + GLUvertex dstLo = eLo.Sym.Org; + double tMinUp, tMaxLo; + GLUvertex isect = new GLUvertex(); + GLUvertex orgMin; + GLUhalfEdge e; + + assert (!Geom.VertEq(dstLo, dstUp)); + assert (Geom.EdgeSign(dstUp, tess.event, orgUp) <= 0); + assert (Geom.EdgeSign(dstLo, tess.event, orgLo) >= 0); + assert (orgUp != tess.event && orgLo != tess.event); + assert (!regUp.fixUpperEdge && !regLo.fixUpperEdge); + + if (orgUp == orgLo) return false; /* right endpoints are the same */ + + tMinUp = Math.min(orgUp.t, dstUp.t); + tMaxLo = Math.max(orgLo.t, dstLo.t); + if (tMinUp > tMaxLo) return false; /* t ranges do not overlap */ + + if (Geom.VertLeq(orgUp, orgLo)) { + if (Geom.EdgeSign(dstLo, orgUp, orgLo) > 0) return false; + } else { + if (Geom.EdgeSign(dstUp, orgLo, orgUp) < 0) return false; + } + + /* At this point the edges intersect, at least marginally */ + DebugEvent(tess); + + Geom.EdgeIntersect(dstUp, orgUp, dstLo, orgLo, isect); + /* The following properties are guaranteed: */ + assert (Math.min(orgUp.t, dstUp.t) <= isect.t); + assert (isect.t <= Math.max(orgLo.t, dstLo.t)); + assert (Math.min(dstLo.s, dstUp.s) <= isect.s); + assert (isect.s <= Math.max(orgLo.s, orgUp.s)); + + if (Geom.VertLeq(isect, tess.event)) { + /* The intersection point lies slightly to the left of the sweep line, + * so move it until it''s slightly to the right of the sweep line. + * (If we had perfect numerical precision, this would never happen + * in the first place). The easiest and safest thing to do is + * replace the intersection by tess.event. + */ + isect.s = tess.event.s; + isect.t = tess.event.t; + } + /* Similarly, if the computed intersection lies to the right of the + * rightmost origin (which should rarely happen), it can cause + * unbelievable inefficiency on sufficiently degenerate inputs. + * (If you have the test program, try running test54.d with the + * "X zoom" option turned on). + */ + orgMin = Geom.VertLeq(orgUp, orgLo) ? orgUp : orgLo; + if (Geom.VertLeq(orgMin, isect)) { + isect.s = orgMin.s; + isect.t = orgMin.t; + } + + if (Geom.VertEq(isect, orgUp) || Geom.VertEq(isect, orgLo)) { + /* Easy case -- intersection at one of the right endpoints */ + CheckForRightSplice(tess, regUp); + return false; + } + + if ((!Geom.VertEq(dstUp, tess.event) + && Geom.EdgeSign(dstUp, tess.event, isect) >= 0) + || (!Geom.VertEq(dstLo, tess.event) + && Geom.EdgeSign(dstLo, tess.event, isect) <= 0)) { + /* Very unusual -- the new upper or lower edge would pass on the + * wrong side of the sweep event, or through it. This can happen + * due to very small numerical errors in the intersection calculation. + */ + if (dstLo == tess.event) { + /* Splice dstLo into eUp, and process the new region(s) */ + if (Mesh.__gl_meshSplitEdge(eUp.Sym) == null) throw new RuntimeException(); + if (!Mesh.__gl_meshSplice(eLo.Sym, eUp)) throw new RuntimeException(); + regUp = TopLeftRegion(regUp); + if (regUp == null) throw new RuntimeException(); + eUp = RegionBelow(regUp).eUp; + FinishLeftRegions(tess, RegionBelow(regUp), regLo); + AddRightEdges(tess, regUp, eUp.Sym.Lnext, eUp, eUp, true); + return true; + } + if (dstUp == tess.event) { + /* Splice dstUp into eLo, and process the new region(s) */ + if (Mesh.__gl_meshSplitEdge(eLo.Sym) == null) throw new RuntimeException(); + if (!Mesh.__gl_meshSplice(eUp.Lnext, eLo.Sym.Lnext)) throw new RuntimeException(); + regLo = regUp; + regUp = TopRightRegion(regUp); + e = RegionBelow(regUp).eUp.Sym.Onext; + regLo.eUp = eLo.Sym.Lnext; + eLo = FinishLeftRegions(tess, regLo, null); + AddRightEdges(tess, regUp, eLo.Onext, eUp.Sym.Onext, e, true); + return true; + } + /* Special case: called from ConnectRightVertex. If either + * edge passes on the wrong side of tess.event, split it + * (and wait for ConnectRightVertex to splice it appropriately). + */ + if (Geom.EdgeSign(dstUp, tess.event, isect) >= 0) { + RegionAbove(regUp).dirty = regUp.dirty = true; + if (Mesh.__gl_meshSplitEdge(eUp.Sym) == null) throw new RuntimeException(); + eUp.Org.s = tess.event.s; + eUp.Org.t = tess.event.t; + } + if (Geom.EdgeSign(dstLo, tess.event, isect) <= 0) { + regUp.dirty = regLo.dirty = true; + if (Mesh.__gl_meshSplitEdge(eLo.Sym) == null) throw new RuntimeException(); + eLo.Org.s = tess.event.s; + eLo.Org.t = tess.event.t; + } + /* leave the rest for ConnectRightVertex */ + return false; + } + + /* General case -- split both edges, splice into new vertex. + * When we do the splice operation, the order of the arguments is + * arbitrary as far as correctness goes. However, when the operation + * creates a new face, the work done is proportional to the size of + * the new face. We expect the faces in the processed part of + * the mesh (ie. eUp.Lface) to be smaller than the faces in the + * unprocessed original contours (which will be eLo.Sym.Lnext.Lface). + */ + if (Mesh.__gl_meshSplitEdge(eUp.Sym) == null) throw new RuntimeException(); + if (Mesh.__gl_meshSplitEdge(eLo.Sym) == null) throw new RuntimeException(); + if (!Mesh.__gl_meshSplice(eLo.Sym.Lnext, eUp)) throw new RuntimeException(); + eUp.Org.s = isect.s; + eUp.Org.t = isect.t; + eUp.Org.pqHandle = tess.pq.pqInsert(eUp.Org); /* __gl_pqSortInsert */ + if (eUp.Org.pqHandle == Long.MAX_VALUE) { + tess.pq.pqDeletePriorityQ(); /* __gl_pqSortDeletePriorityQ */ + tess.pq = null; + throw new RuntimeException(); + } + GetIntersectData(tess, eUp.Org, orgUp, dstUp, orgLo, dstLo); + RegionAbove(regUp).dirty = regUp.dirty = regLo.dirty = true; + return false; + } + + static void WalkDirtyRegions(GLUtessellatorImpl tess, ActiveRegion regUp) +/* + * When the upper or lower edge of any region changes, the region is + * marked "dirty". This routine walks through all the dirty regions + * and makes sure that the dictionary invariants are satisfied + * (see the comments at the beginning of this file). Of course + * new dirty regions can be created as we make changes to restore + * the invariants. + */ { + ActiveRegion regLo = RegionBelow(regUp); + GLUhalfEdge eUp, eLo; + + for (; ;) { + /* Find the lowest dirty region (we walk from the bottom up). */ + while (regLo.dirty) { + regUp = regLo; + regLo = RegionBelow(regLo); + } + if (!regUp.dirty) { + regLo = regUp; + regUp = RegionAbove(regUp); + if (regUp == null || !regUp.dirty) { + /* We've walked all the dirty regions */ + return; + } + } + regUp.dirty = false; + eUp = regUp.eUp; + eLo = regLo.eUp; + + if (eUp.Sym.Org != eLo.Sym.Org) { + /* Check that the edge ordering is obeyed at the Dst vertices. */ + if (CheckForLeftSplice(tess, regUp)) { + + /* If the upper or lower edge was marked fixUpperEdge, then + * we no longer need it (since these edges are needed only for + * vertices which otherwise have no right-going edges). + */ + if (regLo.fixUpperEdge) { + DeleteRegion(tess, regLo); + if (!Mesh.__gl_meshDelete(eLo)) throw new RuntimeException(); + regLo = RegionBelow(regUp); + eLo = regLo.eUp; + } else if (regUp.fixUpperEdge) { + DeleteRegion(tess, regUp); + if (!Mesh.__gl_meshDelete(eUp)) throw new RuntimeException(); + regUp = RegionAbove(regLo); + eUp = regUp.eUp; + } + } + } + if (eUp.Org != eLo.Org) { + if (eUp.Sym.Org != eLo.Sym.Org + && !regUp.fixUpperEdge && !regLo.fixUpperEdge + && (eUp.Sym.Org == tess.event || eLo.Sym.Org == tess.event)) { + /* When all else fails in CheckForIntersect(), it uses tess.event + * as the intersection location. To make this possible, it requires + * that tess.event lie between the upper and lower edges, and also + * that neither of these is marked fixUpperEdge (since in the worst + * case it might splice one of these edges into tess.event, and + * violate the invariant that fixable edges are the only right-going + * edge from their associated vertex). + */ + if (CheckForIntersect(tess, regUp)) { + /* WalkDirtyRegions() was called recursively; we're done */ + return; + } + } else { + /* Even though we can't use CheckForIntersect(), the Org vertices + * may violate the dictionary edge ordering. Check and correct this. + */ + CheckForRightSplice(tess, regUp); + } + } + if (eUp.Org == eLo.Org && eUp.Sym.Org == eLo.Sym.Org) { + /* A degenerate loop consisting of only two edges -- delete it. */ + AddWinding(eLo, eUp); + DeleteRegion(tess, regUp); + if (!Mesh.__gl_meshDelete(eUp)) throw new RuntimeException(); + regUp = RegionAbove(regLo); + } + } + } + + + static void ConnectRightVertex(GLUtessellatorImpl tess, ActiveRegion regUp, + GLUhalfEdge eBottomLeft) +/* + * Purpose: connect a "right" vertex vEvent (one where all edges go left) + * to the unprocessed portion of the mesh. Since there are no right-going + * edges, two regions (one above vEvent and one below) are being merged + * into one. "regUp" is the upper of these two regions. + * + * There are two reasons for doing this (adding a right-going edge): + * - if the two regions being merged are "inside", we must add an edge + * to keep them separated (the combined region would not be monotone). + * - in any case, we must leave some record of vEvent in the dictionary, + * so that we can merge vEvent with features that we have not seen yet. + * For example, maybe there is a vertical edge which passes just to + * the right of vEvent; we would like to splice vEvent into this edge. + * + * However, we don't want to connect vEvent to just any vertex. We don''t + * want the new edge to cross any other edges; otherwise we will create + * intersection vertices even when the input data had no self-intersections. + * (This is a bad thing; if the user's input data has no intersections, + * we don't want to generate any false intersections ourselves.) + * + * Our eventual goal is to connect vEvent to the leftmost unprocessed + * vertex of the combined region (the union of regUp and regLo). + * But because of unseen vertices with all right-going edges, and also + * new vertices which may be created by edge intersections, we don''t + * know where that leftmost unprocessed vertex is. In the meantime, we + * connect vEvent to the closest vertex of either chain, and mark the region + * as "fixUpperEdge". This flag says to delete and reconnect this edge + * to the next processed vertex on the boundary of the combined region. + * Quite possibly the vertex we connected to will turn out to be the + * closest one, in which case we won''t need to make any changes. + */ { + GLUhalfEdge eNew; + GLUhalfEdge eTopLeft = eBottomLeft.Onext; + ActiveRegion regLo = RegionBelow(regUp); + GLUhalfEdge eUp = regUp.eUp; + GLUhalfEdge eLo = regLo.eUp; + boolean degenerate = false; + + if (eUp.Sym.Org != eLo.Sym.Org) { + CheckForIntersect(tess, regUp); + } + + /* Possible new degeneracies: upper or lower edge of regUp may pass + * through vEvent, or may coincide with new intersection vertex + */ + if (Geom.VertEq(eUp.Org, tess.event)) { + if (!Mesh.__gl_meshSplice(eTopLeft.Sym.Lnext, eUp)) throw new RuntimeException(); + regUp = TopLeftRegion(regUp); + if (regUp == null) throw new RuntimeException(); + eTopLeft = RegionBelow(regUp).eUp; + FinishLeftRegions(tess, RegionBelow(regUp), regLo); + degenerate = true; + } + if (Geom.VertEq(eLo.Org, tess.event)) { + if (!Mesh.__gl_meshSplice(eBottomLeft, eLo.Sym.Lnext)) throw new RuntimeException(); + eBottomLeft = FinishLeftRegions(tess, regLo, null); + degenerate = true; + } + if (degenerate) { + AddRightEdges(tess, regUp, eBottomLeft.Onext, eTopLeft, eTopLeft, true); + return; + } + + /* Non-degenerate situation -- need to add a temporary, fixable edge. + * Connect to the closer of eLo.Org, eUp.Org. + */ + if (Geom.VertLeq(eLo.Org, eUp.Org)) { + eNew = eLo.Sym.Lnext; + } else { + eNew = eUp; + } + eNew = Mesh.__gl_meshConnect(eBottomLeft.Onext.Sym, eNew); + if (eNew == null) throw new RuntimeException(); + + /* Prevent cleanup, otherwise eNew might disappear before we've even + * had a chance to mark it as a temporary edge. + */ + AddRightEdges(tess, regUp, eNew, eNew.Onext, eNew.Onext, false); + eNew.Sym.activeRegion.fixUpperEdge = true; + WalkDirtyRegions(tess, regUp); + } + +/* Because vertices at exactly the same location are merged together + * before we process the sweep event, some degenerate cases can't occur. + * However if someone eventually makes the modifications required to + * merge features which are close together, the cases below marked + * TOLERANCE_NONZERO will be useful. They were debugged before the + * code to merge identical vertices in the main loop was added. + */ + private static final boolean TOLERANCE_NONZERO = false; + + static void ConnectLeftDegenerate(GLUtessellatorImpl tess, + ActiveRegion regUp, GLUvertex vEvent) +/* + * The event vertex lies exacty on an already-processed edge or vertex. + * Adding the new vertex involves splicing it into the already-processed + * part of the mesh. + */ { + GLUhalfEdge e, eTopLeft, eTopRight, eLast; + ActiveRegion reg; + + e = regUp.eUp; + if (Geom.VertEq(e.Org, vEvent)) { + /* e.Org is an unprocessed vertex - just combine them, and wait + * for e.Org to be pulled from the queue + */ + assert (TOLERANCE_NONZERO); + SpliceMergeVertices(tess, e, vEvent.anEdge); + return; + } + + if (!Geom.VertEq(e.Sym.Org, vEvent)) { + /* General case -- splice vEvent into edge e which passes through it */ + if (Mesh.__gl_meshSplitEdge(e.Sym) == null) throw new RuntimeException(); + if (regUp.fixUpperEdge) { + /* This edge was fixable -- delete unused portion of original edge */ + if (!Mesh.__gl_meshDelete(e.Onext)) throw new RuntimeException(); + regUp.fixUpperEdge = false; + } + if (!Mesh.__gl_meshSplice(vEvent.anEdge, e)) throw new RuntimeException(); + SweepEvent(tess, vEvent); /* recurse */ + return; + } + + /* vEvent coincides with e.Sym.Org, which has already been processed. + * Splice in the additional right-going edges. + */ + assert (TOLERANCE_NONZERO); + regUp = TopRightRegion(regUp); + reg = RegionBelow(regUp); + eTopRight = reg.eUp.Sym; + eTopLeft = eLast = eTopRight.Onext; + if (reg.fixUpperEdge) { + /* Here e.Sym.Org has only a single fixable edge going right. + * We can delete it since now we have some real right-going edges. + */ + assert (eTopLeft != eTopRight); /* there are some left edges too */ + DeleteRegion(tess, reg); + if (!Mesh.__gl_meshDelete(eTopRight)) throw new RuntimeException(); + eTopRight = eTopLeft.Sym.Lnext; + } + if (!Mesh.__gl_meshSplice(vEvent.anEdge, eTopRight)) throw new RuntimeException(); + if (!Geom.EdgeGoesLeft(eTopLeft)) { + /* e.Sym.Org had no left-going edges -- indicate this to AddRightEdges() */ + eTopLeft = null; + } + AddRightEdges(tess, regUp, eTopRight.Onext, eLast, eTopLeft, true); + } + + + static void ConnectLeftVertex(GLUtessellatorImpl tess, GLUvertex vEvent) +/* + * Purpose: connect a "left" vertex (one where both edges go right) + * to the processed portion of the mesh. Let R be the active region + * containing vEvent, and let U and L be the upper and lower edge + * chains of R. There are two possibilities: + * + * - the normal case: split R into two regions, by connecting vEvent to + * the rightmost vertex of U or L lying to the left of the sweep line + * + * - the degenerate case: if vEvent is close enough to U or L, we + * merge vEvent into that edge chain. The subcases are: + * - merging with the rightmost vertex of U or L + * - merging with the active edge of U or L + * - merging with an already-processed portion of U or L + */ { + ActiveRegion regUp, regLo, reg; + GLUhalfEdge eUp, eLo, eNew; + ActiveRegion tmp = new ActiveRegion(); + + /* assert ( vEvent.anEdge.Onext.Onext == vEvent.anEdge ); */ + + /* Get a pointer to the active region containing vEvent */ + tmp.eUp = vEvent.anEdge.Sym; + /* __GL_DICTLISTKEY */ /* __gl_dictListSearch */ + regUp = (ActiveRegion) Dict.dictKey(Dict.dictSearch(tess.dict, tmp)); + regLo = RegionBelow(regUp); + eUp = regUp.eUp; + eLo = regLo.eUp; + + /* Try merging with U or L first */ + if (Geom.EdgeSign(eUp.Sym.Org, vEvent, eUp.Org) == 0) { + ConnectLeftDegenerate(tess, regUp, vEvent); + return; + } + + /* Connect vEvent to rightmost processed vertex of either chain. + * e.Sym.Org is the vertex that we will connect to vEvent. + */ + reg = Geom.VertLeq(eLo.Sym.Org, eUp.Sym.Org) ? regUp : regLo; + + if (regUp.inside || reg.fixUpperEdge) { + if (reg == regUp) { + eNew = Mesh.__gl_meshConnect(vEvent.anEdge.Sym, eUp.Lnext); + if (eNew == null) throw new RuntimeException(); + } else { + GLUhalfEdge tempHalfEdge = Mesh.__gl_meshConnect(eLo.Sym.Onext.Sym, vEvent.anEdge); + if (tempHalfEdge == null) throw new RuntimeException(); + + eNew = tempHalfEdge.Sym; + } + if (reg.fixUpperEdge) { + if (!FixUpperEdge(reg, eNew)) throw new RuntimeException(); + } else { + ComputeWinding(tess, AddRegionBelow(tess, regUp, eNew)); + } + SweepEvent(tess, vEvent); + } else { + /* The new vertex is in a region which does not belong to the polygon. + * We don''t need to connect this vertex to the rest of the mesh. + */ + AddRightEdges(tess, regUp, vEvent.anEdge, vEvent.anEdge, null, true); + } + } + + + static void SweepEvent(GLUtessellatorImpl tess, GLUvertex vEvent) +/* + * Does everything necessary when the sweep line crosses a vertex. + * Updates the mesh and the edge dictionary. + */ { + ActiveRegion regUp, reg; + GLUhalfEdge e, eTopLeft, eBottomLeft; + + tess.event = vEvent; /* for access in EdgeLeq() */ + DebugEvent(tess); + + /* Check if this vertex is the right endpoint of an edge that is + * already in the dictionary. In this case we don't need to waste + * time searching for the location to insert new edges. + */ + e = vEvent.anEdge; + while (e.activeRegion == null) { + e = e.Onext; + if (e == vEvent.anEdge) { + /* All edges go right -- not incident to any processed edges */ + ConnectLeftVertex(tess, vEvent); + return; + } + } + + /* Processing consists of two phases: first we "finish" all the + * active regions where both the upper and lower edges terminate + * at vEvent (ie. vEvent is closing off these regions). + * We mark these faces "inside" or "outside" the polygon according + * to their winding number, and delete the edges from the dictionary. + * This takes care of all the left-going edges from vEvent. + */ + regUp = TopLeftRegion(e.activeRegion); + if (regUp == null) throw new RuntimeException(); + reg = RegionBelow(regUp); + eTopLeft = reg.eUp; + eBottomLeft = FinishLeftRegions(tess, reg, null); + + /* Next we process all the right-going edges from vEvent. This + * involves adding the edges to the dictionary, and creating the + * associated "active regions" which record information about the + * regions between adjacent dictionary edges. + */ + if (eBottomLeft.Onext == eTopLeft) { + /* No right-going edges -- add a temporary "fixable" edge */ + ConnectRightVertex(tess, regUp, eBottomLeft); + } else { + AddRightEdges(tess, regUp, eBottomLeft.Onext, eTopLeft, eTopLeft, true); + } + } + + +/* Make the sentinel coordinates big enough that they will never be + * merged with real input features. (Even with the largest possible + * input contour and the maximum tolerance of 1.0, no merging will be + * done with coordinates larger than 3 * GLU_TESS_MAX_COORD). + */ + private static final double SENTINEL_COORD = (4.0 * GLU_TESS_MAX_COORD); + + static void AddSentinel(GLUtessellatorImpl tess, double t) +/* + * We add two sentinel edges above and below all other edges, + * to avoid special cases at the top and bottom. + */ { + GLUhalfEdge e; + ActiveRegion reg = new ActiveRegion(); + if (reg == null) throw new RuntimeException(); + + e = Mesh.__gl_meshMakeEdge(tess.mesh); + if (e == null) throw new RuntimeException(); + + e.Org.s = SENTINEL_COORD; + e.Org.t = t; + e.Sym.Org.s = -SENTINEL_COORD; + e.Sym.Org.t = t; + tess.event = e.Sym.Org; /* initialize it */ + + reg.eUp = e; + reg.windingNumber = 0; + reg.inside = false; + reg.fixUpperEdge = false; + reg.sentinel = true; + reg.dirty = false; + reg.nodeUp = Dict.dictInsert(tess.dict, reg); /* __gl_dictListInsertBefore */ + if (reg.nodeUp == null) throw new RuntimeException(); + } + + + static void InitEdgeDict(final GLUtessellatorImpl tess) +/* + * We maintain an ordering of edge intersections with the sweep line. + * This order is maintained in a dynamic dictionary. + */ { + /* __gl_dictListNewDict */ + tess.dict = Dict.dictNewDict(tess, new Dict.DictLeq() { + public boolean leq(Object frame, Object key1, Object key2) { + return EdgeLeq(tess, (ActiveRegion) key1, (ActiveRegion) key2); + } + }); + if (tess.dict == null) throw new RuntimeException(); + + AddSentinel(tess, -SENTINEL_COORD); + AddSentinel(tess, SENTINEL_COORD); + } + + + static void DoneEdgeDict(GLUtessellatorImpl tess) { + ActiveRegion reg; + int fixedEdges = 0; + + /* __GL_DICTLISTKEY */ /* __GL_DICTLISTMIN */ + while ((reg = (ActiveRegion) Dict.dictKey(Dict.dictMin(tess.dict))) != null) { + /* + * At the end of all processing, the dictionary should contain + * only the two sentinel edges, plus at most one "fixable" edge + * created by ConnectRightVertex(). + */ + if (!reg.sentinel) { + assert (reg.fixUpperEdge); + assert (++fixedEdges == 1); + } + assert (reg.windingNumber == 0); + DeleteRegion(tess, reg); +/* __gl_meshDelete( reg.eUp );*/ + } + Dict.dictDeleteDict(tess.dict); /* __gl_dictListDeleteDict */ + } + + + static void RemoveDegenerateEdges(GLUtessellatorImpl tess) +/* + * Remove zero-length edges, and contours with fewer than 3 vertices. + */ { + GLUhalfEdge e, eNext, eLnext; + GLUhalfEdge eHead = tess.mesh.eHead; + + /*LINTED*/ + for (e = eHead.next; e != eHead; e = eNext) { + eNext = e.next; + eLnext = e.Lnext; + + if (Geom.VertEq(e.Org, e.Sym.Org) && e.Lnext.Lnext != e) { + /* Zero-length edge, contour has at least 3 edges */ + + SpliceMergeVertices(tess, eLnext, e); /* deletes e.Org */ + if (!Mesh.__gl_meshDelete(e)) throw new RuntimeException(); /* e is a self-loop */ + e = eLnext; + eLnext = e.Lnext; + } + if (eLnext.Lnext == e) { + /* Degenerate contour (one or two edges) */ + + if (eLnext != e) { + if (eLnext == eNext || eLnext == eNext.Sym) { + eNext = eNext.next; + } + if (!Mesh.__gl_meshDelete(eLnext)) throw new RuntimeException(); + } + if (e == eNext || e == eNext.Sym) { + eNext = eNext.next; + } + if (!Mesh.__gl_meshDelete(e)) throw new RuntimeException(); + } + } + } + + static boolean InitPriorityQ(GLUtessellatorImpl tess) +/* + * Insert all vertices into the priority queue which determines the + * order in which vertices cross the sweep line. + */ { + PriorityQ pq; + GLUvertex v, vHead; + + /* __gl_pqSortNewPriorityQ */ + pq = tess.pq = PriorityQ.pqNewPriorityQ(new PriorityQ.Leq() { + public boolean leq(Object key1, Object key2) { + return Geom.VertLeq(((GLUvertex) key1), (GLUvertex) key2); + } + }); + if (pq == null) return false; + + vHead = tess.mesh.vHead; + for (v = vHead.next; v != vHead; v = v.next) { + v.pqHandle = pq.pqInsert(v); /* __gl_pqSortInsert */ + if (v.pqHandle == Long.MAX_VALUE) break; + } + if (v != vHead || !pq.pqInit()) { /* __gl_pqSortInit */ + tess.pq.pqDeletePriorityQ(); /* __gl_pqSortDeletePriorityQ */ + tess.pq = null; + return false; + } + + return true; + } + + + static void DonePriorityQ(GLUtessellatorImpl tess) { + tess.pq.pqDeletePriorityQ(); /* __gl_pqSortDeletePriorityQ */ + } + + + static boolean RemoveDegenerateFaces(GLUmesh mesh) +/* + * Delete any degenerate faces with only two edges. WalkDirtyRegions() + * will catch almost all of these, but it won't catch degenerate faces + * produced by splice operations on already-processed edges. + * The two places this can happen are in FinishLeftRegions(), when + * we splice in a "temporary" edge produced by ConnectRightVertex(), + * and in CheckForLeftSplice(), where we splice already-processed + * edges to ensure that our dictionary invariants are not violated + * by numerical errors. + * + * In both these cases it is *very* dangerous to delete the offending + * edge at the time, since one of the routines further up the stack + * will sometimes be keeping a pointer to that edge. + */ { + GLUface f, fNext; + GLUhalfEdge e; + + /*LINTED*/ + for (f = mesh.fHead.next; f != mesh.fHead; f = fNext) { + fNext = f.next; + e = f.anEdge; + assert (e.Lnext != e); + + if (e.Lnext.Lnext == e) { + /* A face with only two edges */ + AddWinding(e.Onext, e); + if (!Mesh.__gl_meshDelete(e)) return false; + } + } + return true; + } + + public static boolean __gl_computeInterior(GLUtessellatorImpl tess) +/* + * __gl_computeInterior( tess ) computes the planar arrangement specified + * by the given contours, and further subdivides this arrangement + * into regions. Each region is marked "inside" if it belongs + * to the polygon, according to the rule given by tess.windingRule. + * Each interior region is guaranteed be monotone. + */ { + GLUvertex v, vNext; + + tess.fatalError = false; + + /* Each vertex defines an event for our sweep line. Start by inserting + * all the vertices in a priority queue. Events are processed in + * lexicographic order, ie. + * + * e1 < e2 iff e1.x < e2.x || (e1.x == e2.x && e1.y < e2.y) + */ + RemoveDegenerateEdges(tess); + if (!InitPriorityQ(tess)) return false; /* if error */ + InitEdgeDict(tess); + + /* __gl_pqSortExtractMin */ + while ((v = (GLUvertex) tess.pq.pqExtractMin()) != null) { + for (; ;) { + vNext = (GLUvertex) tess.pq.pqMinimum(); /* __gl_pqSortMinimum */ + if (vNext == null || !Geom.VertEq(vNext, v)) break; + + /* Merge together all vertices at exactly the same location. + * This is more efficient than processing them one at a time, + * simplifies the code (see ConnectLeftDegenerate), and is also + * important for correct handling of certain degenerate cases. + * For example, suppose there are two identical edges A and B + * that belong to different contours (so without this code they would + * be processed by separate sweep events). Suppose another edge C + * crosses A and B from above. When A is processed, we split it + * at its intersection point with C. However this also splits C, + * so when we insert B we may compute a slightly different + * intersection point. This might leave two edges with a small + * gap between them. This kind of error is especially obvious + * when using boundary extraction (GLU_TESS_BOUNDARY_ONLY). + */ + vNext = (GLUvertex) tess.pq.pqExtractMin(); /* __gl_pqSortExtractMin*/ + SpliceMergeVertices(tess, v.anEdge, vNext.anEdge); + } + SweepEvent(tess, v); + } + + /* Set tess.event for debugging purposes */ + /* __GL_DICTLISTKEY */ /* __GL_DICTLISTMIN */ + tess.event = ((ActiveRegion) Dict.dictKey(Dict.dictMin(tess.dict))).eUp.Org; + DebugEvent(tess); + DoneEdgeDict(tess); + DonePriorityQ(tess); + + if (!RemoveDegenerateFaces(tess.mesh)) return false; + Mesh.__gl_meshCheckMesh(tess.mesh); + + return true; + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/TessMono.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/TessMono.java new file mode 100644 index 0000000..ed3a23a --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/TessMono.java @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + +class TessMono { +/* __gl_meshTessellateMonoRegion( face ) tessellates a monotone region + * (what else would it do??) The region must consist of a single + * loop of half-edges (see mesh.h) oriented CCW. "Monotone" in this + * case means that any vertical line intersects the interior of the + * region in a single interval. + * + * Tessellation consists of adding interior edges (actually pairs of + * half-edges), to split the region into non-overlapping triangles. + * + * The basic idea is explained in Preparata and Shamos (which I don''t + * have handy right now), although their implementation is more + * complicated than this one. The are two edge chains, an upper chain + * and a lower chain. We process all vertices from both chains in order, + * from right to left. + * + * The algorithm ensures that the following invariant holds after each + * vertex is processed: the untessellated region consists of two + * chains, where one chain (say the upper) is a single edge, and + * the other chain is concave. The left vertex of the single edge + * is always to the left of all vertices in the concave chain. + * + * Each step consists of adding the rightmost unprocessed vertex to one + * of the two chains, and forming a fan of triangles from the rightmost + * of two chain endpoints. Determining whether we can add each triangle + * to the fan is a simple orientation test. By making the fan as large + * as possible, we restore the invariant (check it yourself). + */ + static boolean __gl_meshTessellateMonoRegion(GLUface face) { + GLUhalfEdge up, lo; + + /* All edges are oriented CCW around the boundary of the region. + * First, find the half-edge whose origin vertex is rightmost. + * Since the sweep goes from left to right, face->anEdge should + * be close to the edge we want. + */ + up = face.anEdge; + assert (up.Lnext != up && up.Lnext.Lnext != up); + + for (; Geom.VertLeq(up.Sym.Org, up.Org); up = up.Onext.Sym) + ; + for (; Geom.VertLeq(up.Org, up.Sym.Org); up = up.Lnext) + ; + lo = up.Onext.Sym; + + while (up.Lnext != lo) { + if (Geom.VertLeq(up.Sym.Org, lo.Org)) { + /* up.Sym.Org is on the left. It is safe to form triangles from lo.Org. + * The EdgeGoesLeft test guarantees progress even when some triangles + * are CW, given that the upper and lower chains are truly monotone. + */ + while (lo.Lnext != up && (Geom.EdgeGoesLeft(lo.Lnext) + || Geom.EdgeSign(lo.Org, lo.Sym.Org, lo.Lnext.Sym.Org) <= 0)) { + GLUhalfEdge tempHalfEdge = Mesh.__gl_meshConnect(lo.Lnext, lo); + if (tempHalfEdge == null) return false; + lo = tempHalfEdge.Sym; + } + lo = lo.Onext.Sym; + } else { + /* lo.Org is on the left. We can make CCW triangles from up.Sym.Org. */ + while (lo.Lnext != up && (Geom.EdgeGoesRight(up.Onext.Sym) + || Geom.EdgeSign(up.Sym.Org, up.Org, up.Onext.Sym.Org) >= 0)) { + GLUhalfEdge tempHalfEdge = Mesh.__gl_meshConnect(up, up.Onext.Sym); + if (tempHalfEdge == null) return false; + up = tempHalfEdge.Sym; + } + up = up.Lnext; + } + } + + /* Now lo.Org == up.Sym.Org == the leftmost vertex. The remaining region + * can be tessellated in a fan from this leftmost vertex. + */ + assert (lo.Lnext != up); + while (lo.Lnext.Lnext != up) { + GLUhalfEdge tempHalfEdge = Mesh.__gl_meshConnect(lo.Lnext, lo); + if (tempHalfEdge == null) return false; + lo = tempHalfEdge.Sym; + } + + return true; + } + + +/* __gl_meshTessellateInterior( mesh ) tessellates each region of + * the mesh which is marked "inside" the polygon. Each such region + * must be monotone. + */ + public static boolean __gl_meshTessellateInterior(GLUmesh mesh) { + GLUface f, next; + + /*LINTED*/ + for (f = mesh.fHead.next; f != mesh.fHead; f = next) { + /* Make sure we don''t try to tessellate the new triangles. */ + next = f.next; + if (f.inside) { + if (!__gl_meshTessellateMonoRegion(f)) return false; + } + } + + return true; + } + + +/* __gl_meshDiscardExterior( mesh ) zaps (ie. sets to NULL) all faces + * which are not marked "inside" the polygon. Since further mesh operations + * on NULL faces are not allowed, the main purpose is to clean up the + * mesh so that exterior loops are not represented in the data structure. + */ + public static void __gl_meshDiscardExterior(GLUmesh mesh) { + GLUface f, next; + + /*LINTED*/ + for (f = mesh.fHead.next; f != mesh.fHead; f = next) { + /* Since f will be destroyed, save its next pointer. */ + next = f.next; + if (!f.inside) { + Mesh.__gl_meshZapFace(f); + } + } + } + +// private static final int MARKED_FOR_DELETION = 0x7fffffff; + +/* __gl_meshSetWindingNumber( mesh, value, keepOnlyBoundary ) resets the + * winding numbers on all edges so that regions marked "inside" the + * polygon have a winding number of "value", and regions outside + * have a winding number of 0. + * + * If keepOnlyBoundary is TRUE, it also deletes all edges which do not + * separate an interior region from an exterior one. + */ + public static boolean __gl_meshSetWindingNumber(GLUmesh mesh, int value, boolean keepOnlyBoundary) { + GLUhalfEdge e, eNext; + + for (e = mesh.eHead.next; e != mesh.eHead; e = eNext) { + eNext = e.next; + if (e.Sym.Lface.inside != e.Lface.inside) { + + /* This is a boundary edge (one side is interior, one is exterior). */ + e.winding = (e.Lface.inside) ? value : -value; + } else { + + /* Both regions are interior, or both are exterior. */ + if (!keepOnlyBoundary) { + e.winding = 0; + } else { + if (!Mesh.__gl_meshDelete(e)) return false; + } + } + } + return true; + } + +} diff --git a/src/de/verschwiegener/lwjgl3/util/glu/tessellation/TessState.java b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/TessState.java new file mode 100644 index 0000000..263b843 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/glu/tessellation/TessState.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* +* Portions Copyright (C) 2003-2006 Sun Microsystems, Inc. +* All rights reserved. +*/ + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** NOTE: The Original Code (as defined below) has been licensed to Sun +** Microsystems, Inc. ("Sun") under the SGI Free Software License B +** (Version 1.1), shown above ("SGI License"). Pursuant to Section +** 3.2(3) of the SGI License, Sun is distributing the Covered Code to +** you under an alternative license ("Alternative License"). This +** Alternative License includes all of the provisions of the SGI License +** except that Section 2.2 and 11 are omitted. Any differences between +** the Alternative License and the SGI License are offered solely by Sun +** and not by SGI. +** +** Original Code. The Original Code is: OpenGL Sample Implementation, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: The application programming interfaces +** established by SGI in conjunction with the Original Code are The +** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released +** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version +** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X +** Window System(R) (Version 1.3), released October 19, 1998. This software +** was created using the OpenGL(R) version 1.2.1 Sample Implementation +** published by SGI, but has not been independently verified as being +** compliant with the OpenGL(R) version 1.2.1 Specification. +** +** Author: Eric Veach, July 1994 +** Java Port: Pepijn Van Eeckhoudt, July 2003 +** Java Port: Nathan Parker Burg, August 2003 +*/ +package de.verschwiegener.lwjgl3.util.glu.tessellation; + +class TessState { + public static final int T_DORMANT = 0; + public static final int T_IN_POLYGON = 1; + public static final int T_IN_CONTOUR = 2; +} diff --git a/src/de/verschwiegener/lwjgl3/util/vector/Matrix.java b/src/de/verschwiegener/lwjgl3/util/vector/Matrix.java new file mode 100644 index 0000000..4a38fc6 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/vector/Matrix.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.vector; + +import java.io.Serializable; +import java.nio.FloatBuffer; + +/** + * + * Base class for matrices. When a matrix is constructed it will be the identity + * matrix unless otherwise stated. + * + * @author cix_foo + * @version $Revision$ + * $Id$ + */ +public abstract class Matrix implements Serializable { + + /** + * Constructor for Matrix. + */ + protected Matrix() { + super(); + } + + /** + * Set this matrix to be the identity matrix. + * @return this + */ + public abstract Matrix setIdentity(); + + + /** + * Invert this matrix + * @return this + */ + public abstract Matrix invert(); + + + /** + * Load from a float buffer. The buffer stores the matrix in column major + * (OpenGL) order. + * + * @param buf A float buffer to read from + * @return this + */ + public abstract Matrix load(FloatBuffer buf); + + + /** + * Load from a float buffer. The buffer stores the matrix in row major + * (mathematical) order. + * + * @param buf A float buffer to read from + * @return this + */ + public abstract Matrix loadTranspose(FloatBuffer buf); + + + /** + * Negate this matrix + * @return this + */ + public abstract Matrix negate(); + + + /** + * Store this matrix in a float buffer. The matrix is stored in column + * major (openGL) order. + * @param buf The buffer to store this matrix in + * @return this + */ + public abstract Matrix store(FloatBuffer buf); + + + /** + * Store this matrix in a float buffer. The matrix is stored in row + * major (maths) order. + * @param buf The buffer to store this matrix in + * @return this + */ + public abstract Matrix storeTranspose(FloatBuffer buf); + + + /** + * Transpose this matrix + * @return this + */ + public abstract Matrix transpose(); + + + /** + * Set this matrix to 0. + * @return this + */ + public abstract Matrix setZero(); + + + /** + * @return the determinant of the matrix + */ + public abstract float determinant(); + + +} diff --git a/src/de/verschwiegener/lwjgl3/util/vector/Matrix2f.java b/src/de/verschwiegener/lwjgl3/util/vector/Matrix2f.java new file mode 100644 index 0000000..183e675 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/vector/Matrix2f.java @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.vector; + +import java.io.Serializable; +import java.nio.FloatBuffer; + +/** + * + * Holds a 2x2 matrix + * + * @author cix_foo + * @version $Revision$ + * $Id$ + */ + +public class Matrix2f extends Matrix implements Serializable { + + private static final long serialVersionUID = 1L; + + public float m00, m01, m10, m11; + + /** + * Constructor for Matrix2f. The matrix is initialised to the identity. + */ + public Matrix2f() { + setIdentity(); + } + + /** + * Constructor + */ + public Matrix2f(Matrix2f src) { + load(src); + } + + /** + * Load from another matrix + * @param src The source matrix + * @return this + */ + public Matrix2f load(Matrix2f src) { + return load(src, this); + } + + /** + * Copy the source matrix to the destination matrix. + * @param src The source matrix + * @param dest The destination matrix, or null if a new one should be created. + * @return The copied matrix + */ + public static Matrix2f load(Matrix2f src, Matrix2f dest) { + if (dest == null) + dest = new Matrix2f(); + + dest.m00 = src.m00; + dest.m01 = src.m01; + dest.m10 = src.m10; + dest.m11 = src.m11; + + return dest; + } + + /** + * Load from a float buffer. The buffer stores the matrix in column major + * (OpenGL) order. + * + * @param buf A float buffer to read from + * @return this + */ + public Matrix load(FloatBuffer buf) { + + m00 = buf.get(); + m01 = buf.get(); + m10 = buf.get(); + m11 = buf.get(); + + return this; + } + + /** + * Load from a float buffer. The buffer stores the matrix in row major + * (mathematical) order. + * + * @param buf A float buffer to read from + * @return this + */ + public Matrix loadTranspose(FloatBuffer buf) { + + m00 = buf.get(); + m10 = buf.get(); + m01 = buf.get(); + m11 = buf.get(); + + return this; + } + + /** + * Store this matrix in a float buffer. The matrix is stored in column + * major (openGL) order. + * @param buf The buffer to store this matrix in + */ + public Matrix store(FloatBuffer buf) { + buf.put(m00); + buf.put(m01); + buf.put(m10); + buf.put(m11); + return this; + } + + /** + * Store this matrix in a float buffer. The matrix is stored in row + * major (maths) order. + * @param buf The buffer to store this matrix in + */ + public Matrix storeTranspose(FloatBuffer buf) { + buf.put(m00); + buf.put(m10); + buf.put(m01); + buf.put(m11); + return this; + } + + + + /** + * Add two matrices together and place the result in a third matrix. + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix2f add(Matrix2f left, Matrix2f right, Matrix2f dest) { + if (dest == null) + dest = new Matrix2f(); + + dest.m00 = left.m00 + right.m00; + dest.m01 = left.m01 + right.m01; + dest.m10 = left.m10 + right.m10; + dest.m11 = left.m11 + right.m11; + + return dest; + } + + /** + * Subtract the right matrix from the left and place the result in a third matrix. + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix2f sub(Matrix2f left, Matrix2f right, Matrix2f dest) { + if (dest == null) + dest = new Matrix2f(); + + dest.m00 = left.m00 - right.m00; + dest.m01 = left.m01 - right.m01; + dest.m10 = left.m10 - right.m10; + dest.m11 = left.m11 - right.m11; + + return dest; + } + + /** + * Multiply the right matrix by the left and place the result in a third matrix. + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix2f mul(Matrix2f left, Matrix2f right, Matrix2f dest) { + if (dest == null) + dest = new Matrix2f(); + + float m00 = left.m00 * right.m00 + left.m10 * right.m01; + float m01 = left.m01 * right.m00 + left.m11 * right.m01; + float m10 = left.m00 * right.m10 + left.m10 * right.m11; + float m11 = left.m01 * right.m10 + left.m11 * right.m11; + + dest.m00 = m00; + dest.m01 = m01; + dest.m10 = m10; + dest.m11 = m11; + + return dest; + } + + /** + * Transform a Vector by a matrix and return the result in a destination + * vector. + * @param left The left matrix + * @param right The right vector + * @param dest The destination vector, or null if a new one is to be created + * @return the destination vector + */ + public static Vector2f transform(Matrix2f left, Vector2f right, Vector2f dest) { + if (dest == null) + dest = new Vector2f(); + + float x = left.m00 * right.x + left.m10 * right.y; + float y = left.m01 * right.x + left.m11 * right.y; + + dest.x = x; + dest.y = y; + + return dest; + } + + /** + * Transpose this matrix + * @return this + */ + public Matrix transpose() { + return transpose(this); + } + + /** + * Transpose this matrix and place the result in another matrix. + * @param dest The destination matrix or null if a new matrix is to be created + * @return the transposed matrix + */ + public Matrix2f transpose(Matrix2f dest) { + return transpose(this, dest); + } + + /** + * Transpose the source matrix and place the result in the destination matrix. + * @param src The source matrix or null if a new matrix is to be created + * @param dest The destination matrix or null if a new matrix is to be created + * @return the transposed matrix + */ + public static Matrix2f transpose(Matrix2f src, Matrix2f dest) { + if (dest == null) + dest = new Matrix2f(); + + float m01 = src.m10; + float m10 = src.m01; + + dest.m01 = m01; + dest.m10 = m10; + + return dest; + } + + /** + * Invert this matrix + * @return this if successful, null otherwise + */ + public Matrix invert() { + return invert(this, this); + } + + /** + * Invert the source matrix and place the result in the destination matrix. + * @param src The source matrix to be inverted + * @param dest The destination matrix or null if a new matrix is to be created + * @return The inverted matrix, or null if source can't be reverted. + */ + public static Matrix2f invert(Matrix2f src, Matrix2f dest) { + /* + *inv(A) = 1/det(A) * adj(A); + */ + + float determinant = src.determinant(); + if (determinant != 0) { + if (dest == null) + dest = new Matrix2f(); + float determinant_inv = 1f/determinant; + float t00 = src.m11*determinant_inv; + float t01 = -src.m01*determinant_inv; + float t11 = src.m00*determinant_inv; + float t10 = -src.m10*determinant_inv; + + dest.m00 = t00; + dest.m01 = t01; + dest.m10 = t10; + dest.m11 = t11; + return dest; + } else + return null; + } + + /** + * Returns a string representation of this matrix + */ + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(m00).append(' ').append(m10).append(' ').append('\n'); + buf.append(m01).append(' ').append(m11).append(' ').append('\n'); + return buf.toString(); + } + + /** + * Negate this matrix + * @return this + */ + public Matrix negate() { + return negate(this); + } + + /** + * Negate this matrix and stash the result in another matrix. + * @param dest The destination matrix, or null if a new matrix is to be created + * @return the negated matrix + */ + public Matrix2f negate(Matrix2f dest) { + return negate(this, dest); + } + + /** + * Negate the source matrix and stash the result in the destination matrix. + * @param src The source matrix to be negated + * @param dest The destination matrix, or null if a new matrix is to be created + * @return the negated matrix + */ + public static Matrix2f negate(Matrix2f src, Matrix2f dest) { + if (dest == null) + dest = new Matrix2f(); + + dest.m00 = -src.m00; + dest.m01 = -src.m01; + dest.m10 = -src.m10; + dest.m11 = -src.m11; + + return dest; + } + + /** + * Set this matrix to be the identity matrix. + * @return this + */ + public Matrix setIdentity() { + return setIdentity(this); + } + + /** + * Set the source matrix to be the identity matrix. + * @param src The matrix to set to the identity. + * @return The source matrix + */ + public static Matrix2f setIdentity(Matrix2f src) { + src.m00 = 1.0f; + src.m01 = 0.0f; + src.m10 = 0.0f; + src.m11 = 1.0f; + return src; + } + + /** + * Set this matrix to 0. + * @return this + */ + public Matrix setZero() { + return setZero(this); + } + + public static Matrix2f setZero(Matrix2f src) { + src.m00 = 0.0f; + src.m01 = 0.0f; + src.m10 = 0.0f; + src.m11 = 0.0f; + return src; + } + + /* (non-Javadoc) + * @see org.lwjgl.vector.Matrix#determinant() + */ + public float determinant() { + return m00 * m11 - m01*m10; + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/vector/Matrix3f.java b/src/de/verschwiegener/lwjgl3/util/vector/Matrix3f.java new file mode 100644 index 0000000..69fb37f --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/vector/Matrix3f.java @@ -0,0 +1,510 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.vector; + +import java.io.Serializable; +import java.nio.FloatBuffer; + +/** + * + * Holds a 3x3 matrix. + * + * @author cix_foo + * @version $Revision$ + * $Id$ + */ + +public class Matrix3f extends Matrix implements Serializable { + + private static final long serialVersionUID = 1L; + + public float m00, + m01, + m02, + m10, + m11, + m12, + m20, + m21, + m22; + + /** + * Constructor for Matrix3f. Matrix is initialised to the identity. + */ + public Matrix3f() { + super(); + setIdentity(); + } + + /** + * Load from another matrix + * @param src The source matrix + * @return this + */ + public Matrix3f load(Matrix3f src) { + return load(src, this); + } + + /** + * Copy source matrix to destination matrix + * @param src The source matrix + * @param dest The destination matrix, or null of a new matrix is to be created + * @return The copied matrix + */ + public static Matrix3f load(Matrix3f src, Matrix3f dest) { + if (dest == null) + dest = new Matrix3f(); + + dest.m00 = src.m00; + dest.m10 = src.m10; + dest.m20 = src.m20; + dest.m01 = src.m01; + dest.m11 = src.m11; + dest.m21 = src.m21; + dest.m02 = src.m02; + dest.m12 = src.m12; + dest.m22 = src.m22; + + return dest; + } + + /** + * Load from a float buffer. The buffer stores the matrix in column major + * (OpenGL) order. + * + * @param buf A float buffer to read from + * @return this + */ + public Matrix load(FloatBuffer buf) { + + m00 = buf.get(); + m01 = buf.get(); + m02 = buf.get(); + m10 = buf.get(); + m11 = buf.get(); + m12 = buf.get(); + m20 = buf.get(); + m21 = buf.get(); + m22 = buf.get(); + + return this; + } + + /** + * Load from a float buffer. The buffer stores the matrix in row major + * (maths) order. + * + * @param buf A float buffer to read from + * @return this + */ + public Matrix loadTranspose(FloatBuffer buf) { + + m00 = buf.get(); + m10 = buf.get(); + m20 = buf.get(); + m01 = buf.get(); + m11 = buf.get(); + m21 = buf.get(); + m02 = buf.get(); + m12 = buf.get(); + m22 = buf.get(); + + return this; + } + + /** + * Store this matrix in a float buffer. The matrix is stored in column + * major (openGL) order. + * @param buf The buffer to store this matrix in + */ + public Matrix store(FloatBuffer buf) { + buf.put(m00); + buf.put(m01); + buf.put(m02); + buf.put(m10); + buf.put(m11); + buf.put(m12); + buf.put(m20); + buf.put(m21); + buf.put(m22); + return this; + } + + /** + * Store this matrix in a float buffer. The matrix is stored in row + * major (maths) order. + * @param buf The buffer to store this matrix in + */ + public Matrix storeTranspose(FloatBuffer buf) { + buf.put(m00); + buf.put(m10); + buf.put(m20); + buf.put(m01); + buf.put(m11); + buf.put(m21); + buf.put(m02); + buf.put(m12); + buf.put(m22); + return this; + } + + /** + * Add two matrices together and place the result in a third matrix. + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix3f add(Matrix3f left, Matrix3f right, Matrix3f dest) { + if (dest == null) + dest = new Matrix3f(); + + dest.m00 = left.m00 + right.m00; + dest.m01 = left.m01 + right.m01; + dest.m02 = left.m02 + right.m02; + dest.m10 = left.m10 + right.m10; + dest.m11 = left.m11 + right.m11; + dest.m12 = left.m12 + right.m12; + dest.m20 = left.m20 + right.m20; + dest.m21 = left.m21 + right.m21; + dest.m22 = left.m22 + right.m22; + + return dest; + } + + /** + * Subtract the right matrix from the left and place the result in a third matrix. + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix3f sub(Matrix3f left, Matrix3f right, Matrix3f dest) { + if (dest == null) + dest = new Matrix3f(); + + dest.m00 = left.m00 - right.m00; + dest.m01 = left.m01 - right.m01; + dest.m02 = left.m02 - right.m02; + dest.m10 = left.m10 - right.m10; + dest.m11 = left.m11 - right.m11; + dest.m12 = left.m12 - right.m12; + dest.m20 = left.m20 - right.m20; + dest.m21 = left.m21 - right.m21; + dest.m22 = left.m22 - right.m22; + + return dest; + } + + /** + * Multiply the right matrix by the left and place the result in a third matrix. + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix3f mul(Matrix3f left, Matrix3f right, Matrix3f dest) { + if (dest == null) + dest = new Matrix3f(); + + float m00 = + left.m00 * right.m00 + left.m10 * right.m01 + left.m20 * right.m02; + float m01 = + left.m01 * right.m00 + left.m11 * right.m01 + left.m21 * right.m02; + float m02 = + left.m02 * right.m00 + left.m12 * right.m01 + left.m22 * right.m02; + float m10 = + left.m00 * right.m10 + left.m10 * right.m11 + left.m20 * right.m12; + float m11 = + left.m01 * right.m10 + left.m11 * right.m11 + left.m21 * right.m12; + float m12 = + left.m02 * right.m10 + left.m12 * right.m11 + left.m22 * right.m12; + float m20 = + left.m00 * right.m20 + left.m10 * right.m21 + left.m20 * right.m22; + float m21 = + left.m01 * right.m20 + left.m11 * right.m21 + left.m21 * right.m22; + float m22 = + left.m02 * right.m20 + left.m12 * right.m21 + left.m22 * right.m22; + + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + + return dest; + } + + /** + * Transform a Vector by a matrix and return the result in a destination + * vector. + * @param left The left matrix + * @param right The right vector + * @param dest The destination vector, or null if a new one is to be created + * @return the destination vector + */ + public static Vector3f transform(Matrix3f left, Vector3f right, Vector3f dest) { + if (dest == null) + dest = new Vector3f(); + + float x = left.m00 * right.x + left.m10 * right.y + left.m20 * right.z; + float y = left.m01 * right.x + left.m11 * right.y + left.m21 * right.z; + float z = left.m02 * right.x + left.m12 * right.y + left.m22 * right.z; + + dest.x = x; + dest.y = y; + dest.z = z; + + return dest; + } + + /** + * Transpose this matrix + * @return this + */ + public Matrix transpose() { + return transpose(this, this); + } + + /** + * Transpose this matrix and place the result in another matrix + * @param dest The destination matrix or null if a new matrix is to be created + * @return the transposed matrix + */ + public Matrix3f transpose(Matrix3f dest) { + return transpose(this, dest); + } + + /** + * Transpose the source matrix and place the result into the destination matrix + * @param src The source matrix to be transposed + * @param dest The destination matrix or null if a new matrix is to be created + * @return the transposed matrix + */ + public static Matrix3f transpose(Matrix3f src, Matrix3f dest) { + if (dest == null) + dest = new Matrix3f(); + float m00 = src.m00; + float m01 = src.m10; + float m02 = src.m20; + float m10 = src.m01; + float m11 = src.m11; + float m12 = src.m21; + float m20 = src.m02; + float m21 = src.m12; + float m22 = src.m22; + + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + return dest; + } + + /** + * @return the determinant of the matrix + */ + public float determinant() { + float f = + m00 * (m11 * m22 - m12 * m21) + + m01 * (m12 * m20 - m10 * m22) + + m02 * (m10 * m21 - m11 * m20); + return f; + } + + /** + * Returns a string representation of this matrix + */ + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(m00).append(' ').append(m10).append(' ').append(m20).append(' ').append('\n'); + buf.append(m01).append(' ').append(m11).append(' ').append(m21).append(' ').append('\n'); + buf.append(m02).append(' ').append(m12).append(' ').append(m22).append(' ').append('\n'); + return buf.toString(); + } + + /** + * Invert this matrix + * @return this if successful, null otherwise + */ + public Matrix invert() { + return invert(this, this); + } + + /** + * Invert the source matrix and put the result into the destination matrix + * @param src The source matrix to be inverted + * @param dest The destination matrix, or null if a new one is to be created + * @return The inverted matrix if successful, null otherwise + */ + public static Matrix3f invert(Matrix3f src, Matrix3f dest) { + float determinant = src.determinant(); + + if (determinant != 0) { + if (dest == null) + dest = new Matrix3f(); + /* do it the ordinary way + * + * inv(A) = 1/det(A) * adj(T), where adj(T) = transpose(Conjugate Matrix) + * + * m00 m01 m02 + * m10 m11 m12 + * m20 m21 m22 + */ + float determinant_inv = 1f/determinant; + + // get the conjugate matrix + float t00 = src.m11 * src.m22 - src.m12* src.m21; + float t01 = - src.m10 * src.m22 + src.m12 * src.m20; + float t02 = src.m10 * src.m21 - src.m11 * src.m20; + float t10 = - src.m01 * src.m22 + src.m02 * src.m21; + float t11 = src.m00 * src.m22 - src.m02 * src.m20; + float t12 = - src.m00 * src.m21 + src.m01 * src.m20; + float t20 = src.m01 * src.m12 - src.m02 * src.m11; + float t21 = -src.m00 * src.m12 + src.m02 * src.m10; + float t22 = src.m00 * src.m11 - src.m01 * src.m10; + + dest.m00 = t00*determinant_inv; + dest.m11 = t11*determinant_inv; + dest.m22 = t22*determinant_inv; + dest.m01 = t10*determinant_inv; + dest.m10 = t01*determinant_inv; + dest.m20 = t02*determinant_inv; + dest.m02 = t20*determinant_inv; + dest.m12 = t21*determinant_inv; + dest.m21 = t12*determinant_inv; + return dest; + } else + return null; + } + + + /** + * Negate this matrix + * @return this + */ + public Matrix negate() { + return negate(this); + } + + /** + * Negate this matrix and place the result in a destination matrix. + * @param dest The destination matrix, or null if a new matrix is to be created + * @return the negated matrix + */ + public Matrix3f negate(Matrix3f dest) { + return negate(this, dest); + } + + /** + * Negate the source matrix and place the result in the destination matrix. + * @param src The source matrix + * @param dest The destination matrix, or null if a new matrix is to be created + * @return the negated matrix + */ + public static Matrix3f negate(Matrix3f src, Matrix3f dest) { + if (dest == null) + dest = new Matrix3f(); + + dest.m00 = -src.m00; + dest.m01 = -src.m02; + dest.m02 = -src.m01; + dest.m10 = -src.m10; + dest.m11 = -src.m12; + dest.m12 = -src.m11; + dest.m20 = -src.m20; + dest.m21 = -src.m22; + dest.m22 = -src.m21; + return dest; + } + + /** + * Set this matrix to be the identity matrix. + * @return this + */ + public Matrix setIdentity() { + return setIdentity(this); + } + + /** + * Set the matrix to be the identity matrix. + * @param m The matrix to be set to the identity + * @return m + */ + public static Matrix3f setIdentity(Matrix3f m) { + m.m00 = 1.0f; + m.m01 = 0.0f; + m.m02 = 0.0f; + m.m10 = 0.0f; + m.m11 = 1.0f; + m.m12 = 0.0f; + m.m20 = 0.0f; + m.m21 = 0.0f; + m.m22 = 1.0f; + return m; + } + + /** + * Set this matrix to 0. + * @return this + */ + public Matrix setZero() { + return setZero(this); + } + + /** + * Set the matrix matrix to 0. + * @param m The matrix to be set to 0 + * @return m + */ + public static Matrix3f setZero(Matrix3f m) { + m.m00 = 0.0f; + m.m01 = 0.0f; + m.m02 = 0.0f; + m.m10 = 0.0f; + m.m11 = 0.0f; + m.m12 = 0.0f; + m.m20 = 0.0f; + m.m21 = 0.0f; + m.m22 = 0.0f; + return m; + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/vector/Matrix4f.java b/src/de/verschwiegener/lwjgl3/util/vector/Matrix4f.java new file mode 100644 index 0000000..dde362a --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/vector/Matrix4f.java @@ -0,0 +1,849 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.vector; + +import java.io.Serializable; +import java.nio.FloatBuffer; + +/** + * Holds a 4x4 float matrix. + * + * @author foo + */ +public class Matrix4f extends Matrix implements Serializable { + private static final long serialVersionUID = 1L; + + public float m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33; + + /** + * Construct a new matrix, initialized to the identity. + */ + public Matrix4f() { + super(); + setIdentity(); + } + + public Matrix4f(final Matrix4f src) { + super(); + load(src); + } + + /** + * Returns a string representation of this matrix + */ + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(m00).append(' ').append(m10).append(' ').append(m20).append(' ').append(m30).append('\n'); + buf.append(m01).append(' ').append(m11).append(' ').append(m21).append(' ').append(m31).append('\n'); + buf.append(m02).append(' ').append(m12).append(' ').append(m22).append(' ').append(m32).append('\n'); + buf.append(m03).append(' ').append(m13).append(' ').append(m23).append(' ').append(m33).append('\n'); + return buf.toString(); + } + + /** + * Set this matrix to be the identity matrix. + * @return this + */ + public Matrix setIdentity() { + return setIdentity(this); + } + + /** + * Set the given matrix to be the identity matrix. + * @param m The matrix to set to the identity + * @return m + */ + public static Matrix4f setIdentity(Matrix4f m) { + m.m00 = 1.0f; + m.m01 = 0.0f; + m.m02 = 0.0f; + m.m03 = 0.0f; + m.m10 = 0.0f; + m.m11 = 1.0f; + m.m12 = 0.0f; + m.m13 = 0.0f; + m.m20 = 0.0f; + m.m21 = 0.0f; + m.m22 = 1.0f; + m.m23 = 0.0f; + m.m30 = 0.0f; + m.m31 = 0.0f; + m.m32 = 0.0f; + m.m33 = 1.0f; + + return m; + } + + /** + * Set this matrix to 0. + * @return this + */ + public Matrix setZero() { + return setZero(this); + } + + /** + * Set the given matrix to 0. + * @param m The matrix to set to 0 + * @return m + */ + public static Matrix4f setZero(Matrix4f m) { + m.m00 = 0.0f; + m.m01 = 0.0f; + m.m02 = 0.0f; + m.m03 = 0.0f; + m.m10 = 0.0f; + m.m11 = 0.0f; + m.m12 = 0.0f; + m.m13 = 0.0f; + m.m20 = 0.0f; + m.m21 = 0.0f; + m.m22 = 0.0f; + m.m23 = 0.0f; + m.m30 = 0.0f; + m.m31 = 0.0f; + m.m32 = 0.0f; + m.m33 = 0.0f; + + return m; + } + + /** + * Load from another matrix4f + * @param src The source matrix + * @return this + */ + public Matrix4f load(Matrix4f src) { + return load(src, this); + } + + /** + * Copy the source matrix to the destination matrix + * @param src The source matrix + * @param dest The destination matrix, or null of a new one is to be created + * @return The copied matrix + */ + public static Matrix4f load(Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + dest.m00 = src.m00; + dest.m01 = src.m01; + dest.m02 = src.m02; + dest.m03 = src.m03; + dest.m10 = src.m10; + dest.m11 = src.m11; + dest.m12 = src.m12; + dest.m13 = src.m13; + dest.m20 = src.m20; + dest.m21 = src.m21; + dest.m22 = src.m22; + dest.m23 = src.m23; + dest.m30 = src.m30; + dest.m31 = src.m31; + dest.m32 = src.m32; + dest.m33 = src.m33; + + return dest; + } + + /** + * Load from a float buffer. The buffer stores the matrix in column major + * (OpenGL) order. + * + * @param buf A float buffer to read from + * @return this + */ + public Matrix load(FloatBuffer buf) { + + m00 = buf.get(); + m01 = buf.get(); + m02 = buf.get(); + m03 = buf.get(); + m10 = buf.get(); + m11 = buf.get(); + m12 = buf.get(); + m13 = buf.get(); + m20 = buf.get(); + m21 = buf.get(); + m22 = buf.get(); + m23 = buf.get(); + m30 = buf.get(); + m31 = buf.get(); + m32 = buf.get(); + m33 = buf.get(); + + return this; + } + + /** + * Load from a float buffer. The buffer stores the matrix in row major + * (maths) order. + * + * @param buf A float buffer to read from + * @return this + */ + public Matrix loadTranspose(FloatBuffer buf) { + + m00 = buf.get(); + m10 = buf.get(); + m20 = buf.get(); + m30 = buf.get(); + m01 = buf.get(); + m11 = buf.get(); + m21 = buf.get(); + m31 = buf.get(); + m02 = buf.get(); + m12 = buf.get(); + m22 = buf.get(); + m32 = buf.get(); + m03 = buf.get(); + m13 = buf.get(); + m23 = buf.get(); + m33 = buf.get(); + + return this; + } + + /** + * Store this matrix in a float buffer. The matrix is stored in column + * major (openGL) order. + * @param buf The buffer to store this matrix in + */ + public Matrix store(FloatBuffer buf) { + buf.put(m00); + buf.put(m01); + buf.put(m02); + buf.put(m03); + buf.put(m10); + buf.put(m11); + buf.put(m12); + buf.put(m13); + buf.put(m20); + buf.put(m21); + buf.put(m22); + buf.put(m23); + buf.put(m30); + buf.put(m31); + buf.put(m32); + buf.put(m33); + return this; + } + + /** + * Store this matrix in a float buffer. The matrix is stored in row + * major (maths) order. + * @param buf The buffer to store this matrix in + */ + public Matrix storeTranspose(FloatBuffer buf) { + buf.put(m00); + buf.put(m10); + buf.put(m20); + buf.put(m30); + buf.put(m01); + buf.put(m11); + buf.put(m21); + buf.put(m31); + buf.put(m02); + buf.put(m12); + buf.put(m22); + buf.put(m32); + buf.put(m03); + buf.put(m13); + buf.put(m23); + buf.put(m33); + return this; + } + + /** + * Store the rotation portion of this matrix in a float buffer. The matrix is stored in column + * major (openGL) order. + * @param buf The buffer to store this matrix in + */ + public Matrix store3f(FloatBuffer buf) { + buf.put(m00); + buf.put(m01); + buf.put(m02); + buf.put(m10); + buf.put(m11); + buf.put(m12); + buf.put(m20); + buf.put(m21); + buf.put(m22); + return this; + } + + /** + * Add two matrices together and place the result in a third matrix. + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix4f add(Matrix4f left, Matrix4f right, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + + dest.m00 = left.m00 + right.m00; + dest.m01 = left.m01 + right.m01; + dest.m02 = left.m02 + right.m02; + dest.m03 = left.m03 + right.m03; + dest.m10 = left.m10 + right.m10; + dest.m11 = left.m11 + right.m11; + dest.m12 = left.m12 + right.m12; + dest.m13 = left.m13 + right.m13; + dest.m20 = left.m20 + right.m20; + dest.m21 = left.m21 + right.m21; + dest.m22 = left.m22 + right.m22; + dest.m23 = left.m23 + right.m23; + dest.m30 = left.m30 + right.m30; + dest.m31 = left.m31 + right.m31; + dest.m32 = left.m32 + right.m32; + dest.m33 = left.m33 + right.m33; + + return dest; + } + + /** + * Subtract the right matrix from the left and place the result in a third matrix. + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix4f sub(Matrix4f left, Matrix4f right, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + + dest.m00 = left.m00 - right.m00; + dest.m01 = left.m01 - right.m01; + dest.m02 = left.m02 - right.m02; + dest.m03 = left.m03 - right.m03; + dest.m10 = left.m10 - right.m10; + dest.m11 = left.m11 - right.m11; + dest.m12 = left.m12 - right.m12; + dest.m13 = left.m13 - right.m13; + dest.m20 = left.m20 - right.m20; + dest.m21 = left.m21 - right.m21; + dest.m22 = left.m22 - right.m22; + dest.m23 = left.m23 - right.m23; + dest.m30 = left.m30 - right.m30; + dest.m31 = left.m31 - right.m31; + dest.m32 = left.m32 - right.m32; + dest.m33 = left.m33 - right.m33; + + return dest; + } + + /** + * Multiply the right matrix by the left and place the result in a third matrix. + * @param left The left source matrix + * @param right The right source matrix + * @param dest The destination matrix, or null if a new one is to be created + * @return the destination matrix + */ + public static Matrix4f mul(Matrix4f left, Matrix4f right, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + + float m00 = left.m00 * right.m00 + left.m10 * right.m01 + left.m20 * right.m02 + left.m30 * right.m03; + float m01 = left.m01 * right.m00 + left.m11 * right.m01 + left.m21 * right.m02 + left.m31 * right.m03; + float m02 = left.m02 * right.m00 + left.m12 * right.m01 + left.m22 * right.m02 + left.m32 * right.m03; + float m03 = left.m03 * right.m00 + left.m13 * right.m01 + left.m23 * right.m02 + left.m33 * right.m03; + float m10 = left.m00 * right.m10 + left.m10 * right.m11 + left.m20 * right.m12 + left.m30 * right.m13; + float m11 = left.m01 * right.m10 + left.m11 * right.m11 + left.m21 * right.m12 + left.m31 * right.m13; + float m12 = left.m02 * right.m10 + left.m12 * right.m11 + left.m22 * right.m12 + left.m32 * right.m13; + float m13 = left.m03 * right.m10 + left.m13 * right.m11 + left.m23 * right.m12 + left.m33 * right.m13; + float m20 = left.m00 * right.m20 + left.m10 * right.m21 + left.m20 * right.m22 + left.m30 * right.m23; + float m21 = left.m01 * right.m20 + left.m11 * right.m21 + left.m21 * right.m22 + left.m31 * right.m23; + float m22 = left.m02 * right.m20 + left.m12 * right.m21 + left.m22 * right.m22 + left.m32 * right.m23; + float m23 = left.m03 * right.m20 + left.m13 * right.m21 + left.m23 * right.m22 + left.m33 * right.m23; + float m30 = left.m00 * right.m30 + left.m10 * right.m31 + left.m20 * right.m32 + left.m30 * right.m33; + float m31 = left.m01 * right.m30 + left.m11 * right.m31 + left.m21 * right.m32 + left.m31 * right.m33; + float m32 = left.m02 * right.m30 + left.m12 * right.m31 + left.m22 * right.m32 + left.m32 * right.m33; + float m33 = left.m03 * right.m30 + left.m13 * right.m31 + left.m23 * right.m32 + left.m33 * right.m33; + + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m03 = m03; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m13 = m13; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + dest.m23 = m23; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.m33 = m33; + + return dest; + } + + /** + * Transform a Vector by a matrix and return the result in a destination + * vector. + * @param left The left matrix + * @param right The right vector + * @param dest The destination vector, or null if a new one is to be created + * @return the destination vector + */ + public static Vector4f transform(Matrix4f left, Vector4f right, Vector4f dest) { + if (dest == null) + dest = new Vector4f(); + + float x = left.m00 * right.x + left.m10 * right.y + left.m20 * right.z + left.m30 * right.w; + float y = left.m01 * right.x + left.m11 * right.y + left.m21 * right.z + left.m31 * right.w; + float z = left.m02 * right.x + left.m12 * right.y + left.m22 * right.z + left.m32 * right.w; + float w = left.m03 * right.x + left.m13 * right.y + left.m23 * right.z + left.m33 * right.w; + + dest.x = x; + dest.y = y; + dest.z = z; + dest.w = w; + + return dest; + } + + /** + * Transpose this matrix + * @return this + */ + public Matrix transpose() { + return transpose(this); + } + + /** + * Translate this matrix + * @param vec The vector to translate by + * @return this + */ + public Matrix4f translate(Vector2f vec) { + return translate(vec, this); + } + + /** + * Translate this matrix + * @param vec The vector to translate by + * @return this + */ + public Matrix4f translate(Vector3f vec) { + return translate(vec, this); + } + + /** + * Scales this matrix + * @param vec The vector to scale by + * @return this + */ + public Matrix4f scale(Vector3f vec) { + return scale(vec, this, this); + } + + /** + * Scales the source matrix and put the result in the destination matrix + * @param vec The vector to scale by + * @param src The source matrix + * @param dest The destination matrix, or null if a new matrix is to be created + * @return The scaled matrix + */ + public static Matrix4f scale(Vector3f vec, Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + dest.m00 = src.m00 * vec.x; + dest.m01 = src.m01 * vec.x; + dest.m02 = src.m02 * vec.x; + dest.m03 = src.m03 * vec.x; + dest.m10 = src.m10 * vec.y; + dest.m11 = src.m11 * vec.y; + dest.m12 = src.m12 * vec.y; + dest.m13 = src.m13 * vec.y; + dest.m20 = src.m20 * vec.z; + dest.m21 = src.m21 * vec.z; + dest.m22 = src.m22 * vec.z; + dest.m23 = src.m23 * vec.z; + return dest; + } + + /** + * Rotates the matrix around the given axis the specified angle + * @param angle the angle, in radians. + * @param axis The vector representing the rotation axis. Must be normalized. + * @return this + */ + public Matrix4f rotate(float angle, Vector3f axis) { + return rotate(angle, axis, this); + } + + /** + * Rotates the matrix around the given axis the specified angle + * @param angle the angle, in radians. + * @param axis The vector representing the rotation axis. Must be normalized. + * @param dest The matrix to put the result, or null if a new matrix is to be created + * @return The rotated matrix + */ + public Matrix4f rotate(float angle, Vector3f axis, Matrix4f dest) { + return rotate(angle, axis, this, dest); + } + + /** + * Rotates the source matrix around the given axis the specified angle and + * put the result in the destination matrix. + * @param angle the angle, in radians. + * @param axis The vector representing the rotation axis. Must be normalized. + * @param src The matrix to rotate + * @param dest The matrix to put the result, or null if a new matrix is to be created + * @return The rotated matrix + */ + public static Matrix4f rotate(float angle, Vector3f axis, Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + float c = (float) Math.cos(angle); + float s = (float) Math.sin(angle); + float oneminusc = 1.0f - c; + float xy = axis.x*axis.y; + float yz = axis.y*axis.z; + float xz = axis.x*axis.z; + float xs = axis.x*s; + float ys = axis.y*s; + float zs = axis.z*s; + + float f00 = axis.x*axis.x*oneminusc+c; + float f01 = xy*oneminusc+zs; + float f02 = xz*oneminusc-ys; + // n[3] not used + float f10 = xy*oneminusc-zs; + float f11 = axis.y*axis.y*oneminusc+c; + float f12 = yz*oneminusc+xs; + // n[7] not used + float f20 = xz*oneminusc+ys; + float f21 = yz*oneminusc-xs; + float f22 = axis.z*axis.z*oneminusc+c; + + float t00 = src.m00 * f00 + src.m10 * f01 + src.m20 * f02; + float t01 = src.m01 * f00 + src.m11 * f01 + src.m21 * f02; + float t02 = src.m02 * f00 + src.m12 * f01 + src.m22 * f02; + float t03 = src.m03 * f00 + src.m13 * f01 + src.m23 * f02; + float t10 = src.m00 * f10 + src.m10 * f11 + src.m20 * f12; + float t11 = src.m01 * f10 + src.m11 * f11 + src.m21 * f12; + float t12 = src.m02 * f10 + src.m12 * f11 + src.m22 * f12; + float t13 = src.m03 * f10 + src.m13 * f11 + src.m23 * f12; + dest.m20 = src.m00 * f20 + src.m10 * f21 + src.m20 * f22; + dest.m21 = src.m01 * f20 + src.m11 * f21 + src.m21 * f22; + dest.m22 = src.m02 * f20 + src.m12 * f21 + src.m22 * f22; + dest.m23 = src.m03 * f20 + src.m13 * f21 + src.m23 * f22; + dest.m00 = t00; + dest.m01 = t01; + dest.m02 = t02; + dest.m03 = t03; + dest.m10 = t10; + dest.m11 = t11; + dest.m12 = t12; + dest.m13 = t13; + return dest; + } + + /** + * Translate this matrix and stash the result in another matrix + * @param vec The vector to translate by + * @param dest The destination matrix or null if a new matrix is to be created + * @return the translated matrix + */ + public Matrix4f translate(Vector3f vec, Matrix4f dest) { + return translate(vec, this, dest); + } + + /** + * Translate the source matrix and stash the result in the destination matrix + * @param vec The vector to translate by + * @param src The source matrix + * @param dest The destination matrix or null if a new matrix is to be created + * @return The translated matrix + */ + public static Matrix4f translate(Vector3f vec, Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + + dest.m30 += src.m00 * vec.x + src.m10 * vec.y + src.m20 * vec.z; + dest.m31 += src.m01 * vec.x + src.m11 * vec.y + src.m21 * vec.z; + dest.m32 += src.m02 * vec.x + src.m12 * vec.y + src.m22 * vec.z; + dest.m33 += src.m03 * vec.x + src.m13 * vec.y + src.m23 * vec.z; + + return dest; + } + + /** + * Translate this matrix and stash the result in another matrix + * @param vec The vector to translate by + * @param dest The destination matrix or null if a new matrix is to be created + * @return the translated matrix + */ + public Matrix4f translate(Vector2f vec, Matrix4f dest) { + return translate(vec, this, dest); + } + + /** + * Translate the source matrix and stash the result in the destination matrix + * @param vec The vector to translate by + * @param src The source matrix + * @param dest The destination matrix or null if a new matrix is to be created + * @return The translated matrix + */ + public static Matrix4f translate(Vector2f vec, Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + + dest.m30 += src.m00 * vec.x + src.m10 * vec.y; + dest.m31 += src.m01 * vec.x + src.m11 * vec.y; + dest.m32 += src.m02 * vec.x + src.m12 * vec.y; + dest.m33 += src.m03 * vec.x + src.m13 * vec.y; + + return dest; + } + + /** + * Transpose this matrix and place the result in another matrix + * @param dest The destination matrix or null if a new matrix is to be created + * @return the transposed matrix + */ + public Matrix4f transpose(Matrix4f dest) { + return transpose(this, dest); + } + + /** + * Transpose the source matrix and place the result in the destination matrix + * @param src The source matrix + * @param dest The destination matrix or null if a new matrix is to be created + * @return the transposed matrix + */ + public static Matrix4f transpose(Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + float m00 = src.m00; + float m01 = src.m10; + float m02 = src.m20; + float m03 = src.m30; + float m10 = src.m01; + float m11 = src.m11; + float m12 = src.m21; + float m13 = src.m31; + float m20 = src.m02; + float m21 = src.m12; + float m22 = src.m22; + float m23 = src.m32; + float m30 = src.m03; + float m31 = src.m13; + float m32 = src.m23; + float m33 = src.m33; + + dest.m00 = m00; + dest.m01 = m01; + dest.m02 = m02; + dest.m03 = m03; + dest.m10 = m10; + dest.m11 = m11; + dest.m12 = m12; + dest.m13 = m13; + dest.m20 = m20; + dest.m21 = m21; + dest.m22 = m22; + dest.m23 = m23; + dest.m30 = m30; + dest.m31 = m31; + dest.m32 = m32; + dest.m33 = m33; + + return dest; + } + + /** + * @return the determinant of the matrix + */ + public float determinant() { + float f = + m00 + * ((m11 * m22 * m33 + m12 * m23 * m31 + m13 * m21 * m32) + - m13 * m22 * m31 + - m11 * m23 * m32 + - m12 * m21 * m33); + f -= m01 + * ((m10 * m22 * m33 + m12 * m23 * m30 + m13 * m20 * m32) + - m13 * m22 * m30 + - m10 * m23 * m32 + - m12 * m20 * m33); + f += m02 + * ((m10 * m21 * m33 + m11 * m23 * m30 + m13 * m20 * m31) + - m13 * m21 * m30 + - m10 * m23 * m31 + - m11 * m20 * m33); + f -= m03 + * ((m10 * m21 * m32 + m11 * m22 * m30 + m12 * m20 * m31) + - m12 * m21 * m30 + - m10 * m22 * m31 + - m11 * m20 * m32); + return f; + } + + /** + * Calculate the determinant of a 3x3 matrix + * @return result + */ + + private static float determinant3x3(float t00, float t01, float t02, + float t10, float t11, float t12, + float t20, float t21, float t22) + { + return t00 * (t11 * t22 - t12 * t21) + + t01 * (t12 * t20 - t10 * t22) + + t02 * (t10 * t21 - t11 * t20); + } + + /** + * Invert this matrix + * @return this if successful, null otherwise + */ + public Matrix invert() { + return invert(this, this); + } + + /** + * Invert the source matrix and put the result in the destination + * @param src The source matrix + * @param dest The destination matrix, or null if a new matrix is to be created + * @return The inverted matrix if successful, null otherwise + */ + public static Matrix4f invert(Matrix4f src, Matrix4f dest) { + float determinant = src.determinant(); + + if (determinant != 0) { + /* + * m00 m01 m02 m03 + * m10 m11 m12 m13 + * m20 m21 m22 m23 + * m30 m31 m32 m33 + */ + if (dest == null) + dest = new Matrix4f(); + float determinant_inv = 1f/determinant; + + // first row + float t00 = determinant3x3(src.m11, src.m12, src.m13, src.m21, src.m22, src.m23, src.m31, src.m32, src.m33); + float t01 = -determinant3x3(src.m10, src.m12, src.m13, src.m20, src.m22, src.m23, src.m30, src.m32, src.m33); + float t02 = determinant3x3(src.m10, src.m11, src.m13, src.m20, src.m21, src.m23, src.m30, src.m31, src.m33); + float t03 = -determinant3x3(src.m10, src.m11, src.m12, src.m20, src.m21, src.m22, src.m30, src.m31, src.m32); + // second row + float t10 = -determinant3x3(src.m01, src.m02, src.m03, src.m21, src.m22, src.m23, src.m31, src.m32, src.m33); + float t11 = determinant3x3(src.m00, src.m02, src.m03, src.m20, src.m22, src.m23, src.m30, src.m32, src.m33); + float t12 = -determinant3x3(src.m00, src.m01, src.m03, src.m20, src.m21, src.m23, src.m30, src.m31, src.m33); + float t13 = determinant3x3(src.m00, src.m01, src.m02, src.m20, src.m21, src.m22, src.m30, src.m31, src.m32); + // third row + float t20 = determinant3x3(src.m01, src.m02, src.m03, src.m11, src.m12, src.m13, src.m31, src.m32, src.m33); + float t21 = -determinant3x3(src.m00, src.m02, src.m03, src.m10, src.m12, src.m13, src.m30, src.m32, src.m33); + float t22 = determinant3x3(src.m00, src.m01, src.m03, src.m10, src.m11, src.m13, src.m30, src.m31, src.m33); + float t23 = -determinant3x3(src.m00, src.m01, src.m02, src.m10, src.m11, src.m12, src.m30, src.m31, src.m32); + // fourth row + float t30 = -determinant3x3(src.m01, src.m02, src.m03, src.m11, src.m12, src.m13, src.m21, src.m22, src.m23); + float t31 = determinant3x3(src.m00, src.m02, src.m03, src.m10, src.m12, src.m13, src.m20, src.m22, src.m23); + float t32 = -determinant3x3(src.m00, src.m01, src.m03, src.m10, src.m11, src.m13, src.m20, src.m21, src.m23); + float t33 = determinant3x3(src.m00, src.m01, src.m02, src.m10, src.m11, src.m12, src.m20, src.m21, src.m22); + + // transpose and divide by the determinant + dest.m00 = t00*determinant_inv; + dest.m11 = t11*determinant_inv; + dest.m22 = t22*determinant_inv; + dest.m33 = t33*determinant_inv; + dest.m01 = t10*determinant_inv; + dest.m10 = t01*determinant_inv; + dest.m20 = t02*determinant_inv; + dest.m02 = t20*determinant_inv; + dest.m12 = t21*determinant_inv; + dest.m21 = t12*determinant_inv; + dest.m03 = t30*determinant_inv; + dest.m30 = t03*determinant_inv; + dest.m13 = t31*determinant_inv; + dest.m31 = t13*determinant_inv; + dest.m32 = t23*determinant_inv; + dest.m23 = t32*determinant_inv; + return dest; + } else + return null; + } + + /** + * Negate this matrix + * @return this + */ + public Matrix negate() { + return negate(this); + } + + /** + * Negate this matrix and place the result in a destination matrix. + * @param dest The destination matrix, or null if a new matrix is to be created + * @return the negated matrix + */ + public Matrix4f negate(Matrix4f dest) { + return negate(this, dest); + } + + /** + * Negate this matrix and place the result in a destination matrix. + * @param src The source matrix + * @param dest The destination matrix, or null if a new matrix is to be created + * @return The negated matrix + */ + public static Matrix4f negate(Matrix4f src, Matrix4f dest) { + if (dest == null) + dest = new Matrix4f(); + + dest.m00 = -src.m00; + dest.m01 = -src.m01; + dest.m02 = -src.m02; + dest.m03 = -src.m03; + dest.m10 = -src.m10; + dest.m11 = -src.m11; + dest.m12 = -src.m12; + dest.m13 = -src.m13; + dest.m20 = -src.m20; + dest.m21 = -src.m21; + dest.m22 = -src.m22; + dest.m23 = -src.m23; + dest.m30 = -src.m30; + dest.m31 = -src.m31; + dest.m32 = -src.m32; + dest.m33 = -src.m33; + + return dest; + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/vector/Quaternion.java b/src/de/verschwiegener/lwjgl3/util/vector/Quaternion.java new file mode 100644 index 0000000..277136a --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/vector/Quaternion.java @@ -0,0 +1,530 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.vector; + +/** + * + * Quaternions for LWJGL! + * + * @author fbi + * @version $Revision$ + * $Id$ + */ + +import java.nio.FloatBuffer; + +public class Quaternion extends Vector implements ReadableVector4f { + private static final long serialVersionUID = 1L; + + public float x, y, z, w; + + /** + * C'tor. The quaternion will be initialized to the identity. + */ + public Quaternion() { + super(); + setIdentity(); + } + + /** + * C'tor + * + * @param src + */ + public Quaternion(ReadableVector4f src) { + set(src); + } + + /** + * C'tor + * + */ + public Quaternion(float x, float y, float z, float w) { + set(x, y, z, w); + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.util.vector.WritableVector2f#set(float, float) + */ + public void set(float x, float y) { + this.x = x; + this.y = y; + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.util.vector.WritableVector3f#set(float, float, float) + */ + public void set(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.util.vector.WritableVector4f#set(float, float, float, + * float) + */ + public void set(float x, float y, float z, float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + /** + * Load from another Vector4f + * + * @param src + * The source vector + * @return this + */ + public Quaternion set(ReadableVector4f src) { + x = src.getX(); + y = src.getY(); + z = src.getZ(); + w = src.getW(); + return this; + } + + /** + * Set this quaternion to the multiplication identity. + * @return this + */ + public Quaternion setIdentity() { + return setIdentity(this); + } + + /** + * Set the given quaternion to the multiplication identity. + * @param q The quaternion + * @return q + */ + public static Quaternion setIdentity(Quaternion q) { + q.x = 0; + q.y = 0; + q.z = 0; + q.w = 1; + return q; + } + + /** + * @return the length squared of the quaternion + */ + public float lengthSquared() { + return x * x + y * y + z * z + w * w; + } + + /** + * Normalise the source quaternion and place the result in another quaternion. + * + * @param src + * The source quaternion + * @param dest + * The destination quaternion, or null if a new quaternion is to be + * created + * @return The normalised quaternion + */ + public static Quaternion normalise(Quaternion src, Quaternion dest) { + float inv_l = 1f/src.length(); + + if (dest == null) + dest = new Quaternion(); + + dest.set(src.x * inv_l, src.y * inv_l, src.z * inv_l, src.w * inv_l); + + return dest; + } + + /** + * Normalise this quaternion and place the result in another quaternion. + * + * @param dest + * The destination quaternion, or null if a new quaternion is to be + * created + * @return the normalised quaternion + */ + public Quaternion normalise(Quaternion dest) { + return normalise(this, dest); + } + + /** + * The dot product of two quaternions + * + * @param left + * The LHS quat + * @param right + * The RHS quat + * @return left dot right + */ + public static float dot(Quaternion left, Quaternion right) { + return left.x * right.x + left.y * right.y + left.z * right.z + left.w + * right.w; + } + + /** + * Calculate the conjugate of this quaternion and put it into the given one + * + * @param dest + * The quaternion which should be set to the conjugate of this + * quaternion + */ + public Quaternion negate(Quaternion dest) { + return negate(this, dest); + } + + /** + * Calculate the conjugate of this quaternion and put it into the given one + * + * @param src + * The source quaternion + * @param dest + * The quaternion which should be set to the conjugate of this + * quaternion + */ + public static Quaternion negate(Quaternion src, Quaternion dest) { + if (dest == null) + dest = new Quaternion(); + + dest.x = -src.x; + dest.y = -src.y; + dest.z = -src.z; + dest.w = src.w; + + return dest; + } + + /** + * Calculate the conjugate of this quaternion + */ + public Vector negate() { + return negate(this, this); + } + + /* (non-Javadoc) + * @see org.lwjgl.util.vector.Vector#load(java.nio.FloatBuffer) + */ + public Vector load(FloatBuffer buf) { + x = buf.get(); + y = buf.get(); + z = buf.get(); + w = buf.get(); + return this; + } + + /* + * (non-Javadoc) + * + * @see org.lwjgl.vector.Vector#scale(float) + */ + public Vector scale(float scale) { + return scale(scale, this, this); + } + + /** + * Scale the source quaternion by scale and put the result in the destination + * @param scale The amount to scale by + * @param src The source quaternion + * @param dest The destination quaternion, or null if a new quaternion is to be created + * @return The scaled quaternion + */ + public static Quaternion scale(float scale, Quaternion src, Quaternion dest) { + if (dest == null) + dest = new Quaternion(); + dest.x = src.x * scale; + dest.y = src.y * scale; + dest.z = src.z * scale; + dest.w = src.w * scale; + return dest; + } + + /* (non-Javadoc) + * @see org.lwjgl.util.vector.ReadableVector#store(java.nio.FloatBuffer) + */ + public Vector store(FloatBuffer buf) { + buf.put(x); + buf.put(y); + buf.put(z); + buf.put(w); + + return this; + } + + /** + * @return x + */ + public final float getX() { + return x; + } + + /** + * @return y + */ + public final float getY() { + return y; + } + + /** + * Set X + * + * @param x + */ + public final void setX(float x) { + this.x = x; + } + + /** + * Set Y + * + * @param y + */ + public final void setY(float y) { + this.y = y; + } + + /** + * Set Z + * + * @param z + */ + public void setZ(float z) { + this.z = z; + } + + /* + * (Overrides) + * + * @see org.lwjgl.vector.ReadableVector3f#getZ() + */ + public float getZ() { + return z; + } + + /** + * Set W + * + * @param w + */ + public void setW(float w) { + this.w = w; + } + + /* + * (Overrides) + * + * @see org.lwjgl.vector.ReadableVector3f#getW() + */ + public float getW() { + return w; + } + + public String toString() { + return "Quaternion: " + x + " " + y + " " + z + " " + w; + } + + /** + * Sets the value of this quaternion to the quaternion product of + * quaternions left and right (this = left * right). Note that this is safe + * for aliasing (e.g. this can be left or right). + * + * @param left + * the first quaternion + * @param right + * the second quaternion + */ + public static Quaternion mul(Quaternion left, Quaternion right, + Quaternion dest) { + if (dest == null) + dest = new Quaternion(); + dest.set(left.x * right.w + left.w * right.x + left.y * right.z + - left.z * right.y, left.y * right.w + left.w * right.y + + left.z * right.x - left.x * right.z, left.z * right.w + + left.w * right.z + left.x * right.y - left.y * right.x, + left.w * right.w - left.x * right.x - left.y * right.y + - left.z * right.z); + return dest; + } + + /** + * + * Multiplies quaternion left by the inverse of quaternion right and places + * the value into this quaternion. The value of both argument quaternions is + * preservered (this = left * right^-1). + * + * @param left + * the left quaternion + * @param right + * the right quaternion + */ + public static Quaternion mulInverse(Quaternion left, Quaternion right, + Quaternion dest) { + float n = right.lengthSquared(); + // zero-div may occur. + n = (n == 0.0 ? n : 1 / n); + // store on stack once for aliasing-safty + if (dest == null) + dest = new Quaternion(); + dest + .set((left.x * right.w - left.w * right.x - left.y + * right.z + left.z * right.y) + * n, (left.y * right.w - left.w * right.y - left.z + * right.x + left.x * right.z) + * n, (left.z * right.w - left.w * right.z - left.x + * right.y + left.y * right.x) + * n, (left.w * right.w + left.x * right.x + left.y + * right.y + left.z * right.z) + * n); + + return dest; + } + + /** + * Sets the value of this quaternion to the equivalent rotation of the + * Axis-Angle argument. + * + * @param a1 + * the axis-angle: (x,y,z) is the axis and w is the angle + */ + public final void setFromAxisAngle(Vector4f a1) { + x = a1.x; + y = a1.y; + z = a1.z; + float n = (float) Math.sqrt(x * x + y * y + z * z); + // zero-div may occur. + float s = (float) (Math.sin(0.5 * a1.w) / n); + x *= s; + y *= s; + z *= s; + w = (float) Math.cos(0.5 * a1.w); + } + + /** + * Sets the value of this quaternion using the rotational component of the + * passed matrix. + * + * @param m + * The matrix + * @return this + */ + public final Quaternion setFromMatrix(Matrix4f m) { + return setFromMatrix(m, this); + } + + /** + * Sets the value of the source quaternion using the rotational component of the + * passed matrix. + * + * @param m + * The source matrix + * @param q + * The destination quaternion, or null if a new quaternion is to be created + * @return q + */ + public static Quaternion setFromMatrix(Matrix4f m, Quaternion q) { + return q.setFromMat(m.m00, m.m01, m.m02, m.m10, m.m11, m.m12, m.m20, + m.m21, m.m22); + } + + /** + * Sets the value of this quaternion using the rotational component of the + * passed matrix. + * + * @param m + * The source matrix + */ + public final Quaternion setFromMatrix(Matrix3f m) { + return setFromMatrix(m, this); + } + + /** + * Sets the value of the source quaternion using the rotational component of the + * passed matrix. + * + * @param m + * The source matrix + * @param q + * The destination quaternion, or null if a new quaternion is to be created + * @return q + */ + public static Quaternion setFromMatrix(Matrix3f m, Quaternion q) { + return q.setFromMat(m.m00, m.m01, m.m02, m.m10, m.m11, m.m12, m.m20, + m.m21, m.m22); + } + + /** + * Private method to perform the matrix-to-quaternion conversion + */ + private Quaternion setFromMat(float m00, float m01, float m02, float m10, + float m11, float m12, float m20, float m21, float m22) { + + float s; + float tr = m00 + m11 + m22; + if (tr >= 0.0) { + s = (float) Math.sqrt(tr + 1.0); + w = s * 0.5f; + s = 0.5f / s; + x = (m21 - m12) * s; + y = (m02 - m20) * s; + z = (m10 - m01) * s; + } else { + float max = Math.max(Math.max(m00, m11), m22); + if (max == m00) { + s = (float) Math.sqrt(m00 - (m11 + m22) + 1.0); + x = s * 0.5f; + s = 0.5f / s; + y = (m01 + m10) * s; + z = (m20 + m02) * s; + w = (m21 - m12) * s; + } else if (max == m11) { + s = (float) Math.sqrt(m11 - (m22 + m00) + 1.0); + y = s * 0.5f; + s = 0.5f / s; + z = (m12 + m21) * s; + x = (m01 + m10) * s; + w = (m02 - m20) * s; + } else { + s = (float) Math.sqrt(m22 - (m00 + m11) + 1.0); + z = s * 0.5f; + s = 0.5f / s; + x = (m20 + m02) * s; + y = (m12 + m21) * s; + w = (m10 - m01) * s; + } + } + return this; + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/vector/ReadableVector.java b/src/de/verschwiegener/lwjgl3/util/vector/ReadableVector.java new file mode 100644 index 0000000..b5a24e0 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/vector/ReadableVector.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.vector; + +import java.nio.FloatBuffer; + +/** + * @author foo + */ +public interface ReadableVector { + /** + * @return the length of the vector + */ + float length(); + /** + * @return the length squared of the vector + */ + float lengthSquared(); + /** + * Store this vector in a FloatBuffer + * @param buf The buffer to store it in, at the current position + * @return this + */ + Vector store(FloatBuffer buf); +} \ No newline at end of file diff --git a/src/de/verschwiegener/lwjgl3/util/vector/ReadableVector2f.java b/src/de/verschwiegener/lwjgl3/util/vector/ReadableVector2f.java new file mode 100644 index 0000000..a414c30 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/vector/ReadableVector2f.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.vector; + +/** + * @author foo + */ +public interface ReadableVector2f extends ReadableVector { + /** + * @return x + */ + float getX(); + /** + * @return y + */ + float getY(); +} \ No newline at end of file diff --git a/src/de/verschwiegener/lwjgl3/util/vector/ReadableVector3f.java b/src/de/verschwiegener/lwjgl3/util/vector/ReadableVector3f.java new file mode 100644 index 0000000..25b2766 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/vector/ReadableVector3f.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.vector; + +/** + * @author foo + */ +public interface ReadableVector3f extends ReadableVector2f { + /** + * @return z + */ + float getZ(); +} \ No newline at end of file diff --git a/src/de/verschwiegener/lwjgl3/util/vector/ReadableVector4f.java b/src/de/verschwiegener/lwjgl3/util/vector/ReadableVector4f.java new file mode 100644 index 0000000..48dd23b --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/vector/ReadableVector4f.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.vector; + +/** + * @author foo + */ +public interface ReadableVector4f extends ReadableVector3f { + + /** + * @return w + */ + float getW(); + +} diff --git a/src/de/verschwiegener/lwjgl3/util/vector/Vector.java b/src/de/verschwiegener/lwjgl3/util/vector/Vector.java new file mode 100644 index 0000000..4808289 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/vector/Vector.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.vector; + +import java.io.Serializable; +import java.nio.FloatBuffer; + +/** + * + * Base class for vectors. + * + * @author cix_foo + * @version $Revision$ + * $Id$ + */ +public abstract class Vector implements Serializable, ReadableVector { + + /** + * Constructor for Vector. + */ + protected Vector() { + super(); + } + + /** + * @return the length of the vector + */ + public final float length() { + return (float) Math.sqrt(lengthSquared()); + } + + + /** + * @return the length squared of the vector + */ + public abstract float lengthSquared(); + + /** + * Load this vector from a FloatBuffer + * @param buf The buffer to load it from, at the current position + * @return this + */ + public abstract Vector load(FloatBuffer buf); + + /** + * Negate a vector + * @return this + */ + public abstract Vector negate(); + + + /** + * Normalise this vector + * @return this + */ + public final Vector normalise() { + float len = length(); + if (len != 0.0f) { + float l = 1.0f / len; + return scale(l); + } else + throw new IllegalStateException("Zero length vector"); + } + + + /** + * Store this vector in a FloatBuffer + * @param buf The buffer to store it in, at the current position + * @return this + */ + public abstract Vector store(FloatBuffer buf); + + + /** + * Scale this vector + * @param scale The scale factor + * @return this + */ + public abstract Vector scale(float scale); + + + +} diff --git a/src/de/verschwiegener/lwjgl3/util/vector/Vector2f.java b/src/de/verschwiegener/lwjgl3/util/vector/Vector2f.java new file mode 100644 index 0000000..1b22c67 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/vector/Vector2f.java @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.vector; + +import java.io.Serializable; +import java.nio.FloatBuffer; + + +/** + * + * Holds a 2-tuple vector. + * + * @author cix_foo + * @version $Revision$ + * $Id$ + */ + +public class Vector2f extends Vector implements Serializable, ReadableVector2f, WritableVector2f { + + private static final long serialVersionUID = 1L; + + public float x, y; + + /** + * Constructor for Vector2f. + */ + public Vector2f() { + super(); + } + + /** + * Constructor. + */ + public Vector2f(ReadableVector2f src) { + set(src); + } + + /** + * Constructor. + */ + public Vector2f(float x, float y) { + set(x, y); + } + + /* (non-Javadoc) + * @see org.lwjgl.util.vector.WritableVector2f#set(float, float) + */ + public void set(float x, float y) { + this.x = x; + this.y = y; + } + + /** + * Load from another Vector2f + * @param src The source vector + * @return this + */ + public Vector2f set(ReadableVector2f src) { + x = src.getX(); + y = src.getY(); + return this; + } + + /** + * @return the length squared of the vector + */ + public float lengthSquared() { + return x * x + y * y; + } + + /** + * Translate a vector + * @param x The translation in x + * @param y the translation in y + * @return this + */ + public Vector2f translate(float x, float y) { + this.x += x; + this.y += y; + return this; + } + + /** + * Negate a vector + * @return this + */ + public Vector negate() { + x = -x; + y = -y; + return this; + } + + /** + * Negate a vector and place the result in a destination vector. + * @param dest The destination vector or null if a new vector is to be created + * @return the negated vector + */ + public Vector2f negate(Vector2f dest) { + if (dest == null) + dest = new Vector2f(); + dest.x = -x; + dest.y = -y; + return dest; + } + + + /** + * Normalise this vector and place the result in another vector. + * @param dest The destination vector, or null if a new vector is to be created + * @return the normalised vector + */ + public Vector2f normalise(Vector2f dest) { + float l = length(); + + if (dest == null) + dest = new Vector2f(x / l, y / l); + else + dest.set(x / l, y / l); + + return dest; + } + + /** + * The dot product of two vectors is calculated as + * v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + * @param left The LHS vector + * @param right The RHS vector + * @return left dot right + */ + public static float dot(Vector2f left, Vector2f right) { + return left.x * right.x + left.y * right.y; + } + + + + /** + * Calculate the angle between two vectors, in radians + * @param a A vector + * @param b The other vector + * @return the angle between the two vectors, in radians + */ + public static float angle(Vector2f a, Vector2f b) { + float dls = dot(a, b) / (a.length() * b.length()); + if (dls < -1f) + dls = -1f; + else if (dls > 1.0f) + dls = 1.0f; + return (float)Math.acos(dls); + } + + /** + * Add a vector to another vector and place the result in a destination + * vector. + * @param left The LHS vector + * @param right The RHS vector + * @param dest The destination vector, or null if a new vector is to be created + * @return the sum of left and right in dest + */ + public static Vector2f add(Vector2f left, Vector2f right, Vector2f dest) { + if (dest == null) + return new Vector2f(left.x + right.x, left.y + right.y); + else { + dest.set(left.x + right.x, left.y + right.y); + return dest; + } + } + + /** + * Subtract a vector from another vector and place the result in a destination + * vector. + * @param left The LHS vector + * @param right The RHS vector + * @param dest The destination vector, or null if a new vector is to be created + * @return left minus right in dest + */ + public static Vector2f sub(Vector2f left, Vector2f right, Vector2f dest) { + if (dest == null) + return new Vector2f(left.x - right.x, left.y - right.y); + else { + dest.set(left.x - right.x, left.y - right.y); + return dest; + } + } + + /** + * Store this vector in a FloatBuffer + * @param buf The buffer to store it in, at the current position + * @return this + */ + public Vector store(FloatBuffer buf) { + buf.put(x); + buf.put(y); + return this; + } + + /** + * Load this vector from a FloatBuffer + * @param buf The buffer to load it from, at the current position + * @return this + */ + public Vector load(FloatBuffer buf) { + x = buf.get(); + y = buf.get(); + return this; + } + + /* (non-Javadoc) + * @see org.lwjgl.vector.Vector#scale(float) + */ + public Vector scale(float scale) { + + x *= scale; + y *= scale; + + return this; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + StringBuilder sb = new StringBuilder(64); + + sb.append("Vector2f["); + sb.append(x); + sb.append(", "); + sb.append(y); + sb.append(']'); + return sb.toString(); + } + + /** + * @return x + */ + public final float getX() { + return x; + } + + /** + * @return y + */ + public final float getY() { + return y; + } + + /** + * Set X + * @param x + */ + public final void setX(float x) { + this.x = x; + } + + /** + * Set Y + * @param y + */ + public final void setY(float y) { + this.y = y; + } + + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Vector2f other = (Vector2f)obj; + + if (x == other.x && y == other.y) return true; + + return false; + } + +} diff --git a/src/de/verschwiegener/lwjgl3/util/vector/Vector3f.java b/src/de/verschwiegener/lwjgl3/util/vector/Vector3f.java new file mode 100644 index 0000000..d221981 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/vector/Vector3f.java @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.vector; + +import java.io.Serializable; +import java.nio.FloatBuffer; + + +/** + * + * Holds a 3-tuple vector. + * + * @author cix_foo + * @version $Revision$ + * $Id$ + */ + +public class Vector3f extends Vector implements Serializable, ReadableVector3f, WritableVector3f { + + private static final long serialVersionUID = 1L; + + public float x, y, z; + + /** + * Constructor for Vector3f. + */ + public Vector3f() { + super(); + } + + /** + * Constructor + */ + public Vector3f(ReadableVector3f src) { + set(src); + } + + /** + * Constructor + */ + public Vector3f(float x, float y, float z) { + set(x, y, z); + } + + /* (non-Javadoc) + * @see org.lwjgl.util.vector.WritableVector2f#set(float, float) + */ + public void set(float x, float y) { + this.x = x; + this.y = y; + } + + /* (non-Javadoc) + * @see org.lwjgl.util.vector.WritableVector3f#set(float, float, float) + */ + public void set(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Load from another Vector3f + * @param src The source vector + * @return this + */ + public Vector3f set(ReadableVector3f src) { + x = src.getX(); + y = src.getY(); + z = src.getZ(); + return this; + } + + /** + * @return the length squared of the vector + */ + public float lengthSquared() { + return x * x + y * y + z * z; + } + + /** + * Translate a vector + * @param x The translation in x + * @param y the translation in y + * @return this + */ + public Vector3f translate(float x, float y, float z) { + this.x += x; + this.y += y; + this.z += z; + return this; + } + + /** + * Add a vector to another vector and place the result in a destination + * vector. + * @param left The LHS vector + * @param right The RHS vector + * @param dest The destination vector, or null if a new vector is to be created + * @return the sum of left and right in dest + */ + public static Vector3f add(Vector3f left, Vector3f right, Vector3f dest) { + if (dest == null) + return new Vector3f(left.x + right.x, left.y + right.y, left.z + right.z); + else { + dest.set(left.x + right.x, left.y + right.y, left.z + right.z); + return dest; + } + } + + /** + * Subtract a vector from another vector and place the result in a destination + * vector. + * @param left The LHS vector + * @param right The RHS vector + * @param dest The destination vector, or null if a new vector is to be created + * @return left minus right in dest + */ + public static Vector3f sub(Vector3f left, Vector3f right, Vector3f dest) { + if (dest == null) + return new Vector3f(left.x - right.x, left.y - right.y, left.z - right.z); + else { + dest.set(left.x - right.x, left.y - right.y, left.z - right.z); + return dest; + } + } + + /** + * The cross product of two vectors. + * + * @param left The LHS vector + * @param right The RHS vector + * @param dest The destination result, or null if a new vector is to be created + * @return left cross right + */ + public static Vector3f cross( + Vector3f left, + Vector3f right, + Vector3f dest) + { + + if (dest == null) + dest = new Vector3f(); + + dest.set( + left.y * right.z - left.z * right.y, + right.x * left.z - right.z * left.x, + left.x * right.y - left.y * right.x + ); + + return dest; + } + + + + /** + * Negate a vector + * @return this + */ + public Vector negate() { + x = -x; + y = -y; + z = -z; + return this; + } + + /** + * Negate a vector and place the result in a destination vector. + * @param dest The destination vector or null if a new vector is to be created + * @return the negated vector + */ + public Vector3f negate(Vector3f dest) { + if (dest == null) + dest = new Vector3f(); + dest.x = -x; + dest.y = -y; + dest.z = -z; + return dest; + } + + + /** + * Normalise this vector and place the result in another vector. + * @param dest The destination vector, or null if a new vector is to be created + * @return the normalised vector + */ + public Vector3f normalise(Vector3f dest) { + float l = length(); + + if (dest == null) + dest = new Vector3f(x / l, y / l, z / l); + else + dest.set(x / l, y / l, z / l); + + return dest; + } + + /** + * The dot product of two vectors is calculated as + * v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + * @param left The LHS vector + * @param right The RHS vector + * @return left dot right + */ + public static float dot(Vector3f left, Vector3f right) { + return left.x * right.x + left.y * right.y + left.z * right.z; + } + + /** + * Calculate the angle between two vectors, in radians + * @param a A vector + * @param b The other vector + * @return the angle between the two vectors, in radians + */ + public static float angle(Vector3f a, Vector3f b) { + float dls = dot(a, b) / (a.length() * b.length()); + if (dls < -1f) + dls = -1f; + else if (dls > 1.0f) + dls = 1.0f; + return (float)Math.acos(dls); + } + + /* (non-Javadoc) + * @see org.lwjgl.vector.Vector#load(FloatBuffer) + */ + public Vector load(FloatBuffer buf) { + x = buf.get(); + y = buf.get(); + z = buf.get(); + return this; + } + + /* (non-Javadoc) + * @see org.lwjgl.vector.Vector#scale(float) + */ + public Vector scale(float scale) { + + x *= scale; + y *= scale; + z *= scale; + + return this; + + } + + /* (non-Javadoc) + * @see org.lwjgl.vector.Vector#store(FloatBuffer) + */ + public Vector store(FloatBuffer buf) { + + buf.put(x); + buf.put(y); + buf.put(z); + + return this; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + StringBuilder sb = new StringBuilder(64); + + sb.append("Vector3f["); + sb.append(x); + sb.append(", "); + sb.append(y); + sb.append(", "); + sb.append(z); + sb.append(']'); + return sb.toString(); + } + + /** + * @return x + */ + public final float getX() { + return x; + } + + /** + * @return y + */ + public final float getY() { + return y; + } + + /** + * Set X + * @param x + */ + public final void setX(float x) { + this.x = x; + } + + /** + * Set Y + * @param y + */ + public final void setY(float y) { + this.y = y; + } + + /** + * Set Z + * @param z + */ + public void setZ(float z) { + this.z = z; + } + + /* (Overrides) + * @see org.lwjgl.vector.ReadableVector3f#getZ() + */ + public float getZ() { + return z; + } + + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Vector3f other = (Vector3f)obj; + + if (x == other.x && y == other.y && z == other.z) return true; + + return false; + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/vector/Vector4f.java b/src/de/verschwiegener/lwjgl3/util/vector/Vector4f.java new file mode 100644 index 0000000..adb8730 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/vector/Vector4f.java @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.vector; + +import java.io.Serializable; +import java.nio.FloatBuffer; + +/** + * + * Holds a 4-tuple vector. + * + * @author cix_foo + * @version $Revision$ + * $Id$ + */ + +public class Vector4f extends Vector implements Serializable, ReadableVector4f, WritableVector4f { + + private static final long serialVersionUID = 1L; + + public float x, y, z, w; + + /** + * Constructor for Vector4f. + */ + public Vector4f() { + super(); + } + + /** + * Constructor + */ + public Vector4f(ReadableVector4f src) { + set(src); + } + + /** + * Constructor + */ + public Vector4f(float x, float y, float z, float w) { + set(x, y, z, w); + } + + /* (non-Javadoc) + * @see org.lwjgl.util.vector.WritableVector2f#set(float, float) + */ + public void set(float x, float y) { + this.x = x; + this.y = y; + } + + /* (non-Javadoc) + * @see org.lwjgl.util.vector.WritableVector3f#set(float, float, float) + */ + public void set(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + } + + /* (non-Javadoc) + * @see org.lwjgl.util.vector.WritableVector4f#set(float, float, float, float) + */ + public void set(float x, float y, float z, float w) { + this.x = x; + this.y = y; + this.z = z; + this.w = w; + } + + /** + * Load from another Vector4f + * @param src The source vector + * @return this + */ + public Vector4f set(ReadableVector4f src) { + x = src.getX(); + y = src.getY(); + z = src.getZ(); + w = src.getW(); + return this; + } + + /** + * @return the length squared of the vector + */ + public float lengthSquared() { + return x * x + y * y + z * z + w * w; + } + + /** + * Translate a vector + * @param x The translation in x + * @param y the translation in y + * @return this + */ + public Vector4f translate(float x, float y, float z, float w) { + this.x += x; + this.y += y; + this.z += z; + this.w += w; + return this; + } + + /** + * Add a vector to another vector and place the result in a destination + * vector. + * @param left The LHS vector + * @param right The RHS vector + * @param dest The destination vector, or null if a new vector is to be created + * @return the sum of left and right in dest + */ + public static Vector4f add(Vector4f left, Vector4f right, Vector4f dest) { + if (dest == null) + return new Vector4f(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w); + else { + dest.set(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w); + return dest; + } + } + + /** + * Subtract a vector from another vector and place the result in a destination + * vector. + * @param left The LHS vector + * @param right The RHS vector + * @param dest The destination vector, or null if a new vector is to be created + * @return left minus right in dest + */ + public static Vector4f sub(Vector4f left, Vector4f right, Vector4f dest) { + if (dest == null) + return new Vector4f(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w); + else { + dest.set(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w); + return dest; + } + } + + + /** + * Negate a vector + * @return this + */ + public Vector negate() { + x = -x; + y = -y; + z = -z; + w = -w; + return this; + } + + /** + * Negate a vector and place the result in a destination vector. + * @param dest The destination vector or null if a new vector is to be created + * @return the negated vector + */ + public Vector4f negate(Vector4f dest) { + if (dest == null) + dest = new Vector4f(); + dest.x = -x; + dest.y = -y; + dest.z = -z; + dest.w = -w; + return dest; + } + + + /** + * Normalise this vector and place the result in another vector. + * @param dest The destination vector, or null if a new vector is to be created + * @return the normalised vector + */ + public Vector4f normalise(Vector4f dest) { + float l = length(); + + if (dest == null) + dest = new Vector4f(x / l, y / l, z / l, w / l); + else + dest.set(x / l, y / l, z / l, w / l); + + return dest; + } + + /** + * The dot product of two vectors is calculated as + * v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + v1.w * v2.w + * @param left The LHS vector + * @param right The RHS vector + * @return left dot right + */ + public static float dot(Vector4f left, Vector4f right) { + return left.x * right.x + left.y * right.y + left.z * right.z + left.w * right.w; + } + + /** + * Calculate the angle between two vectors, in radians + * @param a A vector + * @param b The other vector + * @return the angle between the two vectors, in radians + */ + public static float angle(Vector4f a, Vector4f b) { + float dls = dot(a, b) / (a.length() * b.length()); + if (dls < -1f) + dls = -1f; + else if (dls > 1.0f) + dls = 1.0f; + return (float)Math.acos(dls); + } + + /* (non-Javadoc) + * @see org.lwjgl.vector.Vector#load(FloatBuffer) + */ + public Vector load(FloatBuffer buf) { + x = buf.get(); + y = buf.get(); + z = buf.get(); + w = buf.get(); + return this; + } + + /* (non-Javadoc) + * @see org.lwjgl.vector.Vector#scale(float) + */ + public Vector scale(float scale) { + x *= scale; + y *= scale; + z *= scale; + w *= scale; + return this; + } + + /* (non-Javadoc) + * @see org.lwjgl.vector.Vector#store(FloatBuffer) + */ + public Vector store(FloatBuffer buf) { + + buf.put(x); + buf.put(y); + buf.put(z); + buf.put(w); + + return this; + } + + public String toString() { + return "Vector4f: " + x + " " + y + " " + z + " " + w; + } + + /** + * @return x + */ + public final float getX() { + return x; + } + + /** + * @return y + */ + public final float getY() { + return y; + } + + /** + * Set X + * @param x + */ + public final void setX(float x) { + this.x = x; + } + + /** + * Set Y + * @param y + */ + public final void setY(float y) { + this.y = y; + } + + /** + * Set Z + * @param z + */ + public void setZ(float z) { + this.z = z; + } + + + /* (Overrides) + * @see org.lwjgl.vector.ReadableVector3f#getZ() + */ + public float getZ() { + return z; + } + + /** + * Set W + * @param w + */ + public void setW(float w) { + this.w = w; + } + + /* (Overrides) + * @see org.lwjgl.vector.ReadableVector3f#getZ() + */ + public float getW() { + return w; + } + + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + Vector4f other = (Vector4f)obj; + + if (x == other.x && y == other.y && z == other.z && w == other.w) return true; + + return false; + } +} diff --git a/src/de/verschwiegener/lwjgl3/util/vector/WritableVector2f.java b/src/de/verschwiegener/lwjgl3/util/vector/WritableVector2f.java new file mode 100644 index 0000000..e381b99 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/vector/WritableVector2f.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.vector; + +/** + * Writable interface to Vector2fs + * @author $author$ + * @version $revision$ + * $Id$ + */ +public interface WritableVector2f { + + /** + * Set the X value + * @param x + */ + void setX(float x); + + /** + * Set the Y value + * @param y + */ + void setY(float y); + + /** + * Set the X,Y values + * @param x + * @param y + */ + void set(float x, float y); + +} \ No newline at end of file diff --git a/src/de/verschwiegener/lwjgl3/util/vector/WritableVector3f.java b/src/de/verschwiegener/lwjgl3/util/vector/WritableVector3f.java new file mode 100644 index 0000000..3668f1a --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/vector/WritableVector3f.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.vector; + +/** + * Writable interface to Vector3fs + * @author $author$ + * @version $revision$ + * $Id$ + */ +public interface WritableVector3f extends WritableVector2f { + + /** + * Set the Z value + * @param z + */ + void setZ(float z); + + /** + * Set the X,Y,Z values + * @param x + * @param y + * @param z + */ + void set(float x, float y, float z); + +} \ No newline at end of file diff --git a/src/de/verschwiegener/lwjgl3/util/vector/WritableVector4f.java b/src/de/verschwiegener/lwjgl3/util/vector/WritableVector4f.java new file mode 100644 index 0000000..eb48b65 --- /dev/null +++ b/src/de/verschwiegener/lwjgl3/util/vector/WritableVector4f.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2002-2008 LWJGL Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package de.verschwiegener.lwjgl3.util.vector; + +/** + * Writable interface to Vector4fs + * @author $author$ + * @version $revision$ + * $Id$ + */ +public interface WritableVector4f extends WritableVector3f { + + /** + * Set the W value + * @param w + */ + void setW(float w); + + /** + * Set the X,Y,Z,W values + * @param x + * @param y + * @param z + * @param w + */ + void set(float x, float y, float z, float w); + +} \ No newline at end of file diff --git a/src/org/lwjgl/LWJGLException.java b/src/org/lwjgl/LWJGLException.java new file mode 100644 index 0000000..7696211 --- /dev/null +++ b/src/org/lwjgl/LWJGLException.java @@ -0,0 +1,39 @@ +package org.lwjgl; + +public class LWJGLException extends Exception{ + + private static final long serialVersionUID = 1L; + + /** + * Plain c'tor + */ + public LWJGLException() { + super(); + } + + /** + * Creates a new LWJGLException + * + * @param msg + * String identifier for exception + */ + public LWJGLException(String msg) { + super(msg); + } + + /** + * @param message + * @param cause + */ + public LWJGLException(String message, Throwable cause) { + super(message, cause); + } + + /** + * @param cause + */ + public LWJGLException(Throwable cause) { + super(cause); + } + +} diff --git a/src/org/lwjgl/input/Keyboard.java b/src/org/lwjgl/input/Keyboard.java new file mode 100644 index 0000000..57f87b3 --- /dev/null +++ b/src/org/lwjgl/input/Keyboard.java @@ -0,0 +1,487 @@ +package org.lwjgl.input; + +import de.verschwiegener.lwjgl3.BufferUtils; +import de.verschwiegener.lwjgl3.display.Display; +import org.lwjgl.glfw.GLFWCharCallback; +import org.lwjgl.glfw.GLFWKeyCallback; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +import static org.lwjgl.glfw.GLFW.*; + +public class Keyboard { + + public static final int KEY_NONE = 0x00; + + public static final int KEY_ESCAPE = 0x01; + public static final int KEY_1 = 0x02; + public static final int KEY_2 = 0x03; + public static final int KEY_3 = 0x04; + public static final int KEY_4 = 0x05; + public static final int KEY_5 = 0x06; + public static final int KEY_6 = 0x07; + public static final int KEY_7 = 0x08; + public static final int KEY_8 = 0x09; + public static final int KEY_9 = 0x0A; + public static final int KEY_0 = 0x0B; + public static final int KEY_MINUS = 0x0C; /* - on main keyboard */ + public static final int KEY_EQUALS = 0x0D; + public static final int KEY_BACK = 0x0E; /* backspace */ + public static final int KEY_TAB = 0x0F; + public static final int KEY_Q = 0x10; + public static final int KEY_W = 0x11; + public static final int KEY_E = 0x12; + public static final int KEY_R = 0x13; + public static final int KEY_T = 0x14; + public static final int KEY_Y = 0x15; + public static final int KEY_U = 0x16; + public static final int KEY_I = 0x17; + public static final int KEY_O = 0x18; + public static final int KEY_P = 0x19; + public static final int KEY_LBRACKET = 0x1A; + public static final int KEY_RBRACKET = 0x1B; + public static final int KEY_RETURN = 0x1C; /* Enter on main keyboard */ + public static final int KEY_LCONTROL = 0x1D; + public static final int KEY_A = 0x1E; + public static final int KEY_S = 0x1F; + public static final int KEY_D = 0x20; + public static final int KEY_F = 0x21; + public static final int KEY_G = 0x22; + public static final int KEY_H = 0x23; + public static final int KEY_J = 0x24; + public static final int KEY_K = 0x25; + public static final int KEY_L = 0x26; + public static final int KEY_SEMICOLON = 0x27; + public static final int KEY_APOSTROPHE = 0x28; + public static final int KEY_GRAVE = 0x29; /* accent grave */ + public static final int KEY_LSHIFT = 0x2A; + public static final int KEY_BACKSLASH = 0x2B; + public static final int KEY_Z = 0x2C; + public static final int KEY_X = 0x2D; + public static final int KEY_C = 0x2E; + public static final int KEY_V = 0x2F; + public static final int KEY_B = 0x30; + public static final int KEY_N = 0x31; + public static final int KEY_M = 0x32; + public static final int KEY_COMMA = 0x33; + public static final int KEY_PERIOD = 0x34; /* . on main keyboard */ + public static final int KEY_SLASH = 0x35; /* / on main keyboard */ + public static final int KEY_RSHIFT = 0x36; + public static final int KEY_MULTIPLY = 0x37; /* * on numeric keypad */ + public static final int KEY_LMENU = 0x38; /* left Alt */ + public static final int KEY_SPACE = 0x39; + public static final int KEY_CAPITAL = 0x3A; + public static final int KEY_F1 = 0x3B; + public static final int KEY_F2 = 0x3C; + public static final int KEY_F3 = 0x3D; + public static final int KEY_F4 = 0x3E; + public static final int KEY_F5 = 0x3F; + public static final int KEY_F6 = 0x40; + public static final int KEY_F7 = 0x41; + public static final int KEY_F8 = 0x42; + public static final int KEY_F9 = 0x43; + public static final int KEY_F10 = 0x44; + public static final int KEY_NUMLOCK = 0x45; + public static final int KEY_SCROLL = 0x46; /* Scroll Lock */ + public static final int KEY_NUMPAD7 = 0x47; + public static final int KEY_NUMPAD8 = 0x48; + public static final int KEY_NUMPAD9 = 0x49; + public static final int KEY_SUBTRACT = 0x4A; /* - on numeric keypad */ + public static final int KEY_NUMPAD4 = 0x4B; + public static final int KEY_NUMPAD5 = 0x4C; + public static final int KEY_NUMPAD6 = 0x4D; + public static final int KEY_ADD = 0x4E; /* + on numeric keypad */ + public static final int KEY_NUMPAD1 = 0x4F; + public static final int KEY_NUMPAD2 = 0x50; + public static final int KEY_NUMPAD3 = 0x51; + public static final int KEY_NUMPAD0 = 0x52; + public static final int KEY_DECIMAL = 0x53; /* . on numeric keypad */ + public static final int KEY_F11 = 0x57; + public static final int KEY_F12 = 0x58; + public static final int KEY_F13 = 0x64; /* (NEC PC98) */ + public static final int KEY_F14 = 0x65; /* (NEC PC98) */ + public static final int KEY_F15 = 0x66; /* (NEC PC98) */ + public static final int KEY_F16 = 0x67; /* Extended Function keys - (Mac) */ + public static final int KEY_F17 = 0x68; + public static final int KEY_F18 = 0x69; + public static final int KEY_KANA = 0x70; /* (Japanese keyboard) */ + public static final int KEY_F19 = 0x71; /* Extended Function keys - (Mac) */ + public static final int KEY_CONVERT = 0x79; /* (Japanese keyboard) */ + public static final int KEY_NOCONVERT = 0x7B; /* (Japanese keyboard) */ + public static final int KEY_YEN = 0x7D; /* (Japanese keyboard) */ + public static final int KEY_NUMPADEQUALS = 0x8D; /* = on numeric keypad (NEC PC98) */ + public static final int KEY_CIRCUMFLEX = 0x90; /* (Japanese keyboard) */ + public static final int KEY_AT = 0x91; /* (NEC PC98) */ + public static final int KEY_COLON = 0x92; /* (NEC PC98) */ + public static final int KEY_UNDERLINE = 0x93; /* (NEC PC98) */ + public static final int KEY_KANJI = 0x94; /* (Japanese keyboard) */ + public static final int KEY_STOP = 0x95; /* (NEC PC98) */ + public static final int KEY_AX = 0x96; /* (Japan AX) */ + public static final int KEY_UNLABELED = 0x97; /* (J3100) */ + public static final int KEY_NUMPADENTER = 0x9C; /* Enter on numeric keypad */ + public static final int KEY_RCONTROL = 0x9D; + public static final int KEY_SECTION = 0xA7; /* Section symbol (Mac) */ + public static final int KEY_NUMPADCOMMA = 0xB3; /* , on numeric keypad (NEC PC98) */ + public static final int KEY_DIVIDE = 0xB5; /* / on numeric keypad */ + public static final int KEY_SYSRQ = 0xB7; + public static final int KEY_RMENU = 0xB8; /* right Alt */ + public static final int KEY_FUNCTION = 0xC4; /* Function (Mac) */ + public static final int KEY_PAUSE = 0xC5; /* Pause */ + public static final int KEY_HOME = 0xC7; /* Home on arrow keypad */ + public static final int KEY_UP = 0xC8; /* UpArrow on arrow keypad */ + public static final int KEY_PRIOR = 0xC9; /* PgUp on arrow keypad */ + public static final int KEY_LEFT = 0xCB; /* LeftArrow on arrow keypad */ + public static final int KEY_RIGHT = 0xCD; /* RightArrow on arrow keypad */ + public static final int KEY_END = 0xCF; /* End on arrow keypad */ + public static final int KEY_DOWN = 0xD0; /* DownArrow on arrow keypad */ + public static final int KEY_NEXT = 0xD1; /* PgDn on arrow keypad */ + public static final int KEY_INSERT = 0xD2; /* Insert on arrow keypad */ + public static final int KEY_DELETE = 0xD3; /* Delete on arrow keypad */ + public static final int KEY_CLEAR = 0xDA; /* Clear key (Mac) */ + public static final int KEY_LMETA = 0xDB; /* Left Windows/Option key */ + /** + * The left windows key, mapped to KEY_LMETA + * + * @deprecated Use KEY_LMETA instead + */ + public static final int KEY_LWIN = KEY_LMETA; /* Left Windows key */ + public static final int KEY_RMETA = 0xDC; /* Right Windows/Option key */ + /** + * The right windows key, mapped to KEY_RMETA + * + * @deprecated Use KEY_RMETA instead + */ + public static final int KEY_RWIN = KEY_RMETA; /* Right Windows key */ + public static final int KEY_APPS = 0xDD; /* AppMenu key */ + public static final int KEY_POWER = 0xDE; + public static final int KEY_SLEEP = 0xDF; + + public static final int KEYBOARD_SIZE = 256; + + public static final ByteBuffer keyDownBuffer = BufferUtils.createByteBuffer(KEYBOARD_SIZE); + + private static final int BUFFER_SIZE = 50; + public static final int EVENT_SIZE = 4 + 1 + 4 + 8 + 1; + private static ByteBuffer readBuffer; + private static boolean created; + private static boolean repeat_enabled; + + /** + * Key names + */ + private static final String[] keyName = new String[KEYBOARD_SIZE]; + private static final Map keyMap = new HashMap<>(KEYBOARD_SIZE); + private static final Map glfwKeys = new HashMap<>(348); + private static int counter; + + private static final KeyEvent current_event = new KeyEvent(); + + + static { + // Use reflection to find out key names + Field[] fields = Keyboard.class.getFields(); + try { + for (Field field : fields) { + if (Modifier.isStatic(field.getModifiers()) + && Modifier.isPublic(field.getModifiers()) + && Modifier.isFinal(field.getModifiers()) + && field.getType().equals(int.class) + && field.getName().startsWith("KEY_") + && !field.getName().endsWith("WIN")) { /* Don't use deprecated names */ + + int key = field.getInt(null); + String name = field.getName().substring(4); + keyName[key] = name; + keyMap.put(name, key); + counter++; + } + } + } catch (Exception e) { + } + } + public static int getKeyCount() { + return counter; + } + + public static String getKeyName(int key) { + return keyName[key]; + } + + public static int getKeyIndex(String keyName) { + Integer ret = keyMap.get(keyName); + if (ret == null) + return KEY_NONE; + else + return ret; + } + + public static void create() { + if(created) + return; + + createGLFWKeyMap(); + created = true; + repeat_enabled = false; + readBuffer = ByteBuffer.allocate(EVENT_SIZE * BUFFER_SIZE); + reset();; + } + + public static void destroy() { + if (!created) + return; + created = false; + reset(); + } + + public static boolean next() { + if (!created) + throw new IllegalStateException("Keyboard must be created before you can read events"); + boolean result; + while ((result = readNext(current_event)) && current_event.repeat && !repeat_enabled) + ; + return result; + + } + + private static boolean readNext(KeyEvent event) { + if (readBuffer.hasRemaining()) { + event.key = readBuffer.getInt() & 0xFF; + event.state = readBuffer.get() != 0; + event.character = readBuffer.getChar(); + boolean repeat = readBuffer.get() == 1; + if (repeat_enabled) event.repeat = repeat; + return true; + } else + return false; + } + + public static void enableRepeatEvents(boolean enable) { + System.out.println("SetRepeat: " + enable); + repeat_enabled = enable; + } + + public static int getEventKey() { + return current_event.key; + } + + public static boolean getEventKeyState() { + return current_event.state; + } + + public static char getEventCharacter() { + return (char) current_event.character; + } + + public static boolean isRepeatEvent() { + return repeat_enabled; + } + + public static boolean isCreated() { + return created; + } + + public static boolean isKeyDown(int key) { + if (!created) + throw new IllegalStateException("Keyboard must be created before you can query key state"); + return keyDownBuffer.get(key) != 0; + } + + public static void reset() { + readBuffer.limit(0); + for (int i = 0; i < keyDownBuffer.remaining(); i++) + keyDownBuffer.put(i, (byte) 0); + current_event.reset(); + } + + public static void pollGLFW(){ + glfwSetCharCallback(Display.getWindow(), new GLFWCharCallback() { + @Override + public void invoke(long l, int i) { + parseGLFWCodes(0, 1, i); + } + }); + glfwSetKeyCallback(Display.getWindow(), new GLFWKeyCallback() { + @Override + public void invoke(long l, int key, int scancode, int action, int modifierBits) { + if(!repeat_enabled && action == 2) return; + int poll_key = glfwKeys.get(key); + parseGLFWCodes(poll_key, action,0 ); + + keyDownBuffer.compact(); + keyDownBuffer.put(poll_key, (byte) ((action == 2 || action == 1) ? 1 : 0)); + keyDownBuffer.flip(); + } + }); + } + + + public static void parseGLFWCodes(int key, int action,int character) { + readBuffer.compact(); + readBuffer.putInt(key); + readBuffer.put((byte) ((action == 2 || action == 1) ? 1 : 0)); + readBuffer.putChar((char) character); + readBuffer.put((byte) ((action == 2) ? 1 : 0)); + readBuffer.flip(); + } + + private static void createGLFWKeyMap() { + glfwKeys.put(GLFW_KEY_UNKNOWN, KEY_NONE); + glfwKeys.put(GLFW_KEY_SPACE, KEY_SPACE); + glfwKeys.put(GLFW_KEY_APOSTROPHE, KEY_APOSTROPHE); + glfwKeys.put(GLFW_KEY_COMMA, KEY_COMMA); + glfwKeys.put(GLFW_KEY_MINUS, KEY_MINUS); + glfwKeys.put(GLFW_KEY_PERIOD, KEY_PERIOD); + glfwKeys.put(GLFW_KEY_SLASH, KEY_SLASH); + glfwKeys.put(GLFW_KEY_0, KEY_0); + glfwKeys.put(GLFW_KEY_1, KEY_1); + glfwKeys.put(GLFW_KEY_2, KEY_2); + glfwKeys.put(GLFW_KEY_3, KEY_3); + glfwKeys.put(GLFW_KEY_4, KEY_4); + glfwKeys.put(GLFW_KEY_5, KEY_5); + glfwKeys.put(GLFW_KEY_6, KEY_6); + glfwKeys.put(GLFW_KEY_7, KEY_7); + glfwKeys.put(GLFW_KEY_8, KEY_8); + glfwKeys.put(GLFW_KEY_9, KEY_9); + glfwKeys.put(GLFW_KEY_SEMICOLON, KEY_SEMICOLON); + glfwKeys.put(GLFW_KEY_EQUAL, KEY_EQUALS); + glfwKeys.put(GLFW_KEY_A, KEY_A); + glfwKeys.put(GLFW_KEY_B, KEY_B); + glfwKeys.put(GLFW_KEY_C, KEY_C); + glfwKeys.put(GLFW_KEY_D, KEY_D); + glfwKeys.put(GLFW_KEY_E, KEY_E); + glfwKeys.put(GLFW_KEY_F, KEY_F); + glfwKeys.put(GLFW_KEY_G, KEY_G); + glfwKeys.put(GLFW_KEY_H, KEY_H); + glfwKeys.put(GLFW_KEY_I, KEY_I); + glfwKeys.put(GLFW_KEY_J, KEY_J); + glfwKeys.put(GLFW_KEY_K, KEY_K); + glfwKeys.put(GLFW_KEY_L, KEY_L); + glfwKeys.put(GLFW_KEY_M, KEY_M); + glfwKeys.put(GLFW_KEY_N, KEY_N); + glfwKeys.put(GLFW_KEY_O, KEY_O); + glfwKeys.put(GLFW_KEY_P, KEY_P); + glfwKeys.put(GLFW_KEY_Q, KEY_Q); + glfwKeys.put(GLFW_KEY_R, KEY_R); + glfwKeys.put(GLFW_KEY_S, KEY_S); + glfwKeys.put(GLFW_KEY_T, KEY_T); + glfwKeys.put(GLFW_KEY_U, KEY_U); + glfwKeys.put(GLFW_KEY_V, KEY_V); + glfwKeys.put(GLFW_KEY_W, KEY_W); + glfwKeys.put(GLFW_KEY_X, KEY_X); + glfwKeys.put(GLFW_KEY_Y, KEY_Y); + glfwKeys.put(GLFW_KEY_Z, KEY_Z); + glfwKeys.put(GLFW_KEY_LEFT_BRACKET, KEY_LBRACKET); + glfwKeys.put(GLFW_KEY_BACKSLASH, KEY_BACKSLASH); + glfwKeys.put(GLFW_KEY_RIGHT_BRACKET, KEY_RBRACKET); + glfwKeys.put(GLFW_KEY_GRAVE_ACCENT, KEY_GRAVE); + //IDK which keys this are, but there are not on US Layout + //glfwKeys.put(GLFW_KEY_WORLD_1,KEY_WORLD_1); + //glfwKeys.put(GLFW_KEY_WORLD_2,KEY_WORLD_2); + glfwKeys.put(GLFW_KEY_ESCAPE, KEY_ESCAPE); + glfwKeys.put(GLFW_KEY_ENTER, KEY_RETURN); + glfwKeys.put(GLFW_KEY_TAB, KEY_TAB); + glfwKeys.put(GLFW_KEY_BACKSPACE, KEY_BACK); + glfwKeys.put(GLFW_KEY_INSERT, KEY_INSERT); + glfwKeys.put(GLFW_KEY_DELETE, KEY_DELETE); + glfwKeys.put(GLFW_KEY_RIGHT, KEY_RIGHT); + glfwKeys.put(GLFW_KEY_LEFT, KEY_LEFT); + glfwKeys.put(GLFW_KEY_DOWN, KEY_DOWN); + glfwKeys.put(GLFW_KEY_UP, KEY_UP); + glfwKeys.put(GLFW_KEY_PAGE_UP, KEY_PRIOR); + glfwKeys.put(GLFW_KEY_PAGE_DOWN, KEY_NEXT); + glfwKeys.put(GLFW_KEY_HOME, KEY_HOME); + glfwKeys.put(GLFW_KEY_END, KEY_END); + glfwKeys.put(GLFW_KEY_CAPS_LOCK, KEY_CAPITAL); + glfwKeys.put(GLFW_KEY_SCROLL_LOCK, KEY_SCROLL); + glfwKeys.put(GLFW_KEY_NUM_LOCK, KEY_NUMLOCK); + glfwKeys.put(GLFW_KEY_PRINT_SCREEN, KEY_SYSRQ); + glfwKeys.put(GLFW_KEY_PAUSE, KEY_PAUSE); + glfwKeys.put(GLFW_KEY_F1, KEY_F1); + glfwKeys.put(GLFW_KEY_F2, KEY_F2); + glfwKeys.put(GLFW_KEY_F3, KEY_F3); + glfwKeys.put(GLFW_KEY_F4, KEY_F4); + glfwKeys.put(GLFW_KEY_F5, KEY_F5); + glfwKeys.put(GLFW_KEY_F6, KEY_F6); + glfwKeys.put(GLFW_KEY_F7, KEY_F7); + glfwKeys.put(GLFW_KEY_F8, KEY_F8); + glfwKeys.put(GLFW_KEY_F9, KEY_F9); + glfwKeys.put(GLFW_KEY_F10, KEY_F10); + glfwKeys.put(GLFW_KEY_F11, KEY_F11); + glfwKeys.put(GLFW_KEY_F12, KEY_F12); + glfwKeys.put(GLFW_KEY_F13, KEY_F13); + glfwKeys.put(GLFW_KEY_F14, KEY_F14); + glfwKeys.put(GLFW_KEY_F15, KEY_F15); + glfwKeys.put(GLFW_KEY_F16, KEY_F16); + glfwKeys.put(GLFW_KEY_F17, KEY_F17); + glfwKeys.put(GLFW_KEY_F18, KEY_F18); + glfwKeys.put(GLFW_KEY_F19, KEY_F19); + //lwjgl2 doesnt suport all F keys + //glfwKeys.put(GLFW_KEY_F20,KEY_F20); + //glfwKeys.put(GLFW_KEY_F21,KEY_F21); + //glfwKeys.put(GLFW_KEY_F22,KEY_F22); + //glfwKeys.put(GLFW_KEY_F23,KEY_F23); + //glfwKeys.put(GLFW_KEY_F24,KEY_F24); + //glfwKeys.put(GLFW_KEY_F25,KEY_F25); + glfwKeys.put(GLFW_KEY_KP_0, KEY_NUMPAD0); + glfwKeys.put(GLFW_KEY_KP_1, KEY_NUMPAD1); + glfwKeys.put(GLFW_KEY_KP_2, KEY_NUMPAD2); + glfwKeys.put(GLFW_KEY_KP_3, KEY_NUMPAD3); + glfwKeys.put(GLFW_KEY_KP_4, KEY_NUMPAD4); + glfwKeys.put(GLFW_KEY_KP_5, KEY_NUMPAD5); + glfwKeys.put(GLFW_KEY_KP_6, KEY_NUMPAD6); + glfwKeys.put(GLFW_KEY_KP_7, KEY_NUMPAD7); + glfwKeys.put(GLFW_KEY_KP_8, KEY_NUMPAD8); + glfwKeys.put(GLFW_KEY_KP_9, KEY_NUMPAD9); + glfwKeys.put(GLFW_KEY_KP_DECIMAL, KEY_DECIMAL); + glfwKeys.put(GLFW_KEY_KP_DIVIDE, KEY_DIVIDE); + glfwKeys.put(GLFW_KEY_KP_MULTIPLY, KEY_MULTIPLY); + glfwKeys.put(GLFW_KEY_KP_SUBTRACT, KEY_SUBTRACT); + glfwKeys.put(GLFW_KEY_KP_ADD, KEY_ADD); + glfwKeys.put(GLFW_KEY_KP_ENTER, KEY_NUMPADENTER); + glfwKeys.put(GLFW_KEY_KP_EQUAL, KEY_NUMPADEQUALS); + glfwKeys.put(GLFW_KEY_LEFT_SHIFT, KEY_LSHIFT); + glfwKeys.put(GLFW_KEY_LEFT_CONTROL, KEY_LCONTROL); + glfwKeys.put(GLFW_KEY_LEFT_ALT, KEY_LMENU); + glfwKeys.put(GLFW_KEY_LEFT_SUPER, KEY_LMETA); + glfwKeys.put(GLFW_KEY_RIGHT_SHIFT, KEY_RSHIFT); + glfwKeys.put(GLFW_KEY_RIGHT_CONTROL, KEY_RCONTROL); + glfwKeys.put(GLFW_KEY_RIGHT_ALT, KEY_RMENU); + glfwKeys.put(GLFW_KEY_RIGHT_SUPER, KEY_RMETA); + glfwKeys.put(GLFW_KEY_MENU, KEY_RMENU); + } + + + private static final class KeyEvent { + /** + * The current keyboard character being examined + */ + private int character; + + /** + * The current keyboard event key being examined + */ + private int key; + + /** + * The current state of the key being examined in the event queue + */ + private boolean state; + + /** + * Is the current event a repeated event? + */ + private boolean repeat; + + private void reset() { + character = 0; + key = 0; + state = false; + repeat = false; + } + } + +} diff --git a/src/org/lwjgl/input/Mouse.java b/src/org/lwjgl/input/Mouse.java new file mode 100644 index 0000000..f243b13 --- /dev/null +++ b/src/org/lwjgl/input/Mouse.java @@ -0,0 +1,256 @@ +package org.lwjgl.input; + +import de.verschwiegener.lwjgl3.display.Display; +import org.lwjgl.BufferUtils; +import org.lwjgl.glfw.GLFWCursorEnterCallback; +import org.lwjgl.glfw.GLFWCursorPosCallback; +import org.lwjgl.glfw.GLFWMouseButtonCallback; +import org.lwjgl.glfw.GLFWScrollCallback; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +import static org.lwjgl.glfw.GLFW.*; + +public class Mouse { + + private static boolean created; + + private static String[] buttonName; + private static final Map buttonMap = new HashMap<>(8); + private static ByteBuffer readBuffer; + private static ByteBuffer buttons; + private static final int BUFFER_SIZE = 50; + //Max Nummber of buttons glfw supports https://www.glfw.org/docs/3.3/group__buttons.html + private static final int BUTTON_COUNT = 8; + //int = 4 boolean = 1, long = 8 + // 4 + 1 + 4 + 4 + 4 + 4 + 4 + public static final int EVENT_SIZE = 4 + 1 + 4 + 4 + 4 + 4 + 4; + private static MouseEvent current_Event; + private static boolean isGrabbed; + + private static int last_event_raw_x; + private static int last_event_raw_y; + private static boolean clipMouseCoordinatesToWindow; + + private static int poll_button = 0; + private static int poll_action = 0; + public static int poll_xPos = 0; + public static int poll_yPos = 0; + public static int poll_scrollY = 0; + private static int current_dx = 0; + private static int current_dy = 0; + public static int last_x = 0; + public static int last_y = 0; + private static boolean poll_outside; + private static boolean pollNeed; + + + public static void pollGLFW(){ + if(!created) return; + glfwSetMouseButtonCallback(Display.getWindow(), new GLFWMouseButtonCallback() { + @Override + public void invoke(long l, int i, int i1, int i2) { + poll_button = i; + poll_action = i1; + buttons.put(i, (byte)i1); + pollNeed = true; + } + }); + glfwSetCursorPosCallback(Display.getWindow(), new GLFWCursorPosCallback() { + @Override + public void invoke(long l, double v, double v1) { + if(Display.isActive()){ + last_x = poll_xPos; + last_y = poll_yPos; + poll_xPos = (int) v; + poll_yPos = (int) (Display.getHeight() - v1); + current_dx = ((poll_xPos - last_x) * 8); + current_dy = ((poll_yPos - last_y) * 8); + pollNeed = true; + } + } + }); + glfwSetScrollCallback(Display.getWindow(), new GLFWScrollCallback() { + @Override + public void invoke(long l, double v, double v1) { + poll_scrollY = (int) v1; + pollNeed = true; + } + }); + glfwSetCursorEnterCallback(Display.getWindow(), new GLFWCursorEnterCallback() { + @Override + public void invoke(long l, boolean entered) { + poll_outside = entered; + } + }); + } + public static void createEvent(){ + if(!created || !pollNeed) return; + readBuffer.compact(); + pollNeed = false; + readBuffer.putInt(poll_button); + readBuffer.put((byte)poll_action); + if(isGrabbed){ + readBuffer.putInt(normalize(poll_xPos - last_x)); + readBuffer.putInt(normalize(poll_yPos - last_y)); + }else { + readBuffer.putInt(poll_xPos); + readBuffer.putInt(poll_yPos); + } + readBuffer.putInt(poll_scrollY); + readBuffer.flip(); + } + + + + public static void create(){ + if(created) return; + buttons = BufferUtils.createByteBuffer(BUTTON_COUNT); + readBuffer = ByteBuffer.allocate(EVENT_SIZE * BUFFER_SIZE); + readBuffer.limit(0); + clipMouseCoordinatesToWindow = true; + created = true; + + buttonName = new String[8]; + for (int i = 0; i < 8; i++) { + buttonName[i] = "BUTTON" + i; + buttonMap.put(buttonName[i], i); + } + + } + public static boolean next(){ + if(!created) throw new IllegalStateException("Mouse must be created before you can read events"); + if(readBuffer.hasRemaining()){ + MouseEvent event = new MouseEvent(); + event.eventButton = readBuffer.getInt(); + event.eventState = readBuffer.get() != 0; + + if(isGrabbed){ + event.dx = readBuffer.getInt(); + event.dy = readBuffer.getInt(); + event.x += event.dx; + event.y += event.dy; + last_event_raw_x = event.x; + last_event_raw_y = event.y; + }else{ + int new_event_x = readBuffer.getInt(); + int new_event_y = readBuffer.getInt(); + event.dx = new_event_x - last_event_raw_x; + event.dy = new_event_y - last_event_raw_y; + event.x = new_event_x; + event.y = new_event_y; + last_event_raw_x = new_event_x; + last_event_raw_y = new_event_y; + } + if(clipMouseCoordinatesToWindow) { + event.x = Math.min(Display.getWidth() - 1, Math.max(0, event.x)); + event.y = Math.min(Display.getHeight() - 1, Math.max(0, event.y)); + } + event.event_dwheel = readBuffer.getInt(); + current_Event = event; + return true; + } + return false; + } + + + public static int getButtonIndex(String buttonName) { + Integer ret = buttonMap.get(buttonName); + if (ret == null) + return -1; + else + return ret; + } + + public static String getButtonName(int button) { + if (button >= buttonName.length || button < 0) + return null; + else + return buttonName[button]; + } + + public static boolean isInsideWindow() { + return poll_outside; + } + public static int getEventButton(){ + return current_Event.eventButton; + } + public static boolean getEventButtonState(){ + return current_Event.eventState; + } + public static int getEventDWheel(){ + return current_Event.event_dwheel; + } + public static int getEventX(){ + return current_Event.x; + } + public static int getEventY(){ + return current_Event.y; + } + public static boolean isCreated(){ + return created; + } + public static int getX(){ + return Math.min(Math.max(poll_xPos, 0), Display.getWidth() - 1); + } + public static int getY(){ + return Math.min(Math.max(poll_yPos, 0), Display.getHeight() - 1); + } + public static void setClipMouseCoordinatesToWindow(boolean clip) { + clipMouseCoordinatesToWindow = clip; + } + public static boolean isButtonDown(int button){ + if (!created) throw new IllegalStateException("Mouse must be created before you can poll the button state"); + if (button >= BUTTON_COUNT || button < 0) + return false; + else + return buttons.get(button) == 1; + } + public static void setGrabbed(boolean grab){ + if (!created) throw new IllegalStateException("Mouse must be created before you can poll the button state"); + isGrabbed = grab; + if(!grab) glfwSetInputMode(Display.getWindow(), GLFW_CURSOR, GLFW_CURSOR_NORMAL); + else glfwSetInputMode(Display.getWindow(), GLFW_CURSOR, GLFW_CURSOR_DISABLED); + glfwSetCursorPos(Display.getWindow(), poll_xPos, poll_yPos); + } + public static void setCursorPosition(int x, int y){ + if (!isCreated()) throw new IllegalStateException("Mouse is not created"); + poll_xPos = current_Event.x = x; + poll_yPos = current_Event.y = y; + glfwSetCursorPos(Display.getWindow(), x, y); + } + public static int getDX(){ + int result = current_dx; + current_dx = 0; + return result; + } + public static int getDY(){ + int result = current_dy; + current_dy = 0; + return result; + } + private static int normalize(int input){ + if(input < 0) return input /-1; + return input; + } + + public static int getDWheel() { + return poll_scrollY; + } + + + private static final class MouseEvent { + + private int eventButton; + private boolean eventState; + + private int dx; + private int dy; + private int x; + private int y; + private int event_dwheel; + + } +} diff --git a/src/paulscode/sound/libraries/ChannelLWJGLOpenAL.java b/src/paulscode/sound/libraries/ChannelLWJGLOpenAL.java new file mode 100644 index 0000000..37e09a5 --- /dev/null +++ b/src/paulscode/sound/libraries/ChannelLWJGLOpenAL.java @@ -0,0 +1,696 @@ +package paulscode.sound.libraries; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.LinkedList; +import javax.sound.sampled.AudioFormat; + +// From the lwjgl library, http://www.lwjgl.org +import org.lwjgl.BufferUtils; +import org.lwjgl.openal.AL10; +import org.lwjgl.openal.AL11; + +import paulscode.sound.Channel; +import paulscode.sound.SoundSystemConfig; + +/** + * The ChannelLWJGLOpenAL class is used to reserve a sound-card voice using the + * lwjgl binding of OpenAL. Channels can be either normal or streaming + * channels. + *

+ * This software is based on or using the LWJGL Lightweight Java Gaming + * Library available from + * http://www.lwjgl.org/. + *


+ * LWJGL License: + *
+ * Copyright (c) 2002-2008 Lightweight Java Game Library Project + * All rights reserved. + *
+ * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + *
+ * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + *
+ * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + *
+ * * Neither the name of 'Light Weight Java Game Library' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *


+ * SoundSystem LibraryLWJGLOpenAL License:

+ * + * You are free to use this library for any purpose, commercial or otherwise. + * You may modify this library or source code, and distribute it any way you + * like, provided the following conditions are met: + *
+ * 1) You must abide by the conditions of the aforementioned LWJGL License. + *
+ * 2) You may not falsely claim to be the author of this library or any + * unmodified portion of it. + *
+ * 3) You may not copyright this library or a modified version of it and then + * sue me for copyright infringement. + *
+ * 4) If you modify the source code, you must clearly document the changes + * made before redistributing the modified source code, so other users know + * it is not the original code. + *
+ * 5) You are not required to give me credit for this library in any derived + * work, but if you do, you must also mention my website: + * http://www.paulscode.com + *
+ * 6) I the author will not be responsible for any damages (physical, + * financial, or otherwise) caused by the use if this library or any part + * of it. + *
+ * 7) I the author do not guarantee, warrant, or make any representations, + * either expressed or implied, regarding the use of this library or any + * part of it. + *

+ * Author: Paul Lamb + *
+ * http://www.paulscode.com + *
+ */ +public class ChannelLWJGLOpenAL extends Channel +{ +/** + * OpenAL's IntBuffer identifier for this channel. + */ + public IntBuffer ALSource; + +/** + * OpenAL data format to use when playing back the assigned source. + */ + public int ALformat; // OpenAL data format + +/** + * Sample rate (speed) to use for play-back. + */ + public int sampleRate; // sample rate + +/** + * Miliseconds of buffers previously played (streaming sources). + */ + public float millisPreviouslyPlayed = 0; + +/** + * Constructor: takes channelType identifier and a handle to the OpenAL + * IntBuffer identifier to use for this channel. Possible values for channel + * type can be found in the + * {@link SoundSystemConfig SoundSystemConfig} class. + * @param type Type of channel (normal or streaming). + * @param src Handle to the OpenAL source identifier. + */ + public ChannelLWJGLOpenAL( int type, IntBuffer src ) + { + super( type ); + libraryType = LibraryLWJGLOpenAL.class; + ALSource = src; + } + +/** + * Empties the streamBuffers list, stops and deletes the ALSource, shuts the + * channel down, and removes references to all instantiated objects. + */ + @Override + public void cleanup() + { + if( ALSource != null ) + { + try + { + // Stop playing the source: + AL10.alSourceStopv( ALSource ); + AL10.alGetError(); + } + catch( Exception e ) + {} + try + { + // Delete the source: + AL10.alDeleteSources( ALSource ); + AL10.alGetError(); + } + catch( Exception e ) + {} + ALSource.clear(); + } + ALSource = null; + + super.cleanup(); + } + +/** + * Attaches an OpenAL sound-buffer identifier for the sound data to be played + * back for a normal source. + * @param buf Intbuffer identifier for the sound data to play. + * @return False if an error occurred. + */ + public boolean attachBuffer( IntBuffer buf ) + { + // A sound buffer can only be attached to a normal source: + if( errorCheck( channelType != SoundSystemConfig.TYPE_NORMAL, + "Sound buffers may only be attached to normal " + + "sources." ) ) + return false; + + // send the sound buffer to the channel: + AL10.alSourcei( ALSource.get( 0 ), AL10.AL_BUFFER, + buf.get(0) ); + + + // save the format for later, for determining milliseconds played + if( attachedSource != null && attachedSource.soundBuffer != null && + attachedSource.soundBuffer.audioFormat != null ) + setAudioFormat( attachedSource.soundBuffer.audioFormat ); + + // Check for errors and return: + return checkALError(); + } +/** + * Sets the channel up to receive the specified audio format. + * @param audioFormat Format to use when playing the stream data. + */ + @Override + public void setAudioFormat( AudioFormat audioFormat ) + { + int soundFormat = 0; + if( audioFormat.getChannels() == 1 ) + { + if( audioFormat.getSampleSizeInBits() == 8 ) + { + soundFormat = AL10.AL_FORMAT_MONO8; + } + else if( audioFormat.getSampleSizeInBits() == 16 ) + { + soundFormat = AL10.AL_FORMAT_MONO16; + } + else + { + errorMessage( "Illegal sample size in method " + + "'setAudioFormat'" ); + return; + } + } + else if( audioFormat.getChannels() == 2 ) + { + if( audioFormat.getSampleSizeInBits() == 8 ) + { + soundFormat = AL10.AL_FORMAT_STEREO8; + } + else if( audioFormat.getSampleSizeInBits() == 16 ) + { + soundFormat = AL10.AL_FORMAT_STEREO16; + } + else + { + errorMessage( "Illegal sample size in method " + + "'setAudioFormat'" ); + return; + } + } + else + { + errorMessage( "Audio data neither mono nor stereo in " + + "method 'setAudioFormat'" ); + return; + } + ALformat = soundFormat; + sampleRate = (int) audioFormat.getSampleRate(); + } +/** + * Sets the channel up to receive the specified OpenAL audio format and sample + * rate. + * @param format Format to use. + * @param rate Sample rate (speed) to use. + */ + public void setFormat( int format, int rate ) + { + ALformat = format; + sampleRate = rate; + } + +/** + * Queues up the initial byte[] buffers of data to be streamed. + * @param bufferList List of the first buffers to be played for a streaming source. + * @return False if problem occurred or if end of stream was reached. + */ + @Override + public boolean preLoadBuffers( LinkedList bufferList ) + { + // Stream buffers can only be queued for streaming sources: + if( errorCheck( channelType != SoundSystemConfig.TYPE_STREAMING, + "Buffers may only be queued for streaming sources." ) ) + return false; + + if( errorCheck( bufferList == null, + "Buffer List null in method 'preLoadBuffers'" ) ) + return false; + + IntBuffer streamBuffers; + + // Remember if the channel was playing: + boolean playing = playing(); + // stop the channel if it is playing: + if( playing ) + { + AL10.alSourceStop( ALSource.get( 0 ) ); + checkALError(); + } + // Clear out any previously queued buffers: + int processed = AL10.alGetSourcei( ALSource.get( 0 ), + AL10.AL_BUFFERS_PROCESSED ); + if( processed > 0 ) + { + streamBuffers = BufferUtils.createIntBuffer( processed ); + AL10.alGenBuffers( streamBuffers ); + if( errorCheck( checkALError(), + "Error clearing stream buffers in method 'preLoadBuffers'" ) ) + return false; + AL10.alSourceUnqueueBuffers( ALSource.get( 0 ), streamBuffers ); + if( errorCheck( checkALError(), + "Error unqueuing stream buffers in method 'preLoadBuffers'" ) ) + return false; + } + + // restart the channel if it was previously playing: + if( playing ) + { + AL10.alSourcePlay( ALSource.get( 0 ) ); + checkALError(); + } + + streamBuffers = BufferUtils.createIntBuffer( bufferList.size() ); + AL10.alGenBuffers( streamBuffers ); + if( errorCheck( checkALError(), + "Error generating stream buffers in method 'preLoadBuffers'" ) ) + return false; + + ByteBuffer byteBuffer = null; + for( int i = 0; i < bufferList.size(); i++ ) + { + //byteBuffer = ByteBuffer.wrap( bufferList.get(i), 0, + // bufferList.get(i).length ); + byteBuffer = (ByteBuffer) BufferUtils.createByteBuffer( + bufferList.get(i).length ).put( bufferList.get( i ) ).flip(); + + try + { + AL10.alBufferData( streamBuffers.get(i), ALformat, byteBuffer, + sampleRate ); + } + catch( Exception e ) + { + errorMessage( "Error creating buffers in method " + + "'preLoadBuffers'" ); + printStackTrace( e ); + return false; + } + if( errorCheck( checkALError(), + "Error creating buffers in method 'preLoadBuffers'" ) ) + return false; + + } + + try + { + AL10.alSourceQueueBuffers( ALSource.get( 0 ), streamBuffers ); + } + catch( Exception e ) + { + errorMessage( "Error queuing buffers in method 'preLoadBuffers'" ); + printStackTrace( e ); + return false; + } + if( errorCheck( checkALError(), + "Error queuing buffers in method 'preLoadBuffers'" ) ) + return false; + + AL10.alSourcePlay( ALSource.get( 0 ) ); + if( errorCheck( checkALError(), + "Error playing source in method 'preLoadBuffers'" ) ) + return false; + + // Success: + return true; + } + +/** + * Queues up a byte[] buffer of data to be streamed. + * @param buffer The next buffer to be played for a streaming source. + * @return False if an error occurred or if the channel is shutting down. + */ + @Override + public boolean queueBuffer( byte[] buffer ) + { + // Stream buffers can only be queued for streaming sources: + if( errorCheck( channelType != SoundSystemConfig.TYPE_STREAMING, + "Buffers may only be queued for streaming sources." ) ) + return false; + + //ByteBuffer byteBuffer = ByteBuffer.wrap( buffer, 0, buffer.length ); + ByteBuffer byteBuffer = (ByteBuffer) BufferUtils.createByteBuffer( + buffer.length ).put( buffer ).flip(); + + IntBuffer intBuffer = BufferUtils.createIntBuffer( 1 ); + + AL10.alSourceUnqueueBuffers( ALSource.get( 0 ), intBuffer ); + if( checkALError() ) + return false; + + if( AL10.alIsBuffer( intBuffer.get( 0 ) ) ) + millisPreviouslyPlayed += millisInBuffer( intBuffer.get( 0 ) ); + checkALError(); + + AL10.alBufferData( intBuffer.get(0), ALformat, byteBuffer, sampleRate ); + if( checkALError() ) + return false; + + AL10.alSourceQueueBuffers( ALSource.get( 0 ), intBuffer ); + if( checkALError() ) + return false; + + return true; + } + +/** + * Feeds raw data to the stream. + * @param buffer Buffer containing raw audio data to stream. + * @return Number of prior buffers that have been processed., or -1 if error. + */ + @Override + public int feedRawAudioData( byte[] buffer ) + { + // Stream buffers can only be queued for streaming sources: + if( errorCheck( channelType != SoundSystemConfig.TYPE_STREAMING, + "Raw audio data can only be fed to streaming sources." ) ) + return -1; + + //ByteBuffer byteBuffer = ByteBuffer.wrap( buffer, 0, buffer.length ); + ByteBuffer byteBuffer = (ByteBuffer) BufferUtils.createByteBuffer( + buffer.length ).put( buffer ).flip(); + + IntBuffer intBuffer; + + // Clear out any previously queued buffers: + int processed = AL10.alGetSourcei( ALSource.get( 0 ), + AL10.AL_BUFFERS_PROCESSED ); + if( processed > 0 ) + { + intBuffer = BufferUtils.createIntBuffer( processed ); + AL10.alGenBuffers( intBuffer ); + if( errorCheck( checkALError(), + "Error clearing stream buffers in method 'feedRawAudioData'" ) ) + return -1; + AL10.alSourceUnqueueBuffers( ALSource.get( 0 ), intBuffer ); + if( errorCheck( checkALError(), + "Error unqueuing stream buffers in method 'feedRawAudioData'" ) ) + return -1; + int i; + intBuffer.rewind(); + while( intBuffer.hasRemaining() ) + { + i = intBuffer.get(); + if( AL10.alIsBuffer( i ) ) + { + millisPreviouslyPlayed += millisInBuffer( i ); + } + checkALError(); + } + AL10.alDeleteBuffers( intBuffer ); + checkALError(); + } + intBuffer = BufferUtils.createIntBuffer( 1 ); + AL10.alGenBuffers( intBuffer ); + if( errorCheck( checkALError(), + "Error generating stream buffers in method 'preLoadBuffers'" ) ) + return -1; + + AL10.alBufferData( intBuffer.get(0), ALformat, byteBuffer, sampleRate ); + if( checkALError() ) + return -1; + + AL10.alSourceQueueBuffers( ALSource.get( 0 ), intBuffer ); + if( checkALError() ) + return -1; + + if( attachedSource != null && attachedSource.channel == this && + attachedSource.active() ) + { + // restart the channel if it was previously playing: + if( !playing() ) + { + AL10.alSourcePlay( ALSource.get( 0 ) ); + checkALError(); + } + } + + return processed; + } + +/** + * Returns the number of milliseconds of audio contained in specified buffer. + * @return milliseconds, or 0 if unable to calculate. + */ + public float millisInBuffer( int alBufferi ) + { + return( ( (float) AL10.alGetBufferi( alBufferi, AL10.AL_SIZE ) / + (float) AL10.alGetBufferi( alBufferi, AL10.AL_CHANNELS ) / + ( (float) AL10.alGetBufferi( alBufferi, AL10.AL_BITS ) / 8.0f ) / + (float) sampleRate ) * 1000 ); + } + +/** + * Calculates the number of milliseconds since the channel began playing. + * @return Milliseconds, or -1 if unable to calculate. + */ + @Override + public float millisecondsPlayed() + { + // get number of samples played in current buffer + float offset = (float)AL10.alGetSourcei( ALSource.get( 0 ), + AL11.AL_BYTE_OFFSET ); + + float bytesPerFrame = 1f; + switch( ALformat ) + { + case AL10.AL_FORMAT_MONO8 : + bytesPerFrame = 1f; + break; + case AL10.AL_FORMAT_MONO16 : + bytesPerFrame = 2f; + break; + case AL10.AL_FORMAT_STEREO8 : + bytesPerFrame = 2f; + break; + case AL10.AL_FORMAT_STEREO16 : + bytesPerFrame = 4f; + break; + default : + break; + } + + offset = ( ( (float) offset / bytesPerFrame ) / (float) sampleRate ) + * 1000; + + // add the milliseconds from stream-buffers that played previously + if( channelType == SoundSystemConfig.TYPE_STREAMING ) + offset += millisPreviouslyPlayed; + + // Return millis played: + return( offset ); + } + +/** + * Returns the number of queued byte[] buffers that have finished playing. + * @return Number of buffers processed. + */ + @Override + public int buffersProcessed() + { + // Only streaming sources process buffers: + if( channelType != SoundSystemConfig.TYPE_STREAMING ) + return 0; + + // determine how many have been processed: + int processed = AL10.alGetSourcei( ALSource.get( 0 ), + AL10.AL_BUFFERS_PROCESSED ); + + // Check for errors: + if( checkALError() ) + return 0; + + // Return how many were processed: + return processed; + } + +/** + * Dequeues all previously queued data. + */ + @Override + public void flush() + { + // Only a streaming source can be flushed, because only streaming + // sources have queued buffers: + if( channelType != SoundSystemConfig.TYPE_STREAMING ) + return; + + // determine how many buffers have been queued: + int queued = AL10.alGetSourcei( ALSource.get( 0 ), + AL10.AL_BUFFERS_QUEUED ); + // Check for errors: + if( checkALError() ) + return; + + IntBuffer intBuffer = BufferUtils.createIntBuffer( 1 ); + while( queued > 0 ) + { + try + { + AL10.alSourceUnqueueBuffers( ALSource.get( 0 ), intBuffer ); + } + catch( Exception e ) + { + return; + } + if( checkALError() ) + return; + queued--; + } + millisPreviouslyPlayed = 0; + } + +/** + * Stops the channel, dequeues any queued data, and closes the channel. + */ + @Override + public void close() + { + try + { + AL10.alSourceStop( ALSource.get( 0 ) ); + AL10.alGetError(); + } + catch( Exception e ) + {} + + if( channelType == SoundSystemConfig.TYPE_STREAMING ) + flush(); + } + +/** + * Plays the currently attached normal source, opens this channel up for + * streaming, or resumes playback if this channel was paused. + */ + @Override + public void play() + { + AL10.alSourcePlay( ALSource.get( 0 ) ); + checkALError(); + } + +/** + * Temporarily stops playback for this channel. + */ + @Override + public void pause() + { + AL10.alSourcePause( ALSource.get( 0 ) ); + checkALError(); + } + +/** + * Stops playback for this channel and rewinds the attached source to the + * beginning. + */ + @Override + public void stop() + { + AL10.alSourceStop( ALSource.get( 0 ) ); + if( !checkALError() ) + millisPreviouslyPlayed = 0; + } + +/** + * Rewinds the attached source to the beginning. Stops the source if it was + * paused. + */ + @Override + public void rewind() + { + // rewinding for streaming sources is handled elsewhere + if( channelType == SoundSystemConfig.TYPE_STREAMING ) + return; + + AL10.alSourceRewind( ALSource.get( 0 ) ); + if( !checkALError() ) + millisPreviouslyPlayed = 0; + } + + +/** + * Used to determine if a channel is actively playing a source. This method + * will return false if the channel is paused or stopped and when no data is + * queued to be streamed. + * @return True if this channel is playing a source. + */ + @Override + public boolean playing() + { + int state = AL10.alGetSourcei( ALSource.get( 0 ), + AL10.AL_SOURCE_STATE ); + if( checkALError() ) + return false; + + return( state == AL10.AL_PLAYING ); + } + +/** + * Checks for OpenAL errors, and prints a message if there is an error. + * @return True if there was an error, False if not. + */ + private boolean checkALError() + { + switch( AL10.alGetError() ) + { + case AL10.AL_NO_ERROR: + return false; + case AL10.AL_INVALID_NAME: + errorMessage( "Invalid name parameter." ); + return true; + case AL10.AL_INVALID_ENUM: + errorMessage( "Invalid parameter." ); + return true; + case AL10.AL_INVALID_VALUE: + errorMessage( "Invalid enumerated parameter value." ); + return true; + case AL10.AL_INVALID_OPERATION: + errorMessage( "Illegal call." ); + return true; + case AL10.AL_OUT_OF_MEMORY: + errorMessage( "Unable to allocate memory." ); + return true; + default: + errorMessage( "An unrecognized error occurred." ); + return true; + } + } +} diff --git a/src/paulscode/sound/libraries/LibraryLWJGLOpenAL.java b/src/paulscode/sound/libraries/LibraryLWJGLOpenAL.java new file mode 100644 index 0000000..daa52bd --- /dev/null +++ b/src/paulscode/sound/libraries/LibraryLWJGLOpenAL.java @@ -0,0 +1,1150 @@ +package paulscode.sound.libraries; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.FloatBuffer; +import java.net.URL; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Set; +import javax.sound.sampled.AudioFormat; + +// From the lwjgl library, http://www.lwjgl.org +import org.lwjgl.BufferUtils; +import org.lwjgl.LWJGLException; +import org.lwjgl.openal.*; + +import org.lwjgl.system.MemoryUtil; +import paulscode.sound.Channel; +import paulscode.sound.FilenameURL; +import paulscode.sound.ICodec; +import paulscode.sound.Library; +import paulscode.sound.ListenerData; +import paulscode.sound.SoundBuffer; +import paulscode.sound.SoundSystemConfig; +import paulscode.sound.SoundSystemException; +import paulscode.sound.Source; + +/** + * The LibraryLWJGLOpenAL class interfaces the lwjgl binding of OpenAL. + *

+ * This software is based on or using the LWJGL Lightweight Java Gaming + * Library available from + * http://www.lwjgl.org/. + *


+ * LWJGL License: + *
+ * Copyright (c) 2002-2008 Lightweight Java Game Library Project + * All rights reserved. + *
+ * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + *
+ * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + *
+ * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + *
+ * * Neither the name of 'Light Weight Java Game Library' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *


+ * SoundSystem LibraryLWJGLOpenAL License:

+ * + * You are free to use this library for any purpose, commercial or otherwise. + * You may modify this library or source code, and distribute it any way you + * like, provided the following conditions are met: + *
+ * 1) You must abide by the conditions of the aforementioned LWJGL License. + *
+ * 2) You may not falsely claim to be the author of this library or any + * unmodified portion of it. + *
+ * 3) You may not copyright this library or a modified version of it and then + * sue me for copyright infringement. + *
+ * 4) If you modify the source code, you must clearly document the changes + * made before redistributing the modified source code, so other users know + * it is not the original code. + *
+ * 5) You are not required to give me credit for this library in any derived + * work, but if you do, you must also mention my website: + * http://www.paulscode.com + *
+ * 6) I the author will not be responsible for any damages (physical, + * financial, or otherwise) caused by the use if this library or any part + * of it. + *
+ * 7) I the author do not guarantee, warrant, or make any representations, + * either expressed or implied, regarding the use of this library or any + * part of it. + *

+ * Author: Paul Lamb + *
+ * http://www.paulscode.com + *
+ */ +public class LibraryLWJGLOpenAL extends Library +{ +/** + * Used to return a current value from one of the synchronized + * boolean-interface methods. + */ + private static final boolean GET = false; +/** + * Used to set the value in one of the synchronized boolean-interface methods. + */ + private static final boolean SET = true; +/** + * Used when a parameter for one of the synchronized boolean-interface methods + * is not aplicable. + */ + private static final boolean XXX = false; + +/** + * Position of the listener in 3D space. + */ + private FloatBuffer listenerPositionAL = null; +/** + * Information about the listener's orientation. + */ + private FloatBuffer listenerOrientation = null; +/** + * Velocity of the listener. + */ + private FloatBuffer listenerVelocity = null; +/** + * Map containing OpenAL identifiers for sound buffers. + */ + private HashMap ALBufferMap = null; + +/** + * Whether or not the AL_PITCH control is supported. + */ + private static boolean alPitchSupported = true; + +/** + * Constructor: Instantiates the source map, buffer map and listener + * information. Also sets the library type to + * SoundSystemConfig.LIBRARY_OPENAL + */ + public LibraryLWJGLOpenAL() throws SoundSystemException + { + super(); + ALBufferMap = new HashMap(); + reverseByteOrder = true; + } + +/** + * Initializes OpenAL, creates the listener, and grabs up audio channels. + */ + @Override + public void init() throws SoundSystemException + { + boolean errors = false; // set to 'true' if error(s) occur: + + try + { + // Try and create the sound system: + long device = ALC10.alcOpenDevice((ByteBuffer) null); + if (device == MemoryUtil.NULL) { + throw new IllegalStateException("Failed to open the default OpenAL device."); + } + ALCCapabilities deviceCaps = ALC.createCapabilities(device); + long context = ALC10.alcCreateContext(device, (IntBuffer) null); + if (context == MemoryUtil.NULL) { + throw new IllegalStateException("Failed to create OpenAL context."); + } + ALC10.alcMakeContextCurrent(context); + AL.createCapabilities(deviceCaps); + //AL.create(); + errors = checkALError(); + } + catch( java.lang.Exception e ) + { + // There was an exception + errorMessage( "Unable to initialize OpenAL. Probable cause: " + + "OpenAL not supported." ); + printStackTrace( e ); + throw new Exception( e.getMessage(), + Exception.CREATE ); + } + + // Let user know if the library loaded properly + if( errors ) + importantMessage( "OpenAL did not initialize properly!" ); + else + message( "OpenAL initialized." ); + + // Listener is at the origin, facing along the z axis, no velocity: + listenerPositionAL = BufferUtils.createFloatBuffer( 3 ).put( + new float[] { listener.position.x, + listener.position.y, + listener.position.z } ); + listenerOrientation = BufferUtils.createFloatBuffer( 6 ).put ( + new float[] { listener.lookAt.x, listener.lookAt.y, + listener.lookAt.z, listener.up.x, listener.up.y, + listener.up.z } ); + listenerVelocity = BufferUtils.createFloatBuffer( 3 ).put ( + new float[] { 0.0f, 0.0f, 0.0f } ); + + // Flip the buffers, so they can be used: + listenerPositionAL.flip(); + listenerOrientation.flip(); + listenerVelocity.flip(); + + // Pass the buffers to the sound system, and check for potential errors: + AL10.alListenerfv( AL10.AL_POSITION, listenerPositionAL ); + errors = checkALError() || errors; + AL10.alListenerfv( AL10.AL_ORIENTATION, listenerOrientation ); + errors = checkALError() || errors; + AL10.alListenerfv( AL10.AL_VELOCITY, listenerVelocity ); + errors = checkALError() || errors; + + AL10.alDopplerFactor( SoundSystemConfig.getDopplerFactor() ); + errors = checkALError() || errors; + + AL10.alDopplerVelocity( SoundSystemConfig.getDopplerVelocity() ); + errors = checkALError() || errors; + + // Let user know what caused the above error messages: + if( errors ) + { + importantMessage( "OpenAL did not initialize properly!" ); + throw new Exception( "Problem encountered " + + "while loading OpenAL or " + + "creating the listener. " + + "Probable cause: OpenAL not " + + "supported", + Exception.CREATE ); + } + + super.init(); + + // Check if we can use the AL_PITCH control: + ChannelLWJGLOpenAL channel = (ChannelLWJGLOpenAL) + normalChannels.get( 1 ); + try + { + AL10.alSourcef( channel.ALSource.get( 0 ), + AL10.AL_PITCH, 1.0f ); + if( checkALError() ) + { + alPitchSupported( SET, false ); + throw new Exception( "OpenAL: AL_PITCH not " + + "supported.", Exception.NO_AL_PITCH ); + } + else + { + alPitchSupported( SET, true ); + } + } + catch( java.lang.Exception e ) + { + alPitchSupported( SET, false ); + throw new Exception( "OpenAL: AL_PITCH not " + + "supported.", Exception.NO_AL_PITCH ); + } + } + +/** + * Checks if the OpenAL library type is compatible. + * @return True or false. + */ + public static boolean libraryCompatible() + { + if( AL.getCapabilities() != null) + return true; + + try + { + long device = ALC10.alcOpenDevice((ByteBuffer) null); + if (device == MemoryUtil.NULL) { + throw new IllegalStateException("Failed to open the default OpenAL device."); + } + ALCCapabilities deviceCaps = ALC.createCapabilities(device); + long context = ALC10.alcCreateContext(device, (IntBuffer) null); + if (context == MemoryUtil.NULL) { + throw new IllegalStateException("Failed to create OpenAL context."); + } + ALC10.alcMakeContextCurrent(context); + AL.createCapabilities(deviceCaps); + //AL.create(); + } + catch( java.lang.Exception e ) + { + return false; + } + + try + { + AL.setCurrentProcess(null); + //AL.destroy(); + } + catch( java.lang.Exception e ) + {} + + return true; + } + +/** + * Creates a new channel of the specified type (normal or streaming). Possible + * values for channel type can be found in the + * {@link SoundSystemConfig SoundSystemConfig} class. + * @param type Type of channel. + */ + @Override + protected Channel createChannel( int type ) + { + ChannelLWJGLOpenAL channel; + IntBuffer ALSource; + + ALSource = BufferUtils.createIntBuffer( 1 ); + try + { + AL10.alGenSources( ALSource ); + } + catch( java.lang.Exception e ) + { + AL10.alGetError(); + return null; // no more voices left + } + + if( AL10.alGetError() != AL10.AL_NO_ERROR ) + return null; + + channel = new ChannelLWJGLOpenAL( type, ALSource ); + return channel; + } + + /** + * Stops all sources, shuts down OpenAL, and removes references to all + * instantiated objects. + */ + @Override + public void cleanup() + { + super.cleanup(); + + Set keys = bufferMap.keySet(); + Iterator iter = keys.iterator(); + String filename; + IntBuffer buffer; + + // loop through and clear all sound buffers: + while( iter.hasNext() ) + { + filename = iter.next(); + buffer = ALBufferMap.get( filename ); + if( buffer != null ) + { + AL10.alDeleteBuffers( buffer ); + checkALError(); + buffer.clear(); + } + } + + bufferMap.clear(); + //AL.destroy(); + AL.setCurrentProcess(null); + + bufferMap = null; + listenerPositionAL = null; + listenerOrientation = null; + listenerVelocity = null; + } + +/** + * Pre-loads a sound into memory. + * @param filenameURL Filename/URL of the sound file to load. + * @return True if the sound loaded properly. + */ + @Override + public boolean loadSound( FilenameURL filenameURL ) + { + // Make sure the buffer map exists: + if( bufferMap == null ) + { + bufferMap = new HashMap(); + importantMessage( "Buffer Map was null in method 'loadSound'" ); + } + // Make sure the OpenAL buffer map exists: + if( ALBufferMap == null ) + { + ALBufferMap = new HashMap(); + importantMessage( "Open AL Buffer Map was null in method" + + "'loadSound'" ); + } + + // make sure they gave us a filename: + if( errorCheck( filenameURL == null, + "Filename/URL not specified in method 'loadSound'" ) ) + return false; + + // check if it is already loaded: + if( bufferMap.get( filenameURL.getFilename() ) != null ) + return true; + + ICodec codec = SoundSystemConfig.getCodec( filenameURL.getFilename() ); + if( errorCheck( codec == null, "No codec found for file '" + + filenameURL.getFilename() + + "' in method 'loadSound'" ) ) + return false; + codec.reverseByteOrder( true ); + + URL url = filenameURL.getURL(); + if( errorCheck( url == null, "Unable to open file '" + + filenameURL.getFilename() + + "' in method 'loadSound'" ) ) + return false; + + codec.initialize( url ); + SoundBuffer buffer = codec.readAll(); + codec.cleanup(); + codec = null; + if( errorCheck( buffer == null, + "Sound buffer null in method 'loadSound'" ) ) + return false; + + bufferMap.put( filenameURL.getFilename(), buffer ); + + AudioFormat audioFormat = buffer.audioFormat; + int soundFormat = 0; + if( audioFormat.getChannels() == 1 ) + { + if( audioFormat.getSampleSizeInBits() == 8 ) + { + soundFormat = AL10.AL_FORMAT_MONO8; + } + else if( audioFormat.getSampleSizeInBits() == 16 ) + { + soundFormat = AL10.AL_FORMAT_MONO16; + } + else + { + errorMessage( "Illegal sample size in method 'loadSound'" ); + return false; + } + } + else if( audioFormat.getChannels() == 2 ) + { + if( audioFormat.getSampleSizeInBits() == 8 ) + { + soundFormat = AL10.AL_FORMAT_STEREO8; + } + else if( audioFormat.getSampleSizeInBits() == 16 ) + { + soundFormat = AL10.AL_FORMAT_STEREO16; + } + else + { + errorMessage( "Illegal sample size in method 'loadSound'" ); + return false; + } + } + else + { + errorMessage( "File neither mono nor stereo in method " + + "'loadSound'" ); + return false; + } + + IntBuffer intBuffer = BufferUtils.createIntBuffer( 1 ); + AL10.alGenBuffers( intBuffer ); + if( errorCheck( AL10.alGetError() != AL10.AL_NO_ERROR, + "alGenBuffers error when loading " + + filenameURL.getFilename() ) ) + return false; + +// AL10.alBufferData( intBuffer.get( 0 ), soundFormat, +// ByteBuffer.wrap( buffer.audioData ), +// (int) audioFormat.getSampleRate() ); + AL10.alBufferData( intBuffer.get( 0 ), soundFormat, + (ByteBuffer) BufferUtils.createByteBuffer( + buffer.audioData.length ).put( + buffer.audioData ).flip(), + (int) audioFormat.getSampleRate() ); + + if( errorCheck( AL10.alGetError() != AL10.AL_NO_ERROR, + "alBufferData error when loading " + + filenameURL.getFilename() ) ) + + + if( errorCheck( intBuffer == null, + "Sound buffer was not created for " + + filenameURL.getFilename() ) ) + return false; + + ALBufferMap.put( filenameURL.getFilename(), intBuffer ); + + return true; + } + +/** + * Saves the specified sample data, under the specified identifier. This + * identifier can be later used in place of 'filename' parameters to reference + * the sample data. + * @param buffer the sample data and audio format to save. + * @param identifier What to call the sample. + * @return True if there weren't any problems. + */ + @Override + public boolean loadSound( SoundBuffer buffer, String identifier ) + { + // Make sure the buffer map exists: + if( bufferMap == null ) + { + bufferMap = new HashMap(); + importantMessage( "Buffer Map was null in method 'loadSound'" ); + } + // Make sure the OpenAL buffer map exists: + if( ALBufferMap == null ) + { + ALBufferMap = new HashMap(); + importantMessage( "Open AL Buffer Map was null in method" + + "'loadSound'" ); + } + + // make sure they gave us an identifier: + if( errorCheck( identifier == null, + "Identifier not specified in method 'loadSound'" ) ) + return false; + + // check if it is already loaded: + if( bufferMap.get( identifier ) != null ) + return true; + + if( errorCheck( buffer == null, + "Sound buffer null in method 'loadSound'" ) ) + return false; + + bufferMap.put( identifier, buffer ); + + AudioFormat audioFormat = buffer.audioFormat; + int soundFormat = 0; + if( audioFormat.getChannels() == 1 ) + { + if( audioFormat.getSampleSizeInBits() == 8 ) + { + soundFormat = AL10.AL_FORMAT_MONO8; + } + else if( audioFormat.getSampleSizeInBits() == 16 ) + { + soundFormat = AL10.AL_FORMAT_MONO16; + } + else + { + errorMessage( "Illegal sample size in method 'loadSound'" ); + return false; + } + } + else if( audioFormat.getChannels() == 2 ) + { + if( audioFormat.getSampleSizeInBits() == 8 ) + { + soundFormat = AL10.AL_FORMAT_STEREO8; + } + else if( audioFormat.getSampleSizeInBits() == 16 ) + { + soundFormat = AL10.AL_FORMAT_STEREO16; + } + else + { + errorMessage( "Illegal sample size in method 'loadSound'" ); + return false; + } + } + else + { + errorMessage( "File neither mono nor stereo in method " + + "'loadSound'" ); + return false; + } + + IntBuffer intBuffer = BufferUtils.createIntBuffer( 1 ); + AL10.alGenBuffers( intBuffer ); + if( errorCheck( AL10.alGetError() != AL10.AL_NO_ERROR, + "alGenBuffers error when saving " + + identifier ) ) + return false; + +// AL10.alBufferData( intBuffer.get( 0 ), soundFormat, +// ByteBuffer.wrap( buffer.audioData ), +// (int) audioFormat.getSampleRate() ); + AL10.alBufferData( intBuffer.get( 0 ), soundFormat, + (ByteBuffer) BufferUtils.createByteBuffer( + buffer.audioData.length ).put( + buffer.audioData ).flip(), + (int) audioFormat.getSampleRate() ); + + if( errorCheck( AL10.alGetError() != AL10.AL_NO_ERROR, + "alBufferData error when saving " + + identifier ) ) + + + if( errorCheck( intBuffer == null, + "Sound buffer was not created for " + + identifier ) ) + return false; + + ALBufferMap.put( identifier, intBuffer ); + + return true; + } + +/** + * Removes a pre-loaded sound from memory. This is a good method to use for + * freeing up memory after a large sound file is no longer needed. NOTE: the + * source will remain in memory after this method has been called, for as long + * as the sound is attached to an existing source. + * @param filename Filename/identifier of the sound file to unload. + */ + @Override + public void unloadSound( String filename ) + { + ALBufferMap.remove( filename ); + super.unloadSound( filename ); + } + + /** + * Sets the overall volume to the specified value, affecting all sources. + * @param value New volume, float value ( 0.0f - 1.0f ). + */ + @Override + public void setMasterVolume( float value ) + { + super.setMasterVolume( value ); + + AL10.alListenerf( AL10.AL_GAIN, value ); + checkALError(); + } + +/** + * Creates a new source and places it into the source map. + * @param priority Setting this to true will prevent other sounds from overriding this one. + * @param toStream Setting this to true will load the sound in pieces rather than all at once. + * @param toLoop Should this source loop, or play only once. + * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename. + * @param filenameURL Filename/URL of the sound file to play at this source. + * @param x X position for this source. + * @param y Y position for this source. + * @param z Z position for this source. + * @param attModel Attenuation model to use. + * @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel". + */ + @Override + public void newSource( boolean priority, boolean toStream, boolean toLoop, + String sourcename, FilenameURL filenameURL, float x, + float y, float z, int attModel, float distOrRoll ) + { + IntBuffer myBuffer = null; + if( !toStream ) + { + // Grab the sound buffer for this file: + myBuffer = ALBufferMap.get( filenameURL.getFilename() ); + + // if not found, try loading it: + if( myBuffer == null ) + { + if( !loadSound( filenameURL ) ) + { + errorMessage( "Source '" + sourcename + "' was not created " + + "because an error occurred while loading " + + filenameURL.getFilename() ); + return; + } + } + + // try and grab the sound buffer again: + myBuffer = ALBufferMap.get( filenameURL.getFilename() ); + // see if it was there this time: + if( myBuffer == null ) + { + errorMessage( "Source '" + sourcename + "' was not created " + + "because a sound buffer was not found for " + + filenameURL.getFilename() ); + return; + } + } + SoundBuffer buffer = null; + + if( !toStream ) + { + // Grab the audio data for this file: + buffer = bufferMap.get( filenameURL.getFilename() ); + // if not found, try loading it: + if( buffer == null ) + { + if( !loadSound( filenameURL ) ) + { + errorMessage( "Source '" + sourcename + "' was not created " + + "because an error occurred while loading " + + filenameURL.getFilename() ); + return; + } + } + // try and grab the sound buffer again: + buffer = bufferMap.get( filenameURL.getFilename() ); + // see if it was there this time: + if( buffer == null ) + { + errorMessage( "Source '" + sourcename + "' was not created " + + "because audio data was not found for " + + filenameURL.getFilename() ); + return; + } + } + + sourceMap.put( sourcename, + new SourceLWJGLOpenAL( listenerPositionAL, myBuffer, + priority, toStream, toLoop, + sourcename, filenameURL, buffer, x, + y, z, attModel, distOrRoll, + false ) ); + } + + /** + * Opens a direct line for streaming audio data. + * @param audioFormat Format that the data will be in. + * @param priority Setting this to true will prevent other sounds from overriding this one. + * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename. + * @param x X position for this source. + * @param y Y position for this source. + * @param z Z position for this source. + * @param attModel Attenuation model to use. + * @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel". + */ + @Override + public void rawDataStream( AudioFormat audioFormat, boolean priority, + String sourcename, float x, float y, + float z, int attModel, float distOrRoll ) + { + sourceMap.put( sourcename, + new SourceLWJGLOpenAL( listenerPositionAL, audioFormat, + priority, sourcename, x, y, z, + attModel, distOrRoll ) ); + } + +/** + * Creates and immediately plays a new source. + * @param priority Setting this to true will prevent other sounds from overriding this one. + * @param toStream Setting this to true will load the sound in pieces rather than all at once. + * @param toLoop Should this source loop, or play only once. + * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename. + * @param filenameURL Filename/URL of the sound file to play at this source. + * @param x X position for this source. + * @param y Y position for this source. + * @param z Z position for this source. + * @param attModel Attenuation model to use. + * @param distOrRoll Either the fading distance or rolloff factor, depending on the value of "attmodel". + * @param temporary Whether or not this source should be removed after it finishes playing. + */ + @Override + public void quickPlay( boolean priority, boolean toStream, boolean toLoop, + String sourcename, FilenameURL filenameURL, float x, + float y, float z, int attModel, float distOrRoll, + boolean temporary ) + { + IntBuffer myBuffer = null; + if( !toStream ) + { + // Grab the sound buffer for this file: + myBuffer = ALBufferMap.get( filenameURL.getFilename() ); + // if not found, try loading it: + if( myBuffer == null ) + loadSound( filenameURL ); + // try and grab the sound buffer again: + myBuffer = ALBufferMap.get( filenameURL.getFilename() ); + // see if it was there this time: + if( myBuffer == null ) + { + errorMessage( "Sound buffer was not created for " + + filenameURL.getFilename() ); + return; + } + } + + SoundBuffer buffer = null; + + if( !toStream ) + { + // Grab the sound buffer for this file: + buffer = bufferMap.get( filenameURL.getFilename() ); + // if not found, try loading it: + if( buffer == null ) + { + if( !loadSound( filenameURL ) ) + { + errorMessage( "Source '" + sourcename + "' was not created " + + "because an error occurred while loading " + + filenameURL.getFilename() ); + return; + } + } + // try and grab the sound buffer again: + buffer = bufferMap.get( filenameURL.getFilename() ); + // see if it was there this time: + if( buffer == null ) + { + errorMessage( "Source '" + sourcename + "' was not created " + + "because audio data was not found for " + + filenameURL.getFilename() ); + return; + } + } + SourceLWJGLOpenAL s = new SourceLWJGLOpenAL( listenerPositionAL, + myBuffer, priority, + toStream, toLoop, + sourcename, filenameURL, + buffer, x, y, z, + attModel, distOrRoll, + false ); + + sourceMap.put( sourcename, s ); + play( s ); + if( temporary ) + s.setTemporary( true ); +} + +/** + * Creates sources based on the source map provided. + * @param srcMap Sources to copy. + */ + @Override + public void copySources( HashMap srcMap ) + { + if( srcMap == null ) + return; + Set keys = srcMap.keySet(); + Iterator iter = keys.iterator(); + String sourcename; + Source source; + + // Make sure the buffer map exists: + if( bufferMap == null ) + { + bufferMap = new HashMap(); + importantMessage( "Buffer Map was null in method 'copySources'" ); + } + // Make sure the OpenAL buffer map exists: + if( ALBufferMap == null ) + { + ALBufferMap = new HashMap(); + importantMessage( "Open AL Buffer Map was null in method" + + "'copySources'" ); + } + + // remove any existing sources before starting: + sourceMap.clear(); + + SoundBuffer buffer; + // loop through and copy all the sources: + while( iter.hasNext() ) + { + sourcename = iter.next(); + source = srcMap.get( sourcename ); + if( source != null ) + { + buffer = null; + if( !source.toStream ) + { + loadSound( source.filenameURL ); + buffer = bufferMap.get( source.filenameURL.getFilename() ); + } + if( source.toStream || buffer != null ) + sourceMap.put( sourcename, new SourceLWJGLOpenAL( + listenerPositionAL, + ALBufferMap.get( + source.filenameURL.getFilename() ), + source, buffer ) ); + } + } + } + +/** + * Changes the listener's position. + * @param x Destination X coordinate. + * @param y Destination Y coordinate. + * @param z Destination Z coordinate. + */ + @Override + public void setListenerPosition( float x, float y, float z ) + { + super.setListenerPosition( x, y, z ); + + listenerPositionAL.put( 0, x ); + listenerPositionAL.put( 1, y ); + listenerPositionAL.put( 2, z ); + + // Update OpenAL listener position: + AL10.alListenerfv( AL10.AL_POSITION, listenerPositionAL ); + // Check for errors: + checkALError(); + } + +/** + * Changes the listeners orientation to the specified 'angle' radians + * counterclockwise around the y-Axis. + * @param angle Radians. + */ + @Override + public void setListenerAngle( float angle ) + { + super.setListenerAngle( angle ); + + listenerOrientation.put( 0, listener.lookAt.x ); + listenerOrientation.put( 2, listener.lookAt.z ); + + // Update OpenAL listener orientation: + AL10.alListenerfv( AL10.AL_ORIENTATION, listenerOrientation ); + // Check for errors: + checkALError(); + } + +/** + * Changes the listeners orientation using the specified coordinates. + * @param lookX X element of the look-at direction. + * @param lookY Y element of the look-at direction. + * @param lookZ Z element of the look-at direction. + * @param upX X element of the up direction. + * @param upY Y element of the up direction. + * @param upZ Z element of the up direction. + */ + @Override + public void setListenerOrientation( float lookX, float lookY, float lookZ, + float upX, float upY, float upZ ) + { + super.setListenerOrientation( lookX, lookY, lookZ, upX, upY, upZ ); + listenerOrientation.put( 0, lookX ); + listenerOrientation.put( 1, lookY ); + listenerOrientation.put( 2, lookZ ); + listenerOrientation.put( 3, upX ); + listenerOrientation.put( 4, upY ); + listenerOrientation.put( 5, upZ ); + AL10.alListenerfv( AL10.AL_ORIENTATION, listenerOrientation ); + checkALError(); + } + +/** + * Changes the listeners position and orientation using the specified listener + * data. + * @param l Listener data to use. + */ + @Override + public void setListenerData( ListenerData l ) + { + super.setListenerData( l ); + + listenerPositionAL.put( 0, l.position.x ); + listenerPositionAL.put( 1, l.position.y ); + listenerPositionAL.put( 2, l.position.z ); + AL10.alListenerfv( AL10.AL_POSITION, listenerPositionAL ); + checkALError(); + + listenerOrientation.put( 0, l.lookAt.x ); + listenerOrientation.put( 1, l.lookAt.y ); + listenerOrientation.put( 2, l.lookAt.z ); + listenerOrientation.put( 3, l.up.x ); + listenerOrientation.put( 4, l.up.y ); + listenerOrientation.put( 5, l.up.z ); + AL10.alListenerfv( AL10.AL_ORIENTATION, listenerOrientation ); + checkALError(); + + listenerVelocity.put( 0, l.velocity.x ); + listenerVelocity.put( 1, l.velocity.y ); + listenerVelocity.put( 2, l.velocity.z ); + AL10.alListenerfv( AL10.AL_VELOCITY, listenerVelocity ); + checkALError(); + } + +/** + * Sets the listener's velocity, for use in Doppler effect. + * @param x Velocity along world x-axis. + * @param y Velocity along world y-axis. + * @param z Velocity along world z-axis. + */ + @Override + public void setListenerVelocity( float x, float y, float z ) + { + super.setListenerVelocity( x, y, z ); + + listenerVelocity.put( 0, listener.velocity.x ); + listenerVelocity.put( 1, listener.velocity.y ); + listenerVelocity.put( 2, listener.velocity.z ); + AL10.alListenerfv( AL10.AL_VELOCITY, listenerVelocity ); + } + +/** + * The Doppler parameters have changed. + */ + @Override + public void dopplerChanged() + { + super.dopplerChanged(); + + AL10.alDopplerFactor( SoundSystemConfig.getDopplerFactor() ); + checkALError(); + AL10.alDopplerVelocity( SoundSystemConfig.getDopplerVelocity() ); + checkALError(); + } + +/** + * Checks for OpenAL errors, and prints a message if there is an error. + * @return True if there was an error, False if not. + */ + private boolean checkALError() + { + switch( AL10.alGetError() ) + { + case AL10.AL_NO_ERROR: + return false; + case AL10.AL_INVALID_NAME: + errorMessage( "Invalid name parameter." ); + return true; + case AL10.AL_INVALID_ENUM: + errorMessage( "Invalid parameter." ); + return true; + case AL10.AL_INVALID_VALUE: + errorMessage( "Invalid enumerated parameter value." ); + return true; + case AL10.AL_INVALID_OPERATION: + errorMessage( "Illegal call." ); + return true; + case AL10.AL_OUT_OF_MEMORY: + errorMessage( "Unable to allocate memory." ); + return true; + default: + errorMessage( "An unrecognized error occurred." ); + return true; + } + } + +/** + * Whether or not the AL_PITCH control is supported. + * @return True if AL_PITCH is supported. + */ + public static boolean alPitchSupported() + { + return alPitchSupported( GET, XXX ); + } +/** + * Sets or returns the value of boolean 'alPitchSupported'. + * @param action Action to perform (GET or SET). + * @param value New value if action is SET, otherwise XXX. + * @return value of boolean 'alPitchSupported'. + */ + private static synchronized boolean alPitchSupported( boolean action, + boolean value ) + { + if( action == SET ) + alPitchSupported = value; + return alPitchSupported; + } + +/** + * Returns the short title of this library type. + * @return A short title. + */ + public static String getTitle() + { + return "LWJGL OpenAL"; + } + +/** + * Returns a longer description of this library type. + * @return A longer description. + */ + public static String getDescription() + { + return "The LWJGL binding of OpenAL. For more information, see " + + "http://www.lwjgl.org"; + } + +/** + * Returns the name of the class. + * @return "Library" + library title. + */ + @Override + public String getClassName() + { + return "LibraryLWJGLOpenAL"; + } + +/** + * The LibraryLWJGLOpenAL.Exception class provides library-specific error + * information. + */ + public static class Exception extends SoundSystemException + { + private static final long serialVersionUID = -7502452059037798035L; + /** + * Global identifier for an exception during AL.create(). Probably + * means that OpenAL is not supported. + */ + public static final int CREATE = 101; + /** + * Global identifier for an invalid name parameter in OpenAL. + */ + public static final int INVALID_NAME = 102; + /** + * Global identifier for an invalid parameter in OpenAL. + */ + public static final int INVALID_ENUM = 103; + /** + * Global identifier for an invalid enumerated parameter value in OpenAL. + */ + public static final int INVALID_VALUE = 104; + /** + * Global identifier for an illegal call in OpenAL. + */ + public static final int INVALID_OPERATION = 105; + /** + * Global identifier for OpenAL out of memory. + */ + public static final int OUT_OF_MEMORY = 106; + /** + * Global identifier for an exception while creating the OpenAL Listener. + */ + public static final int LISTENER = 107; + /** + * Global identifier for OpenAL AL_PITCH not supported. + */ + public static final int NO_AL_PITCH = 108; + + /** + * Constructor: Generates a standard "unknown error" exception with the + * specified message. + * @param message A brief description of the problem that occurred. + */ + public Exception( String message ) + { + super( message ); + } + + /** + * Constructor: Generates an exception of the specified type, with the + * specified message. + * @param message A brief description of the problem that occurred. + * @param type Identifier indicating they type of error. + */ + public Exception( String message, int type ) + { + super( message, type ); + } + } +} diff --git a/src/paulscode/sound/libraries/SourceLWJGLOpenAL.java b/src/paulscode/sound/libraries/SourceLWJGLOpenAL.java new file mode 100644 index 0000000..7a771aa --- /dev/null +++ b/src/paulscode/sound/libraries/SourceLWJGLOpenAL.java @@ -0,0 +1,801 @@ +package paulscode.sound.libraries; + +import java.nio.IntBuffer; +import java.nio.FloatBuffer; +import java.util.LinkedList; +import javax.sound.sampled.AudioFormat; + +// From the lwjgl library, http://www.lwjgl.org +import org.lwjgl.BufferUtils; +import org.lwjgl.openal.AL10; + +import paulscode.sound.Channel; +import paulscode.sound.FilenameURL; +import paulscode.sound.Source; +import paulscode.sound.SoundBuffer; +import paulscode.sound.SoundSystemConfig; + +/** + * The SourceLWJGLOpenAL class provides an interface to the lwjgl binding of OpenAL. + *

+ * This software is based on or using the LWJGL Lightweight Java Gaming + * Library available from + * http://www.lwjgl.org/. + *


+ * LWJGL License: + *
+ * Copyright (c) 2002-2008 Lightweight Java Game Library Project + * All rights reserved. + *
+ * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + *
+ * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + *
+ * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + *
+ * * Neither the name of 'Light Weight Java Game Library' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *


+ * SoundSystem LibraryLWJGLOpenAL License:

+ * + * You are free to use this library for any purpose, commercial or otherwise. + * You may modify this library or source code, and distribute it any way you + * like, provided the following conditions are met: + *
+ * 1) You must abide by the conditions of the aforementioned LWJGL License. + *
+ * 2) You may not falsely claim to be the author of this library or any + * unmodified portion of it. + *
+ * 3) You may not copyright this library or a modified version of it and then + * sue me for copyright infringement. + *
+ * 4) If you modify the source code, you must clearly document the changes + * made before redistributing the modified source code, so other users know + * it is not the original code. + *
+ * 5) You are not required to give me credit for this library in any derived + * work, but if you do, you must also mention my website: + * http://www.paulscode.com + *
+ * 6) I the author will not be responsible for any damages (physical, + * financial, or otherwise) caused by the use if this library or any part + * of it. + *
+ * 7) I the author do not guarantee, warrant, or make any representations, + * either expressed or implied, regarding the use of this library or any + * part of it. + *

+ * Author: Paul Lamb + *
+ * http://www.paulscode.com + *
+ */ +public class SourceLWJGLOpenAL extends Source +{ +/** + * The source's basic Channel type-cast to a ChannelLWJGLOpenAL. + */ + private ChannelLWJGLOpenAL channelOpenAL = (ChannelLWJGLOpenAL) channel; + +/** + * OpenAL IntBuffer sound-buffer identifier for this source if it is a normal + * source. + */ + private IntBuffer myBuffer; + +/** + * FloatBuffer containing the listener's 3D coordinates. + */ + private FloatBuffer listenerPosition; + +/** + * FloatBuffer containing the source's 3D coordinates. + */ + private FloatBuffer sourcePosition; + +/** + * FloatBuffer containing the source's velocity vector. + */ + private FloatBuffer sourceVelocity; + +/** + * Constructor: Creates a new source using the specified parameters. + * @param listenerPosition FloatBuffer containing the listener's 3D coordinates. + * @param myBuffer OpenAL IntBuffer sound-buffer identifier to use for a new normal source. + * @param priority Setting this to true will prevent other sounds from overriding this one. + * @param toStream Setting this to true will create a streaming source. + * @param toLoop Should this source loop, or play only once. + * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename. + * @param filenameURL Filename/URL of the sound file to play at this source. + * @param soundBuffer Buffer containing audio data, or null if not loaded yet. + * @param x X position for this source. + * @param y Y position for this source. + * @param z Z position for this source. + * @param attModel Attenuation model to use. + * @param distOrRoll Either the fading distance or rolloff factor, depending on the value of 'att'. + * @param temporary Whether or not to remove this source after it finishes playing. + */ + public SourceLWJGLOpenAL( FloatBuffer listenerPosition, IntBuffer myBuffer, + boolean priority, boolean toStream, + boolean toLoop, String sourcename, + FilenameURL filenameURL, SoundBuffer soundBuffer, + float x, float y, float z, int attModel, + float distOrRoll, boolean temporary ) + { + super( priority, toStream, toLoop, sourcename, filenameURL, soundBuffer, + x, y, z, attModel, distOrRoll, temporary ); + if( codec != null ) + codec.reverseByteOrder( true ); + this.listenerPosition = listenerPosition; + this.myBuffer = myBuffer; + libraryType = LibraryLWJGLOpenAL.class; + pitch = 1.0f; + resetALInformation(); + } + +/** + * Constructor: Creates a new source matching the specified source. + * @param listenerPosition FloatBuffer containing the listener's 3D coordinates. + * @param myBuffer OpenAL IntBuffer sound-buffer identifier to use for a new normal source. + * @param old Source to copy information from. + * @param soundBuffer Buffer containing audio data, or null if not loaded yet. + */ + public SourceLWJGLOpenAL( FloatBuffer listenerPosition, IntBuffer myBuffer, + Source old, SoundBuffer soundBuffer ) + { + super( old, soundBuffer ); + if( codec != null ) + codec.reverseByteOrder( true ); + this.listenerPosition = listenerPosition; + this.myBuffer = myBuffer; + libraryType = LibraryLWJGLOpenAL.class; + pitch = 1.0f; + resetALInformation(); + } + + /** + * Constructor: Creates a new streaming source that will be directly fed with + * raw audio data. + * @param listenerPosition FloatBuffer containing the listener's 3D coordinates. + * @param audioFormat Format that the data will be in. + * @param priority Setting this to true will prevent other sounds from overriding this one. + * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename. + * @param x X position for this source. + * @param y Y position for this source. + * @param z Z position for this source. + * @param attModel Attenuation model to use. + * @param distOrRoll Either the fading distance or rolloff factor, depending on the value of 'att'. + */ + public SourceLWJGLOpenAL( FloatBuffer listenerPosition, + AudioFormat audioFormat, boolean priority, + String sourcename, float x, float y, float z, + int attModel, float distOrRoll ) + { + super( audioFormat, priority, sourcename, x, y, z, attModel, + distOrRoll ); + this.listenerPosition = listenerPosition; + libraryType = LibraryLWJGLOpenAL.class; + pitch = 1.0f; + resetALInformation(); + } + +/** + * Shuts the source down and removes references to all instantiated objects. + */ + @Override + public void cleanup() + { + + super.cleanup(); + } + +/** + * Changes the peripheral information about the source using the specified + * parameters. + * @param listenerPosition FloatBuffer containing the listener's 3D coordinates. + * @param myBuffer OpenAL IntBuffer sound-buffer identifier to use for a new normal source. + * @param priority Setting this to true will prevent other sounds from overriding this one. + * @param toStream Setting this to true will create a streaming source. + * @param toLoop Should this source loop, or play only once. + * @param sourcename A unique identifier for this source. Two sources may not use the same sourcename. + * @param filenameURL Filename/URL of the sound file to play at this source. + * @param soundBuffer Buffer containing audio data, or null if not loaded yet. + * @param x X position for this source. + * @param y Y position for this source. + * @param z Z position for this source. + * @param attModel Attenuation model to use. + * @param distOrRoll Either the fading distance or rolloff factor, depending on the value of 'att'. + * @param temporary Whether or not to remove this source after it finishes playing. + */ + public void changeSource( FloatBuffer listenerPosition, IntBuffer myBuffer, + boolean priority, boolean toStream, + boolean toLoop, String sourcename, + FilenameURL filenameURL, SoundBuffer soundBuffer, + float x, float y, float z, int attModel, + float distOrRoll, boolean temporary ) + { + super.changeSource( priority, toStream, toLoop, sourcename, + filenameURL, soundBuffer, x, y, z, attModel, + distOrRoll, temporary ); + this.listenerPosition = listenerPosition; + this.myBuffer = myBuffer; + pitch = 1.0f; + resetALInformation(); + } + +/** + * Removes the next filename from the sound sequence queue and assigns it to + * this source. This method has no effect on non-streaming sources. This + * method is used internally by SoundSystem, and it is unlikely that the user + * will ever need to use it. + * @return True if there was something in the queue. + */ + @Override + public boolean incrementSoundSequence() + { + if( !toStream ) + { + errorMessage( "Method 'incrementSoundSequence' may only be used " + + "for streaming sources." ); + return false; + } + synchronized( soundSequenceLock ) + { + if( soundSequenceQueue != null && soundSequenceQueue.size() > 0 ) + { + filenameURL = soundSequenceQueue.remove( 0 ); + if( codec != null ) + codec.cleanup(); + codec = SoundSystemConfig.getCodec( filenameURL.getFilename() ); + if( codec != null ) + { + codec.reverseByteOrder( true ); + if( codec.getAudioFormat() == null ) + codec.initialize( filenameURL.getURL() ); + + AudioFormat audioFormat = codec.getAudioFormat(); + + if( audioFormat == null ) + { + errorMessage( "Audio Format null in method " + + "'incrementSoundSequence'" ); + return false; + } + + int soundFormat = 0; + if( audioFormat.getChannels() == 1 ) + { + if( audioFormat.getSampleSizeInBits() == 8 ) + { + soundFormat = AL10.AL_FORMAT_MONO8; + } + else if( audioFormat.getSampleSizeInBits() == 16 ) + { + soundFormat = AL10.AL_FORMAT_MONO16; + } + else + { + errorMessage( "Illegal sample size in method " + + "'incrementSoundSequence'" ); + return false; + } + } + else if( audioFormat.getChannels() == 2 ) + { + if( audioFormat.getSampleSizeInBits() == 8 ) + { + soundFormat = AL10.AL_FORMAT_STEREO8; + } + else if( audioFormat.getSampleSizeInBits() == 16 ) + { + soundFormat = AL10.AL_FORMAT_STEREO16; + } + else + { + errorMessage( "Illegal sample size in method " + + "'incrementSoundSequence'" ); + return false; + } + } + else + { + errorMessage( "Audio data neither mono nor stereo in " + + "method 'incrementSoundSequence'" ); + return false; + } + + // Let the channel know what format and sample rate to use: + channelOpenAL.setFormat( soundFormat, + (int) audioFormat.getSampleRate() ); + preLoad = true; + } + return true; + } + } + return false; + } + +/** + * Called every time the listener's position or orientation changes. + */ + @Override + public void listenerMoved() + { + positionChanged(); + } + +/** + * Moves the source to the specified position. + * @param x X coordinate to move to. + * @param y Y coordinate to move to. + * @param z Z coordinate to move to. + */ + @Override + public void setPosition( float x, float y, float z ) + { + super.setPosition( x, y, z ); + + // Make sure OpenAL information has been created + if( sourcePosition == null ) + resetALInformation(); + else + positionChanged(); + + // put the new position information into the buffer: + sourcePosition.put( 0, x ); + sourcePosition.put( 1, y ); + sourcePosition.put( 2, z ); + + // make sure we are assigned to a channel: + if( channel != null && channel.attachedSource == this && + channelOpenAL != null && channelOpenAL.ALSource != null ) + { + // move the source: + AL10.alSourcefv( channelOpenAL.ALSource.get( 0 ), AL10.AL_POSITION, + sourcePosition ); + checkALError(); + } + } + +/** + * Recalculates the distance from the listner and the gain. + */ + @Override + public void positionChanged() + { + calculateDistance(); + calculateGain(); + + if( channel != null && channel.attachedSource == this && + channelOpenAL != null && channelOpenAL.ALSource != null ) + { + AL10.alSourcef( channelOpenAL.ALSource.get( 0 ), + AL10.AL_GAIN, (gain * sourceVolume + * (float) Math.abs( fadeOutGain ) + * fadeInGain) ); + checkALError(); + } + checkPitch(); + } + +/** + * Checks the source's pitch. + */ + private void checkPitch() + { + if( channel != null && channel.attachedSource == this && + LibraryLWJGLOpenAL.alPitchSupported() && channelOpenAL != null && + channelOpenAL.ALSource != null ) + { + AL10.alSourcef( channelOpenAL.ALSource.get( 0 ), + AL10.AL_PITCH, pitch ); + checkALError(); + } + } + +/** + * Sets whether this source should loop or only play once. + * @param lp True or false. + */ + @Override + public void setLooping( boolean lp ) + { + super.setLooping( lp ); + + // make sure we are assigned to a channel: + if( channel != null && channel.attachedSource == this && + channelOpenAL != null && channelOpenAL.ALSource != null ) + { + if( lp ) + AL10.alSourcei( channelOpenAL.ALSource.get( 0 ), + AL10.AL_LOOPING, AL10.AL_TRUE ); + else + AL10.alSourcei( channelOpenAL.ALSource.get( 0 ), + AL10.AL_LOOPING, AL10.AL_FALSE ); + checkALError(); + } + } + +/** + * Sets this source's attenuation model. + * @param model Attenuation model to use. + */ + @Override + public void setAttenuation( int model ) + { + super.setAttenuation( model ); + // make sure we are assigned to a channel: + if( channel != null && channel.attachedSource == this && + channelOpenAL != null && channelOpenAL.ALSource != null ) + { + // attenuation changed, so update the rolloff factor accordingly + if( model == SoundSystemConfig.ATTENUATION_ROLLOFF ) + AL10.alSourcef( channelOpenAL.ALSource.get( 0 ), + AL10.AL_ROLLOFF_FACTOR, distOrRoll ); + else + AL10.alSourcef( channelOpenAL.ALSource.get( 0 ), + AL10.AL_ROLLOFF_FACTOR, 0.0f ); + checkALError(); + } + } + +/** + * Sets this source's fade distance or rolloff factor, depending on the + * attenuation model. + * @param dr New value for fade distance or rolloff factor. + */ + @Override + public void setDistOrRoll( float dr) + { + super.setDistOrRoll( dr ); + // make sure we are assigned to a channel: + if( channel != null && channel.attachedSource == this && + channelOpenAL != null && channelOpenAL.ALSource != null ) + { + // if we are using rolloff attenuation, then dr is a rolloff factor: + if( attModel == SoundSystemConfig.ATTENUATION_ROLLOFF ) + AL10.alSourcef( channelOpenAL.ALSource.get( 0 ), + AL10.AL_ROLLOFF_FACTOR, dr ); + else + AL10.alSourcef( channelOpenAL.ALSource.get( 0 ), + AL10.AL_ROLLOFF_FACTOR, 0.0f ); + checkALError(); + } + } + +/** + * Sets this source's velocity, for use in Doppler effect. + * @param x Velocity along world x-axis. + * @param y Velocity along world y-axis. + * @param z Velocity along world z-axis. + */ + @Override + public void setVelocity( float x, float y, float z ) + { + super.setVelocity( x, y, z ); + + sourceVelocity = BufferUtils.createFloatBuffer( 3 ).put( new float[] + { x, y, z } ); + sourceVelocity.flip(); + // make sure we are assigned to a channel: + if( channel != null && channel.attachedSource == this && + channelOpenAL != null && channelOpenAL.ALSource != null ) + { + AL10.alSourcefv( channelOpenAL.ALSource.get( 0 ), + AL10.AL_VELOCITY, sourceVelocity ); + checkALError(); + } + } + +/** + * Manually sets this source's pitch. + * @param value A float value ( 0.5f - 2.0f ). + */ + @Override + public void setPitch( float value ) + { + super.setPitch( value ); + checkPitch(); + } + +/** + * Plays the source on the specified channel. + * @param c Channel to play on. + */ + @Override + public void play( Channel c ) + { + if( !active() ) + { + if( toLoop ) + toPlay = true; + return; + } + + if( c == null ) + { + errorMessage( "Unable to play source, because channel was null" ); + return; + } + + boolean newChannel = (channel != c); + if( channel != null && channel.attachedSource != this ) + newChannel = true; + + boolean wasPaused = paused(); + + super.play( c ); + + channelOpenAL = (ChannelLWJGLOpenAL) channel; + + // Make sure the channel exists: + // check if we are already on this channel: + if( newChannel ) + { + setPosition( position.x, position.y, position.z ); + checkPitch(); + + // Send the source's attributes to the channel: + if( channelOpenAL != null && channelOpenAL.ALSource != null ) + { + if( LibraryLWJGLOpenAL.alPitchSupported() ) + { + AL10.alSourcef( channelOpenAL.ALSource.get( 0 ), + AL10.AL_PITCH, pitch ); + checkALError(); + } + AL10.alSourcefv( channelOpenAL.ALSource.get( 0 ), + AL10.AL_POSITION, sourcePosition ); + checkALError(); + + AL10.alSourcefv( channelOpenAL.ALSource.get( 0 ), + AL10.AL_VELOCITY, sourceVelocity ); + + checkALError(); + + if( attModel == SoundSystemConfig.ATTENUATION_ROLLOFF ) + AL10.alSourcef( channelOpenAL.ALSource.get( 0 ), + AL10.AL_ROLLOFF_FACTOR, distOrRoll ); + else + AL10.alSourcef( channelOpenAL.ALSource.get( 0 ), + AL10.AL_ROLLOFF_FACTOR, 0.0f ); + checkALError(); + + if( toLoop && (!toStream) ) + AL10.alSourcei( channelOpenAL.ALSource.get( 0 ), + AL10.AL_LOOPING, AL10.AL_TRUE ); + else + AL10.alSourcei( channelOpenAL.ALSource.get( 0 ), + AL10.AL_LOOPING, AL10.AL_FALSE ); + checkALError(); + } + if( !toStream ) + { + // This is not a streaming source, so make sure there is + // a sound buffer loaded to play: + if( myBuffer == null ) + { + errorMessage( "No sound buffer to play" ); + return; + } + + channelOpenAL.attachBuffer( myBuffer ); + } + } + + // See if we are already playing: + if( !playing() ) + { + if( toStream && !wasPaused ) + { + if( codec == null ) + { + errorMessage( "Decoder null in method 'play'" ); + return; + } + if( codec.getAudioFormat() == null ) + codec.initialize( filenameURL.getURL() ); + + AudioFormat audioFormat = codec.getAudioFormat(); + + if( audioFormat == null ) + { + errorMessage( "Audio Format null in method 'play'" ); + return; + } + + int soundFormat = 0; + if( audioFormat.getChannels() == 1 ) + { + if( audioFormat.getSampleSizeInBits() == 8 ) + { + soundFormat = AL10.AL_FORMAT_MONO8; + } + else if( audioFormat.getSampleSizeInBits() == 16 ) + { + soundFormat = AL10.AL_FORMAT_MONO16; + } + else + { + errorMessage( "Illegal sample size in method 'play'" ); + return; + } + } + else if( audioFormat.getChannels() == 2 ) + { + if( audioFormat.getSampleSizeInBits() == 8 ) + { + soundFormat = AL10.AL_FORMAT_STEREO8; + } + else if( audioFormat.getSampleSizeInBits() == 16 ) + { + soundFormat = AL10.AL_FORMAT_STEREO16; + } + else + { + errorMessage( "Illegal sample size in method 'play'" ); + return; + } + } + else + { + errorMessage( "Audio data neither mono nor stereo in " + + "method 'play'" ); + return; + } + + // Let the channel know what format and sample rate to use: + channelOpenAL.setFormat( soundFormat, + (int) audioFormat.getSampleRate() ); + preLoad = true; + } + channel.play(); + if( pitch != 1.0f ) + checkPitch(); + } + } + +/** + * Queues up the initial stream-buffers for the stream. + * @return False if the end of the stream was reached. + */ + @Override + public boolean preLoad() + { + if( codec == null ) + return false; + + codec.initialize( filenameURL.getURL() ); + LinkedList preLoadBuffers = new LinkedList(); + for( int i = 0; i < SoundSystemConfig.getNumberStreamingBuffers(); i++ ) + { + soundBuffer = codec.read(); + + if( soundBuffer == null || soundBuffer.audioData == null ) + break; + + preLoadBuffers.add( soundBuffer.audioData ); + } + positionChanged(); + + channel.preLoadBuffers( preLoadBuffers ); + + preLoad = false; + return true; + } + +/** + * Resets all the information OpenAL uses to play this source. + */ + private void resetALInformation() + { + // Create buffers for the source's position and velocity + sourcePosition = BufferUtils.createFloatBuffer( 3 ).put( + new float[] { position.x, position.y, position.z } ); + sourceVelocity = BufferUtils.createFloatBuffer( 3 ).put( + new float[] { velocity.x, velocity.y, velocity.z } ); + + // flip the buffers, so they can be used: + sourcePosition.flip(); + sourceVelocity.flip(); + + positionChanged(); + } + +/** + * Calculates this source's distance from the listener. + */ + private void calculateDistance() + { + if( listenerPosition != null ) + { + // Calculate the source's distance from the listener: + double dX = position.x - listenerPosition.get( 0 ); + double dY = position.y - listenerPosition.get( 1 ); + double dZ = position.z - listenerPosition.get( 2 ); + distanceFromListener = (float) Math.sqrt( dX*dX + dY*dY + dZ*dZ ); + } + } + +/** + * If using linear attenuation, calculates the gain for this source based on + * its distance from the listener. + */ + private void calculateGain() + { + // If using linear attenuation, calculate the source's gain: + if( attModel == SoundSystemConfig.ATTENUATION_LINEAR ) + { + if( distanceFromListener <= 0 ) + { + gain = 1.0f; + } + else if( distanceFromListener >= distOrRoll ) + { + gain = 0.0f; + } + else + { + gain = 1.0f - (distanceFromListener / distOrRoll); + } + if( gain > 1.0f ) + gain = 1.0f; + if( gain < 0.0f ) + gain = 0.0f; + } + else + { + gain = 1.0f; + } + } + +/** + * Checks for OpenAL errors, and prints a message if there is an error. + * @return True if there was an error, False if not. + */ + private boolean checkALError() + { + switch( AL10.alGetError() ) + { + case AL10.AL_NO_ERROR: + return false; + case AL10.AL_INVALID_NAME: + errorMessage( "Invalid name parameter." ); + return true; + case AL10.AL_INVALID_ENUM: + errorMessage( "Invalid parameter." ); + return true; + case AL10.AL_INVALID_VALUE: + errorMessage( "Invalid enumerated parameter value." ); + return true; + case AL10.AL_INVALID_OPERATION: + errorMessage( "Illegal call." ); + return true; + case AL10.AL_OUT_OF_MEMORY: + errorMessage( "Unable to allocate memory." ); + return true; + default: + errorMessage( "An unrecognized error occurred." ); + return true; + } + } +}