diff --git a/VulkanDesign.txt b/VulkanDesign.txt new file mode 100644 index 0000000000..6d3ae38f35 --- /dev/null +++ b/VulkanDesign.txt @@ -0,0 +1,243 @@ + +Stuck, again. I need a good way to access vertex data. The current system has some flaws: + + 1. Processes must assume the format and number of components of an attribute. + This is especially concerning. + 2. Data cannot be accessed as struct buffers. This not a huge issue. + 3. Attributes are accessed by a string identifier. + +I want to have a more concrete way to work with attributes. Something along these lines: + +public interface PositionAttribute { + void setPosition(int vertex, Vector3f position); + Vector3f getPosition(int vertex); + void setPositionAccess(DataAccess access); +} + +The problem is that a single attribute can get fairly bloated if you also add nice things like bulk setters and getters. Multiply that by every attribute you want, and it can get quite unwieldy. It also has that odd access setter on each attribute, which for whatever reason really annoys me. + +What would actually be ideal is if everything could be represented by a simple "Vertex" struct. + +struct Vertex { + Vector3f position; + Vector2f texCoord; + Vector3f normal; +} + +(of course, in Java struct declaration is more complicated). The advantage is that all the vertex buffers can be very simply mapped as buffers of Vertex. It would also translate fairly well to instances. + +struct Instance { + Matrix4f worldViewProjection; + Vector4f color; +} + +The problem is what to do with vertex data split across multiple buffers, which could very well be done if multiple shaders want to access the vertex positions, but not everything wants the texCoord and normal data. In which case it'd be represented as so: + +struct VertexPart1 { + Vector3f position; +} +struct VertexPart2 { + Vector2f texCoord; + Vector3f normal; +} + +User-side, this wouldn't be much of a problem. He should know what type of vertex he's working with. But the engine utilities don't necessarily know that. The vertex struct would have to implement an interface (possibly for each attribute, as discussed above), and the utility would have to cast each individual vertex to the desired type. + +for (Vertex v : mesh.getVertices()) { + ((Position)v).setPosition(Vector3f.ZERO); +} + +It's messy and a pain to work with. It also doesn't properly address the dual vertex buffer situation. A possible solution to this is custom iterators which will automatically verify that an entire batch of vertices already meets the requirements. It would work something like this: + +for (Position p : mesh.getVertices(Position.class)) { + p.setPosition(Vector3f.ZERO); +} +for (TexCoord t : mesh.getVertices(TexCoord.class)) { + t.setTexCoord(Vector2f.ZERO); +} +mesh.unmap(); // unmap all used buffers + +getVertices() implementation may look something like this: + +public class Mesh { + private final Collection> buffers = new ArrayList<>(); + private final Map, VertexBuffer> bufferLookup = new HashMap<>(); + public Iterable getVertices(Class type) { + VertexBuffer buf = bufferLookup.get(type); + if (buf != null) { + return buf.getVertices(type); + } + for (VertexBuffer buffer : buffers) { + if (type.isAssignableFrom(buffer.getVertexType())) { + bufferLookup.put(type, buffer); + return buffer.getVertices(type); + } + } + throw new IllegalArgumentException("Mesh does not contain " + type); + } +} +public class VertexBuffer { + private final Class vertexType; + private final GpuBuffer buffer; + public Iterable getVertices(Class type) { + if (!type.isAssignableFrom(vertexType)) { + throw new ClassCastException("Vertex data is not " + type); + } + return (Iterable)map(); + } + public Iterable map() { + return buffer.map((Long address, Integer size) -> V.Buffer.create(address, size)); + } + public Class getVertexType() { + return vertexType; + } +} + +Unfortunately, this doesn't make accessing two different attributes at once very do-able. You'd have to do an awkward multi-iterator loop, which is just a pain to do. It would be better to have to do this: + +List pos = mesh.getVertices(Position.class); +List tex = mesh.getVertices(TexCoord.class); +pos.get(12).setPosition(tex.get(11).toVector3f()); + +How about this: + +Position pos = mesh.getAttribute(Position.class); +TexCoord tex = mesh.getAttribute(TexCoord.class); +pos.set(12, 0f, 0f, 0f); +Vector3f v = pos.get(11); +for (Vector3f v : pos) { + +} +for (int i : Attributes.iterate(pos, tex)) { + +} + +public interface Attribute extends Iterable { + + Attribute map(); + + void set(int element, T value); + + T get(int element); + + int size(); + + // optional + default T get(int element, T store) { + return get(element); + } + + @Override + default Iterator iterator() { + return new IteratorImpl<>(null); + } + + default IteratorImpl iterator(T store) { + return new IteratorImpl<>(store); + } + + class IteratorImpl implements Iterator, Iterable { + + private final Attribute attr; + private final T store; + private int index = 0; + + public IteratorImpl(Attribute attr, T store) { + this.attr = attr; + this.store = store; + } + + public boolean hasNext() { + return index < attr.size(); + } + + public T next() { + return attr.get(index++, store); + } + + public Iterator iterator() { + this.index = 0; + return this; + } + + } + +} +public abstract class AbstractAttribute implements Attribute { + + protected final VertexBuffer vertexBuffer; + protected final int stride, offset, size; + protected ByteBuffer buffer; + + public AbstractAttribute(VertexBuffer vertexBuffer, int stride, int offset, int size) { + this.vertexBuffer = vertexBuffer; + } + +} +public class Position implements Attribute { + + private VertexBuffer buffer; + private int stride, offset; + + @Override + public void set(int element, Vector3f value) { + set(element, value.x, value.y, value.z); + } + + @Override + public Vector3f get(int element) { + Vector3f store = new Vector3f(); + ByteBuffer buf = buffer.map().position(element * stride + offset); + store.x = buf.getFloat(); + store.y = buf.getFloat(); + store.z = buf.getFloat(); + return store; + } + + public void set(int element, float x, float y, float z) { + buffer.map() + .position(element * stride + offset) + .putFloat(x).putFloat(y).putFloat(z); + } + +} + +If mappings are frequent -> streaming buffer +If mappings are less frequent -> dynamic buffer +If only one mapping occurs OR a mapping hasn't occured in a very long time -> static buffer + +Static AND dynamic + Build + 1. Create GPU buffer (device local) + 2. Create CPU buffer (nio) + CPU -> GPU + 1. Acquire staging buffer (host visible and coherent) + 2. Map staging buffer + 3. Copy CPU buffer -> staging buffer + 4. Record copy staging buffer -> GPU buffer + 5. Unmap staging buffer + --- wait for copy to complete + 6. Release staging buffer + GPU -> CPU + 1. Acquire staging buffer (host visible and coherent) + 2. Copy GPU buffer -> staging buffer + --- wait for copy to complete + 3. Map staging buffer + 4. Copy staging buffer -> CPU buffer + OR read directly from staging buffer + (depends on the nature of the read op) + 5. Unmap staging buffer + 6. Release staging buffer +Streaming + Build + 1. Create GPU buffers per frame (host visible and coherent) + 2. Create CPU buffer (nio) + CPU -> GPU + 1. Map frame GPU buffer (if not already mapped) + 2. Copy CPU buffer -> frame GPU buffer + 3. Unmap frame GPU buffer (if necessary) + GPU -> CPU + 1. Map a GPU buffer (if not already mapped) + 2. Copy GPU buffer -> CPU buffer + 3. Unmap GPU buffer (if necessary) + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6b56daa8a7..b08ae5bd7f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -34,6 +34,8 @@ lwjgl3-opencl = { module = "org.lwjgl:lwjgl-opencl", version.ref = "lwjgl3" lwjgl3-opengl = { module = "org.lwjgl:lwjgl-opengl", version.ref = "lwjgl3" } lwjgl3-openvr = { module = "org.lwjgl:lwjgl-openvr", version.ref = "lwjgl3" } lwjgl3-ovr = { module = "org.lwjgl:lwjgl-ovr", version.ref = "lwjgl3" } +lwjgl3-vulkan = { module = "org.lwjgl:lwjgl-vulkan", version.ref = "lwjgl3" } +lwjgl3-shaderc = { module = "org.lwjgl:lwjgl-shaderc", version.ref = "lwjgl3" } mokito-core = "org.mockito:mockito-core:3.12.4" diff --git a/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java b/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java index cc25a40d55..9baa3b64e9 100644 --- a/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java +++ b/jme3-android/src/main/java/com/jme3/app/state/VideoRecorderAppState.java @@ -42,8 +42,8 @@ import com.jme3.renderer.queue.RenderQueue; import com.jme3.system.JmeSystem; import com.jme3.system.Timer; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage; import com.jme3.util.AndroidScreenshots; import com.jme3.util.BufferUtils; import java.io.File; @@ -233,7 +233,7 @@ private class VideoProcessor implements SceneProcessor { private MjpegFileWriter writer; private boolean fastMode = true; - public void addImage(Renderer renderer, FrameBuffer out) { + public void addImage(Renderer renderer, GlFrameBuffer out) { if (freeItems == null) { return; } @@ -241,7 +241,7 @@ public void addImage(Renderer renderer, FrameBuffer out) { final WorkItem item = freeItems.take(); usedItems.add(item); item.buffer.clear(); - renderer.readFrameBufferWithFormat(out, item.buffer, Image.Format.BGRA8); + renderer.readFrameBufferWithFormat(out, item.buffer, GlImage.Format.BGRA8); executor.submit(new Callable() { @Override @@ -307,7 +307,7 @@ public void postQueue(RenderQueue rq) { } @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { numFrames++; addImage(renderManager.getRenderer(), out); } diff --git a/jme3-android/src/main/java/com/jme3/texture/plugins/AndroidBufferImageLoader.java b/jme3-android/src/main/java/com/jme3/texture/plugins/AndroidBufferImageLoader.java index 4dc13519da..f723d6ce76 100644 --- a/jme3-android/src/main/java/com/jme3/texture/plugins/AndroidBufferImageLoader.java +++ b/jme3-android/src/main/java/com/jme3/texture/plugins/AndroidBufferImageLoader.java @@ -37,7 +37,7 @@ import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetLoader; import com.jme3.asset.TextureKey; -import com.jme3.texture.Image; +import com.jme3.texture.GlImage; import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; import java.io.IOException; @@ -72,7 +72,7 @@ private static void convertARGBtoABGR(int[] src, int srcOff, int[] dst, int dstO @Override public Object load(AssetInfo assetInfo) throws IOException { Bitmap bitmap; - Image.Format format; + GlImage.Format format; int bpp; BitmapFactory.Options options = new BitmapFactory.Options(); @@ -102,15 +102,15 @@ public Object load(AssetInfo assetInfo) throws IOException { switch (bitmap.getConfig()) { case ALPHA_8: - format = Image.Format.Alpha8; + format = GlImage.Format.Alpha8; bpp = 1; break; case ARGB_8888: - format = Image.Format.RGBA8; + format = GlImage.Format.RGBA8; bpp = 4; break; case RGB_565: - format = Image.Format.RGB565; + format = GlImage.Format.RGB565; bpp = 2; break; default: @@ -124,7 +124,7 @@ public Object load(AssetInfo assetInfo) throws IOException { ByteBuffer data = BufferUtils.createByteBuffer(bitmap.getWidth() * bitmap.getHeight() * bpp); - if (format == Image.Format.RGBA8) { + if (format == GlImage.Format.RGBA8) { int[] pixelData = new int[width * height]; bitmap.getPixels(pixelData, 0, width, 0, 0, width, height); @@ -163,7 +163,7 @@ public Object load(AssetInfo assetInfo) throws IOException { bitmap.recycle(); - Image image = new Image(format, width, height, data, ColorSpace.sRGB); + GlImage image = new GlImage(format, width, height, data, ColorSpace.sRGB); return image; } diff --git a/jme3-android/src/main/java/com/jme3/texture/plugins/AndroidNativeImageLoader.java b/jme3-android/src/main/java/com/jme3/texture/plugins/AndroidNativeImageLoader.java index c56e51d0f0..754cdf4f34 100644 --- a/jme3-android/src/main/java/com/jme3/texture/plugins/AndroidNativeImageLoader.java +++ b/jme3-android/src/main/java/com/jme3/texture/plugins/AndroidNativeImageLoader.java @@ -34,7 +34,7 @@ import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetLoader; import com.jme3.asset.TextureKey; -import com.jme3.texture.Image; +import com.jme3.texture.GlImage; import java.io.IOException; import java.io.InputStream; @@ -52,10 +52,10 @@ public class AndroidNativeImageLoader implements AssetLoader { System.loadLibrary("decodejme"); } - private static native Image load(InputStream in, boolean flipY, byte[] tmpArray) throws IOException; + private static native GlImage load(InputStream in, boolean flipY, byte[] tmpArray) throws IOException; @Override - public Image load(AssetInfo info) throws IOException { + public GlImage load(AssetInfo info) throws IOException { boolean flip = ((TextureKey) info.getKey()).isFlipY(); try (final InputStream in = info.openStream()) { return load(in, flip, tmpArray); diff --git a/jme3-core/build.gradle b/jme3-core/build.gradle index 58ac362921..89c1795869 100644 --- a/jme3-core/build.gradle +++ b/jme3-core/build.gradle @@ -19,6 +19,62 @@ dependencies { testRuntimeOnly project(':jme3-testdata') testImplementation project(':jme3-desktop') testRuntimeOnly project(':jme3-plugins') + + // jackson yaml parsing + implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.14.2" + + // Use LWJGL3 directly in core. This destroys LWJGL2 and JOGL compatibility. + api (libs.lwjgl3.awt) { + exclude group: 'org.lwjgl', module: 'lwjgl' + } + api libs.lwjgl3.base + api libs.lwjgl3.glfw + api libs.lwjgl3.jawt + api libs.lwjgl3.jemalloc + api libs.lwjgl3.openal + api libs.lwjgl3.opencl + api libs.lwjgl3.opengl + api libs.lwjgl3.vulkan + api libs.lwjgl3.shaderc + + // include natives + runtimeOnly(variantOf(libs.lwjgl3.base){ classifier('natives-windows') }) + runtimeOnly(variantOf(libs.lwjgl3.base){ classifier('natives-windows-x86') }) + runtimeOnly(variantOf(libs.lwjgl3.base){ classifier('natives-linux') }) + runtimeOnly(variantOf(libs.lwjgl3.base){ classifier('natives-linux-arm32') }) + runtimeOnly(variantOf(libs.lwjgl3.base){ classifier('natives-linux-arm64') }) + runtimeOnly(variantOf(libs.lwjgl3.base){ classifier('natives-macos') }) + runtimeOnly(variantOf(libs.lwjgl3.base){ classifier('natives-macos-arm64') }) + runtimeOnly(variantOf(libs.lwjgl3.glfw){ classifier('natives-windows') }) + runtimeOnly(variantOf(libs.lwjgl3.glfw){ classifier('natives-windows-x86') }) + runtimeOnly(variantOf(libs.lwjgl3.glfw){ classifier('natives-linux') }) + runtimeOnly(variantOf(libs.lwjgl3.glfw){ classifier('natives-linux-arm32') }) + runtimeOnly(variantOf(libs.lwjgl3.glfw){ classifier('natives-linux-arm64') }) + runtimeOnly(variantOf(libs.lwjgl3.glfw){ classifier('natives-macos') }) + runtimeOnly(variantOf(libs.lwjgl3.glfw){ classifier('natives-macos-arm64') }) + runtimeOnly(variantOf(libs.lwjgl3.jemalloc){ classifier('natives-windows') }) + runtimeOnly(variantOf(libs.lwjgl3.jemalloc){ classifier('natives-windows-x86') }) + runtimeOnly(variantOf(libs.lwjgl3.jemalloc){ classifier('natives-linux') }) + runtimeOnly(variantOf(libs.lwjgl3.jemalloc){ classifier('natives-linux-arm32') }) + runtimeOnly(variantOf(libs.lwjgl3.jemalloc){ classifier('natives-linux-arm64') }) + runtimeOnly(variantOf(libs.lwjgl3.jemalloc){ classifier('natives-macos') }) + runtimeOnly(variantOf(libs.lwjgl3.jemalloc){ classifier('natives-macos-arm64') }) + runtimeOnly(variantOf(libs.lwjgl3.opengl){ classifier('natives-windows') }) + runtimeOnly(variantOf(libs.lwjgl3.opengl){ classifier('natives-windows-x86') }) + runtimeOnly(variantOf(libs.lwjgl3.opengl){ classifier('natives-linux') }) + runtimeOnly(variantOf(libs.lwjgl3.opengl){ classifier('natives-linux-arm32') }) + runtimeOnly(variantOf(libs.lwjgl3.opengl){ classifier('natives-linux-arm64') }) + runtimeOnly(variantOf(libs.lwjgl3.opengl){ classifier('natives-macos') }) + runtimeOnly(variantOf(libs.lwjgl3.opengl){ classifier('natives-macos-arm64') }) + runtimeOnly(variantOf(libs.lwjgl3.openal){ classifier('natives-windows') }) + runtimeOnly(variantOf(libs.lwjgl3.openal){ classifier('natives-windows-x86') }) + runtimeOnly(variantOf(libs.lwjgl3.openal){ classifier('natives-linux') }) + runtimeOnly(variantOf(libs.lwjgl3.openal){ classifier('natives-linux-arm32') }) + runtimeOnly(variantOf(libs.lwjgl3.openal){ classifier('natives-linux-arm64') }) + runtimeOnly(variantOf(libs.lwjgl3.openal){ classifier('natives-macos') }) + runtimeOnly(variantOf(libs.lwjgl3.openal){ classifier('natives-macos-arm64') }) + runtimeOnly(variantOf(libs.lwjgl3.shaderc) { classifier('natives-linux') }) + } task updateVersionPropertiesFile { diff --git a/jme3-core/src/main/java/com/jme3/anim/AnimFactory.java b/jme3-core/src/main/java/com/jme3/anim/AnimFactory.java index 38af4cf927..4dd84510dd 100644 --- a/jme3-core/src/main/java/com/jme3/anim/AnimFactory.java +++ b/jme3-core/src/main/java/com/jme3/anim/AnimFactory.java @@ -59,8 +59,6 @@ * {@link #buildAnimation(com.jme3.anim.util.HasLocalTransform)} to negate the * final rotation. To prevent an unwanted rotation at the end of the loop, you * may need to add intermediate rotation keyframes. - * - * Inspired by Nehon's {@link com.jme3.animation.AnimationFactory}. */ public class AnimFactory { diff --git a/jme3-core/src/main/java/com/jme3/anim/MorphControl.java b/jme3-core/src/main/java/com/jme3/anim/MorphControl.java index 4c2f0a6c75..c8143b3194 100644 --- a/jme3-core/src/main/java/com/jme3/anim/MorphControl.java +++ b/jme3-core/src/main/java/com/jme3/anim/MorphControl.java @@ -79,7 +79,7 @@ public class MorphControl extends AbstractControl implements Savable { private float[] tmpNormArray; private float[] tmpTanArray; - private static final VertexBuffer.Type bufferTypes[] = VertexBuffer.Type.values(); + private static final GlVertexBuffer.Type bufferTypes[] = GlVertexBuffer.Type.values(); @Override public void setSpatial(Spatial spatial) { @@ -237,17 +237,17 @@ private int getMaxGPUTargets(RenderManager rm, Geometry geom, Material mat, int } private int bindMorphTargetBuffer(Mesh mesh, int targetNumBuffers, int boundBufferIdx, MorphTarget t) { - int start = VertexBuffer.Type.MorphTarget0.ordinal(); + int start = GlVertexBuffer.Type.MorphTarget0.ordinal(); if (targetNumBuffers >= 1) { - activateBuffer(mesh, boundBufferIdx, start, t.getBuffer(VertexBuffer.Type.Position)); + activateBuffer(mesh, boundBufferIdx, start, t.getBuffer(GlVertexBuffer.Type.Position)); boundBufferIdx++; } if (targetNumBuffers >= 2) { - activateBuffer(mesh, boundBufferIdx, start, t.getBuffer(VertexBuffer.Type.Normal)); + activateBuffer(mesh, boundBufferIdx, start, t.getBuffer(GlVertexBuffer.Type.Normal)); boundBufferIdx++; } if (!approximateTangents && targetNumBuffers == 3) { - activateBuffer(mesh, boundBufferIdx, start, t.getBuffer(VertexBuffer.Type.Tangent)); + activateBuffer(mesh, boundBufferIdx, start, t.getBuffer(GlVertexBuffer.Type.Tangent)); boundBufferIdx++; } return boundBufferIdx; @@ -255,17 +255,17 @@ private int bindMorphTargetBuffer(Mesh mesh, int targetNumBuffers, int boundBuff private void writeCpuBuffer(int targetNumBuffers, MorphTarget mt) { if (targetNumBuffers >= 1) { - FloatBuffer dest = mt.getBuffer(VertexBuffer.Type.Position); + FloatBuffer dest = mt.getBuffer(GlVertexBuffer.Type.Position); dest.rewind(); dest.put(tmpPosArray, 0, dest.capacity()); } if (targetNumBuffers >= 2) { - FloatBuffer dest = mt.getBuffer(VertexBuffer.Type.Normal); + FloatBuffer dest = mt.getBuffer(GlVertexBuffer.Type.Normal); dest.rewind(); dest.put(tmpNormArray, 0, dest.capacity()); } if (!approximateTangents && targetNumBuffers == 3) { - FloatBuffer dest = mt.getBuffer(VertexBuffer.Type.Tangent); + FloatBuffer dest = mt.getBuffer(GlVertexBuffer.Type.Tangent); dest.rewind(); dest.put(tmpTanArray, 0, dest.capacity()); } @@ -273,13 +273,13 @@ private void writeCpuBuffer(int targetNumBuffers, MorphTarget mt) { private void mergeMorphTargets(int targetNumBuffers, float weight, MorphTarget t, boolean init) { if (targetNumBuffers >= 1) { - mergeTargetBuffer(tmpPosArray, weight, t.getBuffer(VertexBuffer.Type.Position), init); + mergeTargetBuffer(tmpPosArray, weight, t.getBuffer(GlVertexBuffer.Type.Position), init); } if (targetNumBuffers >= 2) { - mergeTargetBuffer(tmpNormArray, weight, t.getBuffer(VertexBuffer.Type.Normal), init); + mergeTargetBuffer(tmpNormArray, weight, t.getBuffer(GlVertexBuffer.Type.Normal), init); } if (!approximateTangents && targetNumBuffers == 3) { - mergeTargetBuffer(tmpTanArray, weight, t.getBuffer(VertexBuffer.Type.Tangent), init); + mergeTargetBuffer(tmpTanArray, weight, t.getBuffer(GlVertexBuffer.Type.Tangent), init); } } @@ -306,8 +306,8 @@ private void mergeTargetBuffer(float[] array, float weight, FloatBuffer src, boo } private void activateBuffer(Mesh mesh, int idx, int start, FloatBuffer b) { - VertexBuffer.Type t = bufferTypes[start + idx]; - VertexBuffer vb = mesh.getBuffer(t); + GlVertexBuffer.Type t = bufferTypes[start + idx]; + GlVertexBuffer vb = mesh.getBuffer(t); // only set the buffer if it's different if (vb == null || vb.getData() != b) { mesh.setBuffer(t, 3, b); @@ -324,18 +324,18 @@ private float[] ensureCapacity(float[] tmpArray, int size) { private MorphTarget initCpuMorphTarget(Geometry geom) { MorphTarget res = new MorphTarget(); MorphTarget mt = geom.getMesh().getMorphTargets()[0]; - FloatBuffer b = mt.getBuffer(VertexBuffer.Type.Position); + FloatBuffer b = mt.getBuffer(GlVertexBuffer.Type.Position); if (b != null) { - res.setBuffer(VertexBuffer.Type.Position, BufferUtils.createFloatBuffer(b.capacity())); + res.setBuffer(GlVertexBuffer.Type.Position, BufferUtils.createFloatBuffer(b.capacity())); } - b = mt.getBuffer(VertexBuffer.Type.Normal); + b = mt.getBuffer(GlVertexBuffer.Type.Normal); if (b != null) { - res.setBuffer(VertexBuffer.Type.Normal, BufferUtils.createFloatBuffer(b.capacity())); + res.setBuffer(GlVertexBuffer.Type.Normal, BufferUtils.createFloatBuffer(b.capacity())); } if (!approximateTangents) { - b = mt.getBuffer(VertexBuffer.Type.Tangent); + b = mt.getBuffer(GlVertexBuffer.Type.Tangent); if (b != null) { - res.setBuffer(VertexBuffer.Type.Tangent, BufferUtils.createFloatBuffer(b.capacity())); + res.setBuffer(GlVertexBuffer.Type.Tangent, BufferUtils.createFloatBuffer(b.capacity())); } } return res; @@ -343,11 +343,11 @@ private MorphTarget initCpuMorphTarget(Geometry geom) { private int getTargetNumBuffers(MorphTarget morphTarget) { int num = 0; - if (morphTarget.getBuffer(VertexBuffer.Type.Position) != null) num++; - if (morphTarget.getBuffer(VertexBuffer.Type.Normal) != null) num++; + if (morphTarget.getBuffer(GlVertexBuffer.Type.Position) != null) num++; + if (morphTarget.getBuffer(GlVertexBuffer.Type.Normal) != null) num++; // if tangents are not needed we don't count the tangent buffer - if (!approximateTangents && morphTarget.getBuffer(VertexBuffer.Type.Tangent) != null) { + if (!approximateTangents && morphTarget.getBuffer(GlVertexBuffer.Type.Tangent) != null) { num++; } return num; @@ -366,10 +366,10 @@ private int getTargetNumBuffers(MorphTarget morphTarget) { */ private int getRemainingBuffers(Mesh mesh, Renderer renderer) { int nbUsedBuffers = 0; - for (VertexBuffer vb : mesh.getBufferList().getArray()) { - boolean isMorphBuffer = vb.getBufferType().ordinal() >= VertexBuffer.Type.MorphTarget0.ordinal() && vb.getBufferType().ordinal() <= VertexBuffer.Type.MorphTarget9.ordinal(); - if (vb.getBufferType() == VertexBuffer.Type.Index || isMorphBuffer) continue; - if (vb.getUsage() != VertexBuffer.Usage.CpuOnly) { + for (GlVertexBuffer vb : mesh.getBufferList().getArray()) { + boolean isMorphBuffer = vb.getBufferType().ordinal() >= GlVertexBuffer.Type.MorphTarget0.ordinal() && vb.getBufferType().ordinal() <= GlVertexBuffer.Type.MorphTarget9.ordinal(); + if (vb.getBufferType() == GlVertexBuffer.Type.Index || isMorphBuffer) continue; + if (vb.getUsage() != GlVertexBuffer.Usage.CpuOnly) { nbUsedBuffers++; } } @@ -478,7 +478,7 @@ public void visit(Geometry geom) { // this code makes sure that if the mesh has no hardware skinning buffers hardware skinning won't be activated. // this is important, because if HW skinning is activated the shader will declare 2 additional useless attributes, // and we desperately need all the attributes we can find for Morph animation. - if (mesh.getBuffer(VertexBuffer.Type.HWBoneIndex) == null && !geom.getLocalMatParamOverrides().contains(nullNumberOfBones)) { + if (mesh.getBuffer(GlVertexBuffer.Type.HWBoneIndex) == null && !geom.getLocalMatParamOverrides().contains(nullNumberOfBones)) { geom.addMatParamOverride(nullNumberOfBones); } } diff --git a/jme3-core/src/main/java/com/jme3/anim/SkinningControl.java b/jme3-core/src/main/java/com/jme3/anim/SkinningControl.java index 7cc5f3c58b..f64bb0c9fb 100644 --- a/jme3-core/src/main/java/com/jme3/anim/SkinningControl.java +++ b/jme3-core/src/main/java/com/jme3/anim/SkinningControl.java @@ -37,7 +37,7 @@ import com.jme3.math.Matrix4f; import com.jme3.renderer.*; import com.jme3.scene.*; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.scene.control.AbstractControl; import com.jme3.scene.mesh.IndexBuffer; import com.jme3.shader.VarType; @@ -45,6 +45,7 @@ import com.jme3.util.TempVars; import com.jme3.util.clone.Cloner; import com.jme3.util.clone.JmeCloneable; +import com.jme3.vulkan.mesh.AttributeModifier; import java.io.IOException; import java.nio.Buffer; @@ -319,41 +320,30 @@ void resetToBind() { for (Geometry geometry : targets) { Mesh mesh = geometry.getMesh(); if (mesh != null && mesh.isAnimated()) { - Buffer bwBuff = mesh.getBuffer(Type.BoneWeight).getData(); - Buffer biBuff = mesh.getBuffer(Type.BoneIndex).getData(); - if (!biBuff.hasArray() || !bwBuff.hasArray()) { - mesh.prepareForAnim(true); // prepare for software animation + try (AttributeModifier pb = mesh.modify(Type.Position); + AttributeModifier bpb = mesh.modify(Type.BindPosePosition)) { + pb.putReader(bpb, 0, 0, 0, 0, mesh.getVertexCount(), 3); } - VertexBuffer bindPos = mesh.getBuffer(Type.BindPosePosition); - VertexBuffer bindNorm = mesh.getBuffer(Type.BindPoseNormal); - VertexBuffer pos = mesh.getBuffer(Type.Position); - FloatBuffer pb = (FloatBuffer) pos.getData(); - FloatBuffer bpb = (FloatBuffer) bindPos.getData(); - pb.clear(); - bpb.clear(); - - // reset bind normals if there is a BindPoseNormal buffer - if (bindNorm != null) { - VertexBuffer norm = mesh.getBuffer(Type.Normal); - FloatBuffer nb = (FloatBuffer) norm.getData(); - FloatBuffer bnb = (FloatBuffer) bindNorm.getData(); - nb.clear(); - bnb.clear(); - nb.put(bnb).clear(); + if (mesh.attributeExists(Type.BindPoseNormal)) { + try (AttributeModifier nb = mesh.modify(Type.Normal); + AttributeModifier bnb = mesh.modify(Type.BindPoseNormal)) { + nb.putReader(bnb, 0, 0, 0, 0, mesh.getVertexCount(), 3); + } } - - //resetting bind tangents if there is a bind tangent buffer - VertexBuffer bindTangents = mesh.getBuffer(Type.BindPoseTangent); - if (bindTangents != null) { - VertexBuffer tangents = mesh.getBuffer(Type.Tangent); - FloatBuffer tb = (FloatBuffer) tangents.getData(); - FloatBuffer btb = (FloatBuffer) bindTangents.getData(); - tb.clear(); - btb.clear(); - tb.put(btb).clear(); + if (mesh.attributeExists(Type.BindPoseTangent)) { + try (AttributeModifier tb = mesh.modify(Type.Tangent); + AttributeModifier btb = mesh.modify(Type.BindPoseTangent)) { + // todo: 3 or 4 components for tangent??? + tb.putReader(btb, 0, 0, 0, 0, mesh.getVertexCount(), 4); + } } - pb.put(bpb).clear(); + // todo: replace prepareForAnim +// Buffer bwBuff = mesh.getBuffer(Type.BoneWeight).getData(); +// Buffer biBuff = mesh.getBuffer(Type.BoneIndex).getData(); +// if (!biBuff.hasArray() || !bwBuff.hasArray()) { +// mesh.prepareForAnim(true); // prepare for software animation +// } } } } @@ -444,13 +434,12 @@ public Mesh[] getTargets() { */ private void softwareSkinUpdate(Mesh mesh, Matrix4f[] offsetMatrices) { - VertexBuffer tb = mesh.getBuffer(Type.Tangent); - if (tb == null) { + if (!mesh.attributeExists(Type.Tangent)) { //if there are no tangents use the classic skinning applySkinning(mesh, offsetMatrices); } else { //if there are tangents use the skinning with tangents - applySkinningTangents(mesh, offsetMatrices, tb); + applySkinningTangents(mesh, offsetMatrices); } @@ -463,98 +452,48 @@ private void softwareSkinUpdate(Mesh mesh, Matrix4f[] offsetMatrices) { * @param offsetMatrices the offset matrices to apply */ private void applySkinning(Mesh mesh, Matrix4f[] offsetMatrices) { + int maxWeightsPerVert = mesh.getMaxNumWeights(); if (maxWeightsPerVert <= 0) { throw new IllegalStateException("Max weights per vert is incorrectly set!"); } - int fourMinusMaxWeights = 4 - maxWeightsPerVert; - - // NOTE: This code assumes the vertex buffer is in bind pose - // resetToBind() has been called this frame - VertexBuffer vb = mesh.getBuffer(Type.Position); - FloatBuffer fvb = (FloatBuffer) vb.getData(); - fvb.rewind(); - - VertexBuffer nb = mesh.getBuffer(Type.Normal); - FloatBuffer fnb = (FloatBuffer) nb.getData(); - fnb.rewind(); - - // get boneIndexes and weights for mesh - IndexBuffer ib = IndexBuffer.wrapIndexBuffer(mesh.getBuffer(Type.BoneIndex).getData()); - FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData(); - - wb.rewind(); - - float[] weights = wb.array(); - int idxWeights = 0; - - TempVars vars = TempVars.get(); - - float[] posBuf = vars.skinPositions; - float[] normBuf = vars.skinNormals; - - int iterations = (int) FastMath.ceil(fvb.limit() / ((float) posBuf.length)); - int bufLength = posBuf.length; - for (int i = iterations - 1; i >= 0; i--) { - // read next set of positions and normals from native buffer - bufLength = Math.min(posBuf.length, fvb.remaining()); - fvb.get(posBuf, 0, bufLength); - fnb.get(normBuf, 0, bufLength); - int verts = bufLength / 3; - int idxPositions = 0; - - // iterate vertices and apply skinning transform for each effecting bone - for (int vert = verts - 1; vert >= 0; vert--) { - // Skip this vertex if the first weight is zero. - if (weights[idxWeights] == 0) { - idxPositions += 3; - idxWeights += 4; - continue; - } - float nmx = normBuf[idxPositions]; - float vtx = posBuf[idxPositions++]; - float nmy = normBuf[idxPositions]; - float vty = posBuf[idxPositions++]; - float nmz = normBuf[idxPositions]; - float vtz = posBuf[idxPositions++]; + try (AttributeModifier positionBuffer = mesh.modify(Type.Position); + AttributeModifier normalBuffer = mesh.modify(Type.Normal); + AttributeModifier boneIndexBuffer = mesh.modify(Type.BoneIndex); + AttributeModifier boneWeightBuffer = mesh.modify(Type.BoneWeight)) { + for (int v = 0; v < mesh.getVertexCount(); v++) { + if (boneWeightBuffer.getFloat(v, 0) == 0f) { + continue; // skip if first weight is zero + } + float vtx = positionBuffer.getFloat(v, 0); + float vty = positionBuffer.getFloat(v, 1); + float vtz = positionBuffer.getFloat(v, 2); + float nmx = normalBuffer.getFloat(v, 0); + float nmy = normalBuffer.getFloat(v, 1); + float nmz = normalBuffer.getFloat(v, 2); float rx = 0, ry = 0, rz = 0, rnx = 0, rny = 0, rnz = 0; - - for (int w = maxWeightsPerVert - 1; w >= 0; w--) { - float weight = weights[idxWeights]; - Matrix4f mat = offsetMatrices[ib.get(idxWeights++)]; - + for (int w = 0; w < maxWeightsPerVert; w++) { + float weight = boneWeightBuffer.getFloat(v, w); + Matrix4f mat = offsetMatrices[boneIndexBuffer.getInt(v, w)]; + // transform position rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight; ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight; rz += (mat.m20 * vtx + mat.m21 * vty + mat.m22 * vtz + mat.m23) * weight; - + // transform normal rnx += (nmx * mat.m00 + nmy * mat.m01 + nmz * mat.m02) * weight; rny += (nmx * mat.m10 + nmy * mat.m11 + nmz * mat.m12) * weight; rnz += (nmx * mat.m20 + nmy * mat.m21 + nmz * mat.m22) * weight; } - - idxWeights += fourMinusMaxWeights; - - idxPositions -= 3; - normBuf[idxPositions] = rnx; - posBuf[idxPositions++] = rx; - normBuf[idxPositions] = rny; - posBuf[idxPositions++] = ry; - normBuf[idxPositions] = rnz; - posBuf[idxPositions++] = rz; + positionBuffer.putVector3(v, 0, rx, ry, rz); + normalBuffer.putVector3(v, 0, rnx, rny, rnz); } - fvb.position(fvb.position() - bufLength); - fvb.put(posBuf, 0, bufLength); - fnb.position(fnb.position() - bufLength); - fnb.put(normBuf, 0, bufLength); - } - - vars.release(); + positionBuffer.setUpdateNeeded(); + normalBuffer.setUpdateNeeded(); - vb.updateData(fvb); - nb.updateData(fnb); + } } @@ -567,148 +506,61 @@ private void applySkinning(Mesh mesh, Matrix4f[] offsetMatrices) { * * @param mesh the mesh * @param offsetMatrices the offsetMatrices to apply - * @param tb the tangent vertexBuffer */ - private void applySkinningTangents(Mesh mesh, Matrix4f[] offsetMatrices, VertexBuffer tb) { - int maxWeightsPerVert = mesh.getMaxNumWeights(); + private void applySkinningTangents(Mesh mesh, Matrix4f[] offsetMatrices) { + int maxWeightsPerVert = mesh.getMaxNumWeights(); if (maxWeightsPerVert <= 0) { throw new IllegalStateException("Max weights per vert is incorrectly set!"); } - int fourMinusMaxWeights = 4 - maxWeightsPerVert; - - // NOTE: This code assumes the vertex buffer is in bind pose - // resetToBind() has been called this frame - VertexBuffer vb = mesh.getBuffer(Type.Position); - FloatBuffer fvb = (FloatBuffer) vb.getData(); - fvb.rewind(); - - VertexBuffer nb = mesh.getBuffer(Type.Normal); - - FloatBuffer fnb = (nb == null) ? null : (FloatBuffer) nb.getData(); - if (fnb != null) { - fnb.rewind(); - } - - FloatBuffer ftb = (FloatBuffer) tb.getData(); - ftb.rewind(); - - - // get boneIndexes and weights for mesh - IndexBuffer ib = IndexBuffer.wrapIndexBuffer(mesh.getBuffer(Type.BoneIndex).getData()); - FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData(); - - wb.rewind(); - - float[] weights = wb.array(); - int idxWeights = 0; + try (AttributeModifier positionBuffer = mesh.modify(Type.Position); + AttributeModifier normalBuffer = mesh.modify(Type.Normal); + AttributeModifier tangentBuffer = mesh.modify(Type.Tangent); + AttributeModifier boneIndexBuffer = mesh.modify(Type.BoneIndex); + AttributeModifier boneWeightBuffer = mesh.modify(Type.BoneWeight)) { - TempVars vars = TempVars.get(); - - - float[] posBuf = vars.skinPositions; - float[] normBuf = vars.skinNormals; - float[] tanBuf = vars.skinTangents; - - int iterations = (int) FastMath.ceil(fvb.limit() / ((float) posBuf.length)); - int bufLength = 0; - int tanLength = 0; - for (int i = iterations - 1; i >= 0; i--) { - // read next set of positions and normals from native buffer - bufLength = Math.min(posBuf.length, fvb.remaining()); - tanLength = Math.min(tanBuf.length, ftb.remaining()); - fvb.get(posBuf, 0, bufLength); - if (fnb != null) { - fnb.get(normBuf, 0, bufLength); - } - ftb.get(tanBuf, 0, tanLength); - int verts = bufLength / 3; - int idxPositions = 0; - // Tangents have their own index because they have 4 components. - int idxTangents = 0; - - // iterate vertices and apply skinning transform for each effecting bone - for (int vert = verts - 1; vert >= 0; vert--) { - // Skip this vertex if the first weight is zero. - if (weights[idxWeights] == 0) { - idxTangents += 4; - idxPositions += 3; - idxWeights += 4; - continue; + for (int v = 0; v < mesh.getVertexCount(); v++) { + if (boneWeightBuffer.getFloat(v, 0) == 0f) { + continue; // skip if first weight is zero } - - float nmx = normBuf[idxPositions]; - float vtx = posBuf[idxPositions++]; - float nmy = normBuf[idxPositions]; - float vty = posBuf[idxPositions++]; - float nmz = normBuf[idxPositions]; - float vtz = posBuf[idxPositions++]; - - float tnx = tanBuf[idxTangents++]; - float tny = tanBuf[idxTangents++]; - float tnz = tanBuf[idxTangents++]; - - // skipping the 4th component of the tangent since it doesn't have to be transformed - idxTangents++; - + float vtx = positionBuffer.getFloat(v, 0); + float vty = positionBuffer.getFloat(v, 1); + float vtz = positionBuffer.getFloat(v, 2); + float nmx = normalBuffer.getFloat(v, 0); + float nmy = normalBuffer.getFloat(v, 1); + float nmz = normalBuffer.getFloat(v, 2); + float tnx = tangentBuffer.getFloat(v, 0); + float tny = tangentBuffer.getFloat(v, 1); + float tnz = tangentBuffer.getFloat(v, 2); float rx = 0, ry = 0, rz = 0, rnx = 0, rny = 0, rnz = 0, rtx = 0, rty = 0, rtz = 0; - - for (int w = maxWeightsPerVert - 1; w >= 0; w--) { - float weight = weights[idxWeights]; - Matrix4f mat = offsetMatrices[ib.get(idxWeights++)]; - + for (int w = 0; w < maxWeightsPerVert; w++) { + float weight = boneWeightBuffer.getFloat(v, w); + Matrix4f mat = offsetMatrices[boneIndexBuffer.getInt(v, w)]; + // transform position rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight; ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight; rz += (mat.m20 * vtx + mat.m21 * vty + mat.m22 * vtz + mat.m23) * weight; - + // transform normal rnx += (nmx * mat.m00 + nmy * mat.m01 + nmz * mat.m02) * weight; rny += (nmx * mat.m10 + nmy * mat.m11 + nmz * mat.m12) * weight; rnz += (nmx * mat.m20 + nmy * mat.m21 + nmz * mat.m22) * weight; - + // transform tangent (xyz only) rtx += (tnx * mat.m00 + tny * mat.m01 + tnz * mat.m02) * weight; rty += (tnx * mat.m10 + tny * mat.m11 + tnz * mat.m12) * weight; rtz += (tnx * mat.m20 + tny * mat.m21 + tnz * mat.m22) * weight; } - - idxWeights += fourMinusMaxWeights; - - idxPositions -= 3; - - normBuf[idxPositions] = rnx; - posBuf[idxPositions++] = rx; - normBuf[idxPositions] = rny; - posBuf[idxPositions++] = ry; - normBuf[idxPositions] = rnz; - posBuf[idxPositions++] = rz; - - idxTangents -= 4; - - tanBuf[idxTangents++] = rtx; - tanBuf[idxTangents++] = rty; - tanBuf[idxTangents++] = rtz; - - //once again skipping the 4th component of the tangent - idxTangents++; + positionBuffer.putVector3(v, 0, rx, ry, rz); + normalBuffer.putVector3(v, 0, rnx, rny, rnz); + tangentBuffer.putVector3(v, 0, rtx, rty, rtz); } - fvb.position(fvb.position() - bufLength); - fvb.put(posBuf, 0, bufLength); - if (fnb != null) { - fnb.position(fnb.position() - bufLength); - fnb.put(normBuf, 0, bufLength); - } - ftb.position(ftb.position() - tanLength); - ftb.put(tanBuf, 0, tanLength); - } + positionBuffer.setUpdateNeeded(); + normalBuffer.setUpdateNeeded(); + tangentBuffer.setUpdateNeeded(); - vars.release(); - - vb.updateData(fvb); - if (nb != null) { - nb.updateData(fnb); } - tb.updateData(ftb); + } /** diff --git a/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java b/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java index 39b66d5d36..09f0c7774f 100644 --- a/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java +++ b/jme3-core/src/main/java/com/jme3/anim/TransformTrack.java @@ -33,8 +33,8 @@ import com.jme3.anim.interpolator.FrameInterpolator; import com.jme3.anim.util.HasLocalTransform; -import com.jme3.animation.CompactQuaternionArray; -import com.jme3.animation.CompactVector3Array; +import com.jme3.anim.util.CompactQuaternionArray; +import com.jme3.anim.util.CompactVector3Array; import com.jme3.export.*; import com.jme3.math.*; import com.jme3.util.clone.Cloner; diff --git a/jme3-core/src/main/java/com/jme3/anim/interpolator/FrameInterpolator.java b/jme3-core/src/main/java/com/jme3/anim/interpolator/FrameInterpolator.java index 13c7d2f183..edded66809 100644 --- a/jme3-core/src/main/java/com/jme3/anim/interpolator/FrameInterpolator.java +++ b/jme3-core/src/main/java/com/jme3/anim/interpolator/FrameInterpolator.java @@ -31,7 +31,9 @@ */ package com.jme3.anim.interpolator; -import com.jme3.animation.*; +import com.jme3.anim.util.CompactArray; +import com.jme3.anim.util.CompactQuaternionArray; +import com.jme3.anim.util.CompactVector3Array; import com.jme3.math.*; /** @@ -75,7 +77,7 @@ public static FrameInterpolator getThreadDefault() { } public Transform interpolate(float t, int currentIndex, CompactVector3Array translations, - CompactQuaternionArray rotations, CompactVector3Array scales, float[] times) { + CompactQuaternionArray rotations, CompactVector3Array scales, float[] times) { timesReader.setData(times); if (timeInterpolator != null) { t = timeInterpolator.interpolate(t, currentIndex, null, timesReader, null); diff --git a/jme3-core/src/main/java/com/jme3/anim/util/AnimMigrationUtils.java b/jme3-core/src/main/java/com/jme3/anim/util/AnimMigrationUtils.java deleted file mode 100644 index 6c463f329d..0000000000 --- a/jme3-core/src/main/java/com/jme3/anim/util/AnimMigrationUtils.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (c) 2009-2024 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.anim.util; - -import com.jme3.anim.*; -import com.jme3.animation.*; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector3f; -import com.jme3.scene.*; - -import java.util.*; - -public class AnimMigrationUtils { - - private static final AnimControlVisitor animControlVisitor = new AnimControlVisitor(); - private static final SkeletonControlVisitor skeletonControlVisitor = new SkeletonControlVisitor(); - - /** - * A private constructor to inhibit instantiation of this class. - */ - private AnimMigrationUtils() { - } - - public static Spatial migrate(Spatial source) { - Map skeletonArmatureMap = new HashMap<>(); - animControlVisitor.setMappings(skeletonArmatureMap); - source.depthFirstTraversal(animControlVisitor); - skeletonControlVisitor.setMappings(skeletonArmatureMap); - source.depthFirstTraversal(skeletonControlVisitor); - return source; - } - - private static class AnimControlVisitor implements SceneGraphVisitor { - - Map skeletonArmatureMap; - - @Override - public void visit(Spatial spatial) { - AnimControl control = spatial.getControl(AnimControl.class); - if (control != null) { - AnimComposer composer = new AnimComposer(); - Skeleton skeleton = control.getSkeleton(); - if (skeleton == null) { - //only bone anim for now - return; - } - - Joint[] joints = new Joint[skeleton.getBoneCount()]; - for (int i = 0; i < skeleton.getBoneCount(); i++) { - Bone b = skeleton.getBone(i); - Joint j = joints[i]; - if (j == null) { - j = fromBone(b); - joints[i] = j; - } - for (Bone bone : b.getChildren()) { - int index = skeleton.getBoneIndex(bone); - Joint joint = joints[index]; - if (joint == null) { - joint = fromBone(bone); - } - j.addChild(joint); - joints[index] = joint; - } - } - - Armature armature = new Armature(joints); - armature.saveBindPose(); - armature.saveInitialPose(); - skeletonArmatureMap.put(skeleton, armature); - - List tracks = new ArrayList<>(); - - for (String animName : control.getAnimationNames()) { - tracks.clear(); - Animation anim = control.getAnim(animName); - AnimClip clip = new AnimClip(animName); - Joint[] staticJoints = new Joint[joints.length]; - - System.arraycopy(joints, 0, staticJoints, 0, joints.length); - for (Track track : anim.getTracks()) { - if (track instanceof BoneTrack) { - BoneTrack boneTrack = (BoneTrack) track; - int index = boneTrack.getTargetBoneIndex(); - Bone bone = skeleton.getBone(index); - Joint joint = joints[index]; - TransformTrack jointTrack = fromBoneTrack(boneTrack, bone, joint); - tracks.add(jointTrack); - //this joint is animated let's remove it from the static joints - staticJoints[index] = null; - } - //TODO spatial tracks , Effect tracks, Audio tracks - } - - for (int i = 0; i < staticJoints.length; i++) { - padJointTracks(tracks, staticJoints[i]); - } - - clip.setTracks(tracks.toArray(new TransformTrack[tracks.size()])); - - composer.addAnimClip(clip); - } - spatial.removeControl(control); - spatial.addControl(composer); - } - } - - public void setMappings(Map skeletonArmatureMap) { - this.skeletonArmatureMap = skeletonArmatureMap; - } - } - - public static void padJointTracks(List tracks, Joint staticJoint) { - Joint j = staticJoint; - if (j != null) { - // joint has no track , we create one with the default pose - float[] times = new float[]{0}; - Vector3f[] translations = new Vector3f[]{j.getLocalTranslation()}; - Quaternion[] rotations = new Quaternion[]{j.getLocalRotation()}; - Vector3f[] scales = new Vector3f[]{j.getLocalScale()}; - TransformTrack track = new TransformTrack(j, times, translations, rotations, scales); - tracks.add(track); - } - } - - private static class SkeletonControlVisitor implements SceneGraphVisitor { - - Map skeletonArmatureMap; - - @Override - public void visit(Spatial spatial) { - SkeletonControl control = spatial.getControl(SkeletonControl.class); - if (control != null) { - Armature armature = skeletonArmatureMap.get(control.getSkeleton()); - SkinningControl skinningControl = new SkinningControl(armature); - Map> attachedSpatials = new HashMap<>(); - for (int i = 0; i < control.getSkeleton().getBoneCount(); i++) { - Bone b = control.getSkeleton().getBone(i); - Node n = control.getAttachmentsNode(b.getName()); - n.removeFromParent(); - if (!n.getChildren().isEmpty()) { - attachedSpatials.put(b.getName(), n.getChildren()); - } - } - spatial.removeControl(control); - spatial.addControl(skinningControl); - for (String name : attachedSpatials.keySet()) { - List spatials = attachedSpatials.get(name); - for (Spatial child : spatials) { - skinningControl.getAttachmentsNode(name).attachChild(child); - } - } - - } - } - - public void setMappings(Map skeletonArmatureMap) { - this.skeletonArmatureMap = skeletonArmatureMap; - } - } - - public static TransformTrack fromBoneTrack(BoneTrack boneTrack, Bone bone, Joint joint) { - float[] times = new float[boneTrack.getTimes().length]; - int length = times.length; - System.arraycopy(boneTrack.getTimes(), 0, times, 0, length); - //translation - Vector3f[] translations = new Vector3f[length]; - if (boneTrack.getTranslations() != null) { - for (int i = 0; i < boneTrack.getTranslations().length; i++) { - Vector3f oldTrans = boneTrack.getTranslations()[i]; - Vector3f newTrans = new Vector3f(); - newTrans.set(bone.getBindPosition()).addLocal(oldTrans); - translations[i] = newTrans; - } - } - //rotation - Quaternion[] rotations = new Quaternion[length]; - if (boneTrack.getRotations() != null) { - for (int i = 0; i < boneTrack.getRotations().length; i++) { - Quaternion oldRot = boneTrack.getRotations()[i]; - Quaternion newRot = new Quaternion(); - newRot.set(bone.getBindRotation()).multLocal(oldRot); - rotations[i] = newRot; - } - } - //scale - Vector3f[] scales = new Vector3f[length]; - if (boneTrack.getScales() != null) { - for (int i = 0; i < boneTrack.getScales().length; i++) { - Vector3f oldScale = boneTrack.getScales()[i]; - Vector3f newScale = new Vector3f(); - newScale.set(bone.getBindScale()).multLocal(oldScale); - scales[i] = newScale; - } - } - TransformTrack t = new TransformTrack(joint, times, translations, rotations, scales); - return t; - } - - private static Joint fromBone(Bone b) { - Joint j = new Joint(b.getName()); - j.setLocalTranslation(b.getBindPosition()); - j.setLocalRotation(b.getBindRotation()); - j.setLocalScale(b.getBindScale()); - return j; - } - -} diff --git a/jme3-core/src/main/java/com/jme3/animation/AnimationUtils.java b/jme3-core/src/main/java/com/jme3/anim/util/AnimationUtils.java similarity index 96% rename from jme3-core/src/main/java/com/jme3/animation/AnimationUtils.java rename to jme3-core/src/main/java/com/jme3/anim/util/AnimationUtils.java index 3e311a288a..74f7bcb480 100644 --- a/jme3-core/src/main/java/com/jme3/animation/AnimationUtils.java +++ b/jme3-core/src/main/java/com/jme3/anim/util/AnimationUtils.java @@ -1,65 +1,65 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -/** - * - * @author Nehon - */ -public class AnimationUtils { - private AnimationUtils() { - } - - /** - * Clamps the time according to duration and loopMode - * - * @param time the unclamped time value (in seconds) - * @param duration the animation's duration (in seconds) - * @param loopMode the animation's looping behavior (not null) - * @return the clamped time (in seconds) - */ - public static float clampWrapTime(float time, float duration, LoopMode loopMode) { - if (time == 0 || duration == 0) { - return 0; // prevent division by 0 errors - } - switch (loopMode) { - case Cycle: - boolean sign = ((int) (time / duration) % 2) != 0; - return sign ? -(duration - (time % duration)) : time % duration; - case DontLoop: - return time > duration ? duration : (time < 0 ? 0 : time); - case Loop: - return time % duration; - } - return time; - } -} +/* + * Copyright (c) 2009-2021 jMonkeyEngine + * 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 'jMonkeyEngine' 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 com.jme3.anim.util; + +/** + * + * @author Nehon + */ +public class AnimationUtils { + private AnimationUtils() { + } + + /** + * Clamps the time according to duration and loopMode + * + * @param time the unclamped time value (in seconds) + * @param duration the animation's duration (in seconds) + * @param loopMode the animation's looping behavior (not null) + * @return the clamped time (in seconds) + */ + public static float clampWrapTime(float time, float duration, LoopMode loopMode) { + if (time == 0 || duration == 0) { + return 0; // prevent division by 0 errors + } + switch (loopMode) { + case Cycle: + boolean sign = ((int) (time / duration) % 2) != 0; + return sign ? -(duration - (time % duration)) : time % duration; + case DontLoop: + return time > duration ? duration : (time < 0 ? 0 : time); + case Loop: + return time % duration; + } + return time; + } +} diff --git a/jme3-core/src/main/java/com/jme3/animation/CompactArray.java b/jme3-core/src/main/java/com/jme3/anim/util/CompactArray.java similarity index 96% rename from jme3-core/src/main/java/com/jme3/animation/CompactArray.java rename to jme3-core/src/main/java/com/jme3/anim/util/CompactArray.java index 31bb6653ce..8208da5f6c 100644 --- a/jme3-core/src/main/java/com/jme3/animation/CompactArray.java +++ b/jme3-core/src/main/java/com/jme3/anim/util/CompactArray.java @@ -1,328 +1,328 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.util.clone.Cloner; -import com.jme3.util.clone.JmeCloneable; -import java.lang.reflect.Array; -import java.util.HashMap; -import java.util.Map; - -/** - * Object is indexed and stored in primitive float[] - * @author Lim, YongHoon - * - * @param the type of object (i.e. Vector3f) - */ -public abstract class CompactArray implements JmeCloneable { - - protected Map indexPool = new HashMap<>(); - protected int[] index; - protected float[] array; - private boolean invalid; - - /** - * Creates a compact array - */ - public CompactArray() { - } - - /** - * create array using serialized data - * - * @param compressedArray storage for float data - * @param index storage for indices - */ - public CompactArray(float[] compressedArray, int[] index) { - this.array = compressedArray; - this.index = index; - } - - /** - * Add objects. - * They are serialized automatically when get() method is called. - * - * @param objArray the objects to be added (may be null) - */ - @SuppressWarnings("unchecked") - public void add(T... objArray) { - if (objArray == null || objArray.length == 0) { - return; - } - invalid = true; - int base = 0; - if (index == null) { - index = new int[objArray.length]; - } else { - if (indexPool.isEmpty()) { - throw new RuntimeException("Internal is already fixed"); - } - base = index.length; - - int[] tmp = new int[base + objArray.length]; - System.arraycopy(index, 0, tmp, 0, index.length); - index = tmp; - //index = Arrays.copyOf(index, base+objArray.length); - } - for (int j = 0; j < objArray.length; j++) { - T obj = objArray[j]; - if (obj == null) { - index[base + j] = -1; - } else { - Integer i = indexPool.get(obj); - if (i == null) { - i = indexPool.size(); - indexPool.put(obj, i); - } - index[base + j] = i; - } - } - } - - /** - * release objects. - * add() method call is not allowed anymore. - */ - public void freeze() { - serialize(); - indexPool.clear(); - } - - protected void setInvalid(boolean invalid) { - this.invalid = invalid; - } - - /** - * @param index zero-origin index of the element to be altered - * @param value the desired value - */ - public final void set(int index, T value) { - int j = getCompactIndex(index); - serialize(j, value); - } - - /** - * returns the object for the given index - * @param index the index - * @param store an object to store the result - * @return an element - */ - public final T get(int index, T store) { - serialize(); - int j = getCompactIndex(index); - return deserialize(j, store); - } - - /** - * return a float array of serialized data - * @return the pre-existing array - */ - public final float[] getSerializedData() { - serialize(); - return array; - } - - /** - * serialize this compact array - */ - public final void serialize() { - if (invalid) { - int newSize = indexPool.size() * getTupleSize(); - if (array == null || Array.getLength(array) < newSize) { - array = ensureCapacity(array, newSize); - for (Map.Entry entry : indexPool.entrySet()) { - int i = entry.getValue(); - T obj = entry.getKey(); - serialize(i, obj); - } - } - invalid = false; - } - } - - /** - * @return compacted array's primitive size - */ - protected final int getSerializedSize() { - return Array.getLength(getSerializedData()); - } - - /** - * Ensure the capacity for the given array and the given size - * @param arr the array - * @param size the size - * @return an array - */ - protected float[] ensureCapacity(float[] arr, int size) { - if (arr == null) { - return new float[size]; - } else if (arr.length >= size) { - return arr; - } else { - float[] tmp = new float[size]; - System.arraycopy(arr, 0, tmp, 0, arr.length); - return tmp; - //return Arrays.copyOf(arr, size); - } - } - - /** - * Return an array of indices for the given objects - * - * @param objArray the input objects - * @return a new array - */ - @SuppressWarnings("unchecked") - public final int[] getIndex(T... objArray) { - int[] index = new int[objArray.length]; - for (int i = 0; i < index.length; i++) { - T obj = objArray[i]; - index[i] = obj != null ? indexPool.get(obj) : -1; - } - return index; - } - - /** - * returns the corresponding index in the compact array - * - * @param objIndex the input index - * @return object index in the compacted object array - */ - public int getCompactIndex(int objIndex) { - return index != null ? index[objIndex] : objIndex; - } - - /** - * @return uncompressed object size - */ - public final int getTotalObjectSize() { - assert getSerializedSize() % getTupleSize() == 0; - return index != null ? index.length : getSerializedSize() / getTupleSize(); - } - - /** - * @return compressed object size - */ - public final int getCompactObjectSize() { - assert getSerializedSize() % getTupleSize() == 0; - return getSerializedSize() / getTupleSize(); - } - - /** - * decompress and return object array - * @return decompress and return object array - */ - @SuppressWarnings("unchecked") - public final T[] toObjectArray() { - try { - T[] compactArr = (T[]) Array.newInstance(getElementClass(), getSerializedSize() / getTupleSize()); - for (int i = 0; i < compactArr.length; i++) { - compactArr[i] = getElementClass().getDeclaredConstructor().newInstance(); - deserialize(i, compactArr[i]); - } - - T[] objArr = (T[]) Array.newInstance(getElementClass(), getTotalObjectSize()); - for (int i = 0; i < objArr.length; i++) { - int compactIndex = getCompactIndex(i); - objArr[i] = compactArr[compactIndex]; - } - return objArr; - } catch (Exception e) { - return null; - } - } - - /** - * Create a deep clone of this array. - * - * @return a new array - * @throws CloneNotSupportedException never - */ - @Override - public CompactArray clone() throws CloneNotSupportedException { - return Cloner.deepClone(this); - } - - /** - * Create a shallow clone for the JME cloner. - * - * @return a new array - */ - @Override - public Object jmeClone() { - try { - return super.clone(); - } catch (CloneNotSupportedException exception) { - throw new RuntimeException("Can't clone array", exception); - } - } - - /** - * Callback from {@link com.jme3.util.clone.Cloner} to convert this - * shallow-cloned array into a deep-cloned one, using the specified cloner - * to resolve copied fields. - * - * @param cloner the cloner currently cloning this control (not null) - * @param original the array from which this array was shallow-cloned - * (unused) - */ - @Override - public void cloneFields(Cloner cloner, Object original) { - indexPool = cloner.clone(indexPool); - index = cloner.clone(index); - array = cloner.clone(array); - } - - /** - * serialize object - * @param compactIndex compacted object index - * @param store the value to be serialized (not null, unaffected) - */ - protected abstract void serialize(int compactIndex, T store); - - /** - * deserialize object - * @param compactIndex compacted object index - * @param store storage for the result - * @return the deserialized value - */ - protected abstract T deserialize(int compactIndex, T store); - - /** - * serialized size of one object element - * - * @return the number of primitive components (floats) per object - */ - protected abstract int getTupleSize(); - - protected abstract Class getElementClass(); -} +/* + * Copyright (c) 2009-2021 jMonkeyEngine + * 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 'jMonkeyEngine' 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 com.jme3.anim.util; + +import com.jme3.util.clone.Cloner; +import com.jme3.util.clone.JmeCloneable; +import java.lang.reflect.Array; +import java.util.HashMap; +import java.util.Map; + +/** + * Object is indexed and stored in primitive float[] + * @author Lim, YongHoon + * + * @param the type of object (i.e. Vector3f) + */ +public abstract class CompactArray implements JmeCloneable { + + protected Map indexPool = new HashMap<>(); + protected int[] index; + protected float[] array; + private boolean invalid; + + /** + * Creates a compact array + */ + public CompactArray() { + } + + /** + * create array using serialized data + * + * @param compressedArray storage for float data + * @param index storage for indices + */ + public CompactArray(float[] compressedArray, int[] index) { + this.array = compressedArray; + this.index = index; + } + + /** + * Add objects. + * They are serialized automatically when get() method is called. + * + * @param objArray the objects to be added (may be null) + */ + @SuppressWarnings("unchecked") + public void add(T... objArray) { + if (objArray == null || objArray.length == 0) { + return; + } + invalid = true; + int base = 0; + if (index == null) { + index = new int[objArray.length]; + } else { + if (indexPool.isEmpty()) { + throw new RuntimeException("Internal is already fixed"); + } + base = index.length; + + int[] tmp = new int[base + objArray.length]; + System.arraycopy(index, 0, tmp, 0, index.length); + index = tmp; + //index = Arrays.copyOf(index, base+objArray.length); + } + for (int j = 0; j < objArray.length; j++) { + T obj = objArray[j]; + if (obj == null) { + index[base + j] = -1; + } else { + Integer i = indexPool.get(obj); + if (i == null) { + i = indexPool.size(); + indexPool.put(obj, i); + } + index[base + j] = i; + } + } + } + + /** + * release objects. + * add() method call is not allowed anymore. + */ + public void freeze() { + serialize(); + indexPool.clear(); + } + + protected void setInvalid(boolean invalid) { + this.invalid = invalid; + } + + /** + * @param index zero-origin index of the element to be altered + * @param value the desired value + */ + public final void set(int index, T value) { + int j = getCompactIndex(index); + serialize(j, value); + } + + /** + * returns the object for the given index + * @param index the index + * @param store an object to store the result + * @return an element + */ + public final T get(int index, T store) { + serialize(); + int j = getCompactIndex(index); + return deserialize(j, store); + } + + /** + * return a float array of serialized data + * @return the pre-existing array + */ + public final float[] getSerializedData() { + serialize(); + return array; + } + + /** + * serialize this compact array + */ + public final void serialize() { + if (invalid) { + int newSize = indexPool.size() * getTupleSize(); + if (array == null || Array.getLength(array) < newSize) { + array = ensureCapacity(array, newSize); + for (Map.Entry entry : indexPool.entrySet()) { + int i = entry.getValue(); + T obj = entry.getKey(); + serialize(i, obj); + } + } + invalid = false; + } + } + + /** + * @return compacted array's primitive size + */ + protected final int getSerializedSize() { + return Array.getLength(getSerializedData()); + } + + /** + * Ensure the capacity for the given array and the given size + * @param arr the array + * @param size the size + * @return an array + */ + protected float[] ensureCapacity(float[] arr, int size) { + if (arr == null) { + return new float[size]; + } else if (arr.length >= size) { + return arr; + } else { + float[] tmp = new float[size]; + System.arraycopy(arr, 0, tmp, 0, arr.length); + return tmp; + //return Arrays.copyOf(arr, size); + } + } + + /** + * Return an array of indices for the given objects + * + * @param objArray the input objects + * @return a new array + */ + @SuppressWarnings("unchecked") + public final int[] getIndex(T... objArray) { + int[] index = new int[objArray.length]; + for (int i = 0; i < index.length; i++) { + T obj = objArray[i]; + index[i] = obj != null ? indexPool.get(obj) : -1; + } + return index; + } + + /** + * returns the corresponding index in the compact array + * + * @param objIndex the input index + * @return object index in the compacted object array + */ + public int getCompactIndex(int objIndex) { + return index != null ? index[objIndex] : objIndex; + } + + /** + * @return uncompressed object size + */ + public final int getTotalObjectSize() { + assert getSerializedSize() % getTupleSize() == 0; + return index != null ? index.length : getSerializedSize() / getTupleSize(); + } + + /** + * @return compressed object size + */ + public final int getCompactObjectSize() { + assert getSerializedSize() % getTupleSize() == 0; + return getSerializedSize() / getTupleSize(); + } + + /** + * decompress and return object array + * @return decompress and return object array + */ + @SuppressWarnings("unchecked") + public final T[] toObjectArray() { + try { + T[] compactArr = (T[]) Array.newInstance(getElementClass(), getSerializedSize() / getTupleSize()); + for (int i = 0; i < compactArr.length; i++) { + compactArr[i] = getElementClass().getDeclaredConstructor().newInstance(); + deserialize(i, compactArr[i]); + } + + T[] objArr = (T[]) Array.newInstance(getElementClass(), getTotalObjectSize()); + for (int i = 0; i < objArr.length; i++) { + int compactIndex = getCompactIndex(i); + objArr[i] = compactArr[compactIndex]; + } + return objArr; + } catch (Exception e) { + return null; + } + } + + /** + * Create a deep clone of this array. + * + * @return a new array + * @throws CloneNotSupportedException never + */ + @Override + public CompactArray clone() throws CloneNotSupportedException { + return Cloner.deepClone(this); + } + + /** + * Create a shallow clone for the JME cloner. + * + * @return a new array + */ + @Override + public Object jmeClone() { + try { + return super.clone(); + } catch (CloneNotSupportedException exception) { + throw new RuntimeException("Can't clone array", exception); + } + } + + /** + * Callback from {@link com.jme3.util.clone.Cloner} to convert this + * shallow-cloned array into a deep-cloned one, using the specified cloner + * to resolve copied fields. + * + * @param cloner the cloner currently cloning this control (not null) + * @param original the array from which this array was shallow-cloned + * (unused) + */ + @Override + public void cloneFields(Cloner cloner, Object original) { + indexPool = cloner.clone(indexPool); + index = cloner.clone(index); + array = cloner.clone(array); + } + + /** + * serialize object + * @param compactIndex compacted object index + * @param store the value to be serialized (not null, unaffected) + */ + protected abstract void serialize(int compactIndex, T store); + + /** + * deserialize object + * @param compactIndex compacted object index + * @param store storage for the result + * @return the deserialized value + */ + protected abstract T deserialize(int compactIndex, T store); + + /** + * serialized size of one object element + * + * @return the number of primitive components (floats) per object + */ + protected abstract int getTupleSize(); + + protected abstract Class getElementClass(); +} diff --git a/jme3-core/src/main/java/com/jme3/animation/CompactFloatArray.java b/jme3-core/src/main/java/com/jme3/anim/util/CompactFloatArray.java similarity index 99% rename from jme3-core/src/main/java/com/jme3/animation/CompactFloatArray.java rename to jme3-core/src/main/java/com/jme3/anim/util/CompactFloatArray.java index 27d62c2b63..18564e2585 100644 --- a/jme3-core/src/main/java/com/jme3/animation/CompactFloatArray.java +++ b/jme3-core/src/main/java/com/jme3/anim/util/CompactFloatArray.java @@ -29,7 +29,7 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.jme3.animation; +package com.jme3.anim.util; import com.jme3.export.*; diff --git a/jme3-core/src/main/java/com/jme3/animation/CompactQuaternionArray.java b/jme3-core/src/main/java/com/jme3/anim/util/CompactQuaternionArray.java similarity index 96% rename from jme3-core/src/main/java/com/jme3/animation/CompactQuaternionArray.java rename to jme3-core/src/main/java/com/jme3/anim/util/CompactQuaternionArray.java index 21b1bd9ff4..f8d8755829 100644 --- a/jme3-core/src/main/java/com/jme3/animation/CompactQuaternionArray.java +++ b/jme3-core/src/main/java/com/jme3/anim/util/CompactQuaternionArray.java @@ -1,100 +1,100 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.export.*; -import com.jme3.math.Quaternion; -import java.io.IOException; - -/** - * Serialize and compress {@link Quaternion}[] by indexing same values - * It is converted to float[] - * @author Lim, YongHoon - */ -public class CompactQuaternionArray extends CompactArray implements Savable { - - /** - * creates a compact Quaternion array - */ - public CompactQuaternionArray() { - } - - /** - * creates a compact Quaternion array - * @param dataArray the data array - * @param index the indices array - */ - public CompactQuaternionArray(float[] dataArray, int[] index) { - super(dataArray, index); - } - - @Override - protected final int getTupleSize() { - return 4; - } - - @Override - protected final Class getElementClass() { - return Quaternion.class; - } - - @Override - public void write(JmeExporter ex) throws IOException { - serialize(); - OutputCapsule out = ex.getCapsule(this); - out.write(array, "array", null); - out.write(index, "index", null); - } - - @Override - public void read(JmeImporter im) throws IOException { - InputCapsule in = im.getCapsule(this); - array = in.readFloatArray("array", null); - index = in.readIntArray("index", null); - } - - @Override - protected void serialize(int i, Quaternion store) { - int j = i * getTupleSize(); - array[j] = store.getX(); - array[j + 1] = store.getY(); - array[j + 2] = store.getZ(); - array[j + 3] = store.getW(); - } - - @Override - protected Quaternion deserialize(int i, Quaternion store) { - int j = i * getTupleSize(); - store.set(array[j], array[j + 1], array[j + 2], array[j + 3]); - return store; - } -} +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * 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 'jMonkeyEngine' 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 com.jme3.anim.util; + +import com.jme3.export.*; +import com.jme3.math.Quaternion; +import java.io.IOException; + +/** + * Serialize and compress {@link Quaternion}[] by indexing same values + * It is converted to float[] + * @author Lim, YongHoon + */ +public class CompactQuaternionArray extends CompactArray implements Savable { + + /** + * creates a compact Quaternion array + */ + public CompactQuaternionArray() { + } + + /** + * creates a compact Quaternion array + * @param dataArray the data array + * @param index the indices array + */ + public CompactQuaternionArray(float[] dataArray, int[] index) { + super(dataArray, index); + } + + @Override + protected final int getTupleSize() { + return 4; + } + + @Override + protected final Class getElementClass() { + return Quaternion.class; + } + + @Override + public void write(JmeExporter ex) throws IOException { + serialize(); + OutputCapsule out = ex.getCapsule(this); + out.write(array, "array", null); + out.write(index, "index", null); + } + + @Override + public void read(JmeImporter im) throws IOException { + InputCapsule in = im.getCapsule(this); + array = in.readFloatArray("array", null); + index = in.readIntArray("index", null); + } + + @Override + protected void serialize(int i, Quaternion store) { + int j = i * getTupleSize(); + array[j] = store.getX(); + array[j + 1] = store.getY(); + array[j + 2] = store.getZ(); + array[j + 3] = store.getW(); + } + + @Override + protected Quaternion deserialize(int i, Quaternion store) { + int j = i * getTupleSize(); + store.set(array[j], array[j + 1], array[j + 2], array[j + 3]); + return store; + } +} diff --git a/jme3-core/src/main/java/com/jme3/animation/CompactVector3Array.java b/jme3-core/src/main/java/com/jme3/anim/util/CompactVector3Array.java similarity index 96% rename from jme3-core/src/main/java/com/jme3/animation/CompactVector3Array.java rename to jme3-core/src/main/java/com/jme3/anim/util/CompactVector3Array.java index 289f81ec81..ee1f8715d9 100644 --- a/jme3-core/src/main/java/com/jme3/animation/CompactVector3Array.java +++ b/jme3-core/src/main/java/com/jme3/anim/util/CompactVector3Array.java @@ -1,98 +1,98 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.export.*; -import com.jme3.math.Vector3f; -import java.io.IOException; - -/** - * Serialize and compress Vector3f[] by indexing same values - * @author Lim, YongHoon - */ -public class CompactVector3Array extends CompactArray implements Savable { - - /** - * Creates a compact vector array - */ - public CompactVector3Array() { - } - - /** - * creates a compact vector array - * @param dataArray the data array - * @param index the indices - */ - public CompactVector3Array(float[] dataArray, int[] index) { - super(dataArray, index); - } - - @Override - protected final int getTupleSize() { - return 3; - } - - @Override - protected final Class getElementClass() { - return Vector3f.class; - } - - @Override - public void write(JmeExporter ex) throws IOException { - serialize(); - OutputCapsule out = ex.getCapsule(this); - out.write(array, "array", null); - out.write(index, "index", null); - } - - @Override - public void read(JmeImporter im) throws IOException { - InputCapsule in = im.getCapsule(this); - array = in.readFloatArray("array", null); - index = in.readIntArray("index", null); - } - - @Override - protected void serialize(int i, Vector3f store) { - int j = i*getTupleSize(); - array[j] = store.getX(); - array[j+1] = store.getY(); - array[j+2] = store.getZ(); - } - - @Override - protected Vector3f deserialize(int i, Vector3f store) { - int j = i*getTupleSize(); - store.set(array[j], array[j+1], array[j+2]); - return store; - } +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * 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 'jMonkeyEngine' 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 com.jme3.anim.util; + +import com.jme3.export.*; +import com.jme3.math.Vector3f; +import java.io.IOException; + +/** + * Serialize and compress Vector3f[] by indexing same values + * @author Lim, YongHoon + */ +public class CompactVector3Array extends CompactArray implements Savable { + + /** + * Creates a compact vector array + */ + public CompactVector3Array() { + } + + /** + * creates a compact vector array + * @param dataArray the data array + * @param index the indices + */ + public CompactVector3Array(float[] dataArray, int[] index) { + super(dataArray, index); + } + + @Override + protected final int getTupleSize() { + return 3; + } + + @Override + protected final Class getElementClass() { + return Vector3f.class; + } + + @Override + public void write(JmeExporter ex) throws IOException { + serialize(); + OutputCapsule out = ex.getCapsule(this); + out.write(array, "array", null); + out.write(index, "index", null); + } + + @Override + public void read(JmeImporter im) throws IOException { + InputCapsule in = im.getCapsule(this); + array = in.readFloatArray("array", null); + index = in.readIntArray("index", null); + } + + @Override + protected void serialize(int i, Vector3f store) { + int j = i*getTupleSize(); + array[j] = store.getX(); + array[j+1] = store.getY(); + array[j+2] = store.getZ(); + } + + @Override + protected Vector3f deserialize(int i, Vector3f store) { + int j = i*getTupleSize(); + store.set(array[j], array[j+1], array[j+2]); + return store; + } } \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/animation/LoopMode.java b/jme3-core/src/main/java/com/jme3/anim/util/LoopMode.java similarity index 98% rename from jme3-core/src/main/java/com/jme3/animation/LoopMode.java rename to jme3-core/src/main/java/com/jme3/anim/util/LoopMode.java index 33a50b96bc..faa4c3b3bc 100644 --- a/jme3-core/src/main/java/com/jme3/animation/LoopMode.java +++ b/jme3-core/src/main/java/com/jme3/anim/util/LoopMode.java @@ -29,7 +29,7 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.jme3.animation; +package com.jme3.anim.util; /** * LoopMode determines how animations repeat, or if they diff --git a/jme3-core/src/main/java/com/jme3/animation/AnimChannel.java b/jme3-core/src/main/java/com/jme3/animation/AnimChannel.java deleted file mode 100644 index 9f5ef50d56..0000000000 --- a/jme3-core/src/main/java/com/jme3/animation/AnimChannel.java +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.math.FastMath; -import com.jme3.util.TempVars; - -import java.util.BitSet; - -/** - * AnimChannel provides controls, such as play, pause, - * fast-forward, etcetera, for an animation. The animation - * channel may influence the entire model or specific bones of the model's - * skeleton. A single model may have multiple animation channels influencing - * various parts of its body. For example, a character model may have an - * animation channel for its feet, and another for its torso, and - * the animations for each channel are controlled independently. - * - * @author Kirill Vainer - */ -@Deprecated -public final class AnimChannel { - private static final float DEFAULT_BLEND_TIME = 0.15f; - - private AnimControl control; - - private BitSet affectedBones; - - private Animation animation; - private Animation blendFrom; - private float time; - private float speed; - private float timeBlendFrom; - private float blendTime; - private float speedBlendFrom; - private boolean notified = false; - - private LoopMode loopMode, loopModeBlendFrom; - - private float blendAmount = 1f; - private float blendRate = 0; - - public AnimChannel() { - } - - public AnimChannel(AnimControl control) { - this.control = control; - } - - /** - * Returns the parent control of this AnimChannel. - * - * @return the parent control of this AnimChannel. - * @see AnimControl - */ - public AnimControl getControl() { - return control; - } - - /** - * @return The name of the currently playing animation, or null if - * none is assigned. - * - * @see AnimChannel#setAnim(java.lang.String) - */ - public String getAnimationName() { - return animation != null ? animation.getName() : null; - } - - /** - * @return The loop mode currently set for the animation. The loop mode - * determines what will happen to the animation once it finishes - * playing. - * - * For more information, see the LoopMode enum class. - * @see LoopMode - * @see AnimChannel#setLoopMode(com.jme3.animation.LoopMode) - */ - public LoopMode getLoopMode() { - return loopMode; - } - - /** - * @param loopMode Set the loop mode for the channel. The loop mode - * determines what will happen to the animation once it finishes - * playing. - * - * For more information, see the LoopMode enum class. - * @see LoopMode - */ - public void setLoopMode(LoopMode loopMode) { - this.loopMode = loopMode; - } - - /** - * @return The speed that is assigned to the animation channel. The speed - * is a scale value starting from 0.0, at 1.0 the animation will play - * at its default speed. - * - * @see AnimChannel#setSpeed(float) - */ - public float getSpeed() { - return speed; - } - - /** - * @param speed Set the speed of the animation channel. The speed - * is a scale value starting from 0.0, at 1.0 the animation will play - * at its default speed. - */ - public void setSpeed(float speed) { - this.speed = speed; - if (blendTime > 0) { - this.speedBlendFrom = speed; - blendTime = Math.min(blendTime, animation.getLength() / speed); - blendRate = 1 / blendTime; - } - } - - /** - * @return The time of the currently playing animation. The time - * starts at 0 and continues on until getAnimMaxTime(). - * - * @see AnimChannel#setTime(float) - */ - public float getTime() { - return time; - } - - /** - * @param time Set the time of the currently playing animation, the time - * is clamped from 0 to {@link #getAnimMaxTime()}. - */ - public void setTime(float time) { - this.time = FastMath.clamp(time, 0, getAnimMaxTime()); - } - - /** - * @return The length of the currently playing animation, or zero - * if no animation is playing. - * - * @see AnimChannel#getTime() - */ - public float getAnimMaxTime() { - return animation != null ? animation.getLength() : 0f; - } - - /** - * Set the current animation that is played by this AnimChannel. - *

- * This resets the time to zero, and optionally blends the animation - * over blendTime seconds with the currently playing animation. - * Notice that this method will reset the control's speed to 1.0. - * - * @param name The name of the animation to play - * @param blendTime The blend time over which to blend the new animation - * with the old one. If zero, then no blending will occur and the new - * animation will be applied instantly. - */ - public void setAnim(String name, float blendTime) { - if (name == null) - throw new IllegalArgumentException("name cannot be null"); - - if (blendTime < 0f) - throw new IllegalArgumentException("blendTime cannot be less than zero"); - - Animation anim = control.animationMap.get(name); - if (anim == null) - throw new IllegalArgumentException("Cannot find animation named: '" + name + "'"); - - control.notifyAnimChange(this, name); - - if (animation != null && blendTime > 0f) { - this.blendTime = blendTime; - // activate blending - blendTime = Math.min(blendTime, anim.getLength() / speed); - blendFrom = animation; - timeBlendFrom = time; - speedBlendFrom = speed; - loopModeBlendFrom = loopMode; - blendAmount = 0f; - blendRate = 1f / blendTime; - } else { - blendFrom = null; - } - - animation = anim; - time = 0; - speed = 1f; - loopMode = LoopMode.Loop; - notified = false; - } - - /** - * Set the current animation that is played by this AnimChannel. - *

- * See {@link #setAnim(java.lang.String, float)}. - * The blendTime argument by default is 150 milliseconds. - * - * @param name The name of the animation to play - */ - public void setAnim(String name) { - setAnim(name, DEFAULT_BLEND_TIME); - } - - /** - * Add all the bones of the model's skeleton to be - * influenced by this animation channel. - */ - public void addAllBones() { - affectedBones = null; - } - - /** - * Add a single bone to be influenced by this animation channel. - * - * @param name the name of the Bone to be influenced - */ - public void addBone(String name) { - addBone(control.getSkeleton().getBone(name)); - } - - /** - * Add a single bone to be influenced by this animation channel. - * - * @param bone the Bone to be influenced - */ - public void addBone(Bone bone) { - int boneIndex = control.getSkeleton().getBoneIndex(bone); - if (affectedBones == null) { - affectedBones = new BitSet(control.getSkeleton().getBoneCount()); - } - affectedBones.set(boneIndex); - } - - /** - * Add bones to be influenced by this animation channel starting from the - * given bone name and going toward the root bone. - * - * @param name the name of the Bone to use as a starting point - */ - public void addToRootBone(String name) { - addToRootBone(control.getSkeleton().getBone(name)); - } - - /** - * Add bones to be influenced by this animation channel starting from the - * given bone and going toward the root bone. - * - * @param bone the Bone to use as a starting point - */ - public void addToRootBone(Bone bone) { - addBone(bone); - while (bone.getParent() != null) { - bone = bone.getParent(); - addBone(bone); - } - } - - /** - * Add bones to be influenced by this animation channel, starting - * from the given named bone and going toward its children. - * - * @param name the name of the Bone to use as a starting point - */ - public void addFromRootBone(String name) { - addFromRootBone(control.getSkeleton().getBone(name)); - } - - /** - * Add bones to be influenced by this animation channel, starting - * from the given bone and going toward its children. - * - * @param bone the Bone to use as a starting point - */ - public void addFromRootBone(Bone bone) { - addBone(bone); - if (bone.getChildren() == null) - return; - for (Bone childBone : bone.getChildren()) { - addBone(childBone); - addFromRootBone(childBone); - } - } - - BitSet getAffectedBones() { - return affectedBones; - } - - public void reset(boolean rewind) { - if (rewind) { - setTime(0); - if (control.getSkeleton() != null) { - control.getSkeleton().resetAndUpdate(); - } else { - TempVars vars = TempVars.get(); - update(0, vars); - vars.release(); - } - } - animation = null; - notified = false; - } - - void update(float tpf, TempVars vars) { - if (animation == null) - return; - - if (blendFrom != null && blendAmount != 1.0f) { - // The blendFrom anim is set, the actual animation - // playing will be set -// blendFrom.setTime(timeBlendFrom, 1f, control, this, vars); - blendFrom.setTime(timeBlendFrom, 1f - blendAmount, control, this, vars); - - timeBlendFrom += tpf * speedBlendFrom; - timeBlendFrom = AnimationUtils.clampWrapTime(timeBlendFrom, - blendFrom.getLength(), - loopModeBlendFrom); - if (timeBlendFrom < 0) { - timeBlendFrom = -timeBlendFrom; - speedBlendFrom = -speedBlendFrom; - } - - blendAmount += tpf * blendRate; - if (blendAmount > 1f) { - blendAmount = 1f; - blendFrom = null; - } - } - - animation.setTime(time, blendAmount, control, this, vars); - time += tpf * speed; - if (animation.getLength() > 0) { - if (!notified && (time >= animation.getLength() || time < 0)) { - if (loopMode == LoopMode.DontLoop) { - // Note that this flag has to be set before calling the notify, - // since the notify may start a new animation and then unset - // the flag. - notified = true; - } - control.notifyAnimCycleDone(this, animation.getName()); - } - } - time = AnimationUtils.clampWrapTime(time, animation.getLength(), loopMode); - if (time < 0) { - // Negative time indicates that speed should be inverted - // (for cycle loop mode only) - time = -time; - speed = -speed; - } - } -} diff --git a/jme3-core/src/main/java/com/jme3/animation/AnimControl.java b/jme3-core/src/main/java/com/jme3/animation/AnimControl.java deleted file mode 100644 index a7f9f1a35a..0000000000 --- a/jme3-core/src/main/java/com/jme3/animation/AnimControl.java +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.export.*; -import com.jme3.renderer.RenderManager; -import com.jme3.renderer.ViewPort; -import com.jme3.scene.Spatial; -import com.jme3.scene.control.AbstractControl; -import com.jme3.util.TempVars; -import com.jme3.util.clone.Cloner; -import com.jme3.util.clone.JmeCloneable; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -/** - * AnimControl is a Spatial control that allows manipulation - * of skeletal animation. - * - * The control currently supports: - * 1) Animation blending/transitions - * 2) Multiple animation channels - * 3) Multiple skins - * 4) Animation event listeners - * 5) Animated model cloning - * 6) Animated model binary import/export - * 7) Hardware skinning - * 8) Attachments - * 9) Add/remove skins - * - * Planned: - * 1) Morph/Pose animation - * - * @author Kirill Vainer - * @deprecated use {@link com.jme3.anim.AnimComposer} - */ -@Deprecated -public final class AnimControl extends AbstractControl implements Cloneable, JmeCloneable { - - /** - * Skeleton object must contain corresponding data for the targets' weight buffers. - */ - Skeleton skeleton; - /** only used for backward compatibility */ - @Deprecated - private SkeletonControl skeletonControl; - /** - * List of animations - */ - HashMap animationMap = new HashMap<>(); - /** - * Animation channels - */ - private transient ArrayList channels = new ArrayList<>(); - /** - * Animation event listeners - */ - private transient ArrayList listeners = new ArrayList<>(); - - /** - * Creates a new animation control for the given skeleton. - * The method {@link AnimControl#setAnimations(java.util.HashMap) } - * must be called after initialization in order for this class to be useful. - * - * @param skeleton The skeleton to animate - */ - public AnimControl(Skeleton skeleton) { - this.skeleton = skeleton; - reset(); - } - - /** - * Instantiate an animation control with no skeleton, suitable only for - * animations that don't contain any bone tracks. Also used for - * serialization. - */ - public AnimControl() { - } - - @Override - public Object jmeClone() { - AnimControl clone = (AnimControl) super.jmeClone(); - clone.channels = new ArrayList(); - clone.listeners = new ArrayList(); - - return clone; - } - - @Override - public void cloneFields(Cloner cloner, Object original) { - super.cloneFields(cloner, original); - - this.skeleton = cloner.clone(skeleton); - - // Note cloneForSpatial() never actually cloned the animation map... just its reference - HashMap newMap = new HashMap<>(); - - // animationMap is cloned, but only ClonableTracks will be cloned as they need a reference to a cloned spatial - for (Map.Entry e : animationMap.entrySet()) { - newMap.put(e.getKey(), cloner.clone(e.getValue())); - } - - this.animationMap = newMap; - } - - /** - * @param animations Set the animations that this AnimControl - * will be capable of playing. The animations should be compatible - * with the skeleton given in the constructor. - */ - public void setAnimations(HashMap animations) { - animationMap = animations; - } - - /** - * Retrieve an animation from the list of animations. - * - * @param name The name of the animation to retrieve. - * @return The animation corresponding to the given name, or null, if no - * such named animation exists. - */ - public Animation getAnim(String name) { - return animationMap.get(name); - } - - /** - * Adds an animation to be available for playing to this - * AnimControl. - * - * @param anim The animation to add. - */ - public void addAnim(Animation anim) { - animationMap.put(anim.getName(), anim); - } - - /** - * Remove an animation so that it is no longer available for playing. - * - * @param anim The animation to remove. - */ - public void removeAnim(Animation anim) { - if (!animationMap.containsKey(anim.getName())) { - throw new IllegalArgumentException("Given animation does not exist " - + "in this AnimControl"); - } - - animationMap.remove(anim.getName()); - } - - /** - * Create a new animation channel, by default assigned to all bones - * in the skeleton. - * - * @return A new animation channel for this AnimControl. - */ - public AnimChannel createChannel() { - AnimChannel channel = new AnimChannel(this); - channels.add(channel); - return channel; - } - - /** - * Return the animation channel at the given index. - * @param index The index, starting at 0, to retrieve the AnimChannel. - * @return The animation channel at the given index, or throws an exception - * if the index is out of bounds. - * - * @throws IndexOutOfBoundsException If no channel exists at the given index. - */ - public AnimChannel getChannel(int index) { - return channels.get(index); - } - - /** - * @return The number of channels that are controlled by this - * AnimControl. - * - * @see AnimControl#createChannel() - */ - public int getNumChannels() { - return channels.size(); - } - - /** - * Clears all the channels that were created. - * - * @see AnimControl#createChannel() - */ - public void clearChannels() { - for (AnimChannel animChannel : channels) { - for (AnimEventListener list : listeners) { - list.onAnimCycleDone(this, animChannel, animChannel.getAnimationName()); - } - } - channels.clear(); - } - - /** - * @return The skeleton of this AnimControl. - */ - public Skeleton getSkeleton() { - return skeleton; - } - - /** - * Adds a new listener to receive animation related events. - * - * @param listener The listener to add. - */ - public void addListener(AnimEventListener listener) { - if (listeners.contains(listener)) { - throw new IllegalArgumentException("The given listener is already " - + "registered at this AnimControl"); - } - - listeners.add(listener); - } - - /** - * Removes the given listener from listening to events. - * - * @param listener the listener to remove - * @see AnimControl#addListener(com.jme3.animation.AnimEventListener) - */ - public void removeListener(AnimEventListener listener) { - if (!listeners.remove(listener)) { - throw new IllegalArgumentException("The given listener is not " - + "registered at this AnimControl"); - } - } - - /** - * Clears all the listeners added to this AnimControl - * - * @see AnimControl#addListener(com.jme3.animation.AnimEventListener) - */ - public void clearListeners() { - listeners.clear(); - } - - void notifyAnimChange(AnimChannel channel, String name) { - for (int i = 0; i < listeners.size(); i++) { - listeners.get(i).onAnimChange(this, channel, name); - } - } - - void notifyAnimCycleDone(AnimChannel channel, String name) { - for (int i = 0; i < listeners.size(); i++) { - listeners.get(i).onAnimCycleDone(this, channel, name); - } - } - - /** - * Internal use only. - */ - @Override - public void setSpatial(Spatial spatial) { - if (spatial == null && skeletonControl != null) { - this.spatial.removeControl(skeletonControl); - } - - super.setSpatial(spatial); - - //Backward compatibility. - if (spatial != null && skeletonControl != null) { - spatial.addControl(skeletonControl); - } - } - - final void reset() { - if (skeleton != null) { - skeleton.resetAndUpdate(); - } - } - - /** - * @return The names of all animations that this AnimControl - * can play. - */ - public Collection getAnimationNames() { - return animationMap.keySet(); - } - - /** - * Returns the length of the given named animation. - * - * @param name The name of the animation - * @return The length of time, in seconds, of the named animation. - */ - public float getAnimationLength(String name) { - Animation a = animationMap.get(name); - if (a == null) { - throw new IllegalArgumentException("The animation " + name - + " does not exist in this AnimControl"); - } - - return a.getLength(); - } - - /** - * Internal use only. - */ - @Override - protected void controlUpdate(float tpf) { - if (skeleton != null) { - skeleton.reset(); // reset skeleton to bind pose - } - - TempVars vars = TempVars.get(); - for (int i = 0; i < channels.size(); i++) { - channels.get(i).update(tpf, vars); - } - vars.release(); - - if (skeleton != null) { - skeleton.updateWorldVectors(); - } - } - - /** - * Internal use only. - */ - @Override - protected void controlRender(RenderManager rm, ViewPort vp) { - } - - @Override - public void write(JmeExporter ex) throws IOException { - super.write(ex); - OutputCapsule oc = ex.getCapsule(this); - oc.write(skeleton, "skeleton", null); - oc.writeStringSavableMap(animationMap, "animations", null); - } - - @Override - @SuppressWarnings("unchecked") - public void read(JmeImporter im) throws IOException { - super.read(im); - InputCapsule in = im.getCapsule(this); - skeleton = (Skeleton) in.readSavable("skeleton", null); - HashMap loadedAnimationMap = (HashMap) in.readStringSavableMap("animations", null); - if (loadedAnimationMap != null) { - animationMap = loadedAnimationMap; - } - - if (im.getFormatVersion() == 0) { - // Changed for backward compatibility with j3o files generated - // before the AnimControl/SkeletonControl split. - - // If we find a target mesh array the AnimControl creates the - // SkeletonControl for old files and add it to the spatial. - // When backward compatibility isn't needed anymore, this can be deleted. - Savable[] sav = in.readSavableArray("targets", null); - if (sav != null) { - // NOTE: allow the targets to be gathered automatically - skeletonControl = new SkeletonControl(skeleton); - spatial.addControl(skeletonControl); - } - } - } -} diff --git a/jme3-core/src/main/java/com/jme3/animation/AnimEventListener.java b/jme3-core/src/main/java/com/jme3/animation/AnimEventListener.java deleted file mode 100644 index eefa1a9189..0000000000 --- a/jme3-core/src/main/java/com/jme3/animation/AnimEventListener.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -/** - * AnimEventListener allows user code to receive various - * events regarding an AnimControl. For example, when an animation cycle is done. - * - * @author Kirill Vainer - */ -@Deprecated -public interface AnimEventListener { - - /** - * Invoked when an animation "cycle" is done. For non-looping animations, - * this event is invoked when the animation is finished playing. For - * looping animations, this event is invoked each time the animation is restarted. - * - * @param control The control to which the listener is assigned. - * @param channel The channel being altered - * @param animName The new animation that is done. - */ - public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName); - - /** - * Invoked when an animation is set to play by the user on the given channel. - * - * @param control The control to which the listener is assigned. - * @param channel The channel being altered - * @param animName The new animation name set. - */ - public void onAnimChange(AnimControl control, AnimChannel channel, String animName); - -} diff --git a/jme3-core/src/main/java/com/jme3/animation/Animation.java b/jme3-core/src/main/java/com/jme3/animation/Animation.java deleted file mode 100644 index 554e6a0cc5..0000000000 --- a/jme3-core/src/main/java/com/jme3/animation/Animation.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.export.*; -import com.jme3.scene.Spatial; -import com.jme3.util.SafeArrayList; -import com.jme3.util.TempVars; -import com.jme3.util.clone.Cloner; -import com.jme3.util.clone.JmeCloneable; - -import java.io.IOException; - -/** - * The animation class updates the animation target with the tracks of a given type. - * - * @author Kirill Vainer, Marcin Roguski (Kaelthas) - * @deprecated use {@link com.jme3.anim.AnimClip} - */ -@Deprecated -public class Animation implements Savable, Cloneable, JmeCloneable { - /** - * The name of the animation. - */ - private String name; - /** - * The length of the animation. - */ - private float length; - /** - * The tracks of the animation. - */ - private SafeArrayList tracks = new SafeArrayList<>(Track.class); - - /** - * Serialization-only. Do not use. - */ - protected Animation() { - } - - /** - * Creates a new Animation with the given name and length. - * - * @param name The name of the animation. - * @param length Length in seconds of the animation. - */ - public Animation(String name, float length) { - this.name = name; - this.length = length; - } - - /** - * The name of the bone animation - * @return name of the bone animation - */ - public String getName() { - return name; - } - - /** - * Returns the length in seconds of this animation - * - * @return the length in seconds of this animation - */ - public float getLength() { - return length; - } - - /** - * Set the length of the animation - * - * @param length the desired duration (in seconds) - */ - public void setLength(float length) { - this.length = length; - } - - /** - * Sets the name of the animation - * - * @param name the desired name - */ - public void setName(String name) { - this.name = name; - } - - /** - * This method sets the current time of the animation. - * This method behaves differently for every known track type. - * Override this method if you have your own type of track. - * - * @param time the time of the animation - * @param blendAmount the blend amount factor - * @param control the animation control - * @param channel the animation channel - */ - void setTime(float time, float blendAmount, AnimControl control, AnimChannel channel, TempVars vars) { - if (tracks == null) { - return; - } - - for (Track track : tracks) { - track.setTime(time, blendAmount, control, channel, vars); - } - } - - /** - * Set the {@link Track}s to be used by this animation. - * - * @param tracksArray The tracks to set. - */ - public void setTracks(Track[] tracksArray) { - for (Track track : tracksArray) { - tracks.add(track); - } - } - - /** - * Adds a track to this animation - * @param track the track to add - */ - public void addTrack(Track track) { - tracks.add(track); - } - - /** - * removes a track from this animation - * @param track the track to remove - */ - public void removeTrack(Track track) { - tracks.remove(track); - if (track instanceof ClonableTrack) { - ((ClonableTrack) track).cleanUp(); - } - } - - /** - * Returns the tracks set in {@link #setTracks(com.jme3.animation.Track[]) }. - * - * @return the tracks set previously - */ - public Track[] getTracks() { - return tracks.getArray(); - } - - /** - * This method creates a clone of the current object. - * @return a clone of the current object - */ - @Override - public Animation clone() { - try { - Animation result = (Animation) super.clone(); - result.tracks = new SafeArrayList<>(Track.class); - for (Track track : tracks) { - result.tracks.add(track.clone()); - } - return result; - } catch (CloneNotSupportedException e) { - throw new AssertionError(); - } - } - - /** - * - * @param spat the Spatial to clone for - * @return a new instance - */ - public Animation cloneForSpatial(Spatial spat) { - try { - Animation result = (Animation) super.clone(); - result.tracks = new SafeArrayList<>(Track.class); - for (Track track : tracks) { - if (track instanceof ClonableTrack) { - result.tracks.add(((ClonableTrack) track).cloneForSpatial(spat)); - } else { - result.tracks.add(track); - } - } - return result; - } catch (CloneNotSupportedException e) { - throw new AssertionError(); - } - } - - @Override - public Object jmeClone() { - try { - return super.clone(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException("Error cloning", e); - } - } - - @Override - public void cloneFields(Cloner cloner, Object original) { - // There is some logic here that I'm copying, but I'm not sure if - // it's a mistake or not. If a track is not a CloneableTrack then it - // isn't cloned at all... even though they all implement clone() methods. -pspeed - SafeArrayList newTracks = new SafeArrayList<>(Track.class); - for (Track track : tracks) { - if (track instanceof JmeCloneable) { - newTracks.add(cloner.clone(track)); - } else { - // this is the part that seems fishy - newTracks.add(track); - } - } - this.tracks = newTracks; - } - - @Override - public String toString() { - return getClass().getSimpleName() + "[name=" + name + ", length=" + length + ']'; - } - - @Override - public void write(JmeExporter ex) throws IOException { - OutputCapsule out = ex.getCapsule(this); - out.write(name, "name", null); - out.write(length, "length", 0f); - out.write(tracks.getArray(), "tracks", null); - } - - @Override - public void read(JmeImporter im) throws IOException { - InputCapsule in = im.getCapsule(this); - name = in.readString("name", null); - length = in.readFloat("length", 0f); - - Savable[] arr = in.readSavableArray("tracks", null); - if (arr != null) { - // NOTE: Backward compatibility only. Some animations have no - // tracks set at all, even though it makes no sense. - // Since there's a null check in setTime(), - // it's only appropriate that the check is made here as well. - tracks = new SafeArrayList<>(Track.class); - for (Savable savable : arr) { - tracks.add((Track) savable); - } - } - } -} diff --git a/jme3-core/src/main/java/com/jme3/animation/AnimationFactory.java b/jme3-core/src/main/java/com/jme3/animation/AnimationFactory.java deleted file mode 100644 index 74e0daef36..0000000000 --- a/jme3-core/src/main/java/com/jme3/animation/AnimationFactory.java +++ /dev/null @@ -1,499 +0,0 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.math.FastMath; -import com.jme3.math.Quaternion; -import com.jme3.math.Transform; -import com.jme3.math.Vector3f; - -/** - * A convenience class to easily set up a spatial keyframe animation. - * You can add some keyFrames for a given time or a given keyFrameIndex, for translation, rotation, and scale. - * The animationFactory will then generate an appropriate SpatialAnimation by interpolating values between the keyFrames. - *

- * Usage is :
- * - Create the AnimationFactory
- * - add some keyFrames
- * - call the buildAnimation() method that will return a new Animation
- * - add the generated Animation to any existing AnimationControl
- *

- * Note that the first keyFrame (index 0) is defaulted with the identity transform. - * If you want to change that you have to replace this keyFrame with the transform you want. - * - * @author Nehon - * @deprecated use {@link com.jme3.anim.AnimFactory} - */ -@Deprecated -public class AnimationFactory { - - /** - * step for splitting rotations that have an angle above PI/2 - */ - private final static float EULER_STEP = FastMath.QUARTER_PI * 3; - - /** - * enum to determine the type of interpolation - */ - private enum Type { - - Translation, Rotation, Scale; - } - - /** - * inner class to keep track of a rotation using Euler angles - */ - @Deprecated - protected class Rotation { - - /** - * The rotation Quaternion - */ - Quaternion rotation = new Quaternion(); - /** - * This rotation expressed in Euler angles - */ - Vector3f eulerAngles = new Vector3f(); - /** - * the index of the parent key frame is this keyFrame is a split rotation - */ - int masterKeyFrame = -1; - - public Rotation() { - rotation.loadIdentity(); - } - - void set(Quaternion rot) { - rotation.set(rot); - float[] a = new float[3]; - rotation.toAngles(a); - eulerAngles.set(a[0], a[1], a[2]); - } - - void set(float x, float y, float z) { - float[] a = {x, y, z}; - rotation.fromAngles(a); - eulerAngles.set(x, y, z); - } - } - /** - * Name of the animation - */ - protected String name; - /** - * frames per seconds - */ - protected int fps; - /** - * Animation duration in seconds - */ - protected float duration; - /** - * total number of frames - */ - protected int totalFrames; - /** - * time per frame - */ - protected float tpf; - /** - * Time array for this animation - */ - protected float[] times; - /** - * Translation array for this animation - */ - protected Vector3f[] translations; - /** - * rotation array for this animation - */ - protected Quaternion[] rotations; - /** - * scales array for this animation - */ - protected Vector3f[] scales; - /** - * The map of keyFrames to compute the animation. The key is the index of the frame - */ - protected Vector3f[] keyFramesTranslation; - protected Vector3f[] keyFramesScale; - protected Rotation[] keyFramesRotation; - - /** - * Creates an AnimationFactory - * @param duration the desired duration for the resulting animation - * @param name the name of the resulting animation - */ - public AnimationFactory(float duration, String name) { - this(duration, name, 30); - } - - /** - * Creates an AnimationFactory - * @param duration the desired duration for the resulting animation - * @param name the name of the resulting animation - * @param fps the number of frames per second for this animation (default is 30) - */ - public AnimationFactory(float duration, String name, int fps) { - this.name = name; - this.duration = duration; - this.fps = fps; - totalFrames = (int) (fps * duration) + 1; - tpf = 1 / (float) fps; - times = new float[totalFrames]; - translations = new Vector3f[totalFrames]; - rotations = new Quaternion[totalFrames]; - scales = new Vector3f[totalFrames]; - keyFramesTranslation = new Vector3f[totalFrames]; - keyFramesTranslation[0] = new Vector3f(); - keyFramesScale = new Vector3f[totalFrames]; - keyFramesScale[0] = new Vector3f(1, 1, 1); - keyFramesRotation = new Rotation[totalFrames]; - keyFramesRotation[0] = new Rotation(); - - } - - /** - * Adds a key frame for the given Transform at the given time - * @param time the time at which the keyFrame must be inserted - * @param transform the transform to use for this keyFrame - */ - public void addTimeTransform(float time, Transform transform) { - addKeyFrameTransform((int) (time / tpf), transform); - } - - /** - * Adds a key frame for the given Transform at the given keyFrame index - * @param keyFrameIndex the index at which the keyFrame must be inserted - * @param transform the transform to use for this keyFrame - */ - public void addKeyFrameTransform(int keyFrameIndex, Transform transform) { - addKeyFrameTranslation(keyFrameIndex, transform.getTranslation()); - addKeyFrameScale(keyFrameIndex, transform.getScale()); - addKeyFrameRotation(keyFrameIndex, transform.getRotation()); - } - - /** - * Adds a key frame for the given translation at the given time - * @param time the time at which the keyFrame must be inserted - * @param translation the translation to use for this keyFrame - */ - public void addTimeTranslation(float time, Vector3f translation) { - addKeyFrameTranslation((int) (time / tpf), translation); - } - - /** - * Adds a key frame for the given translation at the given keyFrame index - * @param keyFrameIndex the index at which the keyFrame must be inserted - * @param translation the translation to use for this keyFrame - */ - public void addKeyFrameTranslation(int keyFrameIndex, Vector3f translation) { - Vector3f t = getTranslationForFrame(keyFrameIndex); - t.set(translation); - } - - /** - * Adds a key frame for the given rotation at the given time
- * This can't be used if the interpolated angle is higher than PI (180°)
- * Use {@link #addTimeRotationAngles(float time, float x, float y, float z)} instead that uses Euler angle rotations.
- * @param time the time at which the keyFrame must be inserted - * @param rotation the rotation Quaternion to use for this keyFrame - * @see #addTimeRotationAngles(float time, float x, float y, float z) - */ - public void addTimeRotation(float time, Quaternion rotation) { - addKeyFrameRotation((int) (time / tpf), rotation); - } - - /** - * Adds a key frame for the given rotation at the given keyFrame index
- * This can't be used if the interpolated angle is higher than PI (180°)
- * Use {@link #addKeyFrameRotationAngles(int keyFrameIndex, float x, float y, float z)} instead that uses Euler angle rotations. - * @param keyFrameIndex the index at which the keyFrame must be inserted - * @param rotation the rotation Quaternion to use for this keyFrame - * @see #addKeyFrameRotationAngles(int keyFrameIndex, float x, float y, float z) - */ - public void addKeyFrameRotation(int keyFrameIndex, Quaternion rotation) { - Rotation r = getRotationForFrame(keyFrameIndex); - r.set(rotation); - } - - /** - * Adds a key frame for the given rotation at the given time.
- * Rotation is expressed by Euler angles in radians.
- * Note that the generated rotation will be stored as a quaternion and interpolated using a spherical linear interpolation (slerp)
- * Hence, this method may create intermediate keyFrames if the interpolation angle is higher than PI to ensure continuity in animation
- * - * @param time the time at which the keyFrame must be inserted - * @param x the rotation around the x axis (aka yaw) in radians - * @param y the rotation around the y axis (aka roll) in radians - * @param z the rotation around the z axis (aka pitch) in radians - */ - public void addTimeRotationAngles(float time, float x, float y, float z) { - addKeyFrameRotationAngles((int) (time / tpf), x, y, z); - } - - /** - * Adds a key frame for the given rotation at the given key frame index.
- * Rotation is expressed by Euler angles in radians.
- * Note that the generated rotation will be stored as a quaternion and interpolated using a spherical linear interpolation (slerp)
- * Hence, this method may create intermediate keyFrames if the interpolation angle is higher than PI to ensure continuity in animation
- * - * @param keyFrameIndex the index at which the keyFrame must be inserted - * @param x the rotation around the x axis (aka yaw) in radians - * @param y the rotation around the y axis (aka roll) in radians - * @param z the rotation around the z axis (aka pitch) in radians - */ - public void addKeyFrameRotationAngles(int keyFrameIndex, float x, float y, float z) { - Rotation r = getRotationForFrame(keyFrameIndex); - r.set(x, y, z); - - // if the delta of Euler angles is higher than PI, we create intermediate keyframes - // since we are using quaternions and slerp for rotation interpolation, we cannot interpolate over an angle higher than PI - int prev = getPreviousKeyFrame(keyFrameIndex, keyFramesRotation); - if (prev != -1) { - //previous rotation keyframe - Rotation prevRot = keyFramesRotation[prev]; - //the maximum delta angle (x,y or z) - float delta = Math.max(Math.abs(x - prevRot.eulerAngles.x), Math.abs(y - prevRot.eulerAngles.y)); - delta = Math.max(delta, Math.abs(z - prevRot.eulerAngles.z)); - //if delta > PI we have to create intermediates key frames - if (delta >= FastMath.PI) { - //frames delta - int dF = keyFrameIndex - prev; - //angle per frame for x,y ,z - float dXAngle = (x - prevRot.eulerAngles.x) / dF; - float dYAngle = (y - prevRot.eulerAngles.y) / dF; - float dZAngle = (z - prevRot.eulerAngles.z) / dF; - - // the keyFrame step - int keyStep = (int) (dF / delta * EULER_STEP); - // the current keyFrame - int cursor = prev + keyStep; - while (cursor < keyFrameIndex) { - //for each step we create a new rotation by interpolating the angles - Rotation dr = getRotationForFrame(cursor); - dr.masterKeyFrame = keyFrameIndex; - dr.set(prevRot.eulerAngles.x + cursor * dXAngle, prevRot.eulerAngles.y + cursor * dYAngle, prevRot.eulerAngles.z + cursor * dZAngle); - cursor += keyStep; - } - - } - } - - } - - /** - * Adds a key frame for the given scale at the given time - * @param time the time at which the keyFrame must be inserted - * @param scale the scale to use for this keyFrame - */ - public void addTimeScale(float time, Vector3f scale) { - addKeyFrameScale((int) (time / tpf), scale); - } - - /** - * Adds a key frame for the given scale at the given keyFrame index - * @param keyFrameIndex the index at which the keyFrame must be inserted - * @param scale the scale to use for this keyFrame - */ - public void addKeyFrameScale(int keyFrameIndex, Vector3f scale) { - Vector3f s = getScaleForFrame(keyFrameIndex); - s.set(scale); - } - - /** - * returns the translation for a given frame index - * creates the translation if it doesn't exist - * @param keyFrameIndex index - * @return the translation - */ - private Vector3f getTranslationForFrame(int keyFrameIndex) { - if (keyFrameIndex < 0 || keyFrameIndex > totalFrames) { - throw new ArrayIndexOutOfBoundsException("keyFrameIndex must be between 0 and " + totalFrames + " (received " + keyFrameIndex + ")"); - } - Vector3f v = keyFramesTranslation[keyFrameIndex]; - if (v == null) { - v = new Vector3f(); - keyFramesTranslation[keyFrameIndex] = v; - } - return v; - } - - /** - * returns the scale for a given frame index - * creates the scale if it doesn't exist - * @param keyFrameIndex index - * @return the scale - */ - private Vector3f getScaleForFrame(int keyFrameIndex) { - if (keyFrameIndex < 0 || keyFrameIndex > totalFrames) { - throw new ArrayIndexOutOfBoundsException("keyFrameIndex must be between 0 and " + totalFrames + " (received " + keyFrameIndex + ")"); - } - Vector3f v = keyFramesScale[keyFrameIndex]; - if (v == null) { - v = new Vector3f(); - keyFramesScale[keyFrameIndex] = v; - } - return v; - } - - /** - * returns the rotation for a given frame index - * creates the rotation if it doesn't exist - * @param keyFrameIndex index - * @return the rotation - */ - private Rotation getRotationForFrame(int keyFrameIndex) { - if (keyFrameIndex < 0 || keyFrameIndex > totalFrames) { - throw new ArrayIndexOutOfBoundsException("keyFrameIndex must be between 0 and " + totalFrames + " (received " + keyFrameIndex + ")"); - } - Rotation v = keyFramesRotation[keyFrameIndex]; - if (v == null) { - v = new Rotation(); - keyFramesRotation[keyFrameIndex] = v; - } - return v; - } - - /** - * Creates an Animation based on the keyFrames previously added to the factory. - * @return the generated animation - */ - public Animation buildAnimation() { - interpolateTime(); - interpolate(keyFramesTranslation, Type.Translation); - interpolate(keyFramesRotation, Type.Rotation); - interpolate(keyFramesScale, Type.Scale); - - SpatialTrack spatialTrack = new SpatialTrack(times, translations, rotations, scales); - - //creating the animation - Animation spatialAnimation = new Animation(name, duration); - spatialAnimation.setTracks(new SpatialTrack[]{spatialTrack}); - - return spatialAnimation; - } - - /** - * interpolates time values - */ - private void interpolateTime() { - for (int i = 0; i < totalFrames; i++) { - times[i] = i * tpf; - } - } - - /** - * Interpolates over the key frames for the given keyFrame array and the given type of transform - * @param keyFrames the keyFrames array - * @param type the type of transforms - */ - private void interpolate(Object[] keyFrames, Type type) { - int i = 0; - while (i < totalFrames) { - //fetching the next keyFrame index transform in the array - int key = getNextKeyFrame(i, keyFrames); - if (key != -1) { - //computing the frame span to interpolate over - int span = key - i; - //iterating over the frames - for (int j = i; j <= key; j++) { - // computing interpolation value - float val = (j - i) / (float) span; - //interpolating depending on the transform type - switch (type) { - case Translation: - translations[j] = FastMath.interpolateLinear(val, (Vector3f) keyFrames[i], (Vector3f) keyFrames[key]); - break; - case Rotation: - Quaternion rot = new Quaternion(); - rotations[j] = rot.slerp(((Rotation) keyFrames[i]).rotation, ((Rotation) keyFrames[key]).rotation, val); - break; - case Scale: - scales[j] = FastMath.interpolateLinear(val, (Vector3f) keyFrames[i], (Vector3f) keyFrames[key]); - break; - } - } - //jumping to the next keyFrame - i = key; - } else { - //No more key frame, filling the array with the last transform computed. - for (int j = i; j < totalFrames; j++) { - - switch (type) { - case Translation: - translations[j] = ((Vector3f) keyFrames[i]).clone(); - break; - case Rotation: - rotations[j] = ((Rotation) keyFrames[i]).rotation.clone(); - break; - case Scale: - scales[j] = ((Vector3f) keyFrames[i]).clone(); - break; - } - } - //we're done - i = totalFrames; - } - } - } - - /** - * Get the index of the next keyFrame that as a transform - * @param index the start index - * @param keyFrames the keyFrames array - * @return the index of the next keyFrame - */ - private int getNextKeyFrame(int index, Object[] keyFrames) { - for (int i = index + 1; i < totalFrames; i++) { - if (keyFrames[i] != null) { - return i; - } - } - return -1; - } - - /** - * Get the index of the previous keyFrame that as a transform - * @param index the start index - * @param keyFrames the keyFrames array - * @return the index of the previous keyFrame - */ - private int getPreviousKeyFrame(int index, Object[] keyFrames) { - for (int i = index - 1; i >= 0; i--) { - if (keyFrames[i] != null) { - return i; - } - } - return -1; - } -} diff --git a/jme3-core/src/main/java/com/jme3/animation/AudioTrack.java b/jme3-core/src/main/java/com/jme3/animation/AudioTrack.java deleted file mode 100644 index cccb883bee..0000000000 --- a/jme3-core/src/main/java/com/jme3/animation/AudioTrack.java +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.audio.AudioNode; -import com.jme3.export.*; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; -import com.jme3.util.TempVars; -import com.jme3.util.clone.Cloner; - -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * AudioTrack is a track to add to an existing animation, to play a sound during - * an animations for example : gunshot, footstep, shout, etcetera. - * - * Typical usage is: - *

- * AnimControl control model.getControl(AnimControl.class);
- * AudioTrack track = new AudioTrack(existingAudioNode, control.getAnim("TheAnim").getLength());
- * control.getAnim("TheAnim").addTrack(track);
- * 
- * - * This is mostly intended for short sounds, playInstance will be called on the - * AudioNode at time 0 + startOffset. - * - * - * @author Nehon - */ -@Deprecated -public class AudioTrack implements ClonableTrack { - - private static final Logger logger = Logger.getLogger(AudioTrack.class.getName()); - private AudioNode audio; - private float startOffset = 0; - private float length = 0; - private boolean initialized = false; - private boolean started = false; - private boolean played = false; - - //Animation listener to stop the sound when the animation ends or is changed - private class OnEndListener implements AnimEventListener { - - @Override - public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) { - stop(); - } - - @Override - public void onAnimChange(AnimControl control, AnimChannel channel, String animName) { - } - } - - /** - * constructor for serialization only - */ - protected AudioTrack() { - } - - /** - * Creates an AudioTrack - * - * @param audio the AudioNode - * @param length the length of the track (usually the length of the - * animation you want to add the track to) - */ - public AudioTrack(AudioNode audio, float length) { - this.audio = audio; - this.length = length; - setUserData(this); - } - - /** - * Creates an AudioTrack - * - * @param audio the AudioNode - * @param length the length of the track (usually the length of the - * animation you want to add the track to) - * @param startOffset the time in second when the sound will be played after - * the animation starts (default is 0) - */ - public AudioTrack(AudioNode audio, float length, float startOffset) { - this(audio, length); - this.startOffset = startOffset; - } - - /** - * Internal use only - * - * @see Track#setTime(float, float, com.jme3.animation.AnimControl, - * com.jme3.animation.AnimChannel, com.jme3.util.TempVars) - */ - @Override - public void setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars) { - - if (time >= length) { - return; - } - if (!initialized) { - control.addListener(new OnEndListener()); - initialized = true; - } - if (!started && time >= startOffset) { - started = true; - audio.playInstance(); - } - } - - //stops the sound - private void stop() { - audio.stop(); - started = false; - } - - /** - * Return the length of the track - * - * @return length of the track - */ - @Override - public float getLength() { - return length; - } - - @Override - public float[] getKeyFrameTimes() { - return new float[]{startOffset}; - } - - /** - * Clone this track - * - * @return a new track - */ - @Override - public AudioTrack clone() { - return new AudioTrack(audio, length, startOffset); - } - - /** - * This method clone the Track and search for the cloned counterpart of the - * original audio node in the given cloned spatial. The spatial is assumed - * to be the Spatial holding the AnimControl controlling the animation using - * this Track. - * - * @param spatial the Spatial holding the AnimControl - * @return the cloned Track with proper reference - */ - @Override - public Track cloneForSpatial(Spatial spatial) { - AudioTrack audioTrack = new AudioTrack(); - audioTrack.length = this.length; - audioTrack.startOffset = this.startOffset; - - //searching for the newly cloned AudioNode - audioTrack.audio = findAudio(spatial); - if (audioTrack.audio == null) { - logger.log(Level.WARNING, "{0} was not found in {1} or is not bound to this track", new Object[]{audio.getName(), spatial.getName()}); - audioTrack.audio = audio; - } - - //setting user data on the new AudioNode and marking it with a reference to the cloned Track. - setUserData(audioTrack); - - return audioTrack; - } - - @Override - public Object jmeClone() { - try { - return super.clone(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException("Error cloning", e); - } - } - - @Override - public void cloneFields(Cloner cloner, Object original) { - // Duplicating the old cloned state from cloneForSpatial() - this.initialized = false; - this.started = false; - this.played = false; - this.audio = cloner.clone(audio); - } - - /** - * recursive function responsible for finding the newly cloned AudioNode - * - * @param spat - * @return - */ - private AudioNode findAudio(Spatial spat) { - if (spat instanceof AudioNode) { - //spat is an AudioNode - AudioNode em = (AudioNode) spat; - //getting the UserData TrackInfo so check if it should be attached to this Track - TrackInfo t = (TrackInfo) em.getUserData("TrackInfo"); - if (t != null && t.getTracks().contains(this)) { - return em; - } - return null; - - } else if (spat instanceof Node) { - for (Spatial child : ((Node) spat).getChildren()) { - AudioNode em = findAudio(child); - if (em != null) { - return em; - } - } - } - return null; - } - - private void setUserData(AudioTrack audioTrack) { - //fetching the UserData TrackInfo. - TrackInfo data = (TrackInfo) audioTrack.audio.getUserData("TrackInfo"); - - //if it does not exist, we create it and attach it to the AudioNode. - if (data == null) { - data = new TrackInfo(); - audioTrack.audio.setUserData("TrackInfo", data); - } - - //adding the given Track to the TrackInfo. - data.addTrack(audioTrack); - } - - @Override - public void cleanUp() { - TrackInfo t = (TrackInfo) audio.getUserData("TrackInfo"); - t.getTracks().remove(this); - if (!t.getTracks().isEmpty()) { - audio.setUserData("TrackInfo", null); - } - } - - /** - * - * @return the audio node used by this track - */ - public AudioNode getAudio() { - return audio; - } - - /** - * sets the audio node to be used for this track - * - * @param audio the desired AudioNode (alias created) - */ - public void setAudio(AudioNode audio) { - if (this.audio != null) { - TrackInfo data = (TrackInfo) audio.getUserData("TrackInfo"); - data.getTracks().remove(this); - } - this.audio = audio; - setUserData(this); - } - - /** - * - * @return the start offset of the track - */ - public float getStartOffset() { - return startOffset; - } - - /** - * set the start offset of the track - * - * @param startOffset the desired start offset - */ - public void setStartOffset(float startOffset) { - this.startOffset = startOffset; - } - - /** - * Internal use only serialization - * - * @param ex exporter - * @throws IOException exception - */ - @Override - public void write(JmeExporter ex) throws IOException { - OutputCapsule out = ex.getCapsule(this); - out.write(audio, "audio", null); - out.write(length, "length", 0); - out.write(startOffset, "startOffset", 0); - } - - /** - * Internal use only serialization - * - * @param im importer - * @throws IOException Exception - */ - @Override - public void read(JmeImporter im) throws IOException { - InputCapsule in = im.getCapsule(this); - audio = (AudioNode) in.readSavable("audio", null); - length = in.readFloat("length", length); - startOffset = in.readFloat("startOffset", 0); - } -} diff --git a/jme3-core/src/main/java/com/jme3/animation/Bone.java b/jme3-core/src/main/java/com/jme3/animation/Bone.java deleted file mode 100644 index 2d4211b90a..0000000000 --- a/jme3-core/src/main/java/com/jme3/animation/Bone.java +++ /dev/null @@ -1,972 +0,0 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.export.*; -import com.jme3.material.MatParamOverride; -import com.jme3.math.*; -import com.jme3.scene.*; -import com.jme3.shader.VarType; -import com.jme3.util.SafeArrayList; -import com.jme3.util.TempVars; -import com.jme3.util.clone.Cloner; -import com.jme3.util.clone.JmeCloneable; - -import java.io.IOException; -import java.util.ArrayList; - -/** - * Bone describes a bone in the bone-weight skeletal animation - * system. A bone contains a name and an index, as well as relevant - * transformation data. - * - * A bone has 3 sets of transforms : - * 1. The bind transforms, that are the transforms of the bone when the skeleton - * is in its rest pose (also called bind pose or T pose in the literature). - * The bind transforms are expressed in Local space meaning relatively to the - * parent bone. - * - * 2. The Local transforms, that are the transforms of the bone once animation - * or user transforms has been applied to the bind pose. The local transforms are - * expressed in Local space meaning relatively to the parent bone. - * - * 3. The Model transforms, that are the transforms of the bone relatives to the - * rootBone of the skeleton. Those transforms are what is needed to apply skinning - * to the mesh the skeleton controls. - * Note that there can be several rootBones in a skeleton. The one considered for - * these transforms is the one that is an ancestor of this bone. - * - * @author Kirill Vainer - * @author Rémy Bouquet - * @deprecated use {@link com.jme3.anim.Joint} - */ -@Deprecated -public final class Bone implements Savable, JmeCloneable { - - // Version #2: Changed naming of transforms as they were misleading - public static final int SAVABLE_VERSION = 2; - private String name; - private Bone parent; - private ArrayList children = new ArrayList<>(); - /** - * If enabled, user can control bone transform with setUserTransforms. - * Animation transforms are not applied to this bone when enabled. - */ - private boolean userControl = false; - /** - * The attachment node. - */ - private Node attachNode; - /** - * A geometry animated by this node, used when updating the attachments node. - */ - private Geometry targetGeometry = null; - /** - * Bind transform is the local bind transform of this bone. (local space) - */ - private Vector3f bindPos; - private Quaternion bindRot; - private Vector3f bindScale; - - /** - * The inverse bind transforms of this bone expressed in model space - */ - private Vector3f modelBindInversePos; - private Quaternion modelBindInverseRot; - private Vector3f modelBindInverseScale; - - /** - * The local animated or user transform combined with the local bind transform - */ - private Vector3f localPos = new Vector3f(); - private Quaternion localRot = new Quaternion(); - private Vector3f localScale = new Vector3f(1.0f, 1.0f, 1.0f); - /** - * The model transforms of this bone - */ - private Vector3f modelPos = new Vector3f(); - private Quaternion modelRot = new Quaternion(); - private Vector3f modelScale = new Vector3f(); - - // Used for getCombinedTransform - private Transform tmpTransform; - - /** - * Used to handle blending from one animation to another. - * See {@link #blendAnimTransforms(com.jme3.math.Vector3f, com.jme3.math.Quaternion, com.jme3.math.Vector3f, float)} - * on how this variable is used. - */ - private transient float currentWeightSum = -1; - - /** - * Creates a new bone with the given name. - * - * @param name Name to give to this bone - */ - public Bone(String name) { - if (name == null) - throw new IllegalArgumentException("Name cannot be null"); - - this.name = name; - - bindPos = new Vector3f(); - bindRot = new Quaternion(); - bindScale = new Vector3f(1, 1, 1); - - modelBindInversePos = new Vector3f(); - modelBindInverseRot = new Quaternion(); - modelBindInverseScale = new Vector3f(); - } - - /** - * Special-purpose copy constructor. - *

- * Only copies the name, user control state and bind pose transforms from the original. - *

- * The rest of the data is NOT copied, as it will be - * generated automatically when the bone is animated. - * - * @param source The bone from which to copy the data. - */ - Bone(Bone source) { - this.name = source.name; - - userControl = source.userControl; - - bindPos = source.bindPos.clone(); - bindRot = source.bindRot.clone(); - bindScale = source.bindScale.clone(); - - modelBindInversePos = source.modelBindInversePos.clone(); - modelBindInverseRot = source.modelBindInverseRot.clone(); - modelBindInverseScale = source.modelBindInverseScale.clone(); - - // Parent and children will be assigned manually. - } - - /** - * Serialization only. Do not use. - */ - protected Bone() { - } - - @Override - public Object jmeClone() { - try { - Bone clone = (Bone) super.clone(); - return clone; - } catch (CloneNotSupportedException ex) { - throw new AssertionError(); - } - } - - @Override - public void cloneFields(Cloner cloner, Object original) { - - this.parent = cloner.clone(parent); - this.children = cloner.clone(children); - - this.attachNode = cloner.clone(attachNode); - this.targetGeometry = cloner.clone(targetGeometry); - - this.bindPos = cloner.clone(bindPos); - this.bindRot = cloner.clone(bindRot); - this.bindScale = cloner.clone(bindScale); - - this.modelBindInversePos = cloner.clone(modelBindInversePos); - this.modelBindInverseRot = cloner.clone(modelBindInverseRot); - this.modelBindInverseScale = cloner.clone(modelBindInverseScale); - - this.localPos = cloner.clone(localPos); - this.localRot = cloner.clone(localRot); - this.localScale = cloner.clone(localScale); - - this.modelPos = cloner.clone(modelPos); - this.modelRot = cloner.clone(modelRot); - this.modelScale = cloner.clone(modelScale); - - this.tmpTransform = cloner.clone(tmpTransform); - } - - /** - * Returns the name of the bone, set in the constructor. - * - * @return The name of the bone, set in the constructor. - */ - public String getName() { - return name; - } - - /** - * Returns parent bone of this bone, or null if it is a root bone. - * @return The parent bone of this bone, or null if it is a root bone. - */ - public Bone getParent() { - return parent; - } - - /** - * Returns all the children bones of this bone. - * - * @return All the children bones of this bone. - */ - public ArrayList getChildren() { - return children; - } - - /** - * Returns the local position of the bone, relative to the parent bone. - * - * @return The local position of the bone, relative to the parent bone. - */ - public Vector3f getLocalPosition() { - return localPos; - } - - /** - * Returns the local rotation of the bone, relative to the parent bone. - * - * @return The local rotation of the bone, relative to the parent bone. - */ - public Quaternion getLocalRotation() { - return localRot; - } - - /** - * Returns the local scale of the bone, relative to the parent bone. - * - * @return The local scale of the bone, relative to the parent bone. - */ - public Vector3f getLocalScale() { - return localScale; - } - - /** - * Returns the position of the bone in model space. - * - * @return The position of the bone in model space. - */ - public Vector3f getModelSpacePosition() { - return modelPos; - } - - /** - * Returns the rotation of the bone in model space. - * - * @return The rotation of the bone in model space. - */ - public Quaternion getModelSpaceRotation() { - return modelRot; - } - - /** - * Returns the scale of the bone in model space. - * - * @return The scale of the bone in model space. - */ - public Vector3f getModelSpaceScale() { - return modelScale; - } - - /** - * @return the pre-existing vector - * @deprecated use {@link #getModelBindInversePosition()} - */ - @Deprecated - public Vector3f getWorldBindInversePosition() { - return modelBindInversePos; - } - - /** - * Returns the inverse Bind position of this bone expressed in model space. - *

- * The inverse bind pose transform of the bone in model space is its "default" - * transform with no animation applied. - * - * @return the inverse bind position of this bone expressed in model space. - */ - public Vector3f getModelBindInversePosition() { - return modelBindInversePos; - } - - /** - * @return the pre-existing Quaternion - * @deprecated use {@link #getModelBindInverseRotation()} - */ - @Deprecated - public Quaternion getWorldBindInverseRotation() { - return modelBindInverseRot; - } - - /** - * Returns the inverse bind rotation of this bone expressed in model space. - *

- * The inverse bind pose transform of the bone in model space is its "default" - * transform with no animation applied. - * - * @return the inverse bind rotation of this bone expressed in model space. - */ - public Quaternion getModelBindInverseRotation() { - return modelBindInverseRot; - } - - /** - * @return the pre-existing vector - * @deprecated use {@link #getModelBindInverseScale()} - */ - @Deprecated - public Vector3f getWorldBindInverseScale() { - return modelBindInverseScale; - } - - /** - * Returns the inverse world bind pose scale. - *

- * The inverse bind pose transform of the bone in model space is its "default" - * transform with no animation applied. - * - * @return the inverse world bind pose scale. - */ - public Vector3f getModelBindInverseScale() { - return modelBindInverseScale; - } - - public Transform getModelBindInverseTransform() { - Transform t = new Transform(); - t.setTranslation(modelBindInversePos); - t.setRotation(modelBindInverseRot); - if (modelBindInverseScale != null) { - t.setScale(modelBindInverseScale); - } - return t; - } - - public Transform getBindInverseTransform() { - Transform t = new Transform(); - t.setTranslation(bindPos); - t.setRotation(bindRot); - if (bindScale != null) { - t.setScale(bindScale); - } - return t.invert(); - } - - /** - * @return the pre-existing vector - * @deprecated use {@link #getBindPosition()} - */ - @Deprecated - public Vector3f getWorldBindPosition() { - return bindPos; - } - - /** - * Returns the bind position expressed in local space (relative to the parent bone). - *

- * The bind pose transform of the bone in local space is its "default" - * transform with no animation applied. - * - * @return the bind position in local space. - */ - public Vector3f getBindPosition() { - return bindPos; - } - - /** - * @return the pre-existing Quaternion - * @deprecated use {@link #getBindRotation() } - */ - @Deprecated - public Quaternion getWorldBindRotation() { - return bindRot; - } - - /** - * Returns the bind rotation expressed in local space (relative to the parent bone). - *

- * The bind pose transform of the bone in local space is its "default" - * transform with no animation applied. - * - * @return the bind rotation in local space. - */ - public Quaternion getBindRotation() { - return bindRot; - } - - /** - * @return the pre-existing vector - * @deprecated use {@link #getBindScale() } - */ - @Deprecated - public Vector3f getWorldBindScale() { - return bindScale; - } - - /** - * Returns the bind scale expressed in local space (relative to the parent bone). - *

- * The bind pose transform of the bone in local space is its "default" - * transform with no animation applied. - * - * @return the bind scale in local space. - */ - public Vector3f getBindScale() { - return bindScale; - } - - /** - * If enabled, user can control bone transform with setUserTransforms. - * Animation transforms are not applied to this bone when enabled. - * - * @param enable true for direct control, false for canned animations - */ - public void setUserControl(boolean enable) { - userControl = enable; - } - - /** - * Add a new child to this bone. Shouldn't be used by user code. - * Can corrupt skeleton. - * - * @param bone The bone to add - */ - public void addChild(Bone bone) { - children.add(bone); - bone.parent = this; - } - - /** - * - * @deprecated use {@link #updateModelTransforms() } - */ - @Deprecated - public final void updateWorldVectors() { - updateModelTransforms(); - } - - /** - * Updates the model transforms for this bone and, possibly, the attachments node - * if not null. - *

- * The model transform of this bone is computed by combining the parent's - * model transform with this bone's local transform. - */ - public final void updateModelTransforms() { - if (currentWeightSum == 1f) { - currentWeightSum = -1; - } else if (currentWeightSum != -1f) { - // Apply the weight to the local transform - if (currentWeightSum == 0) { - localRot.set(bindRot); - localPos.set(bindPos); - localScale.set(bindScale); - } else { - float invWeightSum = 1f - currentWeightSum; - localRot.nlerp(bindRot, invWeightSum); - localPos.interpolateLocal(bindPos, invWeightSum); - localScale.interpolateLocal(bindScale, invWeightSum); - } - - // Future invocations of transform blend will start over. - currentWeightSum = -1; - } - - if (parent != null) { - //rotation - parent.modelRot.mult(localRot, modelRot); - - //scale - //For scale parent scale is not taken into account! - // worldScale.set(localScale); - parent.modelScale.mult(localScale, modelScale); - - //translation - //scale and rotation of parent affect bone position - parent.modelRot.mult(localPos, modelPos); - modelPos.multLocal(parent.modelScale); - modelPos.addLocal(parent.modelPos); - } else { - modelRot.set(localRot); - modelPos.set(localPos); - modelScale.set(localScale); - } - - if (attachNode != null) { - updateAttachNode(); - } - } - - /** - * Update the local transform of the attachments node. - */ - private void updateAttachNode() { - Node attachParent = attachNode.getParent(); - if (attachParent == null || targetGeometry == null - || targetGeometry.getParent() == attachParent - && targetGeometry.getLocalTransform().isIdentity()) { - /* - * The animated meshes are in the same coordinate system as the - * attachments node: no further transforms are needed. - */ - attachNode.setLocalTranslation(modelPos); - attachNode.setLocalRotation(modelRot); - attachNode.setLocalScale(modelScale); - - } else if (targetGeometry.isIgnoreTransform()) { - /* - * The animated meshes ignore transforms: match the world transform - * of the attachments node to the bone's transform. - */ - attachNode.setLocalTranslation(modelPos); - attachNode.setLocalRotation(modelRot); - attachNode.setLocalScale(modelScale); - attachNode.getLocalTransform().combineWithParent(attachNode.getParent().getWorldTransform().invert()); - - } else { - Spatial loopSpatial = targetGeometry; - Transform combined = new Transform(modelPos, modelRot, modelScale); - /* - * Climb the scene graph applying local transforms until the - * attachments node's parent is reached. - */ - while (loopSpatial != attachParent && loopSpatial != null) { - Transform localTransform = loopSpatial.getLocalTransform(); - combined.combineWithParent(localTransform); - loopSpatial = loopSpatial.getParent(); - } - attachNode.setLocalTransform(combined); - } - } - - /** - * Updates world transforms for this bone and its children. - */ - public final void update() { - this.updateModelTransforms(); - - for (int i = children.size() - 1; i >= 0; i--) { - children.get(i).update(); - } - } - - /** - * Saves the current bone state as its binding pose, including its children. - */ - void setBindingPose() { - bindPos.set(localPos); - bindRot.set(localRot); - bindScale.set(localScale); - - if (modelBindInversePos == null) { - modelBindInversePos = new Vector3f(); - modelBindInverseRot = new Quaternion(); - modelBindInverseScale = new Vector3f(); - } - - // Save inverse derived position/scale/orientation, used for calculate offset transform later - modelBindInversePos.set(modelPos); - modelBindInversePos.negateLocal(); - - modelBindInverseRot.set(modelRot); - modelBindInverseRot.inverseLocal(); - - modelBindInverseScale.set(Vector3f.UNIT_XYZ); - modelBindInverseScale.divideLocal(modelScale); - - for (Bone b : children) { - b.setBindingPose(); - } - } - - /** - * Reset the bone and its children to bind pose. - */ - final void reset() { - if (!userControl) { - localPos.set(bindPos); - localRot.set(bindRot); - localScale.set(bindScale); - } - - for (int i = children.size() - 1; i >= 0; i--) { - children.get(i).reset(); - } - } - - /** - * Stores the skinning transform in the specified Matrix4f. - * The skinning transform applies the animation of the bone to a vertex. - * - * This assumes that the world transforms for the entire bone hierarchy - * have already been computed, otherwise this method will return undefined - * results. - * - * @param outTransform - */ - void getOffsetTransform(Matrix4f outTransform, Quaternion tmp1, Vector3f tmp2, Vector3f tmp3, Matrix3f tmp4) { - // Computing scale - Vector3f scale = modelScale.mult(modelBindInverseScale, tmp3); - - // Computing rotation - Quaternion rotate = modelRot.mult(modelBindInverseRot, tmp1); - - // Computing translation - // Translation depends on rotation and scale. - Vector3f translate = modelPos.add(rotate.mult(scale.mult(modelBindInversePos, tmp2), tmp2), tmp2); - - // Populating the matrix - outTransform.setTransform(translate, scale, rotate.toRotationMatrix(tmp4)); - } - - /** - * - * Sets the transforms of this bone in local space (relative to the parent bone) - * - * @param translation the translation in local space - * @param rotation the rotation in local space - * @param scale the scale in local space - */ - public void setUserTransforms(Vector3f translation, Quaternion rotation, Vector3f scale) { - if (!userControl) { - throw new IllegalStateException("You must call setUserControl(true) in order to setUserTransform to work"); - } - - localPos.set(bindPos); - localRot.set(bindRot); - localScale.set(bindScale); - - localPos.addLocal(translation); - localRot.multLocal(rotation); - localScale.multLocal(scale); - } - - /** - * Sets the transforms of this bone in model space (relative to the root bone) - * - * Must update all bones in skeleton for this to work. - * @param translation translation in model space - * @param rotation rotation in model space - */ - public void setUserTransformsInModelSpace(Vector3f translation, Quaternion rotation) { - if (!userControl) { - throw new IllegalStateException("You must call setUserControl(true) in order to setUserTransformsInModelSpace to work"); - } - - // TODO: add scale here ??? - modelPos.set(translation); - modelRot.set(rotation); - - // if there is an attached Node we need to set its local transforms too. - if (attachNode != null) { - attachNode.setLocalTranslation(translation); - attachNode.setLocalRotation(rotation); - } - } - - /** - * Returns the local transform of this bone combined with the given position and rotation - * @param position a position - * @param rotation a rotation - * @return the resulting Transform (in reusable temporary storage!) - */ - public Transform getCombinedTransform(Vector3f position, Quaternion rotation) { - if (tmpTransform == null) { - tmpTransform = new Transform(); - } - rotation.mult(localPos, tmpTransform.getTranslation()).addLocal(position); - tmpTransform.setRotation(rotation).getRotation().multLocal(localRot); - return tmpTransform; - } - - /** - * Access the attachments node of this bone. If this bone doesn't already - * have an attachments node, create one. Models and effects attached to the - * attachments node will follow this bone's motions. - * - * @param boneIndex this bone's index in its skeleton (≥0) - * @param targets a list of geometries animated by this bone's skeleton (not - * null, unaffected) - */ - Node getAttachmentsNode(int boneIndex, SafeArrayList targets) { - targetGeometry = null; - /* - * Search for a geometry animated by this particular bone. - */ - for (Geometry geometry : targets) { - Mesh mesh = geometry.getMesh(); - if (mesh != null && mesh.isAnimatedByBone(boneIndex)) { - targetGeometry = geometry; - break; - } - } - - if (attachNode == null) { - attachNode = new Node(name + "_attachnode"); - attachNode.setUserData("AttachedBone", this); - //We don't want the node to have NumberOfBones set by a parent node, so we force it to null. - attachNode.addMatParamOverride(new MatParamOverride(VarType.Int, "NumberOfBones", null)); - } - - return attachNode; - } - - /** - * Used internally after model cloning. - * @param attachNode - */ - void setAttachmentsNode(Node attachNode) { - this.attachNode = attachNode; - } - - /** - * Sets the local animation transform of this bone. - * Bone is assumed to be in bind pose when this is called. - */ - void setAnimTransforms(Vector3f translation, Quaternion rotation, Vector3f scale) { - if (userControl) { - return; - } - -// localPos.addLocal(translation); -// localRot.multLocal(rotation); - //localRot = localRot.mult(rotation); - - localPos.set(bindPos).addLocal(translation); - localRot.set(bindRot).multLocal(rotation); - - if (scale != null) { - localScale.set(bindScale).multLocal(scale); - } - } - - /** - * Blends the given animation transform onto the bone's local transform. - *

- * Subsequent calls of this method stack up, with the final transformation - * of the bone computed at {@link #updateModelTransforms() } which resets - * the stack. - *

- * E.g. a single transform blend with weight = 0.5 followed by an - * updateModelTransforms() call will result in final transform = transform * 0.5. - * Two transform blends with weight = 0.5 each will result in the two - * transforms blended together (nlerp) with blend = 0.5. - * - * @param translation The translation to blend in - * @param rotation The rotation to blend in - * @param scale The scale to blend in - * @param weight The weight of the transform to apply. Set to 1.0 to prevent - * any other transform from being applied until updateModelTransforms(). - */ - void blendAnimTransforms(Vector3f translation, Quaternion rotation, Vector3f scale, float weight) { - if (userControl) { - return; - } - - if (weight == 0) { - // Do not apply this transform at all. - return; - } - - if (currentWeightSum == 1) { - return; // More than 2 transforms are being blended - } else if (currentWeightSum == -1 || currentWeightSum == 0) { - // Set the transform fully - localPos.set(bindPos).addLocal(translation); - localRot.set(bindRot).multLocal(rotation); - if (scale != null) { - localScale.set(bindScale).multLocal(scale); - } - // Set the weight. It will be applied in updateModelTransforms(). - currentWeightSum = weight; - } else { - // The weight is already set. - // Blend in the new transform. - TempVars vars = TempVars.get(); - - Vector3f tmpV = vars.vect1; - Vector3f tmpV2 = vars.vect2; - Quaternion tmpQ = vars.quat1; - - tmpV.set(bindPos).addLocal(translation); - localPos.interpolateLocal(tmpV, weight); - - tmpQ.set(bindRot).multLocal(rotation); - localRot.nlerp(tmpQ, weight); - - if (scale != null) { - tmpV2.set(bindScale).multLocal(scale); - localScale.interpolateLocal(tmpV2, weight); - } - - // Ensures no new weights will be blended in the future. - currentWeightSum = 1; - - vars.release(); - } - } - - /** - * Sets the local bind transform of the bone. - * Call setBindingPose() after all of the bones' bind transforms are set to save them. - * - * @param translation the desired bind translation (not null, unaffected) - * @param rotation the desired bind rotation (not null, unaffected) - * @param scale the desired bind scale (unaffected) or null for no scaling - */ - public void setBindTransforms(Vector3f translation, Quaternion rotation, Vector3f scale) { - bindPos.set(translation); - bindRot.set(rotation); - //ogre.xml can have null scale values breaking this if the check is removed - if (scale != null) { - bindScale.set(scale); - } - - localPos.set(translation); - localRot.set(rotation); - if (scale != null) { - localScale.set(scale); - } - } - - private String toString(int depth) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < depth; i++) { - sb.append('-'); - } - - sb.append(name).append(" bone\n"); - for (Bone child : children) { - sb.append(child.toString(depth + 1)); - } - return sb.toString(); - } - - @Override - public String toString() { - return this.toString(0); - } - - @Override - @SuppressWarnings("unchecked") - public void read(JmeImporter im) throws IOException { - InputCapsule input = im.getCapsule(this); - - name = input.readString("name", null); - int ver = input.getSavableVersion(Bone.class); - if (ver < 2) { - bindPos = (Vector3f) input.readSavable("initialPos", null); - bindRot = (Quaternion) input.readSavable("initialRot", null); - bindScale = (Vector3f) input.readSavable("initialScale", new Vector3f(1.0f, 1.0f, 1.0f)); - } else { - bindPos = (Vector3f) input.readSavable("bindPos", null); - bindRot = (Quaternion) input.readSavable("bindRot", null); - bindScale = (Vector3f) input.readSavable("bindScale", new Vector3f(1.0f, 1.0f, 1.0f)); - } - - attachNode = (Node) input.readSavable("attachNode", null); - targetGeometry = (Geometry) input.readSavable("targetGeometry", null); - - localPos.set(bindPos); - localRot.set(bindRot); - localScale.set(bindScale); - - ArrayList childList = input.readSavableArrayList("children", null); - for (int i = childList.size() - 1; i >= 0; i--) { - this.addChild(childList.get(i)); - } - - // NOTE: Parent skeleton will call update() then setBindingPose() - // after Skeleton has been de-serialized. - // Therefore, worldBindInversePos and worldBindInverseRot - // will be reconstructed based on that information. - } - - @Override - public void write(JmeExporter ex) throws IOException { - OutputCapsule output = ex.getCapsule(this); - - output.write(name, "name", null); - output.write(attachNode, "attachNode", null); - output.write(targetGeometry, "targetGeometry", null); - output.write(bindPos, "bindPos", null); - output.write(bindRot, "bindRot", null); - output.write(bindScale, "bindScale", new Vector3f(1.0f, 1.0f, 1.0f)); - output.writeSavableArrayList(children, "children", null); - } - - /** - * Sets the rotation of the bone in object space. - * Warning: you need to call {@link #setUserControl(boolean)} with true to be able to do that operation - * - * @param rot the desired rotation (not null, unaffected) - */ - public void setLocalRotation(Quaternion rot) { - if (!userControl) { - throw new IllegalStateException("User control must be on bone to allow user transforms"); - } - this.localRot.set(rot); - } - - /** - * Sets the position of the bone in object space. - * Warning: you need to call {@link #setUserControl(boolean)} with true to be able to do that operation - * - * @param pos the desired translation (not null, unaffected) - */ - public void setLocalTranslation(Vector3f pos) { - if (!userControl) { - throw new IllegalStateException("User control must be on bone to allow user transforms"); - } - this.localPos.set(pos); - } - - /** - * Sets the scale of the bone in object space. - * Warning: you need to call {@link #setUserControl(boolean)} with true to be able to do that operation - * @param scale the scale to apply - */ - public void setLocalScale(Vector3f scale) { - if (!userControl) { - throw new IllegalStateException("User control must be on bone to allow user transforms"); - } - this.localScale.set(scale); - } - - /** - * returns true if this bone can be directly manipulated by the user. - * @see #setUserControl(boolean) - * @return true if it can be manipulated - */ - public boolean hasUserControl() { - return userControl; - } -} diff --git a/jme3-core/src/main/java/com/jme3/animation/BoneTrack.java b/jme3-core/src/main/java/com/jme3/animation/BoneTrack.java deleted file mode 100644 index 88781f8a08..0000000000 --- a/jme3-core/src/main/java/com/jme3/animation/BoneTrack.java +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.export.*; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector3f; -import com.jme3.util.TempVars; -import com.jme3.util.clone.Cloner; -import com.jme3.util.clone.JmeCloneable; -import java.io.IOException; -import java.util.BitSet; - -/** - * Contains a list of transforms and times for each keyframe. - * - * @author Kirill Vainer - * @deprecated use {@link com.jme3.anim.AnimTrack} - */ -@Deprecated -public final class BoneTrack implements JmeCloneable, Track { - - /** - * Bone index in the skeleton which this track affects. - */ - private int targetBoneIndex; - - /** - * Transforms and times for track. - */ - private CompactVector3Array translations; - private CompactQuaternionArray rotations; - private CompactVector3Array scales; - private float[] times; - - /** - * Serialization-only. Do not use. - */ - protected BoneTrack() { - } - - /** - * Creates a bone track for the given bone index - * @param targetBoneIndex the bone index - * @param times a float array with the time of each frame - * @param translations the translation of the bone for each frame - * @param rotations the rotation of the bone for each frame - */ - public BoneTrack(int targetBoneIndex, float[] times, Vector3f[] translations, Quaternion[] rotations) { - this.targetBoneIndex = targetBoneIndex; - this.setKeyframes(times, translations, rotations); - } - - /** - * Creates a bone track for the given bone index - * @param targetBoneIndex the bone index - * @param times a float array with the time of each frame - * @param translations the translation of the bone for each frame - * @param rotations the rotation of the bone for each frame - * @param scales the scale of the bone for each frame - */ - public BoneTrack(int targetBoneIndex, float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) { - this.targetBoneIndex = targetBoneIndex; - this.setKeyframes(times, translations, rotations, scales); - } - - /** - * Creates a bone track for the given bone index - * @param targetBoneIndex the bone's index - */ - public BoneTrack(int targetBoneIndex) { - this.targetBoneIndex = targetBoneIndex; - } - - /** - * @return the bone index of this bone track. - */ - public int getTargetBoneIndex() { - return targetBoneIndex; - } - - /** - * return the array of rotations of this track - * @return an array - */ - public Quaternion[] getRotations() { - return rotations.toObjectArray(); - } - - /** - * returns the array of scales for this track - * @return an array or null - */ - public Vector3f[] getScales() { - return scales == null ? null : scales.toObjectArray(); - } - - /** - * returns the arrays of time for this track - * @return the pre-existing array - */ - public float[] getTimes() { - return times; - } - - /** - * returns the array of translations of this track - * @return an array - */ - public Vector3f[] getTranslations() { - return translations.toObjectArray(); - } - - /** - * Set the translations and rotations for this bone track - * - * @param times the time of each frame, measured from the start of the track - * (not null, length>0) - * @param translations the translation of the bone for each frame (not null, - * same length as times) - * @param rotations the rotation of the bone for each frame (not null, same - * length as times) - */ - public void setKeyframes(float[] times, Vector3f[] translations, Quaternion[] rotations) { - if (times.length == 0) { - throw new RuntimeException("BoneTrack with no keyframes!"); - } - - assert translations != null; - assert times.length == translations.length; - assert rotations != null; - assert times.length == rotations.length; - - this.times = times; - this.translations = new CompactVector3Array(); - this.translations.add(translations); - this.translations.freeze(); - this.rotations = new CompactQuaternionArray(); - this.rotations.add(rotations); - this.rotations.freeze(); - } - - /** - * Set the translations, rotations and scales for this bone track - * - * @param times the time of each frame, measured from the start of the track - * (not null, length>0) - * @param translations the translation of the bone for each frame (not null, - * same length as times) - * @param rotations the rotation of the bone for each frame (not null, same - * length as times) - * @param scales the scale of the bone for each frame (ignored if null) - */ - public void setKeyframes(float[] times, Vector3f[] translations, Quaternion[] rotations, Vector3f[] scales) { - this.setKeyframes(times, translations, rotations); - if (scales != null) { - assert times.length == scales.length; - this.scales = new CompactVector3Array(); - this.scales.add(scales); - this.scales.freeze(); - } - } - - /** - * - * Modify the bone which this track modifies in the skeleton to contain - * the correct animation transforms for a given time. - * The transforms can be interpolated in some method from the keyframes. - * - * @param time the current time of the animation - * @param weight the weight of the animation - * @param control to access the Skeleton - * @param channel which bones can be affected - * @param vars storage for temporary values - */ - @Override - public void setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars) { - BitSet affectedBones = channel.getAffectedBones(); - if (affectedBones != null && !affectedBones.get(targetBoneIndex)) { - return; - } - - Bone target = control.getSkeleton().getBone(targetBoneIndex); - - Vector3f tempV = vars.vect1; - Vector3f tempS = vars.vect2; - Quaternion tempQ = vars.quat1; - Vector3f tempV2 = vars.vect3; - Vector3f tempS2 = vars.vect4; - Quaternion tempQ2 = vars.quat2; - - int lastFrame = times.length - 1; - if (time < 0 || lastFrame == 0) { - rotations.get(0, tempQ); - translations.get(0, tempV); - if (scales != null) { - scales.get(0, tempS); - } - } else if (time >= times[lastFrame]) { - rotations.get(lastFrame, tempQ); - translations.get(lastFrame, tempV); - if (scales != null) { - scales.get(lastFrame, tempS); - } - } else { - int startFrame = 0; - int endFrame = 1; - // use lastFrame so we never overflow the array - int i; - for (i = 0; i < lastFrame && times[i] < time; i++) { - startFrame = i; - endFrame = i + 1; - } - - float blend = (time - times[startFrame]) - / (times[endFrame] - times[startFrame]); - - rotations.get(startFrame, tempQ); - translations.get(startFrame, tempV); - if (scales != null) { - scales.get(startFrame, tempS); - } - rotations.get(endFrame, tempQ2); - translations.get(endFrame, tempV2); - if (scales != null) { - scales.get(endFrame, tempS2); - } - tempQ.nlerp(tempQ2, blend); - tempV.interpolateLocal(tempV2, blend); - tempS.interpolateLocal(tempS2, blend); - } - -// if (weight != 1f) { - target.blendAnimTransforms(tempV, tempQ, scales != null ? tempS : null, weight); -// } else { -// target.setAnimTransforms(tempV, tempQ, scales != null ? tempS : null); -// } - } - - /** - * @return the length of the track - */ - @Override - public float getLength() { - return times == null ? 0 : times[times.length - 1] - times[0]; - } - - @Override - public float[] getKeyFrameTimes() { - return times; - } - - /** - * Create a deep clone of this track. - * - * @return a new track - */ - @Override - public BoneTrack clone() { - return Cloner.deepClone(this); - } - - /** - * Create a shallow clone for the JME cloner. - * - * @return a new track - */ - @Override - public BoneTrack jmeClone() { - try { - return (BoneTrack) super.clone(); - } catch (CloneNotSupportedException exception) { - throw new RuntimeException("Can't clone track", exception); - } - } - - /** - * Callback from {@link com.jme3.util.clone.Cloner} to convert this - * shallow-cloned track into a deep-cloned one, using the specified cloner - * to resolve copied fields. - * - * @param cloner the cloner currently cloning this control (not null) - * @param original the track from which this track was shallow-cloned - * (unused) - */ - @Override - public void cloneFields(Cloner cloner, Object original) { - translations = cloner.clone(translations); - rotations = cloner.clone(rotations); - scales = cloner.clone(scales); - times = cloner.clone(times); - } - - @Override - public void write(JmeExporter ex) throws IOException { - OutputCapsule oc = ex.getCapsule(this); - oc.write(targetBoneIndex, "boneIndex", 0); - oc.write(translations, "translations", null); - oc.write(rotations, "rotations", null); - oc.write(times, "times", null); - oc.write(scales, "scales", null); - } - - @Override - public void read(JmeImporter im) throws IOException { - InputCapsule ic = im.getCapsule(this); - targetBoneIndex = ic.readInt("boneIndex", 0); - - translations = (CompactVector3Array) ic.readSavable("translations", null); - rotations = (CompactQuaternionArray) ic.readSavable("rotations", null); - times = ic.readFloatArray("times", null); - scales = (CompactVector3Array) ic.readSavable("scales", null); - - //Backward compatibility for old j3o files generated before revision 6807 - if (im.getFormatVersion() == 0) { - if (translations == null) { - Savable[] sav = ic.readSavableArray("translations", null); - if (sav != null) { - translations = new CompactVector3Array(); - Vector3f[] transCopy = new Vector3f[sav.length]; - System.arraycopy(sav, 0, transCopy, 0, sav.length); - translations.add(transCopy); - translations.freeze(); - } - } - if (rotations == null) { - Savable[] sav = ic.readSavableArray("rotations", null); - if (sav != null) { - rotations = new CompactQuaternionArray(); - Quaternion[] rotCopy = new Quaternion[sav.length]; - System.arraycopy(sav, 0, rotCopy, 0, sav.length); - rotations.add(rotCopy); - rotations.freeze(); - } - } - } - } - - public void setTime(float time, float weight, AnimControl control, AnimChannel channel) { - throw new UnsupportedOperationException("Not supported yet."); - } -} diff --git a/jme3-core/src/main/java/com/jme3/animation/ClonableTrack.java b/jme3-core/src/main/java/com/jme3/animation/ClonableTrack.java deleted file mode 100644 index 6c7da6636f..0000000000 --- a/jme3-core/src/main/java/com/jme3/animation/ClonableTrack.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.scene.Spatial; -import com.jme3.util.clone.JmeCloneable; - -/** - * An interface that allow to clone a Track for a given Spatial. - * The spatial fed to the method is the Spatial holding the AnimControl controlling the Animation using this track. - * - * Implement this interface only if you make your own Savable Track and that the track has a direct reference to a Spatial in the scene graph. - * This Spatial is assumed to be a child of the spatial holding the AnimControl. - * - * - * @author Nehon - */ -@Deprecated -public interface ClonableTrack extends Track, JmeCloneable { - - /** - * Allows to clone the track for a given Spatial. - * The spatial fed to the method is the Spatial holding the AnimControl controlling the Animation using this track. - * This method will be called during the loading process of a j3o model by the assetManager. - * The assetManager keeps the original model in cache and returns a clone of the model. - * - * This method purpose is to find the cloned reference of the original spatial which it refers to in the cloned model. - * - * See EffectTrack for a proper implementation. - * - * @param spatial the spatial holding the AnimControl - * @return the cloned Track - */ - public Track cloneForSpatial(Spatial spatial); - - /** - * Method responsible for cleaning UserData on referenced Spatials when the Track is deleted - */ - public void cleanUp(); -} diff --git a/jme3-core/src/main/java/com/jme3/animation/EffectTrack.java b/jme3-core/src/main/java/com/jme3/animation/EffectTrack.java deleted file mode 100644 index 60a0f30dae..0000000000 --- a/jme3-core/src/main/java/com/jme3/animation/EffectTrack.java +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.effect.ParticleEmitter; -import com.jme3.export.*; -import com.jme3.renderer.RenderManager; -import com.jme3.renderer.ViewPort; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; -import com.jme3.scene.Spatial.CullHint; -import com.jme3.scene.control.AbstractControl; -import com.jme3.util.TempVars; -import com.jme3.util.clone.Cloner; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * EffectTrack is a track to add to an existing animation, to emit particles - * during animations for example: exhaust, dust raised by footsteps, shock - * waves, lightning, etc... - * - * usage is - *

- * AnimControl control model.getControl(AnimControl.class);
- * EffectTrack track = new EffectTrack(existingEmitter, control.getAnim("TheAnim").getLength());
- * control.getAnim("TheAnim").addTrack(track);
- * 
- * - * if the emitter emits 0 particles per second, emitAllPArticles will be - * called on it at time 0 + startOffset. if it has more it will start - * emitting normally at time 0 + startOffset. - * - * - * @author Nehon - */ -@Deprecated -public class EffectTrack implements ClonableTrack { - - private static final Logger logger = Logger.getLogger(EffectTrack.class.getName()); - private ParticleEmitter emitter; - private float startOffset = 0; - private float particlesPerSeconds = 0; - private float length = 0; - private boolean emitted = false; - private boolean initialized = false; - //control responsible for disable and cull the emitter once all particles are gone - final private KillParticleControl killParticles = new KillParticleControl(); - - public static class KillParticleControl extends AbstractControl { - - ParticleEmitter emitter; - boolean stopRequested = false; - boolean remove = false; - - public KillParticleControl() { - } - - @Override - public void setSpatial(Spatial spatial) { - super.setSpatial(spatial); - if (spatial != null) { - if (spatial instanceof ParticleEmitter) { - emitter = (ParticleEmitter) spatial; - } else { - throw new IllegalArgumentException("KillParticleEmitter can only ba attached to ParticleEmitter"); - } - } - } - - @Override - protected void controlUpdate(float tpf) { - if (remove) { - emitter.removeControl(this); - return; - } - if (emitter.getNumVisibleParticles() == 0) { - emitter.setCullHint(CullHint.Always); - emitter.setEnabled(false); - emitter.removeControl(this); - stopRequested = false; - } - } - - @Override - public Object jmeClone() { - KillParticleControl c = new KillParticleControl(); - //this control should be removed as it shouldn't have been persisted in the first place - //In the quest to find the less hackish solution to achieve this, - //making it remove itself from the spatial in the first update loop when loaded was the less bad. - c.remove = true; - c.spatial = spatial; - return c; - } - - @Override - protected void controlRender(RenderManager rm, ViewPort vp) { - } - } - - //Anim listener that stops the emitter when the animation is finished or changed. - private class OnEndListener implements AnimEventListener { - - @Override - public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) { - stop(); - } - - @Override - public void onAnimChange(AnimControl control, AnimChannel channel, String animName) { - } - } - - /** - * constructor only for serialization - */ - protected EffectTrack() { - } - - /** - * Creates and EffectTrack - * - * @param emitter the emitter of the track - * @param length the length of the track (usually the length of the - * animation you want to add the track to) - */ - public EffectTrack(ParticleEmitter emitter, float length) { - this.emitter = emitter; - //saving particles per second value - this.particlesPerSeconds = emitter.getParticlesPerSec(); - //setting the emitter to not emit. - this.emitter.setParticlesPerSec(0); - this.length = length; - //Marking the emitter with a reference to this track for further use in deserialization. - setUserData(this); - - } - - /** - * Creates and EffectTrack - * - * @param emitter the emitter of the track - * @param length the length of the track (usually the length of the - * animation you want to add the track to) - * @param startOffset the time in second when the emitter will be triggered - * after the animation starts (default is 0) - */ - public EffectTrack(ParticleEmitter emitter, float length, float startOffset) { - this(emitter, length); - this.startOffset = startOffset; - } - - /** - * Internal use only - * - * @see Track#setTime(float, float, com.jme3.animation.AnimControl, - * com.jme3.animation.AnimChannel, com.jme3.util.TempVars) - */ - @Override - public void setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars) { - - if (time >= length) { - return; - } - //first time adding the Animation listener to stop the track at the end of the animation - if (!initialized) { - control.addListener(new OnEndListener()); - initialized = true; - } - //checking for time to trigger the effect - if (!emitted && time >= startOffset) { - emitted = true; - emitter.setCullHint(CullHint.Dynamic); - emitter.setEnabled(true); - //if the emitter has 0 particles per second, emit all particles in one shot - if (particlesPerSeconds == 0) { - emitter.emitAllParticles(); - if (!killParticles.stopRequested) { - emitter.addControl(killParticles); - killParticles.stopRequested = true; - } - } else { - //else reset its former particlePerSec value to let it emit. - emitter.setParticlesPerSec(particlesPerSeconds); - } - } - } - - // Stop the emitter from emitting. - private void stop() { - emitter.setParticlesPerSec(0); - emitted = false; - if (!killParticles.stopRequested) { - emitter.addControl(killParticles); - killParticles.stopRequested = true; - } - - } - - /** - * Return the length of the track - * - * @return length of the track - */ - @Override - public float getLength() { - return length; - } - - @Override - public float[] getKeyFrameTimes() { - return new float[]{startOffset}; - } - - /** - * Clone this track - * - * @return a new instance - */ - @Override - public EffectTrack clone() { - return new EffectTrack(emitter, length, startOffset); - } - - /** - * This method clone the Track and search for the cloned counterpart of the - * original emitter in the given cloned spatial. The spatial is assumed to - * be the Spatial holding the AnimControl controlling the animation using - * this Track. - * - * @param spatial the Spatial holding the AnimControl - * @return the cloned Track with proper reference - */ - @Override - public Track cloneForSpatial(Spatial spatial) { - EffectTrack effectTrack = new EffectTrack(); - effectTrack.particlesPerSeconds = this.particlesPerSeconds; - effectTrack.length = this.length; - effectTrack.startOffset = this.startOffset; - - //searching for the newly cloned ParticleEmitter - effectTrack.emitter = findEmitter(spatial); - if (effectTrack.emitter == null) { - logger.log(Level.WARNING, "{0} was not found in {1} or is not bound to this track", new Object[]{emitter.getName(), spatial.getName()}); - effectTrack.emitter = emitter; - } - - removeUserData(this); - //setting user data on the new emitter and marking it with a reference to the cloned Track. - setUserData(effectTrack); - effectTrack.emitter.setParticlesPerSec(0); - return effectTrack; - } - - @Override - public Object jmeClone() { - try { - return super.clone(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException("Error cloning", e); - } - } - - @Override - public void cloneFields(Cloner cloner, Object original) { - this.emitter = cloner.clone(emitter); - } - - /** - * recursive function responsible for finding the newly cloned Emitter - * - * @param spat - * @return - */ - private ParticleEmitter findEmitter(Spatial spat) { - if (spat instanceof ParticleEmitter) { - //spat is a PArticleEmitter - ParticleEmitter em = (ParticleEmitter) spat; - //getting the UserData TrackInfo so check if it should be attached to this Track - TrackInfo t = (TrackInfo) em.getUserData("TrackInfo"); - if (t != null && t.getTracks().contains(this)) { - return em; - } - return null; - - } else if (spat instanceof Node) { - for (Spatial child : ((Node) spat).getChildren()) { - ParticleEmitter em = findEmitter(child); - if (em != null) { - return em; - } - } - } - return null; - } - - @Override - public void cleanUp() { - TrackInfo t = (TrackInfo) emitter.getUserData("TrackInfo"); - t.getTracks().remove(this); - if (t.getTracks().isEmpty()) { - emitter.setUserData("TrackInfo", null); - } - } - - /** - * - * @return the emitter used by this track - */ - public ParticleEmitter getEmitter() { - return emitter; - } - - /** - * Sets the Emitter to use in this track - * - * @param emitter the emitter to be controlled (alias created) - */ - public void setEmitter(ParticleEmitter emitter) { - if (this.emitter != null) { - TrackInfo data = (TrackInfo) emitter.getUserData("TrackInfo"); - data.getTracks().remove(this); - } - this.emitter = emitter; - //saving particles per second value - this.particlesPerSeconds = emitter.getParticlesPerSec(); - //setting the emitter to not emit. - this.emitter.setParticlesPerSec(0); - setUserData(this); - } - - /** - * - * @return the start offset of the track - */ - public float getStartOffset() { - return startOffset; - } - - /** - * set the start offset of the track - * - * @param startOffset the start offset (in seconds) - */ - public void setStartOffset(float startOffset) { - this.startOffset = startOffset; - } - - private void setUserData(EffectTrack effectTrack) { - //fetching the UserData TrackInfo. - TrackInfo data = (TrackInfo) effectTrack.emitter.getUserData("TrackInfo"); - - //if it does not exist, we create it and attach it to the emitter. - if (data == null) { - data = new TrackInfo(); - effectTrack.emitter.setUserData("TrackInfo", data); - } - - //adding the given Track to the TrackInfo. - data.addTrack(effectTrack); - } - - private void removeUserData(EffectTrack effectTrack) { - //fetching the UserData TrackInfo. - TrackInfo data = (TrackInfo) effectTrack.emitter.getUserData("TrackInfo"); - - //if it does not exist, we create it and attach it to the emitter. - if (data == null) { - return; - } - - //removing the given Track to the TrackInfo. - data.getTracks().remove(effectTrack); - } - - /** - * Internal use only serialization - * - * @param ex exporter - * @throws IOException exception - */ - @Override - public void write(JmeExporter ex) throws IOException { - OutputCapsule out = ex.getCapsule(this); - //reset the particle emission rate on the emitter before saving. - emitter.setParticlesPerSec(particlesPerSeconds); - out.write(emitter, "emitter", null); - out.write(particlesPerSeconds, "particlesPerSeconds", 0); - out.write(length, "length", 0); - out.write(startOffset, "startOffset", 0); - //Setting emission rate to 0 so that this track can go on being used. - emitter.setParticlesPerSec(0); - } - - /** - * Internal use only serialization - * - * @param im importer - * @throws IOException Exception - */ - @Override - public void read(JmeImporter im) throws IOException { - InputCapsule in = im.getCapsule(this); - this.particlesPerSeconds = in.readFloat("particlesPerSeconds", 0); - //reading the emitter even if the track will then reference its cloned counterpart if it's loaded with the assetManager. - //This also avoid null pointer exception if the model is not loaded via the AssetManager. - emitter = (ParticleEmitter) in.readSavable("emitter", null); - emitter.setParticlesPerSec(0); - //if the emitter was saved with a KillParticleControl we remove it. -// Control c = emitter.getControl(KillParticleControl.class); -// if(c!=null){ -// emitter.removeControl(c); -// } - //emitter.removeControl(KillParticleControl.class); - length = in.readFloat("length", length); - startOffset = in.readFloat("startOffset", 0); - } -} diff --git a/jme3-core/src/main/java/com/jme3/animation/Pose.java b/jme3-core/src/main/java/com/jme3/animation/Pose.java deleted file mode 100644 index f2208e1937..0000000000 --- a/jme3-core/src/main/java/com/jme3/animation/Pose.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2009-2020 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.export.*; -import com.jme3.math.Vector3f; -import com.jme3.util.BufferUtils; - -import java.io.IOException; -import java.nio.FloatBuffer; - -/** - * A pose is a list of offsets that say where a mesh vertices should be for this pose. - */ -@Deprecated -public final class Pose implements Savable, Cloneable { - - private String name; - private int targetMeshIndex; - - private Vector3f[] offsets; - private int[] indices; - - private transient final Vector3f tempVec = new Vector3f(); - private transient final Vector3f tempVec2 = new Vector3f(); - - public Pose(String name, int targetMeshIndex, Vector3f[] offsets, int[] indices) { - this.name = name; - this.targetMeshIndex = targetMeshIndex; - this.offsets = offsets; - this.indices = indices; - } - - /** - * Serialization-only. Do not use. - */ - protected Pose() { - } - - public int getTargetMeshIndex() { - return targetMeshIndex; - } - - /** - * Applies the offsets of this pose to the vertex buffer given by the blend factor. - * - * @param blend Blend factor, 0 = no change to vertex buffer, 1 = apply full offsets - * @param vertexBuffer Vertex buffer to apply this pose to - */ - public void apply(float blend, FloatBuffer vertexBuffer) { - for (int i = 0; i < indices.length; i++) { - Vector3f offset = offsets[i]; - int vertIndex = indices[i]; - - tempVec.set(offset).multLocal(blend); - - // acquire vertex - BufferUtils.populateFromBuffer(tempVec2, vertexBuffer, vertIndex); - - // add offset multiplied by factor - tempVec2.addLocal(tempVec); - - // write modified vertex - BufferUtils.setInBuffer(tempVec2, vertexBuffer, vertIndex); - } - } - - /** - * This method creates a clone of the current object. - * @return a clone of the current object - */ - @Override - public Pose clone() { - try { - Pose result = (Pose) super.clone(); - result.indices = this.indices.clone(); - if (this.offsets != null) { - result.offsets = new Vector3f[this.offsets.length]; - for (int i = 0; i < this.offsets.length; ++i) { - result.offsets[i] = this.offsets[i].clone(); - } - } - return result; - } catch (CloneNotSupportedException e) { - throw new AssertionError(); - } - } - - @Override - public void write(JmeExporter e) throws IOException { - OutputCapsule out = e.getCapsule(this); - out.write(name, "name", ""); - out.write(targetMeshIndex, "meshIndex", -1); - out.write(offsets, "offsets", null); - out.write(indices, "indices", null); - } - - @Override - public void read(JmeImporter i) throws IOException { - InputCapsule in = i.getCapsule(this); - name = in.readString("name", ""); - targetMeshIndex = in.readInt("meshIndex", -1); - indices = in.readIntArray("indices", null); - - Savable[] readSavableArray = in.readSavableArray("offsets", null); - if (readSavableArray != null) { - offsets = new Vector3f[readSavableArray.length]; - System.arraycopy(readSavableArray, 0, offsets, 0, readSavableArray.length); - } - } -} diff --git a/jme3-core/src/main/java/com/jme3/animation/PoseTrack.java b/jme3-core/src/main/java/com/jme3/animation/PoseTrack.java deleted file mode 100644 index 4a7405ef7c..0000000000 --- a/jme3-core/src/main/java/com/jme3/animation/PoseTrack.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.export.*; -import com.jme3.util.TempVars; -import java.io.IOException; - -/** - * A single track of pose animation associated with a certain mesh. - */ -@Deprecated -public final class PoseTrack implements Track { - private int targetMeshIndex; - private PoseFrame[] frames; - private float[] times; - - public static class PoseFrame implements Savable, Cloneable { - - Pose[] poses; - float[] weights; - - public PoseFrame(Pose[] poses, float[] weights) { - this.poses = poses; - this.weights = weights; - } - - /** - * Serialization-only. Do not use. - */ - protected PoseFrame() { - } - - /** - * This method creates a clone of the current object. - * @return a clone of the current object - */ - @Override - public PoseFrame clone() { - try { - PoseFrame result = (PoseFrame) super.clone(); - result.weights = this.weights.clone(); - if (this.poses != null) { - result.poses = new Pose[this.poses.length]; - for (int i = 0; i < this.poses.length; ++i) { - result.poses[i] = this.poses[i].clone(); - } - } - return result; - } catch (CloneNotSupportedException e) { - throw new AssertionError(); - } - } - - @Override - public void write(JmeExporter e) throws IOException { - OutputCapsule out = e.getCapsule(this); - out.write(poses, "poses", null); - out.write(weights, "weights", null); - } - - @Override - public void read(JmeImporter i) throws IOException { - InputCapsule in = i.getCapsule(this); - weights = in.readFloatArray("weights", null); - - Savable[] readSavableArray = in.readSavableArray("poses", null); - if (readSavableArray != null) { - poses = new Pose[readSavableArray.length]; - System.arraycopy(readSavableArray, 0, poses, 0, readSavableArray.length); - } - } - } - - public PoseTrack(int targetMeshIndex, float[] times, PoseFrame[] frames) { - this.targetMeshIndex = targetMeshIndex; - this.times = times; - this.frames = frames; - } - - /** - * Serialization-only. Do not use. - */ - protected PoseTrack() { - } - - @Override - public void setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars) { - // TODO: When MeshControl is created, it will gather targets - // list automatically which is then retrieved here. - - /* - Mesh target = targets[targetMeshIndex]; - if (time < times[0]) { - applyFrame(target, 0, weight); - } else if (time > times[times.length - 1]) { - applyFrame(target, times.length - 1, weight); - } else { - int startFrame = 0; - for (int i = 0; i < times.length; i++) { - if (times[i] < time) { - startFrame = i; - } - } - - int endFrame = startFrame + 1; - float blend = (time - times[startFrame]) / (times[endFrame] - times[startFrame]); - applyFrame(target, startFrame, blend * weight); - applyFrame(target, endFrame, (1f - blend) * weight); - } - */ - } - - /** - * @return the length of the track - */ - @Override - public float getLength() { - return times == null ? 0 : times[times.length - 1] - times[0]; - } - - @Override - public float[] getKeyFrameTimes() { - return times; - } - - /** - * This method creates a clone of the current object. - * @return a clone of the current object - */ - @Override - public PoseTrack clone() { - try { - PoseTrack result = (PoseTrack) super.clone(); - result.times = this.times.clone(); - if (this.frames != null) { - result.frames = new PoseFrame[this.frames.length]; - for (int i = 0; i < this.frames.length; ++i) { - result.frames[i] = this.frames[i].clone(); - } - } - return result; - } catch (CloneNotSupportedException e) { - throw new AssertionError(); - } - } - - @Override - public void write(JmeExporter e) throws IOException { - OutputCapsule out = e.getCapsule(this); - out.write(targetMeshIndex, "meshIndex", 0); - out.write(frames, "frames", null); - out.write(times, "times", null); - } - - @Override - public void read(JmeImporter i) throws IOException { - InputCapsule in = i.getCapsule(this); - targetMeshIndex = in.readInt("meshIndex", 0); - times = in.readFloatArray("times", null); - - Savable[] readSavableArray = in.readSavableArray("frames", null); - if (readSavableArray != null) { - frames = new PoseFrame[readSavableArray.length]; - System.arraycopy(readSavableArray, 0, frames, 0, readSavableArray.length); - } - } -} diff --git a/jme3-core/src/main/java/com/jme3/animation/Skeleton.java b/jme3-core/src/main/java/com/jme3/animation/Skeleton.java deleted file mode 100644 index 28a8515b2d..0000000000 --- a/jme3-core/src/main/java/com/jme3/animation/Skeleton.java +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.anim.Armature; -import com.jme3.export.*; -import com.jme3.math.Matrix4f; -import com.jme3.util.TempVars; -import com.jme3.util.clone.Cloner; -import com.jme3.util.clone.JmeCloneable; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * Skeleton is a convenience class for managing a bone hierarchy. - * Skeleton updates the world transforms to reflect the current local - * animated matrixes. - * - * @author Kirill Vainer - * @deprecated use {@link Armature} - */ -@Deprecated -public final class Skeleton implements Savable, JmeCloneable { - - private Bone[] rootBones; - private Bone[] boneList; - - /** - * Contains the skinning matrices, multiplying it by a vertex affected by a bone - * will cause it to go to the animated position. - */ - private transient Matrix4f[] skinningMatrixes; - - /** - * Creates a skeleton from a bone list. - * The root bones are found automatically. - *

- * Note that using this constructor will cause the bones in the list - * to have their bind pose recomputed based on their local transforms. - * - * @param boneList The list of bones to manage by this Skeleton - */ - public Skeleton(Bone[] boneList) { - this.boneList = boneList; - - List rootBoneList = new ArrayList<>(); - for (int i = boneList.length - 1; i >= 0; i--) { - Bone b = boneList[i]; - if (b.getParent() == null) { - rootBoneList.add(b); - } - } - rootBones = rootBoneList.toArray(new Bone[rootBoneList.size()]); - - createSkinningMatrices(); - - for (int i = rootBones.length - 1; i >= 0; i--) { - Bone rootBone = rootBones[i]; - rootBone.update(); - rootBone.setBindingPose(); - } - } - - /** - * Special-purpose copy constructor. - *

- * Shallow copies bind pose data from the source skeleton, does not - * copy any other data. - * - * @param source The source Skeleton to copy from - */ - public Skeleton(Skeleton source) { - Bone[] sourceList = source.boneList; - boneList = new Bone[sourceList.length]; - for (int i = 0; i < sourceList.length; i++) { - boneList[i] = new Bone(sourceList[i]); - } - - rootBones = new Bone[source.rootBones.length]; - for (int i = 0; i < rootBones.length; i++) { - rootBones[i] = recreateBoneStructure(source.rootBones[i]); - } - createSkinningMatrices(); - - for (int i = rootBones.length - 1; i >= 0; i--) { - rootBones[i].update(); - } - } - - /** - * Serialization only. Do not use. - */ - protected Skeleton() { - } - - @Override - public Object jmeClone() { - try { - Skeleton clone = (Skeleton) super.clone(); - return clone; - } catch (CloneNotSupportedException ex) { - throw new AssertionError(); - } - } - - @Override - public void cloneFields(Cloner cloner, Object original) { - this.rootBones = cloner.clone(rootBones); - this.boneList = cloner.clone(boneList); - this.skinningMatrixes = cloner.clone(skinningMatrixes); - } - - private void createSkinningMatrices() { - skinningMatrixes = new Matrix4f[boneList.length]; - for (int i = 0; i < skinningMatrixes.length; i++) { - skinningMatrixes[i] = new Matrix4f(); - } - } - - private Bone recreateBoneStructure(Bone sourceRoot) { - Bone targetRoot = getBone(sourceRoot.getName()); - List children = sourceRoot.getChildren(); - for (int i = 0; i < children.size(); i++) { - Bone sourceChild = children.get(i); - // find my version of the child - Bone targetChild = getBone(sourceChild.getName()); - targetRoot.addChild(targetChild); - recreateBoneStructure(sourceChild); - } - - return targetRoot; - } - - /** - * Updates world transforms for all bones in this skeleton. - * Typically called after setting local animation transforms. - */ - public void updateWorldVectors() { - for (int i = rootBones.length - 1; i >= 0; i--) { - rootBones[i].update(); - } - } - - /** - * Saves the current skeleton state as its binding pose. - */ - public void setBindingPose() { - for (int i = rootBones.length - 1; i >= 0; i--) { - rootBones[i].setBindingPose(); - } - } - - /** - * Reset the skeleton to bind pose. - */ - public final void reset() { - for (int i = rootBones.length - 1; i >= 0; i--) { - rootBones[i].reset(); - } - } - - /** - * Reset the skeleton to bind pose and updates the bones - */ - public final void resetAndUpdate() { - for (int i = rootBones.length - 1; i >= 0; i--) { - Bone rootBone = rootBones[i]; - rootBone.reset(); - rootBone.update(); - } - } - - /** - * returns the array of all root bones of this skeleton - * @return the pre-existing array - */ - public Bone[] getRoots() { - return rootBones; - } - - /** - * return a bone for the given index - * - * @param index the (zero-based) bone index (≥0) - * @return the pre-existing instance - */ - public Bone getBone(int index) { - return boneList[index]; - } - - /** - * returns the bone with the given name - * - * @param name the name to search for - * @return the pre-existing instance, or null if not found - */ - public Bone getBone(String name) { - for (int i = 0; i < boneList.length; i++) { - if (boneList[i].getName().equals(name)) { - return boneList[i]; - } - } - return null; - } - - /** - * returns the bone index of the given bone - * - * @param bone the Bone to search for (unaffected) - * @return the index (≥0) or -1 if not found - */ - public int getBoneIndex(Bone bone) { - for (int i = 0; i < boneList.length; i++) { - if (boneList[i] == bone) { - return i; - } - } - - return -1; - } - - /** - * returns the bone index of the bone that has the given name - * - * @param name the name to search for - * @return the index (≥0) or -1 if not found - */ - public int getBoneIndex(String name) { - for (int i = 0; i < boneList.length; i++) { - if (boneList[i].getName().equals(name)) { - return i; - } - } - - return -1; - } - - /** - * Compute the skinning matrices for each bone of the skeleton that would be used to transform vertices of associated meshes - * @return the pre-existing matrices - */ - public Matrix4f[] computeSkinningMatrices() { - TempVars vars = TempVars.get(); - for (int i = 0; i < boneList.length; i++) { - boneList[i].getOffsetTransform(skinningMatrixes[i], vars.quat1, vars.vect1, vars.vect2, vars.tempMat3); - } - vars.release(); - return skinningMatrixes; - } - - /** - * returns the number of bones of this skeleton - * @return the count (≥0) - */ - public int getBoneCount() { - return boneList.length; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("Skeleton - ").append(boneList.length).append(" bones, ").append(rootBones.length).append(" roots\n"); - for (Bone rootBone : rootBones) { - sb.append(rootBone.toString()); - } - return sb.toString(); - } - - @Override - public void read(JmeImporter im) throws IOException { - InputCapsule input = im.getCapsule(this); - - Savable[] boneRootsAsSav = input.readSavableArray("rootBones", null); - rootBones = new Bone[boneRootsAsSav.length]; - System.arraycopy(boneRootsAsSav, 0, rootBones, 0, boneRootsAsSav.length); - - Savable[] boneListAsSavable = input.readSavableArray("boneList", null); - boneList = new Bone[boneListAsSavable.length]; - System.arraycopy(boneListAsSavable, 0, boneList, 0, boneListAsSavable.length); - - createSkinningMatrices(); - - for (Bone rootBone : rootBones) { - rootBone.reset(); - rootBone.update(); - rootBone.setBindingPose(); - } - } - - @Override - public void write(JmeExporter ex) throws IOException { - OutputCapsule output = ex.getCapsule(this); - output.write(rootBones, "rootBones", null); - output.write(boneList, "boneList", null); - } -} diff --git a/jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java b/jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java deleted file mode 100644 index 2428919c5e..0000000000 --- a/jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java +++ /dev/null @@ -1,745 +0,0 @@ -/* - * Copyright (c) 2009-2023 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.anim.SkinningControl; -import com.jme3.export.*; -import com.jme3.material.MatParamOverride; -import com.jme3.math.FastMath; -import com.jme3.math.Matrix4f; -import com.jme3.renderer.*; -import com.jme3.scene.*; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.control.AbstractControl; -import com.jme3.scene.mesh.IndexBuffer; -import com.jme3.shader.VarType; -import com.jme3.util.SafeArrayList; -import com.jme3.util.TempVars; -import com.jme3.util.clone.Cloner; -import com.jme3.util.clone.JmeCloneable; -import java.io.IOException; -import java.nio.Buffer; -import java.nio.FloatBuffer; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * The Skeleton control deforms a model according to a skeleton, It handles the - * computation of the deformation matrices and performs the transformations on - * the mesh - * - * @author Rémy Bouquet Based on AnimControl by Kirill Vainer - * @deprecated use {@link SkinningControl} - */ -@Deprecated -public class SkeletonControl extends AbstractControl implements Cloneable, JmeCloneable { - - /** - * The skeleton of the model. - */ - private Skeleton skeleton; - - /** - * List of geometries affected by this control. - */ - private SafeArrayList targets = new SafeArrayList<>(Geometry.class); - - /** - * Used to track when a mesh was updated. Meshes are only updated if they - * are visible in at least one camera. - */ - private boolean wasMeshUpdated = false; - - /** - * User wishes to use hardware skinning if available. - */ - private transient boolean hwSkinningDesired = true; - - /** - * Hardware skinning is currently being used. - */ - private transient boolean hwSkinningEnabled = false; - - /** - * Hardware skinning was tested on this GPU, results - * are stored in {@link #hwSkinningSupported} variable. - */ - private transient boolean hwSkinningTested = false; - - /** - * If hardware skinning was {@link #hwSkinningTested tested}, then - * this variable will be set to true if supported, and false if otherwise. - */ - private transient boolean hwSkinningSupported = false; - - /** - * Bone offset matrices, recreated each frame - */ - private transient Matrix4f[] offsetMatrices; - - private MatParamOverride numberOfBonesParam; - private MatParamOverride boneMatricesParam; - - /** - * Serialization only. Do not use. - */ - protected SkeletonControl() { - } - - private void switchToHardware() { - numberOfBonesParam.setEnabled(true); - boneMatricesParam.setEnabled(true); - - // Next full 10 bones (e.g. 30 on 24 bones) - int numBones = ((skeleton.getBoneCount() / 10) + 1) * 10; - numberOfBonesParam.setValue(numBones); - - for (Geometry geometry : targets) { - Mesh mesh = geometry.getMesh(); - if (mesh != null && mesh.isAnimated()) { - mesh.prepareForAnim(false); - } - } - } - - private void switchToSoftware() { - numberOfBonesParam.setEnabled(false); - boneMatricesParam.setEnabled(false); - - for (Geometry geometry : targets) { - Mesh mesh = geometry.getMesh(); - if (mesh != null && mesh.isAnimated()) { - mesh.prepareForAnim(true); - } - } - } - - private boolean testHardwareSupported(RenderManager rm) { - - //Only 255 bones max supported with hardware skinning - if (skeleton.getBoneCount() > 255) { - return false; - } - - switchToHardware(); - - try { - rm.preloadScene(spatial); - return true; - } catch (RendererException e) { - Logger.getLogger(SkeletonControl.class.getName()).log(Level.WARNING, "Could not enable HW skinning due to shader compile error:", e); - return false; - } - } - - /** - * Specifies if hardware skinning is preferred. If it is preferred and - * supported by GPU, it shall be enabled, if it's not preferred, or not - * supported by GPU, then it shall be disabled. - * - * @param preferred true to prefer hardware skinning, false to prefer - * software skinning (default=true) - * @see #isHardwareSkinningUsed() - */ - public void setHardwareSkinningPreferred(boolean preferred) { - hwSkinningDesired = preferred; - } - - /** - * @return True if hardware skinning is preferable to software skinning. - * Set to false by default. - * - * @see #setHardwareSkinningPreferred(boolean) - */ - public boolean isHardwareSkinningPreferred() { - return hwSkinningDesired; - } - - /** - * @return True is hardware skinning is activated and is currently used, false otherwise. - */ - public boolean isHardwareSkinningUsed() { - return hwSkinningEnabled; - } - - /** - * Creates a skeleton control. The list of targets will be acquired - * automatically when the control is attached to a node. - * - * @param skeleton the skeleton - */ - public SkeletonControl(Skeleton skeleton) { - if (skeleton == null) { - throw new IllegalArgumentException("skeleton cannot be null"); - } - this.skeleton = skeleton; - this.numberOfBonesParam = new MatParamOverride(VarType.Int, "NumberOfBones", null); - this.boneMatricesParam = new MatParamOverride(VarType.Matrix4Array, "BoneMatrices", null); - } - - /** - * If specified the geometry has an animated mesh, add its mesh and material - * to the lists of animation targets. - */ - private void findTargets(Geometry geometry) { - Mesh mesh = geometry.getMesh(); - if (mesh != null && mesh.isAnimated()) { - targets.add(geometry); - } - } - - private void findTargets(Node node) { - for (Spatial child : node.getChildren()) { - if (child instanceof Geometry) { - findTargets((Geometry) child); - } else if (child instanceof Node) { - findTargets((Node) child); - } - } - } - - @Override - public void setSpatial(Spatial spatial) { - Spatial oldSpatial = this.spatial; - super.setSpatial(spatial); - updateTargetsAndMaterials(spatial); - - if (oldSpatial != null) { - oldSpatial.removeMatParamOverride(numberOfBonesParam); - oldSpatial.removeMatParamOverride(boneMatricesParam); - } - - if (spatial != null) { - spatial.removeMatParamOverride(numberOfBonesParam); - spatial.removeMatParamOverride(boneMatricesParam); - spatial.addMatParamOverride(numberOfBonesParam); - spatial.addMatParamOverride(boneMatricesParam); - } - } - - private void controlRenderSoftware() { - resetToBind(); // reset morph meshes to bind pose - - offsetMatrices = skeleton.computeSkinningMatrices(); - - for (Geometry geometry : targets) { - Mesh mesh = geometry.getMesh(); - // NOTE: This assumes code higher up has - // already ensured this mesh is animated. - // Otherwise a crash will happen in skin update. - softwareSkinUpdate(mesh, offsetMatrices); - } - } - - private void controlRenderHardware() { - offsetMatrices = skeleton.computeSkinningMatrices(); - boneMatricesParam.setValue(offsetMatrices); - } - - @Override - protected void controlRender(RenderManager rm, ViewPort vp) { - if (!wasMeshUpdated) { - updateTargetsAndMaterials(spatial); - - // Prevent illegal cases. These should never happen. - assert hwSkinningTested || (!hwSkinningTested && !hwSkinningSupported && !hwSkinningEnabled); - assert !hwSkinningEnabled || (hwSkinningEnabled && hwSkinningTested && hwSkinningSupported); - - if (hwSkinningDesired && !hwSkinningTested) { - hwSkinningTested = true; - hwSkinningSupported = testHardwareSupported(rm); - - if (hwSkinningSupported) { - hwSkinningEnabled = true; - - Logger.getLogger(SkeletonControl.class.getName()).log(Level.INFO, "Hardware skinning engaged for {0}", spatial); - } else { - switchToSoftware(); - } - } else if (hwSkinningDesired && hwSkinningSupported && !hwSkinningEnabled) { - switchToHardware(); - hwSkinningEnabled = true; - } else if (!hwSkinningDesired && hwSkinningEnabled) { - switchToSoftware(); - hwSkinningEnabled = false; - } - - if (hwSkinningEnabled) { - controlRenderHardware(); - } else { - controlRenderSoftware(); - } - - wasMeshUpdated = true; - } - } - - @Override - protected void controlUpdate(float tpf) { - wasMeshUpdated = false; - } - - //only do this for software updates - void resetToBind() { - for (Geometry geometry : targets) { - Mesh mesh = geometry.getMesh(); - if (mesh != null && mesh.isAnimated()) { - Buffer bwBuff = mesh.getBuffer(Type.BoneWeight).getData(); - Buffer biBuff = mesh.getBuffer(Type.BoneIndex).getData(); - if (!biBuff.hasArray() || !bwBuff.hasArray()) { - mesh.prepareForAnim(true); // prepare for software animation - } - VertexBuffer bindPos = mesh.getBuffer(Type.BindPosePosition); - VertexBuffer bindNorm = mesh.getBuffer(Type.BindPoseNormal); - VertexBuffer pos = mesh.getBuffer(Type.Position); - FloatBuffer pb = (FloatBuffer) pos.getData(); - FloatBuffer bpb = (FloatBuffer) bindPos.getData(); - pb.clear(); - bpb.clear(); - - // reset bind normals if there is a BindPoseNormal buffer - if (bindNorm != null) { - VertexBuffer norm = mesh.getBuffer(Type.Normal); - FloatBuffer nb = (FloatBuffer) norm.getData(); - FloatBuffer bnb = (FloatBuffer) bindNorm.getData(); - nb.clear(); - bnb.clear(); - nb.put(bnb).clear(); - } - - //reset bind tangents if there is a bind tangent buffer - VertexBuffer bindTangents = mesh.getBuffer(Type.BindPoseTangent); - if (bindTangents != null) { - VertexBuffer tangents = mesh.getBuffer(Type.Tangent); - FloatBuffer tb = (FloatBuffer) tangents.getData(); - FloatBuffer btb = (FloatBuffer) bindTangents.getData(); - tb.clear(); - btb.clear(); - tb.put(btb).clear(); - } - - pb.put(bpb).clear(); - } - } - } - - @Override - public Object jmeClone() { - return super.jmeClone(); - } - - @Override - public void cloneFields(Cloner cloner, Object original) { - super.cloneFields(cloner, original); - - this.skeleton = cloner.clone(skeleton); - - // If the targets were cloned then this will clone them. If the targets - // were shared then this will share them. - this.targets = cloner.clone(targets); - - this.numberOfBonesParam = cloner.clone(numberOfBonesParam); - this.boneMatricesParam = cloner.clone(boneMatricesParam); - } - - /** - * Access the attachments node of the named bone. If the bone doesn't - * already have an attachments node, create one and attach it to the scene - * graph. Models and effects attached to the attachments node will follow - * the bone's motions. - * - * @param boneName the name of the bone - * @return the attachments node of the bone - */ - public Node getAttachmentsNode(String boneName) { - Bone b = skeleton.getBone(boneName); - if (b == null) { - throw new IllegalArgumentException("Given bone name does not exist " - + "in the skeleton."); - } - - updateTargetsAndMaterials(spatial); - int boneIndex = skeleton.getBoneIndex(b); - Node n = b.getAttachmentsNode(boneIndex, targets); - /* - * Select a node to parent the attachments node. - */ - Node parent; - if (spatial instanceof Node) { - parent = (Node) spatial; // the usual case - } else { - parent = spatial.getParent(); - } - parent.attachChild(n); - - return n; - } - - /** - * returns the skeleton of this control - * - * @return the pre-existing instance - */ - public Skeleton getSkeleton() { - return skeleton; - } - - /** - * Enumerate the target meshes of this control. - * - * @return a new array - */ - public Mesh[] getTargets() { - Mesh[] result = new Mesh[targets.size()]; - int i = 0; - for (Geometry geometry : targets) { - Mesh mesh = geometry.getMesh(); - result[i] = mesh; - i++; - } - - return result; - } - - /** - * Update the mesh according to the given transformation matrices - * - * @param mesh then mesh - * @param offsetMatrices the transformation matrices to apply - */ - private void softwareSkinUpdate(Mesh mesh, Matrix4f[] offsetMatrices) { - - VertexBuffer tb = mesh.getBuffer(Type.Tangent); - if (tb == null) { - //if there are no tangents use the classic skinning - applySkinning(mesh, offsetMatrices); - } else { - //if there are tangents use the skinning with tangents - applySkinningTangents(mesh, offsetMatrices, tb); - } - } - - /** - * Method to apply skinning transforms to a mesh's buffers - * - * @param mesh the mesh - * @param offsetMatrices the offset matrices to apply - */ - private void applySkinning(Mesh mesh, Matrix4f[] offsetMatrices) { - int maxWeightsPerVert = mesh.getMaxNumWeights(); - if (maxWeightsPerVert <= 0) { - throw new IllegalStateException("Max weights per vert is incorrectly set!"); - } - int fourMinusMaxWeights = 4 - maxWeightsPerVert; - - // NOTE: This code assumes the vertex buffer is in bind pose - // resetToBind() has been called this frame - VertexBuffer vb = mesh.getBuffer(Type.Position); - FloatBuffer fvb = (FloatBuffer) vb.getData(); - fvb.rewind(); - - VertexBuffer nb = mesh.getBuffer(Type.Normal); - FloatBuffer fnb = (FloatBuffer) nb.getData(); - fnb.rewind(); - - // get boneIndexes and weights for mesh - IndexBuffer ib = IndexBuffer.wrapIndexBuffer(mesh.getBuffer(Type.BoneIndex).getData()); - FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData(); - - wb.rewind(); - - float[] weights = wb.array(); - int idxWeights = 0; - - TempVars vars = TempVars.get(); - - float[] posBuf = vars.skinPositions; - float[] normBuf = vars.skinNormals; - - int iterations = (int) FastMath.ceil(fvb.limit() / ((float) posBuf.length)); - int bufLength = posBuf.length; - for (int i = iterations - 1; i >= 0; i--) { - // read next set of positions and normals from native buffer - bufLength = Math.min(posBuf.length, fvb.remaining()); - fvb.get(posBuf, 0, bufLength); - fnb.get(normBuf, 0, bufLength); - int verts = bufLength / 3; - int idxPositions = 0; - - // iterate vertices and apply skinning transform for each effecting bone - for (int vert = verts - 1; vert >= 0; vert--) { - // Skip this vertex if the first weight is zero. - if (weights[idxWeights] == 0) { - idxPositions += 3; - idxWeights += 4; - continue; - } - - float nmx = normBuf[idxPositions]; - float vtx = posBuf[idxPositions++]; - float nmy = normBuf[idxPositions]; - float vty = posBuf[idxPositions++]; - float nmz = normBuf[idxPositions]; - float vtz = posBuf[idxPositions++]; - - float rx = 0, ry = 0, rz = 0, rnx = 0, rny = 0, rnz = 0; - - for (int w = maxWeightsPerVert - 1; w >= 0; w--) { - float weight = weights[idxWeights]; - Matrix4f mat = offsetMatrices[ib.get(idxWeights++)]; - - rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight; - ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight; - rz += (mat.m20 * vtx + mat.m21 * vty + mat.m22 * vtz + mat.m23) * weight; - - rnx += (nmx * mat.m00 + nmy * mat.m01 + nmz * mat.m02) * weight; - rny += (nmx * mat.m10 + nmy * mat.m11 + nmz * mat.m12) * weight; - rnz += (nmx * mat.m20 + nmy * mat.m21 + nmz * mat.m22) * weight; - } - - idxWeights += fourMinusMaxWeights; - - idxPositions -= 3; - normBuf[idxPositions] = rnx; - posBuf[idxPositions++] = rx; - normBuf[idxPositions] = rny; - posBuf[idxPositions++] = ry; - normBuf[idxPositions] = rnz; - posBuf[idxPositions++] = rz; - } - - fvb.position(fvb.position() - bufLength); - fvb.put(posBuf, 0, bufLength); - fnb.position(fnb.position() - bufLength); - fnb.put(normBuf, 0, bufLength); - } - - vars.release(); - - vb.updateData(fvb); - nb.updateData(fnb); - - } - - /** - * Specific method for skinning with tangents to avoid cluttering the - * classic skinning calculation with null checks that would slow down the - * process even if tangents don't have to be computed. Also the iteration - * has additional indexes since tangent has 4 components instead of 3 for - * pos and norm - * - * @param mesh the mesh - * @param offsetMatrices the offset matrices to apply - * @param tb the tangent vertexBuffer - */ - private void applySkinningTangents(Mesh mesh, Matrix4f[] offsetMatrices, VertexBuffer tb) { - int maxWeightsPerVert = mesh.getMaxNumWeights(); - - if (maxWeightsPerVert <= 0) { - throw new IllegalStateException("Max weights per vert is incorrectly set!"); - } - - int fourMinusMaxWeights = 4 - maxWeightsPerVert; - - // NOTE: This code assumes the vertex buffer is in bind pose - // resetToBind() has been called this frame - VertexBuffer vb = mesh.getBuffer(Type.Position); - FloatBuffer fvb = (FloatBuffer) vb.getData(); - fvb.rewind(); - - VertexBuffer nb = mesh.getBuffer(Type.Normal); - - FloatBuffer fnb = (nb == null) ? null : (FloatBuffer) nb.getData(); - if (fnb != null) { - fnb.rewind(); - } - - FloatBuffer ftb = (FloatBuffer) tb.getData(); - ftb.rewind(); - - // get boneIndexes and weights for mesh - IndexBuffer ib = IndexBuffer.wrapIndexBuffer(mesh.getBuffer(Type.BoneIndex).getData()); - FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData(); - - wb.rewind(); - - float[] weights = wb.array(); - int idxWeights = 0; - - TempVars vars = TempVars.get(); - - float[] posBuf = vars.skinPositions; - float[] normBuf = vars.skinNormals; - float[] tanBuf = vars.skinTangents; - - int iterations = (int) FastMath.ceil(fvb.limit() / ((float) posBuf.length)); - int bufLength = 0; - int tanLength = 0; - for (int i = iterations - 1; i >= 0; i--) { - // read next set of positions and normals from native buffer - bufLength = Math.min(posBuf.length, fvb.remaining()); - tanLength = Math.min(tanBuf.length, ftb.remaining()); - fvb.get(posBuf, 0, bufLength); - if (fnb != null) { - fnb.get(normBuf, 0, bufLength); - } - ftb.get(tanBuf, 0, tanLength); - int verts = bufLength / 3; - int idxPositions = 0; - // Tangents have their own index because they have 4 components. - int idxTangents = 0; - - // iterate vertices and apply skinning transform for each effecting bone - for (int vert = verts - 1; vert >= 0; vert--) { - // Skip this vertex if the first weight is zero. - if (weights[idxWeights] == 0) { - idxTangents += 4; - idxPositions += 3; - idxWeights += 4; - continue; - } - - float nmx = normBuf[idxPositions]; - float vtx = posBuf[idxPositions++]; - float nmy = normBuf[idxPositions]; - float vty = posBuf[idxPositions++]; - float nmz = normBuf[idxPositions]; - float vtz = posBuf[idxPositions++]; - - float tnx = tanBuf[idxTangents++]; - float tny = tanBuf[idxTangents++]; - float tnz = tanBuf[idxTangents++]; - - // skipping the 4th component of the tangent since it doesn't have to be transformed - idxTangents++; - - float rx = 0, ry = 0, rz = 0, rnx = 0, rny = 0, rnz = 0, rtx = 0, rty = 0, rtz = 0; - - for (int w = maxWeightsPerVert - 1; w >= 0; w--) { - float weight = weights[idxWeights]; - Matrix4f mat = offsetMatrices[ib.get(idxWeights++)]; - - rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight; - ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight; - rz += (mat.m20 * vtx + mat.m21 * vty + mat.m22 * vtz + mat.m23) * weight; - - rnx += (nmx * mat.m00 + nmy * mat.m01 + nmz * mat.m02) * weight; - rny += (nmx * mat.m10 + nmy * mat.m11 + nmz * mat.m12) * weight; - rnz += (nmx * mat.m20 + nmy * mat.m21 + nmz * mat.m22) * weight; - - rtx += (tnx * mat.m00 + tny * mat.m01 + tnz * mat.m02) * weight; - rty += (tnx * mat.m10 + tny * mat.m11 + tnz * mat.m12) * weight; - rtz += (tnx * mat.m20 + tny * mat.m21 + tnz * mat.m22) * weight; - } - - idxWeights += fourMinusMaxWeights; - - idxPositions -= 3; - - normBuf[idxPositions] = rnx; - posBuf[idxPositions++] = rx; - normBuf[idxPositions] = rny; - posBuf[idxPositions++] = ry; - normBuf[idxPositions] = rnz; - posBuf[idxPositions++] = rz; - - idxTangents -= 4; - - tanBuf[idxTangents++] = rtx; - tanBuf[idxTangents++] = rty; - tanBuf[idxTangents++] = rtz; - - //once again skipping the 4th component of the tangent - idxTangents++; - } - - fvb.position(fvb.position() - bufLength); - fvb.put(posBuf, 0, bufLength); - if (fnb != null) { - fnb.position(fnb.position() - bufLength); - fnb.put(normBuf, 0, bufLength); - } - ftb.position(ftb.position() - tanLength); - ftb.put(tanBuf, 0, tanLength); - } - - vars.release(); - - vb.updateData(fvb); - if (nb != null) { - nb.updateData(fnb); - } - tb.updateData(ftb); - } - - @Override - public void write(JmeExporter ex) throws IOException { - super.write(ex); - OutputCapsule oc = ex.getCapsule(this); - oc.write(skeleton, "skeleton", null); - - oc.write(numberOfBonesParam, "numberOfBonesParam", null); - oc.write(boneMatricesParam, "boneMatricesParam", null); - } - - @Override - public void read(JmeImporter im) throws IOException { - super.read(im); - InputCapsule in = im.getCapsule(this); - skeleton = (Skeleton) in.readSavable("skeleton", null); - - numberOfBonesParam = (MatParamOverride) in.readSavable("numberOfBonesParam", null); - boneMatricesParam = (MatParamOverride) in.readSavable("boneMatricesParam", null); - - if (numberOfBonesParam == null) { - numberOfBonesParam = new MatParamOverride(VarType.Int, "NumberOfBones", null); - boneMatricesParam = new MatParamOverride(VarType.Matrix4Array, "BoneMatrices", null); - getSpatial().addMatParamOverride(numberOfBonesParam); - getSpatial().addMatParamOverride(boneMatricesParam); - } - } - - /** - * Update the lists of animation targets. - * - * @param spatial the controlled spatial - */ - private void updateTargetsAndMaterials(Spatial spatial) { - targets.clear(); - - if (spatial instanceof Node) { - findTargets((Node) spatial); - } else if (spatial instanceof Geometry) { - findTargets((Geometry) spatial); - } - } -} diff --git a/jme3-core/src/main/java/com/jme3/animation/SpatialTrack.java b/jme3-core/src/main/java/com/jme3/animation/SpatialTrack.java deleted file mode 100644 index 5c7e4cf0ca..0000000000 --- a/jme3-core/src/main/java/com/jme3/animation/SpatialTrack.java +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (c) 2009-2020 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.export.*; -import com.jme3.math.Quaternion; -import com.jme3.math.Vector3f; -import com.jme3.scene.Spatial; -import com.jme3.util.TempVars; -import com.jme3.util.clone.Cloner; -import com.jme3.util.clone.JmeCloneable; -import java.io.IOException; - -/** - * This class represents the track for spatial animation. - * - * @author Marcin Roguski (Kaelthas) - */ -@Deprecated -public class SpatialTrack implements JmeCloneable, Track { - - /** - * Translations of the track. - */ - private CompactVector3Array translations; - - /** - * Rotations of the track. - */ - private CompactQuaternionArray rotations; - - /** - * Scales of the track. - */ - private CompactVector3Array scales; - - /** - * The spatial to which this track applies. - * Note that this is optional, if no spatial is defined, the AnimControl's Spatial will be used. - */ - private Spatial trackSpatial; - - /** - * The times of the animations frames. - */ - private float[] times; - - public SpatialTrack() { - } - - /** - * Creates a spatial track for the given track data. - * - * @param times - * a float array with the time of each frame - * @param translations - * the translation of the bone for each frame - * @param rotations - * the rotation of the bone for each frame - * @param scales - * the scale of the bone for each frame - */ - public SpatialTrack(float[] times, Vector3f[] translations, - Quaternion[] rotations, Vector3f[] scales) { - setKeyframes(times, translations, rotations, scales); - } - - /** - * - * Modify the spatial which this track modifies. - * - * @param time - * the current time of the animation - */ - @Override - public void setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars) { - Spatial spatial = trackSpatial; - if (spatial == null) { - spatial = control.getSpatial(); - } - - Vector3f tempV = vars.vect1; - Vector3f tempS = vars.vect2; - Quaternion tempQ = vars.quat1; - Vector3f tempV2 = vars.vect3; - Vector3f tempS2 = vars.vect4; - Quaternion tempQ2 = vars.quat2; - - int lastFrame = times.length - 1; - if (time < 0 || lastFrame == 0) { - if (rotations != null) - rotations.get(0, tempQ); - if (translations != null) - translations.get(0, tempV); - if (scales != null) { - scales.get(0, tempS); - } - } else if (time >= times[lastFrame]) { - if (rotations != null) - rotations.get(lastFrame, tempQ); - if (translations != null) - translations.get(lastFrame, tempV); - if (scales != null) { - scales.get(lastFrame, tempS); - } - } else { - int startFrame = 0; - int endFrame = 1; - // use lastFrame so we never overflow the array - for (int i = 0; i < lastFrame && times[i] < time; ++i) { - startFrame = i; - endFrame = i + 1; - } - - float blend = (time - times[startFrame]) / (times[endFrame] - times[startFrame]); - - if (rotations != null) - rotations.get(startFrame, tempQ); - if (translations != null) - translations.get(startFrame, tempV); - if (scales != null) { - scales.get(startFrame, tempS); - } - if (rotations != null) - rotations.get(endFrame, tempQ2); - if (translations != null) - translations.get(endFrame, tempV2); - if (scales != null) { - scales.get(endFrame, tempS2); - } - tempQ.nlerp(tempQ2, blend); - tempV.interpolateLocal(tempV2, blend); - tempS.interpolateLocal(tempS2, blend); - } - - if (translations != null) { - spatial.setLocalTranslation(tempV); - } - if (rotations != null) { - spatial.setLocalRotation(tempQ); - } - if (scales != null) { - spatial.setLocalScale(tempS); - } - } - - /** - * Set the translations, rotations and scales for this track. - * - * @param times - * a float array with the time of each frame - * @param translations - * the translation of the bone for each frame - * @param rotations - * the rotation of the bone for each frame - * @param scales - * the scale of the bone for each frame - */ - public void setKeyframes(float[] times, Vector3f[] translations, - Quaternion[] rotations, Vector3f[] scales) { - if (times.length == 0) { - throw new RuntimeException("BoneTrack with no keyframes!"); - } - - this.times = times; - if (translations != null) { - assert times.length == translations.length; - this.translations = new CompactVector3Array(); - this.translations.add(translations); - this.translations.freeze(); - } - if (rotations != null) { - assert times.length == rotations.length; - this.rotations = new CompactQuaternionArray(); - this.rotations.add(rotations); - this.rotations.freeze(); - } - if (scales != null) { - assert times.length == scales.length; - this.scales = new CompactVector3Array(); - this.scales.add(scales); - this.scales.freeze(); - } - } - - /** - * @return the array of rotations of this track - */ - public Quaternion[] getRotations() { - return rotations == null ? null : rotations.toObjectArray(); - } - - /** - * @return the array of scales for this track - */ - public Vector3f[] getScales() { - return scales == null ? null : scales.toObjectArray(); - } - - /** - * @return the arrays of time for this track - */ - public float[] getTimes() { - return times; - } - - /** - * @return the array of translations of this track - */ - public Vector3f[] getTranslations() { - return translations == null ? null : translations.toObjectArray(); - } - - /** - * @return the length of the track - */ - @Override - public float getLength() { - return times == null ? 0 : times[times.length - 1] - times[0]; - } - - /** - * Create a clone with the same track spatial. - * - * @return a new track - */ - @Override - public SpatialTrack clone() { - Cloner cloner = new Cloner(); - cloner.setClonedValue(trackSpatial, trackSpatial); - return cloner.clone(this); - } - - @Override - public float[] getKeyFrameTimes() { - return times; - } - - public void setTrackSpatial(Spatial trackSpatial) { - this.trackSpatial = trackSpatial; - } - - public Spatial getTrackSpatial() { - return trackSpatial; - } - - /** - * Create a shallow clone for the JME cloner. - * - * @return a new track - */ - @Override - public SpatialTrack jmeClone() { - try { - return (SpatialTrack) super.clone(); - } catch (CloneNotSupportedException exception) { - throw new RuntimeException("Can't clone track", exception); - } - } - - /** - * Callback from {@link com.jme3.util.clone.Cloner} to convert this - * shallow-cloned track into a deep-cloned one, using the specified cloner - * to resolve copied fields. - * - * @param cloner the cloner currently cloning this control (not null) - * @param original the track from which this track was shallow-cloned - * (unused) - */ - @Override - public void cloneFields(Cloner cloner, Object original) { - translations = cloner.clone(translations); - rotations = cloner.clone(rotations); - scales = cloner.clone(scales); - trackSpatial = cloner.clone(trackSpatial); - times = cloner.clone(times); - } - - @Override - public void write(JmeExporter ex) throws IOException { - OutputCapsule oc = ex.getCapsule(this); - oc.write(translations, "translations", null); - oc.write(rotations, "rotations", null); - oc.write(times, "times", null); - oc.write(scales, "scales", null); - oc.write(trackSpatial, "trackSpatial", null); - } - - @Override - public void read(JmeImporter im) throws IOException { - InputCapsule ic = im.getCapsule(this); - translations = (CompactVector3Array) ic.readSavable("translations", null); - rotations = (CompactQuaternionArray) ic.readSavable("rotations", null); - times = ic.readFloatArray("times", null); - scales = (CompactVector3Array) ic.readSavable("scales", null); - trackSpatial = (Spatial) ic.readSavable("trackSpatial", null); - } -} diff --git a/jme3-core/src/main/java/com/jme3/animation/Track.java b/jme3-core/src/main/java/com/jme3/animation/Track.java deleted file mode 100644 index a9d753cd18..0000000000 --- a/jme3-core/src/main/java/com/jme3/animation/Track.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.export.Savable; -import com.jme3.util.TempVars; - -@Deprecated -public interface Track extends Savable, Cloneable { - - /** - * Sets the time of the animation. - * - * Internally, the track will retrieve objects from the control - * and modify them according to the properties of the channel and the - * given parameters. - * - * @param time The time in the animation - * @param weight The weight from 0 to 1 on how much to apply the track - * @param control The control which the track should affect - * @param channel The channel which the track should affect - * @param vars temporary storage - */ - public void setTime(float time, float weight, AnimControl control, AnimChannel channel, TempVars vars); - - /** - * @return the length of the track - */ - public float getLength(); - - /** - * This method creates a clone of the current object. - * @return a clone of the current object - */ - public Track clone(); - - /** - * Get the times in seconds for all keyframes. - * - * All keyframe times should be between 0.0 and {@link #getLength() length}. - * Modifying the provided array is not allowed, as it may corrupt internal - * state. - * - * @return the keyframe times - */ - public float[] getKeyFrameTimes(); -} diff --git a/jme3-core/src/main/java/com/jme3/animation/TrackInfo.java b/jme3-core/src/main/java/com/jme3/animation/TrackInfo.java deleted file mode 100644 index 5d3c1cbfba..0000000000 --- a/jme3-core/src/main/java/com/jme3/animation/TrackInfo.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.animation; - -import com.jme3.export.*; -import com.jme3.util.clone.Cloner; -import com.jme3.util.clone.JmeCloneable; - -import java.io.IOException; -import java.util.ArrayList; - -/** - * This class is intended as a UserData added to a Spatial that is referenced by a Track. - * (ParticleEmitter for EffectTrack and AudioNode for AudioTrack) - * It holds the list of tracks that are directly referencing the Spatial. - * - * This is used when loading a Track to find the cloned reference of a Spatial in the cloned model returned by the assetManager. - * - * @author Nehon - */ -@Deprecated -public class TrackInfo implements Savable, JmeCloneable { - - ArrayList tracks = new ArrayList<>(); - - public TrackInfo() { - } - - @Override - public void write(JmeExporter ex) throws IOException { - OutputCapsule c = ex.getCapsule(this); - c.writeSavableArrayList(tracks, "tracks", null); - } - - @Override - @SuppressWarnings("unchecked") - public void read(JmeImporter im) throws IOException { - InputCapsule c = im.getCapsule(this); - tracks = c.readSavableArrayList("tracks", null); - } - - public ArrayList getTracks() { - return tracks; - } - - public void addTrack(Track track) { - tracks.add(track); - } - - @Override - public Object jmeClone() { - try { - return super.clone(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException("Error cloning", e); - } - } - - @Override - public void cloneFields(Cloner cloner, Object original) { - this.tracks = cloner.clone(tracks); - } -} diff --git a/jme3-core/src/main/java/com/jme3/animation/package.html b/jme3-core/src/main/java/com/jme3/animation/package.html deleted file mode 100644 index 672cfeddec..0000000000 --- a/jme3-core/src/main/java/com/jme3/animation/package.html +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - -The com.jme3.animation package contains various classes -for managing animation inside a jME3 application. Currently, the majority -of classes are for handling skeletal animation. The primary control class is -the {@link com.jme3.animation.AnimControl}, through which animations can be played, -looped, combined, transitioned, etc. - -

Usage

- -

- -// Create or load a model with skeletal animation:
-Spatial model = assetManager.loadModel("...");
-
-// Retrieve the AnimControl.
-AnimControl animCtrl = model.getControl(AnimControl.class);
-
-// Create an animation channel, by default assigned to all bones.
-AnimChannel animChan = animCtrl.createChannel();
-
-// Play an animation
-animChan.setAnim("MyAnim");
-
-
-

Skeletal Animation System

-
-

-jME3 uses a system of bone-weights: A vertex is assigned up to 4 bones by which -it is influenced and 4 weights that describe how much the bone influences the -vertex. The maximum weight value being 1.0, and the requirement that all 4 weights -for a given vertex must sum to 1.0. This data is specified -for each skin/mesh that is influenced by the animation control via the -{link com.jme3.scene.VertexBuffer}s BoneWeight and BoneIndex. -The BoneIndex buffer must be of the format UnsignedByte, thus -placing the limit of up to 256 bones for a skeleton. The BoneWeight buffer -should be of the format Float. Both buffers should reference 4 -bones, even if the maximum number of bones any vertex is influenced is less or more -than 4.
-If a vertex is influenced by less than 4 bones, the indices following the last -valid bone should be 0 and the weights following the last valid bone should be 0.0. -The buffers are designed in such a way so as to permit hardware skinning.
-

-The {@link com.jme3.animation.Skeleton} class describes a bone hierarchy with one -or more root bones having children, thus containing all bones of the skeleton. -In addition to accessing the bones in the skeleton via the tree hierarchy, it -is also possible to access bones via index. The index for any given bone is -arbitrary and does not depend on the bone's location in the tree hierarchy. -It is this index that is specified in the BoneIndex VertexBuffer mentioned above -, and is also used to apply transformations to the bones through the animations.
-

-Every bone has a local and model space transformation. The local space -transformation is relative to its parent, if it has one, otherwise it is relative -to the model. The model space transformation is relative to model space. -The bones additionally have a bind pose transformation, which describes -the transformations for bones when no animated pose is applied to the skeleton. -All bones must have a bind pose transformation before they can be -animated. To set the bind pose for the skeleton, set the local (relative -to parent) transformations for all the bones using the call -{@link com.jme3.animation.Bone#setBindTransforms(com.jme3.math.Vector3f, com.jme3.math.Quaternion, com.jme3.math.Vector3f) }. -Then call {@link com.jme3.animation.Skeleton#updateWorldVectors() } followed by -{@link com.jme3.animation.Skeleton#setBindingPose() }.
-

-Animations are stored in a HashMap object, accessed by name. An animation -is simply a list of tracks, each track describes a timeline with each keyframe -having a transformation. For bone animations, every track is assigned to a bone, -while for morph animations, every track is assigned to a mesh.
-

- - - diff --git a/jme3-core/src/main/java/com/jme3/app/BasicProfiler.java b/jme3-core/src/main/java/com/jme3/app/BasicProfiler.java index 1c95730bbc..73e6117fcb 100644 --- a/jme3-core/src/main/java/com/jme3/app/BasicProfiler.java +++ b/jme3-core/src/main/java/com/jme3/app/BasicProfiler.java @@ -40,8 +40,10 @@ import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.util.BufferUtils; +import com.jme3.vulkan.mesh.AttributeModifier; + import java.nio.FloatBuffer; /** @@ -135,10 +137,13 @@ public Mesh getMesh() { protected final void createMesh() { if (mesh == null) { mesh = new Mesh(); - mesh.setMode(Mesh.Mode.Lines); + // mesh is no longer responsible for the draw mode + //mesh.setMode(Mesh.Mode.Lines); } - mesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(size * 4 * 3)); + mesh.setVertexCount(size * 4); + // todo: fix + //mesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(size * 4 * 3)); FloatBuffer cb = BufferUtils.createFloatBuffer(size * 4 * 4); for (int i = 0; i < size; i++) { @@ -153,19 +158,22 @@ protected final void createMesh() { } protected void updateMesh() { - FloatBuffer pb = (FloatBuffer) mesh.getBuffer(Type.Position).getData(); - pb.rewind(); - float scale = 1 / 1000000f; // scaled to ms as pixels - for (int i = 0; i < size; i++) { - float t1 = frames[i * 2] * scale; - float t2 = frames[i * 2 + 1] * scale; - - pb.put(i).put(0).put(0); - pb.put(i).put(t1).put(0); - pb.put(i).put(t1).put(0); - pb.put(i).put(t2).put(0); + try (AttributeModifier pos = mesh.modify(Type.Position)) { + float scale = 1 / 1000000f; // scaled to ms as pixels + for (int i = 0; i < size; i++) { + float t1 = frames[i * 2] * scale; + float t2 = frames[i * 2 + 1] * scale; + int vert = i * 4; + pos.putVector3(vert, 0, i, 0, 0); + pos.putVector3(vert + 1, 0, i, t1, 0); + pos.putVector3(vert + 2, 0, i, t1, 0); + pos.putVector3(vert + 3, 0, i, t2, 0); +// pb.put(i).put(0).put(0); +// pb.put(i).put(t1).put(0); +// pb.put(i).put(t1).put(0); +// pb.put(i).put(t2).put(0); + } } - mesh.setBuffer(Type.Position, 3, pb); } @Override diff --git a/jme3-core/src/main/java/com/jme3/app/BasicProfilerState.java b/jme3-core/src/main/java/com/jme3/app/BasicProfilerState.java index 68ee1483fb..a45507f2a4 100644 --- a/jme3-core/src/main/java/com/jme3/app/BasicProfilerState.java +++ b/jme3-core/src/main/java/com/jme3/app/BasicProfilerState.java @@ -42,7 +42,9 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; import com.jme3.scene.Node; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; +import com.jme3.vulkan.mesh.exp.Color; +import com.jme3.vulkan.mesh.exp.Position; /** * Provides a basic profiling visualization that shows @@ -120,44 +122,43 @@ protected void refreshBackground() { int size = profiler.getFrameCount(); float frameTime = 1000f / 60; - mesh.setBuffer(Type.Position, 3, new float[] { - - // first quad - 0, 0, 0, - size, 0, 0, - size, frameTime, 0, - 0, frameTime, 0, - // second quad - 0, frameTime, 0, - size, frameTime, 0, - size, frameTime * 2, 0, - 0, frameTime * 2, 0, - - // A lower dark border just to frame the - // 'update' stats against bright backgrounds - 0, -2, 0, - size, -2, 0, - size, 0, 0, - 0, 0, 0 + mesh.getVertices().as(Position.class).setPosition(0, new float[] { + // first quad + 0, 0, 0, + size, 0, 0, + size, frameTime, 0, + 0, frameTime, 0, + // second quad + 0, frameTime, 0, + size, frameTime, 0, + size, frameTime * 2, 0, + 0, frameTime * 2, 0, + + // A lower dark border just to frame the + // 'update' stats against bright backgrounds + 0, -2, 0, + size, -2, 0, + size, 0, 0, + 0, 0, 0 }); - mesh.setBuffer(Type.Color, 4, new float[] { - // first quad, within normal frame limits - 0, 1, 0, 0.25f, - 0, 1, 0, 0.25f, - 0, 0.25f, 0, 0.25f, - 0, 0.25f, 0, 0.25f, - - // Second quad, dropped frames - 0.25f, 0, 0, 0.25f, - 0.25f, 0, 0, 0.25f, - 1, 0, 0, 0.25f, - 1, 0, 0, 0.25f, - - 0, 0, 0, 0.5f, - 0, 0, 0, 0.5f, - 0, 0, 0, 0.5f, - 0, 0, 0, 0.5f + mesh.getVertices().as(Color.class).setColor(0, new float[] { + // first quad, within normal frame limits + 0, 1, 0, 0.25f, + 0, 1, 0, 0.25f, + 0, 0.25f, 0, 0.25f, + 0, 0.25f, 0, 0.25f, + + // Second quad, dropped frames + 0.25f, 0, 0, 0.25f, + 0.25f, 0, 0, 0.25f, + 1, 0, 0, 0.25f, + 1, 0, 0, 0.25f, + + 0, 0, 0, 0.5f, + 0, 0, 0, 0.5f, + 0, 0, 0, 0.5f, + 0, 0, 0, 0.5f }); mesh.setBuffer(Type.Index, 3, new short[] { diff --git a/jme3-core/src/main/java/com/jme3/app/state/ScreenshotAppState.java b/jme3-core/src/main/java/com/jme3/app/state/ScreenshotAppState.java index 462971a91c..357fef96ec 100644 --- a/jme3-core/src/main/java/com/jme3/app/state/ScreenshotAppState.java +++ b/jme3-core/src/main/java/com/jme3/app/state/ScreenshotAppState.java @@ -44,7 +44,7 @@ import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; import com.jme3.system.JmeSystem; -import com.jme3.texture.FrameBuffer; +import com.jme3.texture.GlFrameBuffer; import com.jme3.util.BufferUtils; import java.io.File; import java.io.FileOutputStream; @@ -273,7 +273,7 @@ public void postQueue(RenderQueue rq) { } @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { if (capture) { capture = false; diff --git a/jme3-core/src/main/java/com/jme3/asset/AssetManager.java b/jme3-core/src/main/java/com/jme3/asset/AssetManager.java index 73762a1234..55eaa0f360 100644 --- a/jme3-core/src/main/java/com/jme3/asset/AssetManager.java +++ b/jme3-core/src/main/java/com/jme3/asset/AssetManager.java @@ -42,6 +42,7 @@ import com.jme3.scene.Spatial; import com.jme3.scene.plugins.OBJLoader; import com.jme3.shader.ShaderGenerator; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture; import com.jme3.texture.plugins.TGALoader; import java.io.IOException; diff --git a/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java b/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java index a8ff73f2c4..331b0d71e0 100644 --- a/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java +++ b/jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java @@ -44,7 +44,9 @@ import com.jme3.shader.Glsl300ShaderGenerator; import com.jme3.shader.ShaderGenerator; import com.jme3.system.JmeSystem; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture; + import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -405,7 +407,7 @@ public Object loadAsset(String name) { } @Override - public Texture loadTexture(TextureKey key) { + public GlTexture loadTexture(TextureKey key) { return loadAsset(key); } diff --git a/jme3-core/src/main/java/com/jme3/asset/TextureKey.java b/jme3-core/src/main/java/com/jme3/asset/TextureKey.java index be096de5da..8b9d24a2ca 100644 --- a/jme3-core/src/main/java/com/jme3/asset/TextureKey.java +++ b/jme3-core/src/main/java/com/jme3/asset/TextureKey.java @@ -31,22 +31,22 @@ */ package com.jme3.asset; -import com.jme3.texture.Texture.Type; +import com.jme3.texture.GlTexture.Type; import com.jme3.asset.cache.AssetCache; import com.jme3.asset.cache.WeakRefCloneAssetCache; import com.jme3.export.InputCapsule; import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; import com.jme3.texture.TextureProcessor; import java.io.IOException; /** * Used to load textures from image files such as JPG or PNG. - * Note that texture loaders actually load the asset as an {@link Image} - * object, which is then converted to a {@link Texture} in the + * Note that texture loaders actually load the asset as an {@link GlImage} + * object, which is then converted to a {@link GlTexture} in the * {@link TextureProcessor#postProcess(com.jme3.asset.AssetKey, java.lang.Object)} * method. Since textures are cloneable smart assets, the texture stored * in the cache will be collected when all clones of the texture become @@ -54,12 +54,12 @@ * * @author Kirill Vainer */ -public class TextureKey extends AssetKey { +public class TextureKey extends AssetKey { private boolean generateMips; private boolean flipY; private int anisotropy; - private Texture.Type textureTypeHint = Texture.Type.TwoDimensional; + private GlTexture.Type textureTypeHint = GlTexture.Type.TwoDimensional; public TextureKey(String name, boolean flipY) { super(name); @@ -213,7 +213,7 @@ public void read(JmeImporter im) throws IOException { // Backwards compat textureTypeHint = Type.CubeMap; } else { - textureTypeHint = ic.readEnum("tex_type", Texture.Type.class, Type.TwoDimensional); + textureTypeHint = ic.readEnum("tex_type", GlTexture.Type.class, Type.TwoDimensional); } } } diff --git a/jme3-core/src/main/java/com/jme3/backend/Engine.java b/jme3-core/src/main/java/com/jme3/backend/Engine.java new file mode 100644 index 0000000000..7717244dcb --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/backend/Engine.java @@ -0,0 +1,11 @@ +package com.jme3.backend; + +import com.jme3.vulkan.mesh.InputRate; +import com.jme3.vulkan.mesh.VertexBinding; +import com.jme3.vulkan.util.IntEnum; + +public interface Engine { + + VertexBinding.Builder createMeshVertexBinding(IntEnum rate); + +} diff --git a/jme3-core/src/main/java/com/jme3/backend/VulkanEngine.java b/jme3-core/src/main/java/com/jme3/backend/VulkanEngine.java new file mode 100644 index 0000000000..896a3aa233 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/backend/VulkanEngine.java @@ -0,0 +1,24 @@ +package com.jme3.backend; + +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.mesh.InputRate; +import com.jme3.vulkan.mesh.VertexBinding; +import com.jme3.vulkan.mesh.VulkanVertexBinding; +import com.jme3.vulkan.util.IntEnum; + +public class VulkanEngine implements Engine { + + private final LogicalDevice device; + private final int frames; + + public VulkanEngine(LogicalDevice device, int frames) { + this.device = device; + this.frames = frames; + } + + @Override + public VertexBinding.Builder createMeshVertexBinding(IntEnum rate) { + return VulkanVertexBinding.create(device, rate); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java b/jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java index 6b0e023b34..85ed907781 100644 --- a/jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java +++ b/jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java @@ -43,6 +43,10 @@ import com.jme3.scene.Mesh; import com.jme3.scene.Spatial; import com.jme3.util.TempVars; +import com.jme3.vulkan.mesh.VertexReader; +import com.jme3.vulkan.mesh.attribute.Position; +import com.jme3.vulkan.util.FloatBufferModifier; + import java.io.IOException; import java.nio.FloatBuffer; //import com.jme.scene.TriMesh; @@ -129,9 +133,37 @@ public Type getType() { */ @Override public void computeFromPoints(FloatBuffer points) { + containAABB(new FloatBufferModifier(points, 3)); + } + + @Override + public void computeFromPoints(VertexReader points) { containAABB(points); } + @Override + public void computeFromPoints(Position position) { + float minX = Float.POSITIVE_INFINITY, + minY = Float.POSITIVE_INFINITY, + minZ = Float.POSITIVE_INFINITY; + float maxX = Float.NEGATIVE_INFINITY, + maxY = Float.NEGATIVE_INFINITY, + maxZ = Float.NEGATIVE_INFINITY; + for (Vector3f p : position.iterator(new Vector3f())) { + minX = Math.min(minX, p.x); + minY = Math.min(minY, p.y); + minZ = Math.min(minZ, p.z); + maxX = Math.max(maxX, p.x); + maxY = Math.max(maxY, p.y); + maxZ = Math.max(maxZ, p.z); + } + center.set(minX + maxX, minY + maxY, minZ + maxZ); + center.multLocal(0.5f); + xExtent = maxX - center.x; + yExtent = maxY - center.y; + zExtent = maxZ - center.z; + } + /** * computeFromTris creates a new Bounding Box from a given * set of triangles. It is used in OBBTree calculations. @@ -236,21 +268,19 @@ public static void checkMinMax(Vector3f min, Vector3f max, Vector3f point) { * @param points * the list of points. */ - public void containAABB(FloatBuffer points) { + public void containAABB(VertexReader points) { if (points == null) { return; } - points.rewind(); - if (points.remaining() <= 2) // we need at least a 3 float vector + //points.rewind(); + if (points.limit() == 0) // we need at least a 3 float vector { return; } TempVars vars = TempVars.get(); - float[] tmpArray = vars.skinPositions; - float minX = Float.POSITIVE_INFINITY, minY = Float.POSITIVE_INFINITY, minZ = Float.POSITIVE_INFINITY; @@ -258,37 +288,17 @@ public void containAABB(FloatBuffer points) { maxY = Float.NEGATIVE_INFINITY, maxZ = Float.NEGATIVE_INFINITY; - int iterations = (int) FastMath.ceil(points.limit() / ((float) tmpArray.length)); - for (int i = iterations - 1; i >= 0; i--) { - int bufLength = Math.min(tmpArray.length, points.remaining()); - points.get(tmpArray, 0, bufLength); - - for (int j = 0; j < bufLength; j += 3) { - vars.vect1.x = tmpArray[j]; - vars.vect1.y = tmpArray[j + 1]; - vars.vect1.z = tmpArray[j + 2]; - - if (vars.vect1.x < minX) { - minX = vars.vect1.x; - } - if (vars.vect1.x > maxX) { - maxX = vars.vect1.x; - } - - if (vars.vect1.y < minY) { - minY = vars.vect1.y; - } - if (vars.vect1.y > maxY) { - maxY = vars.vect1.y; - } - - if (vars.vect1.z < minZ) { - minZ = vars.vect1.z; - } - if (vars.vect1.z > maxZ) { - maxZ = vars.vect1.z; - } - } + for (int i = 0, l = points.limit(); i < l; i++) { +// vars.vect1.x = tmpArray[j]; +// vars.vect1.y = tmpArray[j + 1]; +// vars.vect1.z = tmpArray[j + 2]; + points.getVector3(i, 0, vars.vect1); + minX = Math.min(minX, vars.vect1.x); + minY = Math.min(minY, vars.vect1.y); + minZ = Math.min(minZ, vars.vect1.z); + maxX = Math.max(maxX, vars.vect1.x); + maxY = Math.max(maxY, vars.vect1.y); + maxZ = Math.max(maxZ, vars.vect1.z); } vars.release(); diff --git a/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java b/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java index 67cf7263f8..a0687efae9 100644 --- a/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java +++ b/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java @@ -39,8 +39,12 @@ import com.jme3.export.JmeImporter; import com.jme3.math.*; import com.jme3.scene.Spatial; -import com.jme3.util.BufferUtils; import com.jme3.util.TempVars; +import com.jme3.vulkan.mesh.VertexReader; +import com.jme3.vulkan.mesh.VertexWriter; +import com.jme3.vulkan.util.FloatBufferModifier; +import org.lwjgl.system.MemoryStack; + import java.io.IOException; import java.nio.FloatBuffer; import java.util.Objects; @@ -120,6 +124,11 @@ public void setRadius(float radius) { */ @Override public void computeFromPoints(FloatBuffer points) { + calcWelzl(new FloatBufferModifier(points, 3)); + } + + @Override + public void computeFromPoints(VertexReader points) { calcWelzl(points); } @@ -184,15 +193,17 @@ public void computeFromTris(Triangle[] tris, int start, int end) { * @param points * The points to calculate the minimum bounds from. */ - public void calcWelzl(FloatBuffer points) { + public void calcWelzl(VertexReader points) { if (center == null) { center = new Vector3f(); } - FloatBuffer buf = BufferUtils.createFloatBuffer(points.limit()); - points.rewind(); - buf.put(points); - buf.flip(); - recurseMini(buf, buf.limit() / 3, 0, 0); + assert points.components() == 3; + try (MemoryStack stack = MemoryStack.stackPush()) { + FloatBuffer buf = stack.mallocFloat(points.limit() * 3); + points.getFloats(0, 0, buf); + FloatBufferModifier bufMod = new FloatBufferModifier(buf, 3); + recurseMini(bufMod, bufMod.limit(), 0, 0); + } } /** @@ -210,7 +221,7 @@ public void calcWelzl(FloatBuffer points) { * A variable simulating pointer arithmetic from C++, and offset * in points. */ - private void recurseMini(FloatBuffer points, int p, int b, int ap) { + private void recurseMini(VertexWriter points, int p, int b, int ap) { //TempVars vars = TempVars.get(); Vector3f tempA = new Vector3f(); //vars.vect1; @@ -225,36 +236,51 @@ private void recurseMini(FloatBuffer points, int p, int b, int ap) { break; case 1: this.radius = 1f - RADIUS_EPSILON; - BufferUtils.populateFromBuffer(center, points, ap - 1); + //BufferUtils.populateFromBuffer(center, points, ap - 1); + points.getVector3(ap - 1, 0, center); break; case 2: - BufferUtils.populateFromBuffer(tempA, points, ap - 1); - BufferUtils.populateFromBuffer(tempB, points, ap - 2); + //BufferUtils.populateFromBuffer(tempA, points, ap - 1); + //BufferUtils.populateFromBuffer(tempB, points, ap - 2); + points.getVector3(ap - 1, 0, tempA); + points.getVector3(ap - 2, 0, tempB); setSphere(tempA, tempB); break; case 3: - BufferUtils.populateFromBuffer(tempA, points, ap - 1); - BufferUtils.populateFromBuffer(tempB, points, ap - 2); - BufferUtils.populateFromBuffer(tempC, points, ap - 3); +// BufferUtils.populateFromBuffer(tempA, points, ap - 1); +// BufferUtils.populateFromBuffer(tempB, points, ap - 2); +// BufferUtils.populateFromBuffer(tempC, points, ap - 3); + points.getVector3(ap - 1, 0, tempA); + points.getVector3(ap - 2, 0, tempB); + points.getVector3(ap - 3, 0, tempC); setSphere(tempA, tempB, tempC); break; case 4: - BufferUtils.populateFromBuffer(tempA, points, ap - 1); - BufferUtils.populateFromBuffer(tempB, points, ap - 2); - BufferUtils.populateFromBuffer(tempC, points, ap - 3); - BufferUtils.populateFromBuffer(tempD, points, ap - 4); +// BufferUtils.populateFromBuffer(tempA, points, ap - 1); +// BufferUtils.populateFromBuffer(tempB, points, ap - 2); +// BufferUtils.populateFromBuffer(tempC, points, ap - 3); +// BufferUtils.populateFromBuffer(tempD, points, ap - 4); + points.getVector3(ap - 1, 0, tempA); + points.getVector3(ap - 2, 0, tempB); + points.getVector3(ap - 3, 0, tempC); + points.getVector3(ap - 4, 0, tempD); setSphere(tempA, tempB, tempC, tempD); //vars.release(); return; } for (int i = 0; i < p; i++) { - BufferUtils.populateFromBuffer(tempA, points, i + ap); + //BufferUtils.populateFromBuffer(tempA, points, i + ap); + points.getVector3(i + ap, 0, tempA); if (tempA.distanceSquared(center) - (radius * radius) > RADIUS_EPSILON - 1f) { for (int j = i; j > 0; j--) { - BufferUtils.populateFromBuffer(tempB, points, j + ap); - BufferUtils.populateFromBuffer(tempC, points, j - 1 + ap); - BufferUtils.setInBuffer(tempC, points, j + ap); - BufferUtils.setInBuffer(tempB, points, j - 1 + ap); +// BufferUtils.populateFromBuffer(tempB, points, j + ap); +// BufferUtils.populateFromBuffer(tempC, points, j - 1 + ap); + points.getVector3(j + ap, 0, tempB); + points.getVector3(j - 1 + ap, 0, tempC); +// BufferUtils.setInBuffer(tempC, points, j + ap); +// BufferUtils.setInBuffer(tempB, points, j - 1 + ap); + points.putVector3(j + ap, 0, tempC); + points.putVector3(j - 1 + ap, 0, tempB); } recurseMini(points, i, b + 1, ap + 1); } @@ -274,7 +300,7 @@ private void recurseMini(FloatBuffer points, int p, int b, int ap) { * The 3rd point inside the sphere. * @param C * The 4th point inside the sphere. - * @see #calcWelzl(java.nio.FloatBuffer) + * @see #calcWelzl(VertexReader) */ private void setSphere(Vector3f O, Vector3f A, Vector3f B, Vector3f C) { Vector3f a = A.subtract(O); @@ -307,7 +333,7 @@ private void setSphere(Vector3f O, Vector3f A, Vector3f B, Vector3f C) { * The 2nd point inside the sphere. * @param B * The 3rd point inside the sphere. - * @see #calcWelzl(java.nio.FloatBuffer) + * @see #calcWelzl(VertexReader) */ private void setSphere(Vector3f O, Vector3f A, Vector3f B) { Vector3f a = A.subtract(O); @@ -335,7 +361,7 @@ private void setSphere(Vector3f O, Vector3f A, Vector3f B) { * The 1st point inside the sphere. * @param A * The 2nd point inside the sphere. - * @see #calcWelzl(java.nio.FloatBuffer) + * @see #calcWelzl(VertexReader) */ private void setSphere(Vector3f O, Vector3f A) { radius = FastMath.sqrt(((A.x - O.x) * (A.x - O.x) + (A.y - O.y) diff --git a/jme3-core/src/main/java/com/jme3/bounding/BoundingVolume.java b/jme3-core/src/main/java/com/jme3/bounding/BoundingVolume.java index 3a80764910..59339fd5a8 100644 --- a/jme3-core/src/main/java/com/jme3/bounding/BoundingVolume.java +++ b/jme3-core/src/main/java/com/jme3/bounding/BoundingVolume.java @@ -38,6 +38,9 @@ import com.jme3.export.Savable; import com.jme3.math.*; import com.jme3.util.TempVars; +import com.jme3.vulkan.mesh.VertexReader; +import com.jme3.vulkan.mesh.attribute.Position; + import java.io.IOException; import java.nio.FloatBuffer; import java.util.Objects; @@ -149,6 +152,10 @@ public final BoundingVolume transform(Transform trans) { */ public abstract void computeFromPoints(FloatBuffer points); + public abstract void computeFromPoints(VertexReader points); + + public abstract void computeFromPoints(Position position); + /** * merge combines two bounding volumes into a single bounding * volume that contains both this bounding volume and the parameter volume. diff --git a/jme3-core/src/main/java/com/jme3/cinematic/Cinematic.java b/jme3-core/src/main/java/com/jme3/cinematic/Cinematic.java index daca01b843..82b2e323c4 100644 --- a/jme3-core/src/main/java/com/jme3/cinematic/Cinematic.java +++ b/jme3-core/src/main/java/com/jme3/cinematic/Cinematic.java @@ -31,7 +31,7 @@ */ package com.jme3.cinematic; -import com.jme3.animation.LoopMode; +import com.jme3.anim.util.LoopMode; import com.jme3.app.Application; import com.jme3.app.state.AppState; import com.jme3.app.state.AppStateManager; diff --git a/jme3-core/src/main/java/com/jme3/cinematic/events/AbstractCinematicEvent.java b/jme3-core/src/main/java/com/jme3/cinematic/events/AbstractCinematicEvent.java index 85bae7c857..96a3b32070 100644 --- a/jme3-core/src/main/java/com/jme3/cinematic/events/AbstractCinematicEvent.java +++ b/jme3-core/src/main/java/com/jme3/cinematic/events/AbstractCinematicEvent.java @@ -31,8 +31,8 @@ */ package com.jme3.cinematic.events; -import com.jme3.animation.AnimationUtils; -import com.jme3.animation.LoopMode; +import com.jme3.anim.util.AnimationUtils; +import com.jme3.anim.util.LoopMode; import com.jme3.app.Application; import com.jme3.cinematic.Cinematic; import com.jme3.cinematic.PlayState; diff --git a/jme3-core/src/main/java/com/jme3/cinematic/events/AnimEvent.java b/jme3-core/src/main/java/com/jme3/cinematic/events/AnimEvent.java index 7d7721e166..20632969f0 100644 --- a/jme3-core/src/main/java/com/jme3/cinematic/events/AnimEvent.java +++ b/jme3-core/src/main/java/com/jme3/cinematic/events/AnimEvent.java @@ -33,7 +33,7 @@ import com.jme3.anim.AnimComposer; import com.jme3.anim.tween.action.Action; -import com.jme3.animation.LoopMode; +import com.jme3.anim.util.LoopMode; import com.jme3.app.Application; import com.jme3.cinematic.Cinematic; import com.jme3.cinematic.PlayState; diff --git a/jme3-core/src/main/java/com/jme3/cinematic/events/AnimationEvent.java b/jme3-core/src/main/java/com/jme3/cinematic/events/AnimationEvent.java deleted file mode 100644 index 6511da701b..0000000000 --- a/jme3-core/src/main/java/com/jme3/cinematic/events/AnimationEvent.java +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.cinematic.events; - -import com.jme3.animation.*; -import com.jme3.app.Application; -import com.jme3.cinematic.Cinematic; -import com.jme3.cinematic.PlayState; -import com.jme3.export.*; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; - -import java.io.IOException; -import java.util.*; -import java.util.logging.Logger; - -/** - * An event based on an animation of a model. The model has to hold an - * AnimControl with valid animation (bone or spatial animations). - * - * It helps to schedule the playback of an animation on a model in a Cinematic. - * - * - * @author Nehon - * @deprecated use {@link AnimEvent} - */ -@Deprecated -public class AnimationEvent extends AbstractCinematicEvent { - - // Version #2: directly keeping track on the model instead of trying to retrieve - //it from the scene according to its name, because the name is not supposed to be unique - //For backward compatibility, if the model is null it's looked up into the scene - public static final int SAVABLE_VERSION = 2; - private static final Logger log = Logger.getLogger(AnimationEvent.class.getName()); - public static final String MODEL_CHANNELS = "modelChannels"; - protected AnimChannel channel; - protected String animationName; - protected Spatial model; - //kept for backward compatibility - protected String modelName; - protected float blendTime = 0; - protected int channelIndex = 0; - // parent cinematic - protected Cinematic cinematic; - - /** - * used for serialization don't call directly use one of the following - * constructors - */ - protected AnimationEvent() { - super(); - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - */ - public AnimationEvent(Spatial model, String animationName) { - this.model = model; - this.modelName = model.getName(); - this.animationName = animationName; - initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName); - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param initialDuration the initial duration of the event - */ - public AnimationEvent(Spatial model, String animationName, float initialDuration) { - super(initialDuration); - this.model = model; - this.modelName = model.getName(); - this.animationName = animationName; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param loopMode the loopMode - * @see LoopMode - */ - public AnimationEvent(Spatial model, String animationName, LoopMode loopMode) { - super(loopMode); - initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName); - this.model = model; - this.modelName = model.getName(); - this.animationName = animationName; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param initialDuration the initial duration of the event - * @param loopMode the loopMode - * @see LoopMode - */ - public AnimationEvent(Spatial model, String animationName, float initialDuration, LoopMode loopMode) { - super(initialDuration, loopMode); - this.model = model; - this.modelName = model.getName(); - this.animationName = animationName; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param initialDuration the initial duration of the event - * @param blendTime the time interval during which the animation will be blended - * @see AnimChannel#setAnim(java.lang.String, float) - */ - public AnimationEvent(Spatial model, String animationName, float initialDuration, float blendTime) { - super(initialDuration); - this.model = model; - this.modelName = model.getName(); - this.animationName = animationName; - this.blendTime = blendTime; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param loopMode the loopMode - * @see LoopMode - * @param blendTime the time interval during which the animation will be blended - * @see AnimChannel#setAnim(java.lang.String, float) - */ - public AnimationEvent(Spatial model, String animationName, LoopMode loopMode, float blendTime) { - super(loopMode); - initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName); - this.model = model; - this.modelName = model.getName(); - this.animationName = animationName; - this.blendTime = blendTime; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param initialDuration the initial duration of the event - * @param loopMode the loopMode - * @see LoopMode - * @param blendTime the time interval during which the animation will be blended - * @see AnimChannel#setAnim(java.lang.String, float) - */ - public AnimationEvent(Spatial model, String animationName, float initialDuration, LoopMode loopMode, float blendTime) { - super(initialDuration, loopMode); - this.model = model; - this.modelName = model.getName(); - this.animationName = animationName; - this.blendTime = blendTime; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param loopMode the loopMode - * @see LoopMode - * @param channelIndex the index of the channel default is 0. Events on the - * same channelIndex will use the same channel. - */ - public AnimationEvent(Spatial model, String animationName, LoopMode loopMode, int channelIndex) { - super(loopMode); - initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName); - this.model = model; - this.modelName = model.getName(); - this.animationName = animationName; - this.channelIndex = channelIndex; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param channelIndex the index of the channel default is 0. Events on the - * same channelIndex will use the same channel. - */ - public AnimationEvent(Spatial model, String animationName, int channelIndex) { - this.model = model; - this.modelName = model.getName(); - this.animationName = animationName; - initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName); - this.channelIndex = channelIndex; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param loopMode the desired mode (Loop/DontLoop/Cycle) - * @param channelIndex the index of the channel default is 0. Events on the - * @param blendTime the time interval during which the animation will be blended - * same channelIndex will use the same channel. - */ - public AnimationEvent(Spatial model, String animationName, LoopMode loopMode, int channelIndex, float blendTime) { - this.model = model; - this.modelName = model.getName(); - this.animationName = animationName; - this.loopMode = loopMode; - initialDuration = model.getControl(AnimControl.class).getAnimationLength(animationName); - this.channelIndex = channelIndex; - this.blendTime = blendTime; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param initialDuration the initial duration of the event - * @param channelIndex the index of the channel default is 0. Events on the - * same channelIndex will use the same channel. - */ - public AnimationEvent(Spatial model, String animationName, float initialDuration, int channelIndex) { - super(initialDuration); - this.model = model; - this.modelName = model.getName(); - this.animationName = animationName; - this.channelIndex = channelIndex; - } - - /** - * creates an animation event - * - * @param model the model on which the animation will be played - * @param animationName the name of the animation to play - * @param initialDuration the initial duration of the event - * @param loopMode the loopMode - * @see LoopMode - * @param channelIndex the index of the channel default is 0. Events on the - * same channelIndex will use the same channel. - */ - public AnimationEvent(Spatial model, String animationName, float initialDuration, LoopMode loopMode, int channelIndex) { - super(initialDuration, loopMode); - this.model = model; - this.modelName = model.getName(); - this.animationName = animationName; - this.channelIndex = channelIndex; - } - - @Override - @SuppressWarnings("unchecked") - public void initEvent(Application app, Cinematic cinematic) { - super.initEvent(app, cinematic); - this.cinematic = cinematic; - if (channel == null) { - Object s = cinematic.getEventData(MODEL_CHANNELS, model); - if (s == null) { - s = new HashMap(); - int numChannels = model.getControl(AnimControl.class).getNumChannels(); - for (int i = 0; i < numChannels; i++) { - ((HashMap) s) - .put(i, model.getControl(AnimControl.class).getChannel(i)); - } - cinematic.putEventData(MODEL_CHANNELS, model, s); - } - - Map map = (Map) s; - this.channel = map.get(channelIndex); - if (this.channel == null) { - if (model == null) { - //the model is null we try to find it according to the name - //this should occur only when loading an old saved cinematic - //otherwise it's an error - model = cinematic.getScene().getChild(modelName); - } - if (model != null) { - if (cinematic.getScene() != null) { - Spatial sceneModel = cinematic.getScene().getChild(model.getName()); - if (sceneModel != null) { - Node parent = sceneModel.getParent(); - parent.detachChild(sceneModel); - sceneModel = model; - parent.attachChild(sceneModel); - } else { - cinematic.getScene().attachChild(model); - } - } - - channel = model.getControl(AnimControl.class).createChannel(); - map.put(channelIndex, channel); - } else { - //it's an error - throw new UnsupportedOperationException("model should not be null"); - } - } - } - } - - @Override - public void setTime(float time) { - super.setTime(time); - if (!animationName.equals(channel.getAnimationName())) { - channel.setAnim(animationName, blendTime); - } - float t = time; - if (loopMode == LoopMode.Loop) { - t = t % channel.getAnimMaxTime(); - } - if (loopMode == LoopMode.Cycle) { - float parity = (float) Math.ceil(time / channel.getAnimMaxTime()); - if (parity > 0 && parity % 2 == 0) { - t = channel.getAnimMaxTime() - t % channel.getAnimMaxTime(); - } else { - t = t % channel.getAnimMaxTime(); - } - - } - if (t < 0) { - channel.setTime(0); - channel.reset(true); - } - if (t > channel.getAnimMaxTime()) { - channel.setTime(t); - channel.getControl().update(0); - stop(); - } else { - channel.setTime(t); - channel.getControl().update(0); - } - } - - @Override - public void onPlay() { - channel.getControl().setEnabled(true); - if (playState == PlayState.Stopped) { - channel.setAnim(animationName, blendTime); - channel.setSpeed(speed); - channel.setLoopMode(loopMode); - channel.setTime(0); - } - } - - @Override - public void setSpeed(float speed) { - super.setSpeed(speed); - if (channel != null) { - channel.setSpeed(speed); - } - } - - @Override - public void onUpdate(float tpf) { - } - - @Override - public void onStop() { - } - - @Override - public void forceStop() { - if (channel != null) { - channel.setTime(time); - channel.reset(false); - } - super.forceStop(); - } - - @Override - public void onPause() { - if (channel != null) { - channel.getControl().setEnabled(false); - } - } - - @Override - public void setLoopMode(LoopMode loopMode) { - super.setLoopMode(loopMode); - if (channel != null) { - channel.setLoopMode(loopMode); - } - } - - @Override - public void write(JmeExporter ex) throws IOException { - super.write(ex); - OutputCapsule oc = ex.getCapsule(this); - - oc.write(model, "model", null); - oc.write(modelName, "modelName", null); - oc.write(animationName, "animationName", ""); - oc.write(blendTime, "blendTime", 0f); - oc.write(channelIndex, "channelIndex", 0); - } - - @Override - public void read(JmeImporter im) throws IOException { - super.read(im); - InputCapsule ic = im.getCapsule(this); -// if (im.getFormatVersion() == 0) { - modelName = ic.readString("modelName", ""); -// } - //FIXME always the same issue, because of the cloning of assets, this won't work - //we have to somehow store userdata in the spatial and then recurse the - //scene sub scenegraph to find the correct instance of the model - //This brings a reflection about the cinematic being an appstate, - //shouldn't it be a control over the scene - // this would allow to use the cloneForSpatial method and automatically - //rebind cloned references of original objects. - //for now as nobody probably ever saved a cinematic, this is not a critical issue - model = (Spatial) ic.readSavable("model", null); - animationName = ic.readString("animationName", ""); - blendTime = ic.readFloat("blendTime", 0f); - channelIndex = ic.readInt("channelIndex", 0); - } - - @Override - @SuppressWarnings("unchecked") - public void dispose() { - super.dispose(); - if (cinematic != null) { - Object o = cinematic.getEventData(MODEL_CHANNELS, model); - if (o != null) { - Collection values = ((HashMap) o).values(); - while (values.remove(channel)); - if (values.isEmpty()) { - cinematic.removeEventData(MODEL_CHANNELS, model); - } - } - cinematic = null; - channel = null; - } - } -} diff --git a/jme3-core/src/main/java/com/jme3/cinematic/events/AnimationTrack.java b/jme3-core/src/main/java/com/jme3/cinematic/events/AnimationTrack.java deleted file mode 100644 index 7e8a258495..0000000000 --- a/jme3-core/src/main/java/com/jme3/cinematic/events/AnimationTrack.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.cinematic.events; - -import com.jme3.animation.LoopMode; -import com.jme3.scene.Spatial; - -/** - * @deprecated use AnimationEvent instead - * @author Nehon - */ -@Deprecated -public class AnimationTrack extends AnimationEvent { - - public AnimationTrack() { - super(); - } - - public AnimationTrack(Spatial model, String animationName) { - super(model, animationName); - } - - public AnimationTrack(Spatial model, String animationName, float initialDuration) { - super(model, animationName, initialDuration); - } - - public AnimationTrack(Spatial model, String animationName, LoopMode loopMode) { - super(model, animationName, loopMode); - - } - - public AnimationTrack(Spatial model, String animationName, float initialDuration, LoopMode loopMode) { - super(model, animationName, initialDuration, loopMode); - } -} diff --git a/jme3-core/src/main/java/com/jme3/cinematic/events/CinematicEvent.java b/jme3-core/src/main/java/com/jme3/cinematic/events/CinematicEvent.java index 4515ca8a3a..9488b8ecd8 100644 --- a/jme3-core/src/main/java/com/jme3/cinematic/events/CinematicEvent.java +++ b/jme3-core/src/main/java/com/jme3/cinematic/events/CinematicEvent.java @@ -31,7 +31,7 @@ */ package com.jme3.cinematic.events; -import com.jme3.animation.LoopMode; +import com.jme3.anim.util.LoopMode; import com.jme3.app.Application; import com.jme3.cinematic.Cinematic; import com.jme3.cinematic.PlayState; diff --git a/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java b/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java index d4751807ef..7289c50a81 100644 --- a/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java +++ b/jme3-core/src/main/java/com/jme3/cinematic/events/MotionEvent.java @@ -31,8 +31,8 @@ */ package com.jme3.cinematic.events; -import com.jme3.animation.AnimationUtils; -import com.jme3.animation.LoopMode; +import com.jme3.anim.util.AnimationUtils; +import com.jme3.anim.util.LoopMode; import com.jme3.app.Application; import com.jme3.cinematic.Cinematic; import com.jme3.cinematic.MotionPath; diff --git a/jme3-core/src/main/java/com/jme3/cinematic/events/MotionTrack.java b/jme3-core/src/main/java/com/jme3/cinematic/events/MotionTrack.java index eda7f9c709..a579ce6c6c 100644 --- a/jme3-core/src/main/java/com/jme3/cinematic/events/MotionTrack.java +++ b/jme3-core/src/main/java/com/jme3/cinematic/events/MotionTrack.java @@ -31,7 +31,7 @@ */ package com.jme3.cinematic.events; -import com.jme3.animation.LoopMode; +import com.jme3.anim.util.LoopMode; import com.jme3.cinematic.MotionPath; import com.jme3.scene.Spatial; diff --git a/jme3-core/src/main/java/com/jme3/cinematic/events/SoundEvent.java b/jme3-core/src/main/java/com/jme3/cinematic/events/SoundEvent.java index b899781bbe..444bee93e1 100644 --- a/jme3-core/src/main/java/com/jme3/cinematic/events/SoundEvent.java +++ b/jme3-core/src/main/java/com/jme3/cinematic/events/SoundEvent.java @@ -31,7 +31,7 @@ */ package com.jme3.cinematic.events; -import com.jme3.animation.LoopMode; +import com.jme3.anim.util.LoopMode; import com.jme3.app.Application; import com.jme3.audio.AudioData; import com.jme3.audio.AudioNode; diff --git a/jme3-core/src/main/java/com/jme3/cinematic/events/SoundTrack.java b/jme3-core/src/main/java/com/jme3/cinematic/events/SoundTrack.java index 33d34a571e..f7108f9b9e 100644 --- a/jme3-core/src/main/java/com/jme3/cinematic/events/SoundTrack.java +++ b/jme3-core/src/main/java/com/jme3/cinematic/events/SoundTrack.java @@ -31,7 +31,7 @@ */ package com.jme3.cinematic.events; -import com.jme3.animation.LoopMode; +import com.jme3.anim.util.LoopMode; /** * A sound track to be played in a cinematic. diff --git a/jme3-core/src/main/java/com/jme3/collision/bih/BIHTree.java b/jme3-core/src/main/java/com/jme3/collision/bih/BIHTree.java index fca434f750..62747b44ae 100644 --- a/jme3-core/src/main/java/com/jme3/collision/bih/BIHTree.java +++ b/jme3-core/src/main/java/com/jme3/collision/bih/BIHTree.java @@ -47,16 +47,12 @@ import com.jme3.math.Vector3f; import com.jme3.scene.CollisionData; import com.jme3.scene.Mesh; -import com.jme3.scene.Mesh.Mode; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.mesh.IndexBuffer; -import com.jme3.scene.mesh.VirtualIndexBuffer; -import com.jme3.scene.mesh.WrappedIndexBuffer; import com.jme3.util.TempVars; +import com.jme3.vulkan.mesh.VertexReader; + import java.io.IOException; import static java.lang.Math.max; -import java.nio.FloatBuffer; public class BIHTree implements CollisionData { @@ -72,24 +68,25 @@ public class BIHTree implements CollisionData { // private transient CollisionResults boundResults = new CollisionResults(); private transient float[] bihSwapTmp; - private void initTriList(FloatBuffer vb, IndexBuffer ib) { + private void initTriList(VertexReader vb, IndexBuffer ib) { pointData = new float[numTris * 3 * 3]; int p = 0; + for (int i = 0; i < numTris * 3; i += 3) { int vert = ib.get(i) * 3; - pointData[p++] = vb.get(vert++); - pointData[p++] = vb.get(vert++); - pointData[p++] = vb.get(vert); + pointData[p++] = vb.getFloat(vert, 0); + pointData[p++] = vb.getFloat(vert, 1); + pointData[p++] = vb.getFloat(vert, 2); vert = ib.get(i + 1) * 3; - pointData[p++] = vb.get(vert++); - pointData[p++] = vb.get(vert++); - pointData[p++] = vb.get(vert); + pointData[p++] = vb.getFloat(vert, 0); + pointData[p++] = vb.getFloat(vert, 1); + pointData[p++] = vb.getFloat(vert, 2); vert = ib.get(i + 2) * 3; - pointData[p++] = vb.get(vert++); - pointData[p++] = vb.get(vert++); - pointData[p++] = vb.get(vert); + pointData[p++] = vb.getFloat(vert, 0); + pointData[p++] = vb.getFloat(vert, 1); + pointData[p++] = vb.getFloat(vert, 2); } triIndices = new int[numTris]; @@ -98,6 +95,14 @@ private void initTriList(FloatBuffer vb, IndexBuffer ib) { } } + public BIHTree(VertexReader positions, IndexBuffer indices) { + maxTrisPerNode = MAX_TRIS_PER_NODE; + bihSwapTmp = new float[9]; + numTris = indices.size() / 3; + initTriList(positions, indices); + } + + @Deprecated public BIHTree(Mesh mesh, int maxTrisPerNode) { this.mesh = mesh; this.maxTrisPerNode = maxTrisPerNode; @@ -111,21 +116,23 @@ public BIHTree(Mesh mesh, int maxTrisPerNode) { bihSwapTmp = new float[9]; - VertexBuffer vBuffer = mesh.getBuffer(Type.Position); - if (vBuffer == null) { - throw new IllegalArgumentException("A mesh should at least contain a Position buffer"); - } - IndexBuffer ib = mesh.getIndexBuffer(); - FloatBuffer vb = (FloatBuffer) vBuffer.getData(); + // Code incompatible with new Mesh interface - if (ib == null) { - ib = new VirtualIndexBuffer(mesh.getVertexCount(), mesh.getMode()); - } else if (mesh.getMode() != Mode.Triangles) { - ib = new WrappedIndexBuffer(mesh); - } +// VertexBuffer vBuffer = mesh.getBuffer(Type.Position); +// if (vBuffer == null) { +// throw new IllegalArgumentException("A mesh should at least contain a Position buffer"); +// } +// IndexBuffer ib = mesh.getIndexBuffer(); +// FloatBuffer vb = (FloatBuffer) vBuffer.getData(); + +// if (ib == null) { +// ib = new VirtualIndexBuffer(mesh.getVertexCount(), mesh.getMode()); +// } else if (mesh.getMode() != Mode.Triangles) { +// ib = new WrappedIndexBuffer(mesh); +// } - numTris = ib.size() / 3; - initTriList(vb, ib); + //numTris = ib.size() / 3; + //initTriList(vb, ib); } public BIHTree(Mesh mesh) { diff --git a/jme3-core/src/main/java/com/jme3/compat/ComponentFactory.java b/jme3-core/src/main/java/com/jme3/compat/ComponentFactory.java new file mode 100644 index 0000000000..0655b2200f --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/compat/ComponentFactory.java @@ -0,0 +1,21 @@ +package com.jme3.compat; + +import com.jme3.material.Material; +import com.jme3.scene.Mesh; +import com.jme3.scene.shape.Box; + +public interface ComponentFactory { + + Material createMaterial(String material); + + Material createBlankMaterial(String matdef); + + Mesh createBlankMesh(); + + Mesh migrateMesh(Mesh mesh); + + default Mesh createBoxMesh(float x, float y, float z) { + return migrateMesh(new Box(x, y, z)); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/compat/VulkanFactory.java b/jme3-core/src/main/java/com/jme3/compat/VulkanFactory.java new file mode 100644 index 0000000000..682315fe9d --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/compat/VulkanFactory.java @@ -0,0 +1,37 @@ +package com.jme3.compat; + +import com.jme3.material.Material; +import com.jme3.scene.Mesh; +import com.jme3.vulkan.buffers.generate.BufferGenerator; +import com.jme3.vulkan.mesh.AdaptiveMesh; +import com.jme3.vulkan.mesh.MeshDescription; +import com.jme3.vulkan.mesh.VkMesh; + +public class VulkanFactory implements ComponentFactory { + + private MeshDescription meshDescription; + private BufferGenerator bufferGenerator; + + @Override + public Material createMaterial(String material) { + throw new UnsupportedOperationException("To be implemented."); + } + + @Override + public Material createBlankMaterial(String matdef) { + throw new UnsupportedOperationException("To be implemented."); + } + + @Override + public Mesh createBlankMesh() { + return new AdaptiveMesh(meshDescription, bufferGenerator); + } + + @Override + public Mesh migrateMesh(Mesh mesh) { + if (!(mesh instanceof VkMesh)) { + + } + } + +} diff --git a/jme3-core/src/main/java/com/jme3/compat/package-info.java b/jme3-core/src/main/java/com/jme3/compat/package-info.java new file mode 100644 index 0000000000..c17132ec5f --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/compat/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains utilities for Vulkan and OpenGL compatible applications. + */ +package com.jme3.compat; \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/dev/NotFullyImplemented.java b/jme3-core/src/main/java/com/jme3/dev/NotFullyImplemented.java new file mode 100644 index 0000000000..ee9ee0411d --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/dev/NotFullyImplemented.java @@ -0,0 +1,8 @@ +package com.jme3.dev; + +/** + * Marks a member as needing implementation. + */ +public @interface NotFullyImplemented { + +} diff --git a/jme3-core/src/main/java/com/jme3/dev/package-info.java b/jme3-core/src/main/java/com/jme3/dev/package-info.java new file mode 100644 index 0000000000..6ddb80d471 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/dev/package-info.java @@ -0,0 +1,5 @@ +/** + * For engine development purposes only. + * Todo: delete + */ +package com.jme3.dev; \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/effect/ParticleMesh.java b/jme3-core/src/main/java/com/jme3/effect/ParticleMesh.java index b4333ae9f8..f685fcfbb0 100644 --- a/jme3-core/src/main/java/com/jme3/effect/ParticleMesh.java +++ b/jme3-core/src/main/java/com/jme3/effect/ParticleMesh.java @@ -33,7 +33,7 @@ import com.jme3.math.Matrix3f; import com.jme3.renderer.Camera; -import com.jme3.scene.Mesh; +import com.jme3.scene.GlMesh; /** * The ParticleMesh is the underlying visual implementation of a @@ -41,7 +41,7 @@ * * @author Kirill Vainer */ -public abstract class ParticleMesh extends Mesh { +public abstract class ParticleMesh extends GlMesh { /** * Type of particle mesh diff --git a/jme3-core/src/main/java/com/jme3/effect/ParticlePointMesh.java b/jme3-core/src/main/java/com/jme3/effect/ParticlePointMesh.java index 8f43bec5b2..a493814f23 100644 --- a/jme3-core/src/main/java/com/jme3/effect/ParticlePointMesh.java +++ b/jme3-core/src/main/java/com/jme3/effect/ParticlePointMesh.java @@ -33,9 +33,9 @@ import com.jme3.math.Matrix3f; import com.jme3.renderer.Camera; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Format; -import com.jme3.scene.VertexBuffer.Usage; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Format; +import com.jme3.scene.GlVertexBuffer.Usage; import com.jme3.util.BufferUtils; import java.nio.ByteBuffer; import java.nio.FloatBuffer; @@ -63,11 +63,11 @@ public void initParticleData(ParticleEmitter emitter, int numParticles) { FloatBuffer pb = BufferUtils.createVector3Buffer(numParticles); // if the buffer is already set only update the data - VertexBuffer buf = getBuffer(VertexBuffer.Type.Position); + GlVertexBuffer buf = getBuffer(GlVertexBuffer.Type.Position); if (buf != null) { buf.updateData(pb); } else { - VertexBuffer pvb = new VertexBuffer(VertexBuffer.Type.Position); + GlVertexBuffer pvb = new GlVertexBuffer(GlVertexBuffer.Type.Position); pvb.setupData(Usage.Stream, 3, Format.Float, pb); setBuffer(pvb); } @@ -75,11 +75,11 @@ public void initParticleData(ParticleEmitter emitter, int numParticles) { // set colors ByteBuffer cb = BufferUtils.createByteBuffer(numParticles * 4); - buf = getBuffer(VertexBuffer.Type.Color); + buf = getBuffer(GlVertexBuffer.Type.Color); if (buf != null) { buf.updateData(cb); } else { - VertexBuffer cvb = new VertexBuffer(VertexBuffer.Type.Color); + GlVertexBuffer cvb = new GlVertexBuffer(GlVertexBuffer.Type.Color); cvb.setupData(Usage.Stream, 4, Format.UnsignedByte, cb); cvb.setNormalized(true); setBuffer(cvb); @@ -88,11 +88,11 @@ public void initParticleData(ParticleEmitter emitter, int numParticles) { // set sizes FloatBuffer sb = BufferUtils.createFloatBuffer(numParticles); - buf = getBuffer(VertexBuffer.Type.Size); + buf = getBuffer(GlVertexBuffer.Type.Size); if (buf != null) { buf.updateData(sb); } else { - VertexBuffer svb = new VertexBuffer(VertexBuffer.Type.Size); + GlVertexBuffer svb = new GlVertexBuffer(GlVertexBuffer.Type.Size); svb.setupData(Usage.Stream, 1, Format.Float, sb); setBuffer(svb); } @@ -100,11 +100,11 @@ public void initParticleData(ParticleEmitter emitter, int numParticles) { // set UV-scale FloatBuffer tb = BufferUtils.createFloatBuffer(numParticles * 4); - buf = getBuffer(VertexBuffer.Type.TexCoord); + buf = getBuffer(GlVertexBuffer.Type.TexCoord); if (buf != null) { buf.updateData(tb); } else { - VertexBuffer tvb = new VertexBuffer(VertexBuffer.Type.TexCoord); + GlVertexBuffer tvb = new GlVertexBuffer(GlVertexBuffer.Type.TexCoord); tvb.setupData(Usage.Stream, 4, Format.Float, tb); setBuffer(tvb); } @@ -114,16 +114,16 @@ public void initParticleData(ParticleEmitter emitter, int numParticles) { @Override public void updateParticleData(Particle[] particles, Camera cam, Matrix3f inverseRotation) { - VertexBuffer pvb = getBuffer(VertexBuffer.Type.Position); + GlVertexBuffer pvb = getBuffer(GlVertexBuffer.Type.Position); FloatBuffer positions = (FloatBuffer) pvb.getData(); - VertexBuffer cvb = getBuffer(VertexBuffer.Type.Color); + GlVertexBuffer cvb = getBuffer(GlVertexBuffer.Type.Color); ByteBuffer colors = (ByteBuffer) cvb.getData(); - VertexBuffer svb = getBuffer(VertexBuffer.Type.Size); + GlVertexBuffer svb = getBuffer(GlVertexBuffer.Type.Size); FloatBuffer sizes = (FloatBuffer) svb.getData(); - VertexBuffer tvb = getBuffer(VertexBuffer.Type.TexCoord); + GlVertexBuffer tvb = getBuffer(GlVertexBuffer.Type.TexCoord); FloatBuffer texcoords = (FloatBuffer) tvb.getData(); float sizeScale = emitter.getWorldScale().x; diff --git a/jme3-core/src/main/java/com/jme3/effect/ParticleTriMesh.java b/jme3-core/src/main/java/com/jme3/effect/ParticleTriMesh.java index 62a214e2fd..c3e8b87ef8 100644 --- a/jme3-core/src/main/java/com/jme3/effect/ParticleTriMesh.java +++ b/jme3-core/src/main/java/com/jme3/effect/ParticleTriMesh.java @@ -35,9 +35,9 @@ import com.jme3.math.Matrix3f; import com.jme3.math.Vector3f; import com.jme3.renderer.Camera; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Format; -import com.jme3.scene.VertexBuffer.Usage; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Format; +import com.jme3.scene.GlVertexBuffer.Usage; import com.jme3.util.BufferUtils; import com.jme3.util.TempVars; import java.nio.ByteBuffer; @@ -64,22 +64,22 @@ public void initParticleData(ParticleEmitter emitter, int numParticles) { // set positions FloatBuffer pb = BufferUtils.createVector3Buffer(numParticles * 4); // if the buffer is already set only update the data - VertexBuffer buf = getBuffer(VertexBuffer.Type.Position); + GlVertexBuffer buf = getBuffer(GlVertexBuffer.Type.Position); if (buf != null) { buf.updateData(pb); } else { - VertexBuffer pvb = new VertexBuffer(VertexBuffer.Type.Position); + GlVertexBuffer pvb = new GlVertexBuffer(GlVertexBuffer.Type.Position); pvb.setupData(Usage.Stream, 3, Format.Float, pb); setBuffer(pvb); } // set colors ByteBuffer cb = BufferUtils.createByteBuffer(numParticles * 4 * 4); - buf = getBuffer(VertexBuffer.Type.Color); + buf = getBuffer(GlVertexBuffer.Type.Color); if (buf != null) { buf.updateData(cb); } else { - VertexBuffer cvb = new VertexBuffer(VertexBuffer.Type.Color); + GlVertexBuffer cvb = new GlVertexBuffer(GlVertexBuffer.Type.Color); cvb.setupData(Usage.Stream, 4, Format.UnsignedByte, cb); cvb.setNormalized(true); setBuffer(cvb); @@ -96,11 +96,11 @@ public void initParticleData(ParticleEmitter emitter, int numParticles) { } tb.flip(); - buf = getBuffer(VertexBuffer.Type.TexCoord); + buf = getBuffer(GlVertexBuffer.Type.TexCoord); if (buf != null) { buf.updateData(tb); } else { - VertexBuffer tvb = new VertexBuffer(VertexBuffer.Type.TexCoord); + GlVertexBuffer tvb = new GlVertexBuffer(GlVertexBuffer.Type.TexCoord); tvb.setupData(Usage.Static, 2, Format.Float, tb); setBuffer(tvb); } @@ -122,11 +122,11 @@ public void initParticleData(ParticleEmitter emitter, int numParticles) { } ib.flip(); - buf = getBuffer(VertexBuffer.Type.Index); + buf = getBuffer(GlVertexBuffer.Type.Index); if (buf != null) { buf.updateData(ib); } else { - VertexBuffer ivb = new VertexBuffer(VertexBuffer.Type.Index); + GlVertexBuffer ivb = new GlVertexBuffer(GlVertexBuffer.Type.Index); ivb.setupData(Usage.Static, 3, Format.UnsignedShort, ib); setBuffer(ivb); } @@ -140,7 +140,7 @@ public void setImagesXY(int imagesX, int imagesY) { this.imagesY = imagesY; if (imagesX != 1 || imagesY != 1) { uniqueTexCoords = true; - getBuffer(VertexBuffer.Type.TexCoord).setUsage(Usage.Stream); + getBuffer(GlVertexBuffer.Type.TexCoord).setUsage(Usage.Stream); } } @@ -153,13 +153,13 @@ public void updateParticleData(Particle[] particles, Camera cam, Matrix3f invers // SortUtil.msort(particles, particlesCopy, comparator); // particles = particlesCopy; - VertexBuffer pvb = getBuffer(VertexBuffer.Type.Position); + GlVertexBuffer pvb = getBuffer(GlVertexBuffer.Type.Position); FloatBuffer positions = (FloatBuffer) pvb.getData(); - VertexBuffer cvb = getBuffer(VertexBuffer.Type.Color); + GlVertexBuffer cvb = getBuffer(GlVertexBuffer.Type.Color); ByteBuffer colors = (ByteBuffer) cvb.getData(); - VertexBuffer tvb = getBuffer(VertexBuffer.Type.TexCoord); + GlVertexBuffer tvb = getBuffer(GlVertexBuffer.Type.TexCoord); FloatBuffer texcoords = (FloatBuffer) tvb.getData(); Vector3f camUp = cam.getUp(); diff --git a/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshFaceShape.java b/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshFaceShape.java index cda0c911eb..0ab92547f9 100644 --- a/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshFaceShape.java +++ b/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshFaceShape.java @@ -32,10 +32,13 @@ package com.jme3.effect.shapes; import com.jme3.math.FastMath; +import com.jme3.math.Triangle; import com.jme3.math.Vector3f; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.util.BufferUtils; +import com.jme3.vulkan.mesh.AttributeModifier; + import java.util.ArrayList; import java.util.List; @@ -62,22 +65,26 @@ public EmitterMeshFaceShape(List meshes) { @Override public void setMeshes(List meshes) { - this.vertices = new ArrayList>(meshes.size()); - this.normals = new ArrayList>(meshes.size()); + this.vertices = new ArrayList<>(meshes.size()); + this.normals = new ArrayList<>(meshes.size()); + int[] indices = new int[3]; for (Mesh mesh : meshes) { - Vector3f[] vertexTable = BufferUtils.getVector3Array(mesh.getFloatBuffer(Type.Position)); - int[] indices = new int[3]; - List vertices = new ArrayList<>(mesh.getTriangleCount() * 3); - List normals = new ArrayList<>(mesh.getTriangleCount()); - for (int i = 0; i < mesh.getTriangleCount(); ++i) { - mesh.getTriangle(i, indices); - vertices.add(vertexTable[indices[0]]); - vertices.add(vertexTable[indices[1]]); - vertices.add(vertexTable[indices[2]]); - normals.add(FastMath.computeNormal(vertexTable[indices[0]], vertexTable[indices[1]], vertexTable[indices[2]])); + try (AttributeModifier pos = mesh.modify(Type.Position)) { + List vertices = new ArrayList<>(mesh.getTriangleCount() * 3); + List normals = new ArrayList<>(mesh.getTriangleCount()); + for (int i = 0; i < mesh.getTriangleCount(); ++i) { + mesh.getTriangle(i, indices); + Vector3f v1 = pos.getVector3(indices[0], 0, null); + Vector3f v2 = pos.getVector3(indices[1], 0, null); + Vector3f v3 = pos.getVector3(indices[2], 0, null); + vertices.add(v1); + vertices.add(v2); + vertices.add(v3); + normals.add(FastMath.computeNormal(v1, v2, v3)); + } + this.vertices.add(vertices); + this.normals.add(normals); } - this.vertices.add(vertices); - this.normals.add(normals); } } diff --git a/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshVertexShape.java b/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshVertexShape.java index cadb07ca48..1268a66477 100644 --- a/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshVertexShape.java +++ b/jme3-core/src/main/java/com/jme3/effect/shapes/EmitterMeshVertexShape.java @@ -38,9 +38,11 @@ import com.jme3.math.FastMath; import com.jme3.math.Vector3f; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.util.BufferUtils; import com.jme3.util.clone.Cloner; +import com.jme3.vulkan.mesh.AttributeModifier; + import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -80,34 +82,36 @@ public EmitterMeshVertexShape(List meshes) { public void setMeshes(List meshes) { Map vertToNormalMap = new HashMap<>(); - this.vertices = new ArrayList>(meshes.size()); - this.normals = new ArrayList>(meshes.size()); + this.vertices = new ArrayList<>(meshes.size()); + this.normals = new ArrayList<>(meshes.size()); + final Vector3f temp = new Vector3f(); for (Mesh mesh : meshes) { // fetching the data - float[] vertexTable = BufferUtils.getFloatArray(mesh.getFloatBuffer(Type.Position)); - float[] normalTable = BufferUtils.getFloatArray(mesh.getFloatBuffer(Type.Normal)); - - // unifying normals - for (int i = 0; i < vertexTable.length; i += 3) {// the tables should have the same size and be dividable by 3 - Vector3f vert = new Vector3f(vertexTable[i], vertexTable[i + 1], vertexTable[i + 2]); - Vector3f norm = vertToNormalMap.get(vert); - if (norm == null) { - norm = new Vector3f(normalTable[i], normalTable[i + 1], normalTable[i + 2]); - vertToNormalMap.put(vert, norm); - } else { - norm.addLocal(normalTable[i], normalTable[i + 1], normalTable[i + 2]); + try (AttributeModifier pos = mesh.modify(Type.Position); + AttributeModifier norms = mesh.modify(Type.Normal)) { + + // unifying normals + for (int i = 0; i < mesh.getVertexCount(); i++) {// the tables should have the same size and be dividable by 3 + Vector3f vert = pos.getVector3(i, 0, null); + Vector3f norm = vertToNormalMap.get(vert); + if (norm == null) { + norm = norms.getVector3(i, 0, null); + vertToNormalMap.put(vert, norm); + } else { + norm.addLocal(norms.getVector3(i, 0, temp)); + } } - } - // adding data to vertices and normals - List vertices = new ArrayList<>(vertToNormalMap.size()); - List normals = new ArrayList<>(vertToNormalMap.size()); - for (Entry entry : vertToNormalMap.entrySet()) { - vertices.add(entry.getKey()); - normals.add(entry.getValue().normalizeLocal()); + // adding data to vertices and normals + List vertices = new ArrayList<>(vertToNormalMap.size()); + List normals = new ArrayList<>(vertToNormalMap.size()); + for (Entry entry : vertToNormalMap.entrySet()) { + vertices.add(entry.getKey()); + normals.add(entry.getValue().normalizeLocal()); + } + this.vertices.add(vertices); + this.normals.add(normals); } - this.vertices.add(vertices); - this.normals.add(normals); } } diff --git a/jme3-core/src/main/java/com/jme3/environment/EnvironmentCamera.java b/jme3-core/src/main/java/com/jme3/environment/EnvironmentCamera.java index 2f8ee78d9b..7be5bbe880 100644 --- a/jme3-core/src/main/java/com/jme3/environment/EnvironmentCamera.java +++ b/jme3-core/src/main/java/com/jme3/environment/EnvironmentCamera.java @@ -66,7 +66,7 @@ public class EnvironmentCamera extends BaseAppState { protected static Vector3f[] axisY = new Vector3f[6]; protected static Vector3f[] axisZ = new Vector3f[6]; - protected Image.Format imageFormat = Image.Format.RGB16F; + protected GlImage.Format imageFormat = GlImage.Format.RGB16F; public TextureCubeMap debugEnv; @@ -98,9 +98,9 @@ public class EnvironmentCamera extends BaseAppState { axisZ[5] = Vector3f.UNIT_Z.mult(-1f); } - protected Image images[]; + protected GlImage images[]; protected ViewPort[] viewports; - protected FrameBuffer[] framebuffers; + protected GlFrameBuffer[] framebuffers; protected ByteBuffer[] buffers; protected Vector3f position = new Vector3f(); @@ -146,7 +146,7 @@ public EnvironmentCamera(int size, Vector3f position) { * @param position the position of the camera. * @param imageFormat the ImageFormat to use for the resulting texture. */ - public EnvironmentCamera(int size, Vector3f position, Image.Format imageFormat) { + public EnvironmentCamera(int size, Vector3f position, GlImage.Format imageFormat) { this.size = size; this.position.set(position); this.imageFormat = imageFormat; @@ -183,7 +183,7 @@ public void render(final RenderManager renderManager) { size * size * imageFormat.getBitsPerPixel() / 8); renderManager.getRenderer().readFrameBufferWithFormat( framebuffers[i], buffers[i], imageFormat); - images[i] = new Image(imageFormat, size, size, buffers[i], + images[i] = new GlImage(imageFormat, size, size, buffers[i], ColorSpace.Linear); MipMapGenerator.generateMipMaps(images[i]); } @@ -297,9 +297,9 @@ protected void initialize(Application app) { final Texture2D[] textures = new Texture2D[6]; viewports = new ViewPort[6]; - framebuffers = new FrameBuffer[6]; + framebuffers = new GlFrameBuffer[6]; buffers = new ByteBuffer[6]; - images = new Image[6]; + images = new GlImage[6]; for (int i = 0; i < 6; i++) { cameras[i] = createOffCamera(size, position, axisX[i], axisY[i], axisZ[i]); @@ -314,11 +314,11 @@ protected void initialize(Application app) { protected void cleanup(Application app) { this.backGroundColor = null; - for (final FrameBuffer frameBuffer : framebuffers) { + for (final GlFrameBuffer frameBuffer : framebuffers) { frameBuffer.dispose(); } - for (final Image image : images) { + for (final GlImage image : images) { if (image != null) { image.dispose(); } @@ -330,7 +330,7 @@ protected void cleanup(Application app) { * * @return the enum value */ - public Image.Format getImageFormat() { + public GlImage.Format getImageFormat() { return imageFormat; } @@ -383,10 +383,10 @@ protected ViewPort createOffViewPort(final String name, final Camera offCamera) * @param offView the off-screen viewport to be used (alias created) * @return a new instance */ - protected FrameBuffer createOffScreenFrameBuffer(int mapSize, ViewPort offView) { + protected GlFrameBuffer createOffScreenFrameBuffer(int mapSize, ViewPort offView) { // create offscreen framebuffer - final FrameBuffer offBuffer = new FrameBuffer(mapSize, mapSize, 1); - offBuffer.setDepthBuffer(Image.Format.Depth); + final GlFrameBuffer offBuffer = new GlFrameBuffer(mapSize, mapSize, 1); + offBuffer.setDepthBuffer(GlImage.Format.Depth); offView.setOutputFrameBuffer(offBuffer); return offBuffer; } diff --git a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java index 6f07fd1c1c..a6e83d96e0 100644 --- a/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java +++ b/jme3-core/src/main/java/com/jme3/environment/EnvironmentProbeControl.java @@ -49,7 +49,7 @@ import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.scene.control.Control; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage.Format; /** * A control that automatically handles environment bake and rebake including diff --git a/jme3-core/src/main/java/com/jme3/environment/FastLightProbeFactory.java b/jme3-core/src/main/java/com/jme3/environment/FastLightProbeFactory.java index 12ba5e99c1..0abe35f999 100644 --- a/jme3-core/src/main/java/com/jme3/environment/FastLightProbeFactory.java +++ b/jme3-core/src/main/java/com/jme3/environment/FastLightProbeFactory.java @@ -40,7 +40,7 @@ import com.jme3.renderer.RenderManager; import com.jme3.scene.Node; import com.jme3.scene.Spatial; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage.Format; /** * A faster LightProbeFactory that uses GPU accelerated algorithms. diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java index 6831914945..003ed3abc3 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/GenericEnvBaker.java @@ -48,13 +48,13 @@ import com.jme3.renderer.ViewPort; import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Texture; -import com.jme3.texture.FrameBuffer.FrameBufferTarget; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture.MagFilter; -import com.jme3.texture.Texture.MinFilter; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlFrameBuffer.FrameBufferTarget; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture.MagFilter; +import com.jme3.texture.GlTexture.MinFilter; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.texture.TextureCubeMap; import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; @@ -168,9 +168,9 @@ public void clean() { @Override public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, float frustumFar, Predicate filter) { - FrameBuffer envbakers[] = new FrameBuffer[6]; + GlFrameBuffer envbakers[] = new GlFrameBuffer[6]; for (int i = 0; i < 6; i++) { - envbakers[i] = new FrameBuffer(envMap.getImage().getWidth(), envMap.getImage().getHeight(), 1); + envbakers[i] = new GlFrameBuffer(envMap.getImage().getWidth(), envMap.getImage().getHeight(), 1); envbakers[i].setDepthTarget(FrameBufferTarget.newTarget(depthFormat)); envbakers[i].setSrgb(false); envbakers[i].addColorTarget(FrameBufferTarget.newTarget(envMap).face(TextureCubeMap.Face.values()[i])); @@ -181,7 +181,7 @@ public void bakeEnvironment(Spatial scene, Vector3f position, float frustumNear, } for (int i = 0; i < 6; i++) { - FrameBuffer envbaker = envbakers[i]; + GlFrameBuffer envbaker = envbakers[i]; ViewPort viewPort = new ViewPort("EnvBaker", updateAndGetInternalCamera(i, envbaker.getWidth(), envbaker.getHeight(), position, frustumNear, frustumFar)); viewPort.setClearFlags(true, true, true); @@ -236,10 +236,10 @@ protected void startPulling() { * id of face if cubemap or 0 otherwise * @return the ByteBuffer containing the pulled data */ - protected ByteBuffer pull(FrameBuffer fb, Texture env, int faceId) { + protected ByteBuffer pull(GlFrameBuffer fb, GlTexture env, int faceId) { - if (fb.getColorTarget().getFormat() != env.getImage().getFormat()) - throw new IllegalArgumentException("Format mismatch: " + fb.getColorTarget().getFormat() + "!=" + env.getImage().getFormat()); + if (fb.getColorTarget().getFormat() != env.getImage().getGlFormat()) + throw new IllegalArgumentException("Format mismatch: " + fb.getColorTarget().getFormat() + "!=" + env.getImage().getGlFormat()); ByteBuffer face = BufferUtils.createByteBuffer(fb.getWidth() * fb.getHeight() * (fb.getColorTarget().getFormat().getBitsPerPixel() / 8)); renderManager.getRenderer().readFrameBufferWithFormat(fb, face, fb.getColorTarget().getFormat()); @@ -269,7 +269,7 @@ protected ByteBuffer pull(FrameBuffer fb, Texture env, int faceId) { * @param tx * the texture to pull into */ - protected void endPulling(Texture tx) { + protected void endPulling(GlTexture tx) { for (int i = 0; i < bos.size(); i++) { ByteArrayOutputStream bo = bos.get(i); if (bo != null) { diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java index 5b79a4922f..714a5f2ea3 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBaker.java @@ -36,6 +36,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import com.jme3.asset.AssetManager; +import com.jme3.material.GlMaterial; import com.jme3.material.Material; import com.jme3.math.FastMath; import com.jme3.math.Vector3f; @@ -43,14 +44,14 @@ import com.jme3.renderer.RenderManager; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Box; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture.MagFilter; -import com.jme3.texture.Texture.MinFilter; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture.MagFilter; +import com.jme3.texture.GlTexture.MinFilter; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.texture.Texture2D; import com.jme3.texture.TextureCubeMap; -import com.jme3.texture.FrameBuffer.FrameBufferTarget; +import com.jme3.texture.GlFrameBuffer.FrameBufferTarget; import com.jme3.texture.image.ColorSpace; import com.jme3.ui.Picture; @@ -98,7 +99,7 @@ public IBLGLEnvBaker(RenderManager rm, AssetManager am, Format format, Format de int[] sizes = new int[nbMipMaps]; for (int i = 0; i < nbMipMaps; i++) { int size = (int) FastMath.pow(2, nbMipMaps - 1 - i); - sizes[i] = size * size * (specular.getImage().getFormat().getBitsPerPixel() / 8); + sizes[i] = size * size * (specular.getImage().getGlFormat().getBitsPerPixel() / 8); } specular.getImage().setMipMapSizes(sizes); @@ -125,16 +126,16 @@ private void bakeSpecularIBL(int mip, float roughness, Material mat, Geometry sc int mipWidth = (int) (specular.getImage().getWidth() * FastMath.pow(0.5f, mip)); int mipHeight = (int) (specular.getImage().getHeight() * FastMath.pow(0.5f, mip)); - FrameBuffer specularbakers[] = new FrameBuffer[6]; + GlFrameBuffer specularbakers[] = new GlFrameBuffer[6]; for (int i = 0; i < 6; i++) { - specularbakers[i] = new FrameBuffer(mipWidth, mipHeight, 1); + specularbakers[i] = new GlFrameBuffer(mipWidth, mipHeight, 1); specularbakers[i].setSrgb(false); specularbakers[i].addColorTarget(FrameBufferTarget.newTarget(specular).level(mip).face(i)); specularbakers[i].setMipMapsGenerationHint(false); } for (int i = 0; i < 6; i++) { - FrameBuffer specularbaker = specularbakers[i]; + GlFrameBuffer specularbaker = specularbakers[i]; mat.setInt("FaceId", i); screen.updateLogicalState(0); @@ -159,7 +160,7 @@ public void bakeSpecularIBL() { Box boxm = new Box(1, 1, 1); Geometry screen = new Geometry("BakeBox", boxm); - Material mat = new Material(assetManager, "Common/IBL/IBLKernels.j3md"); + Material mat = new GlMaterial(assetManager, "Common/IBL/IBLKernels.j3md"); mat.setBoolean("UseSpecularIBL", true); mat.setTexture("EnvMap", envMap); screen.setMaterial(mat); @@ -209,7 +210,7 @@ public Texture2D genBRTF() { screen.setWidth(1); screen.setHeight(1); - FrameBuffer brtfbaker = new FrameBuffer(brtf.getImage().getWidth(), brtf.getImage().getHeight(), 1); + GlFrameBuffer brtfbaker = new GlFrameBuffer(brtf.getImage().getWidth(), brtf.getImage().getHeight(), 1); brtfbaker.setSrgb(false); brtfbaker.addColorTarget(FrameBufferTarget.newTarget(brtf)); @@ -219,7 +220,7 @@ public Texture2D genBRTF() { Camera envcam = updateAndGetInternalCamera(0, brtf.getImage().getWidth(), brtf.getImage().getHeight(), Vector3f.ZERO, 1, 1000); - Material mat = new Material(assetManager, "Common/IBL/IBLKernels.j3md"); + Material mat = new GlMaterial(assetManager, "Common/IBL/IBLKernels.j3md"); mat.setBoolean("UseBRDF", true); screen.setMaterial(mat); @@ -250,7 +251,7 @@ public void bakeIrradiance() { Box boxm = new Box(1, 1, 1); Geometry screen = new Geometry("BakeBox", boxm); - FrameBuffer irradiancebaker = new FrameBuffer(irradiance.getImage().getWidth(), irradiance.getImage().getHeight(), 1); + GlFrameBuffer irradiancebaker = new GlFrameBuffer(irradiance.getImage().getWidth(), irradiance.getImage().getHeight(), 1); irradiancebaker.setSrgb(false); if (isTexturePulling()) { @@ -262,7 +263,7 @@ public void bakeIrradiance() { FrameBufferTarget.newTarget(irradiance).face(TextureCubeMap.Face.values()[i])); } - Material mat = new Material(assetManager, "Common/IBL/IBLKernels.j3md"); + Material mat = new GlMaterial(assetManager, "Common/IBL/IBLKernels.j3md"); mat.setBoolean("UseIrradiance", true); mat.setTexture("EnvMap", envMap); screen.setMaterial(mat); diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java index f6284f14ea..288594b0dd 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLGLEnvBakerLight.java @@ -35,6 +35,7 @@ import java.util.logging.Logger; import com.jme3.asset.AssetManager; import com.jme3.environment.util.EnvMapUtils; +import com.jme3.material.GlMaterial; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; import com.jme3.math.FastMath; @@ -44,11 +45,11 @@ import com.jme3.renderer.RenderManager; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Box; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage; import com.jme3.texture.Texture2D; -import com.jme3.texture.FrameBuffer.FrameBufferTarget; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlFrameBuffer.FrameBufferTarget; +import com.jme3.texture.GlImage.Format; import com.jme3.texture.image.ColorSpace; import com.jme3.texture.image.ImageRaster; import com.jme3.util.BufferUtils; @@ -96,7 +97,7 @@ public void bakeSphericalHarmonicsCoefficients() { Box boxm = new Box(1, 1, 1); Geometry screen = new Geometry("BakeBox", boxm); - Material mat = new Material(assetManager, "Common/IBLSphH/IBLSphH.j3md"); + Material mat = new GlMaterial(assetManager, "Common/IBLSphH/IBLSphH.j3md"); mat.setTexture("Texture", envMap); mat.setVector2("Resolution", new Vector2f(envMap.getImage().getWidth(), envMap.getImage().getHeight())); screen.setMaterial(mat); @@ -117,7 +118,7 @@ public void bakeSphericalHarmonicsCoefficients() { Texture2D shCoefTx[] = { new Texture2D(NUM_SH_COEFFICIENT, 1, 1, format), new Texture2D(NUM_SH_COEFFICIENT, 1, 1, format) }; - FrameBuffer shbaker[] = { new FrameBuffer(NUM_SH_COEFFICIENT, 1, 1), new FrameBuffer(NUM_SH_COEFFICIENT, 1, 1) }; + GlFrameBuffer shbaker[] = { new GlFrameBuffer(NUM_SH_COEFFICIENT, 1, 1), new GlFrameBuffer(NUM_SH_COEFFICIENT, 1, 1) }; shbaker[0].setSrgb(false); shbaker[0].addColorTarget(FrameBufferTarget.newTarget(shCoefTx[0])); @@ -149,7 +150,7 @@ public void bakeSphericalHarmonicsCoefficients() { renderManager.getRenderer().readFrameBufferWithFormat(shbaker[renderOnT], shCoefRaw, shbaker[renderOnT].getColorTarget().getFormat()); shCoefRaw.rewind(); - Image img = new Image(format, NUM_SH_COEFFICIENT, 1, shCoefRaw, ColorSpace.Linear); + GlImage img = new GlImage(format, NUM_SH_COEFFICIENT, 1, shCoefRaw, ColorSpace.Linear); ImageRaster imgr = ImageRaster.create(img); shCoef = new Vector3f[NUM_SH_COEFFICIENT]; diff --git a/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java b/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java index 26b3c1cd65..8731eded1c 100644 --- a/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java +++ b/jme3-core/src/main/java/com/jme3/environment/baker/IBLHybridEnvBakerLight.java @@ -37,19 +37,20 @@ import java.util.logging.Logger; import com.jme3.asset.AssetManager; import com.jme3.environment.util.EnvMapUtils; +import com.jme3.material.GlMaterial; import com.jme3.material.Material; import com.jme3.math.FastMath; import com.jme3.math.Vector3f; import com.jme3.renderer.RenderManager; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Box; -import com.jme3.texture.FrameBuffer; +import com.jme3.texture.GlFrameBuffer; import com.jme3.texture.TextureCubeMap; -import com.jme3.texture.FrameBuffer.FrameBufferTarget; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture.MagFilter; -import com.jme3.texture.Texture.MinFilter; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlFrameBuffer.FrameBufferTarget; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture.MagFilter; +import com.jme3.texture.GlTexture.MinFilter; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.texture.image.ColorSpace; /** @@ -97,7 +98,7 @@ public IBLHybridEnvBakerLight(RenderManager rm, AssetManager am, Format format, int[] sizes = new int[nbMipMaps]; for (int i = 0; i < nbMipMaps; i++) { int size = (int) FastMath.pow(2, nbMipMaps - 1 - i); - sizes[i] = size * size * (specular.getImage().getFormat().getBitsPerPixel() / 8); + sizes[i] = size * size * (specular.getImage().getGlFormat().getBitsPerPixel() / 8); } specular.getImage().setMipMapSizes(sizes); specular.getImage().setMipmapsGenerated(true); @@ -115,16 +116,16 @@ private void bakeSpecularIBL(int mip, float roughness, Material mat, Geometry sc int mipWidth = (int) (specular.getImage().getWidth() * FastMath.pow(0.5f, mip)); int mipHeight = (int) (specular.getImage().getHeight() * FastMath.pow(0.5f, mip)); - FrameBuffer specularbakers[] = new FrameBuffer[6]; + GlFrameBuffer specularbakers[] = new GlFrameBuffer[6]; for (int i = 0; i < 6; i++) { - specularbakers[i] = new FrameBuffer(mipWidth, mipHeight, 1); + specularbakers[i] = new GlFrameBuffer(mipWidth, mipHeight, 1); specularbakers[i].setSrgb(false); specularbakers[i].addColorTarget(FrameBufferTarget.newTarget(specular).level(mip).face(i)); specularbakers[i].setMipMapsGenerationHint(false); } for (int i = 0; i < 6; i++) { - FrameBuffer specularbaker = specularbakers[i]; + GlFrameBuffer specularbaker = specularbakers[i]; mat.setInt("FaceId", i); screen.updateLogicalState(0); @@ -149,7 +150,7 @@ public void bakeSpecularIBL() { Box boxm = new Box(1, 1, 1); Geometry screen = new Geometry("BakeBox", boxm); - Material mat = new Material(assetManager, "Common/IBL/IBLKernels.j3md"); + Material mat = new GlMaterial(assetManager, "Common/IBL/IBLKernels.j3md"); mat.setBoolean("UseSpecularIBL", true); mat.setTexture("EnvMap", envMap); screen.setMaterial(mat); diff --git a/jme3-core/src/main/java/com/jme3/environment/util/BoundingSphereDebug.java b/jme3-core/src/main/java/com/jme3/environment/util/BoundingSphereDebug.java index 622164e4a1..14f19fcda6 100644 --- a/jme3-core/src/main/java/com/jme3/environment/util/BoundingSphereDebug.java +++ b/jme3-core/src/main/java/com/jme3/environment/util/BoundingSphereDebug.java @@ -36,8 +36,8 @@ import com.jme3.math.ColorRGBA; import com.jme3.math.FastMath; import com.jme3.scene.Geometry; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.util.BufferUtils; import java.nio.FloatBuffer; import java.nio.ShortBuffer; @@ -49,7 +49,7 @@ * * @author nehon */ -public class BoundingSphereDebug extends Mesh { +public class BoundingSphereDebug extends GlMesh { protected int vertCount; protected int triCount; @@ -155,7 +155,7 @@ private void setIndexData() { public static Geometry createDebugSphere(AssetManager assetManager) { BoundingSphereDebug mesh = new BoundingSphereDebug(); Geometry geom = new Geometry("BoundingDebug", mesh); - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + Material mat = Backend.material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat.setBoolean("VertexColor", true); mat.getAdditionalRenderState().setWireframe(true); geom.setMaterial(mat); diff --git a/jme3-core/src/main/java/com/jme3/environment/util/CubeMapWrapper.java b/jme3-core/src/main/java/com/jme3/environment/util/CubeMapWrapper.java index 322caa92d1..046f02251f 100644 --- a/jme3-core/src/main/java/com/jme3/environment/util/CubeMapWrapper.java +++ b/jme3-core/src/main/java/com/jme3/environment/util/CubeMapWrapper.java @@ -32,7 +32,7 @@ package com.jme3.environment.util; import com.jme3.math.*; -import com.jme3.texture.Image; +import com.jme3.texture.GlImage; import com.jme3.texture.TextureCubeMap; import com.jme3.texture.image.DefaultImageRaster; import com.jme3.texture.image.MipMapImageRaster; @@ -53,7 +53,7 @@ public class CubeMapWrapper { private final DefaultImageRaster raster; private int[] sizes; private final Vector2f uvs = new Vector2f(); - private final Image image; + private final GlImage image; private final ColorRGBA tmpColor = new ColorRGBA(); @@ -245,7 +245,7 @@ public void initMipMaps(int nbMipMaps) { int totalSize = 0; for (int i = 0; i < nbMipMaps; i++) { int size = (int) pow(2, maxMipMap - 1 - i); - sizes[i] = size * size * image.getFormat().getBitsPerPixel() / 8; + sizes[i] = size * size * image.getGlFormat().getBitsPerPixel() / 8; totalSize += sizes[i]; } diff --git a/jme3-core/src/main/java/com/jme3/environment/util/EnvMapUtils.java b/jme3-core/src/main/java/com/jme3/environment/util/EnvMapUtils.java index 81c12e4a2c..015f16b15d 100644 --- a/jme3-core/src/main/java/com/jme3/environment/util/EnvMapUtils.java +++ b/jme3-core/src/main/java/com/jme3/environment/util/EnvMapUtils.java @@ -32,6 +32,7 @@ package com.jme3.environment.util; import com.jme3.asset.AssetManager; +import com.jme3.material.GlMaterial; import com.jme3.material.Material; import com.jme3.math.*; import com.jme3.scene.Geometry; @@ -113,8 +114,8 @@ private EnvMapUtils() { * @param format the format of the image * @return a cube map */ - public static TextureCubeMap makeCubeMap(Image rightImg, Image leftImg, Image upImg, Image downImg, Image backImg, Image frontImg, Image.Format format) { - Image cubeImage = new Image(format, leftImg.getWidth(), leftImg.getHeight(), null, ColorSpace.Linear); + public static TextureCubeMap makeCubeMap(GlImage rightImg, GlImage leftImg, GlImage upImg, GlImage downImg, GlImage backImg, GlImage frontImg, GlImage.Format format) { + GlImage cubeImage = new GlImage(format, leftImg.getWidth(), leftImg.getHeight(), null, ColorSpace.Linear); cubeImage.addData(rightImg.getData(0)); cubeImage.addData(leftImg.getData(0)); @@ -129,9 +130,9 @@ public static TextureCubeMap makeCubeMap(Image rightImg, Image leftImg, Image up TextureCubeMap cubeMap = new TextureCubeMap(cubeImage); cubeMap.setAnisotropicFilter(0); - cubeMap.setMagFilter(Texture.MagFilter.Bilinear); - cubeMap.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); - cubeMap.setWrap(Texture.WrapMode.EdgeClamp); + cubeMap.setMagFilter(GlTexture.MagFilter.Bilinear); + cubeMap.setMinFilter(GlTexture.MinFilter.BilinearNoMipMaps); + cubeMap.setWrap(GlTexture.WrapMode.EdgeClamp); return cubeMap; } @@ -151,8 +152,8 @@ public static TextureCubeMap makeCubeMap(Image rightImg, Image leftImg, Image up * @return a new instance */ public static TextureCubeMap duplicateCubeMap(TextureCubeMap sourceMap) { - Image srcImg = sourceMap.getImage(); - Image cubeImage = new Image(srcImg.getFormat(), srcImg.getWidth(), srcImg.getHeight(), null, srcImg.getColorSpace()); + GlImage srcImg = sourceMap.getImage(); + GlImage cubeImage = new GlImage(srcImg.getGlFormat(), srcImg.getWidth(), srcImg.getHeight(), null, srcImg.getColorSpace()); for (ByteBuffer d : srcImg.getData()) { cubeImage.addData(d.duplicate()); @@ -164,7 +165,7 @@ public static TextureCubeMap duplicateCubeMap(TextureCubeMap sourceMap) { cubeMap.setAnisotropicFilter(sourceMap.getAnisotropicFilter()); cubeMap.setMagFilter(sourceMap.getMagFilter()); cubeMap.setMinFilter(sourceMap.getMinFilter()); - cubeMap.setWrap(sourceMap.getWrap(Texture.WrapAxis.S)); + cubeMap.setWrap(sourceMap.getWrap(GlTexture.WrapAxis.S)); return cubeMap; } @@ -621,7 +622,7 @@ public static Node getCubeMapCrossDebugView(TextureCubeMap cubeMap, AssetManager for (int i = 0; i < 6; i++) { pics[i] = new Picture("bla"); - Texture2D tex = new Texture2D(new Image(cubeMap.getImage().getFormat(), size, size, cubeMap.getImage().getData(i), cubeMap.getImage().getColorSpace())); + Texture2D tex = new Texture2D(new GlImage(cubeMap.getImage().getGlFormat(), size, size, cubeMap.getImage().getData(i), cubeMap.getImage().getColorSpace())); pics[i].setTexture(assetManager, tex, true); pics[i].setWidth(size); @@ -644,7 +645,7 @@ public static Node getCubeMapCrossDebugView(TextureCubeMap cubeMap, AssetManager Quad q = new Quad(size * 4, size * 3); Geometry g = new Geometry("bg", q); - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + Material mat = new GlMaterial(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat.setColor("Color", ColorRGBA.Black); g.setMaterial(mat); g.setLocalTranslation(0, 0, 0); @@ -678,7 +679,7 @@ public static Node getCubeMapCrossDebugViewWithMipMaps(TextureCubeMap cubeMap, A ByteBuffer data = BufferUtils.createByteBuffer(dataArray); pics[i] = new Picture("bla"); - Texture2D tex = new Texture2D(new Image(cubeMap.getImage().getFormat(), size, size, data, cubeMap.getImage().getColorSpace())); + Texture2D tex = new Texture2D(new GlImage(cubeMap.getImage().getGlFormat(), size, size, data, cubeMap.getImage().getColorSpace())); pics[i].setTexture(assetManager, tex, true); pics[i].setWidth(size); @@ -705,7 +706,7 @@ public static Node getCubeMapCrossDebugViewWithMipMaps(TextureCubeMap cubeMap, A Quad q = new Quad(cubeMap.getImage().getWidth() * 4 + nbMips, guiOffset + size); Geometry g = new Geometry("bg", q); - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + Material mat = new GlMaterial(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat.setColor("Color", ColorRGBA.Black); g.setMaterial(mat); g.setLocalTranslation(0, 0, 0); @@ -721,11 +722,11 @@ public static Node getCubeMapCrossDebugViewWithMipMaps(TextureCubeMap cubeMap, A * @param imageFormat the format of the image * @return the initialized Irradiance map */ - public static TextureCubeMap createIrradianceMap(int size, Image.Format imageFormat) { + public static TextureCubeMap createIrradianceMap(int size, GlImage.Format imageFormat) { TextureCubeMap irrMap = new TextureCubeMap(size, size, imageFormat); - irrMap.setMagFilter(Texture.MagFilter.Bilinear); - irrMap.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); + irrMap.setMagFilter(GlTexture.MagFilter.Bilinear); + irrMap.setMinFilter(GlTexture.MinFilter.BilinearNoMipMaps); irrMap.getImage().setColorSpace(ColorSpace.Linear); return irrMap; } @@ -736,11 +737,11 @@ public static TextureCubeMap createIrradianceMap(int size, Image.Format imageFor * @param imageFormat the format of the image * @return the initialized prefiltered env map */ - public static TextureCubeMap createPrefilteredEnvMap(int size, Image.Format imageFormat) { + public static TextureCubeMap createPrefilteredEnvMap(int size, GlImage.Format imageFormat) { TextureCubeMap pem = new TextureCubeMap(size, size, imageFormat); - pem.setMagFilter(Texture.MagFilter.Bilinear); - pem.setMinFilter(Texture.MinFilter.Trilinear); + pem.setMagFilter(GlTexture.MagFilter.Bilinear); + pem.setMinFilter(GlTexture.MinFilter.Trilinear); pem.getImage().setColorSpace(ColorSpace.Linear); int nbMipMap = Math.min(6, (int) (Math.log(size) / Math.log(2))); CubeMapWrapper targetWrapper = new CubeMapWrapper(pem); diff --git a/jme3-core/src/main/java/com/jme3/environment/util/LightsDebugState.java b/jme3-core/src/main/java/com/jme3/environment/util/LightsDebugState.java index e2a159bf04..01629b00c6 100644 --- a/jme3-core/src/main/java/com/jme3/environment/util/LightsDebugState.java +++ b/jme3-core/src/main/java/com/jme3/environment/util/LightsDebugState.java @@ -34,6 +34,7 @@ import com.jme3.app.Application; import com.jme3.app.state.BaseAppState; import com.jme3.light.*; +import com.jme3.material.GlMaterial; import com.jme3.material.Material; import com.jme3.renderer.RenderManager; import com.jme3.scene.Geometry; @@ -68,7 +69,7 @@ protected void initialize(Application app) { debugNode = new Node("Environment debug Node"); Sphere s = new Sphere(16, 16, 0.15f); debugGeom = new Geometry("debugEnvProbe", s); - debugMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/reflect.j3md"); + debugMaterial = new GlMaterial(app.getAssetManager(), "Common/MatDefs/Misc/reflect.j3md"); debugGeom.setMaterial(debugMaterial); debugBounds = BoundingSphereDebug.createDebugSphere(app.getAssetManager()); if (scene == null) { diff --git a/jme3-core/src/main/java/com/jme3/export/SavableClassUtil.java b/jme3-core/src/main/java/com/jme3/export/SavableClassUtil.java index 79364c96ea..45846a67cd 100644 --- a/jme3-core/src/main/java/com/jme3/export/SavableClassUtil.java +++ b/jme3-core/src/main/java/com/jme3/export/SavableClassUtil.java @@ -31,7 +31,6 @@ */ package com.jme3.export; -import com.jme3.animation.Animation; import com.jme3.effect.shapes.*; import com.jme3.material.MatParamTexture; @@ -71,8 +70,6 @@ private static void addRemapping(String oldClass, Class newCl addRemapping("com.jme3.effect.EmitterMeshVertexShape", EmitterMeshVertexShape.class); addRemapping("com.jme3.effect.EmitterPointShape", EmitterPointShape.class); addRemapping("com.jme3.material.Material$MatParamTexture", MatParamTexture.class); - addRemapping("com.jme3.animation.BoneAnimation", Animation.class); - addRemapping("com.jme3.animation.SpatialAnimation", Animation.class); // Even though we no longer include Blender loading as part of the engine, // we leave this line in so that old j3os will still work and just not diff --git a/jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java b/jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java index c4ac9b26f0..8388c631d0 100644 --- a/jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java +++ b/jme3-core/src/main/java/com/jme3/font/BitmapTextPage.java @@ -34,8 +34,10 @@ import com.jme3.material.Material; import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Type; +import com.jme3.texture.Texture; import com.jme3.texture.Texture2D; import com.jme3.util.BufferUtils; import com.jme3.util.clone.Cloner; @@ -55,11 +57,11 @@ class BitmapTextPage extends Geometry { private final short[] idx; private final byte[] color; private final int page; - private final Texture2D texture; + private final Texture texture; private final LinkedList pageQuads = new LinkedList<>(); BitmapTextPage(BitmapFont font, boolean arrayBased, int page) { - super("BitmapFont", new Mesh()); + super("BitmapFont", new GlMesh()); setRequiresUpdates(false); setBatchHint(BatchHint.Never); if (font == null) { @@ -74,7 +76,7 @@ class BitmapTextPage extends Geometry { } setMaterial(mat); - this.texture = (Texture2D) mat.getTextureParam("ColorMap").getTextureValue(); + this.texture = mat.getTexture("ColorMap"); // initialize buffers Mesh m = getMesh(); @@ -113,7 +115,7 @@ class BitmapTextPage extends Geometry { this(font, false, 0); } - Texture2D getTexture() { + Texture getTexture() { return texture; } @@ -166,10 +168,10 @@ void assemble(Letters quads) { int vertCount = pageQuads.size() * 4; int triCount = pageQuads.size() * 2; - VertexBuffer pb = m.getBuffer(Type.Position); - VertexBuffer tb = m.getBuffer(Type.TexCoord); - VertexBuffer ib = m.getBuffer(Type.Index); - VertexBuffer cb = m.getBuffer(Type.Color); + GlVertexBuffer pb = m.getBuffer(Type.Position); + GlVertexBuffer tb = m.getBuffer(Type.TexCoord); + GlVertexBuffer ib = m.getBuffer(Type.Index); + GlVertexBuffer cb = m.getBuffer(Type.Color); FloatBuffer fpb = (FloatBuffer) pb.getData(); FloatBuffer ftb = (FloatBuffer) tb.getData(); diff --git a/jme3-core/src/main/java/com/jme3/json/JsonReadable.java b/jme3-core/src/main/java/com/jme3/json/JsonReadable.java new file mode 100644 index 0000000000..6facc3bd28 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/json/JsonReadable.java @@ -0,0 +1,7 @@ +package com.jme3.json; + +public interface JsonReadable { + + void read(SavableData.Info info); + +} diff --git a/jme3-core/src/main/java/com/jme3/json/SavableData.java b/jme3-core/src/main/java/com/jme3/json/SavableData.java new file mode 100644 index 0000000000..e27a5ed83d --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/json/SavableData.java @@ -0,0 +1,105 @@ +package com.jme3.json; + +import com.fasterxml.jackson.databind.JsonNode; +import com.jme3.asset.AssetManager; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; + +public class SavableData { + + public static final String TYPE = "type"; + + private static final Map> natives = new HashMap<>(); + + private final AssetManager assetManager; + private final JsonNode imports; + + public SavableData(AssetManager assetManager, JsonNode imports) { + this.assetManager = assetManager; + this.imports = imports; + } + + public T load(String name, JsonNode properties) { + return load(name, null, properties); + } + + @SuppressWarnings("unchecked") + public T load(String name, String defaultType, JsonNode properties) { + String type = getType(name, defaultType, properties); + Info i = new Info(name, properties); + if (imports != null && imports.hasNonNull(type)) { + return createFromClass(imports.get(type).asText(), i); + } + Function nativeFunc = natives.get(type); + if (nativeFunc != null) { + return (T)nativeFunc.apply(i); + } + return createFromClass(type, i); + } + + @SuppressWarnings("unchecked") + private T createFromClass(String className, Info i) { + try { + Class clazz = Class.forName(className); + Constructor c = clazz.getDeclaredConstructor(); + Object obj = c.newInstance(); + if (obj instanceof JsonReadable) { + ((JsonReadable)obj).read(i); + } + return (T)obj; + } catch (ClassNotFoundException + | InvocationTargetException + | InstantiationException + | IllegalAccessException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Class \"" + className + "\" does not have a parameterless constructor.", e); + } + } + + private String getType(String name, String defaultType, JsonNode properties) { + JsonNode typeNode = properties.get(TYPE); + if (typeNode == null || !typeNode.isTextual()) { + return Objects.requireNonNull(defaultType, "Type is not defined for \"" + name + "\"."); + } + else return typeNode.asText(); + } + + public class Info { + + private final String name; + private final JsonNode properties; + + private Info(String name, JsonNode properties) { + this.name = name; + this.properties = properties; + } + + public T load(String name, JsonNode properties) { + return SavableData.this.load(name, properties); + } + + public T load(String name, String defaultType, JsonNode properties) { + return SavableData.this.load(name, defaultType, properties); + } + + public AssetManager getAssetManager() { + return assetManager; + } + + public String getName() { + return name; + } + + public JsonNode getProperties() { + return properties; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java b/jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java index d6f688662f..a654d3403b 100644 --- a/jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java +++ b/jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java @@ -63,10 +63,9 @@ public void setCamera(Camera camera) { } @Override - public void filterLights(Geometry geometry, LightList filteredLightList) { + public void filterLights(Geometry geometry, LightList worldLights, LightList filteredLightList) { TempVars vars = TempVars.get(); try { - LightList worldLights = geometry.getWorldLightList(); for (int i = 0; i < worldLights.size(); i++) { Light light = worldLights.get(i); diff --git a/jme3-core/src/main/java/com/jme3/light/LightFilter.java b/jme3-core/src/main/java/com/jme3/light/LightFilter.java index 7196255d2c..4b21686ad1 100644 --- a/jme3-core/src/main/java/com/jme3/light/LightFilter.java +++ b/jme3-core/src/main/java/com/jme3/light/LightFilter.java @@ -44,11 +44,11 @@ public interface LightFilter { /** * Sets the camera for which future filtering is to be done against in - * {@link #filterLights(com.jme3.scene.Geometry, com.jme3.light.LightList)}. + * {@link #filterLights(com.jme3.scene.Geometry, com.jme3.light.LightList, com.jme3.light.LightList)}. * * @param camera The camera to perform light filtering against. */ - public void setCamera(Camera camera); + void setCamera(Camera camera); /** * Determine which lights on the {@link Geometry#getWorldLightList() world @@ -68,5 +68,5 @@ public interface LightFilter { * @param geometry The geometry for which the light filtering is performed. * @param filteredLightList The results are to be stored here. */ - public void filterLights(Geometry geometry, LightList filteredLightList); + void filterLights(Geometry geometry, LightList lights, LightList filteredLightList); } diff --git a/jme3-core/src/main/java/com/jme3/light/NullLightFilter.java b/jme3-core/src/main/java/com/jme3/light/NullLightFilter.java index 55f46d1802..b24558d0e4 100644 --- a/jme3-core/src/main/java/com/jme3/light/NullLightFilter.java +++ b/jme3-core/src/main/java/com/jme3/light/NullLightFilter.java @@ -47,7 +47,8 @@ public void setCamera(Camera camera) { } @Override - public void filterLights(Geometry geometry, LightList filteredLightList) { + public void filterLights(Geometry geometry, LightList lights, LightList filteredLightList) { } + } diff --git a/jme3-core/src/main/java/com/jme3/material/GlMaterial.java b/jme3-core/src/main/java/com/jme3/material/GlMaterial.java new file mode 100644 index 0000000000..4fbe7924f1 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/material/GlMaterial.java @@ -0,0 +1,1320 @@ +/* + * Copyright (c) 2009-2025 jMonkeyEngine + * 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 'jMonkeyEngine' 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 com.jme3.material; + +import com.jme3.asset.AssetKey; +import com.jme3.asset.AssetManager; +import com.jme3.asset.CloneableSmartAsset; +import com.jme3.dev.NotFullyImplemented; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.export.Savable; +import com.jme3.light.LightList; +import com.jme3.material.RenderState.BlendMode; +import com.jme3.material.RenderState.FaceCullMode; +import com.jme3.material.TechniqueDef.LightMode; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Matrix4f; +import com.jme3.math.Vector2f; +import com.jme3.math.Vector3f; +import com.jme3.math.Vector4f; +import com.jme3.renderer.Caps; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.Renderer; +import com.jme3.renderer.TextureUnitException; +import com.jme3.renderer.queue.RenderQueue.Bucket; +import com.jme3.scene.Geometry; +import com.jme3.scene.GlMesh; +import com.jme3.shader.*; +import com.jme3.shader.bufferobject.BufferObject; +import com.jme3.texture.*; +import com.jme3.texture.image.ColorSpace; +import com.jme3.util.ListMap; +import com.jme3.util.SafeArrayList; +import com.jme3.vulkan.buffers.GpuBuffer; + +import java.io.IOException; +import java.util.Collection; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * GlMaterial describes the rendering style for a given + * {@link Geometry}. + *

A material is essentially a list of {@link MatParam parameters}, + * those parameters map to uniforms which are defined in a shader. + * Setting the parameters can modify the behavior of a + * shader. + *

+ * + * @author Kirill Vainer + */ +public class GlMaterial implements Material, CloneableSmartAsset, Cloneable, Savable { + + // Version #2: Fixed issue with RenderState.apply*** flags not getting exported + public static final int SAVABLE_VERSION = 2; + private static final Logger logger = Logger.getLogger(GlMaterial.class.getName()); + + private AssetKey key; + private String name; + private MaterialDef def; + private ListMap paramValues = new ListMap<>(); + private Technique technique; + private HashMap techniques = new HashMap<>(); + private RenderState additionalState = null; + private final RenderState mergedRenderState = new RenderState(); + private boolean transparent = false; + private boolean receivesShadows = false; + private int sortingId = -1; + + @Override + public void setParam(String uniform, String param, Object value) { + setParam(param, value); + } + + /** + * Manages and tracks texture and buffer binding units for rendering. + * Used internally by the Material class. + */ + public static class BindUnits { + /** The current texture unit counter. */ + public int textureUnit = 0; + /** The current buffer unit counter. */ + public int bufferUnit = 0; + } + private BindUnits bindUnits = new BindUnits(); + + /** + * Constructs a new Material instance based on a provided MaterialDef. + * The material's parameters will be initialized with default values from the definition. + * + * @param def The material definition to use (cannot be null). + * @throws IllegalArgumentException if def is null. + */ + public GlMaterial(MaterialDef def) { + if (def == null) { + throw new IllegalArgumentException("Material definition cannot be null"); + } + this.def = def; + + // Load default values from definition (if any) + for (MatParam param : def.getMaterialParams()) { + if (param.getValue() != null) { + setParam(param.getName(), param.getVarType(), param.getValue()); + } + } + } + + /** + * Constructs a new Material by loading its MaterialDef from the asset manager. + * + * @param assetManager The asset manager to load the MaterialDef from. + * @param defName The asset path of the .j3md file. + */ + public GlMaterial(AssetManager assetManager, String defName) { + this(assetManager.loadAsset(new AssetKey(defName))); + } + + /** + * For serialization only. Do not use. + */ + public GlMaterial() { + } + + /** + * Returns the asset key name of the asset from which this material was loaded. + *

This value will be null unless this material was loaded from a .j3m file.

+ * + * @return Asset key name of the .j3m file, or null if not loaded from a file. + */ + public String getAssetName() { + return key != null ? key.getName() : null; + } + + /** + * Returns the user-defined name of the material. + * This name is distinct from the asset name and may be null or not unique. + * + * @return The name of the material, or null. + */ + public String getName() { + return name; + } + + /** + * Sets the user-defined name of the material. + * The name is not the same as the asset name. + * It can be null, and there is no guarantee of its uniqueness. + * + * @param name The name of the material. + */ + public void setName(String name) { + this.name = name; + } + + @Override + public void setKey(AssetKey key) { + this.key = key; + } + + @Override + public AssetKey getKey() { + return key; + } + + /** + * Returns the sorting ID or sorting index for this material. + * + *

The sorting ID is used internally by the system to sort rendering + * of geometries. It sorted to reduce shader switches, if the shaders + * are equal, then it is sorted by textures. + * + * @return The sorting ID used for sorting geometries for rendering. + */ + @Override + public int getSortId() { + if (sortingId == -1 && technique != null) { + sortingId = technique.getSortId() << 16; + int texturesSortId = 17; + for (int i = 0; i < paramValues.size(); i++) { + MatParam param = paramValues.getValue(i); + if (!param.getVarType().isTextureType()) { + continue; + } + GlTexture texture = (GlTexture) param.getValue(); + if (texture == null) { + continue; + } + GlImage image = texture.getImage(); + if (image == null) { + continue; + } + int textureId = image.getNativeObject(); + if (textureId == -1) { + textureId = 0; + } + texturesSortId = texturesSortId * 23 + textureId; + } + sortingId |= texturesSortId & 0xFFFF; + } + return sortingId; + } + + /** + * Clones this material. The result is returned. + */ + @Override + public GlMaterial clone() { + try { + GlMaterial mat = (GlMaterial) super.clone(); + + if (additionalState != null) { + mat.additionalState = additionalState.clone(); + } + mat.technique = null; + mat.techniques = new HashMap(); + + mat.paramValues = new ListMap(); + for (int i = 0; i < paramValues.size(); i++) { + Map.Entry entry = paramValues.getEntry(i); + mat.paramValues.put(entry.getKey(), entry.getValue().clone()); + } + + mat.sortingId = -1; + + return mat; + } catch (CloneNotSupportedException ex) { + throw new AssertionError(ex); + } + } + + /** + * Compares two materials for content equality. + * This methods compare definition, parameters, additional render states. + * Since materials are mutable objects, implementing equals() properly is not possible, + * hence the name contentEquals(). + * + * @param otherObj the material to compare to this material + * @return true if the materials are equal. + */ + public boolean contentEquals(Object otherObj) { + if (!(otherObj instanceof GlMaterial)) { + return false; + } + + GlMaterial other = (GlMaterial) otherObj; + + // Early exit if the material are the same object + if (this == other) { + return true; + } + + // Check material definition + if (this.getMaterialDef() != other.getMaterialDef()) { + return false; + } + + // Early exit if the size of the params is different + if (this.paramValues.size() != other.paramValues.size()) { + return false; + } + + // Checking technique + if (this.technique != null || other.technique != null) { + // Techniques are considered equal if their names are the same + // E.g. if user chose custom technique for one material but + // uses default technique for other material, the materials + // are not equal. + String thisDefName = this.technique != null + ? this.technique.getDef().getName() + : TechniqueDef.DEFAULT_TECHNIQUE_NAME; + + String otherDefName = other.technique != null + ? other.technique.getDef().getName() + : TechniqueDef.DEFAULT_TECHNIQUE_NAME; + + if (!thisDefName.equals(otherDefName)) { + return false; + } + } + + // Comparing parameters + for (String paramKey : paramValues.keySet()) { + MatParam thisParam = this.getMatParam(paramKey); + MatParam otherParam = other.getMatParam(paramKey); + + // This param does not exist in compared mat + if (otherParam == null) { + return false; + } + + if (!otherParam.equals(thisParam)) { + return false; + } + } + + // Comparing additional render states + if (additionalState == null) { + if (other.additionalState != null) { + return false; + } + } else { + if (!additionalState.equals(other.additionalState)) { + return false; + } + } + + return true; + } + + /** + * Works like {@link Object#hashCode() } except it may change together with the material as the material is mutable by definition. + * + * @return value for use in hashing + */ + public int contentHashCode() { + int hash = 7; + hash = 29 * hash + (this.def != null ? this.def.hashCode() : 0); + hash = 29 * hash + (this.paramValues != null ? this.paramValues.hashCode() : 0); + hash = 29 * hash + (this.technique != null ? this.technique.getDef().getName().hashCode() : 0); + hash = 29 * hash + (this.additionalState != null ? this.additionalState.contentHashCode() : 0); + return hash; + } + + /** + * Returns the currently active technique. + *

+ * The technique is selected automatically by the {@link RenderManager} + * based on system capabilities. Users may select their own + * technique by using + * {@link #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) }. + * + * @return the currently active technique. + * + * @see #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) + */ + public Technique getActiveTechnique() { + return technique; + } + + /** + * Check if the transparent value marker is set on this material. + * @return True if the transparent value marker is set on this material. + * @see #setTransparent(boolean) + */ + public boolean isTransparent() { + return transparent; + } + + /** + * Set the transparent value marker. + * + *

This value is merely a marker, by itself it does nothing. + * Generally model loaders will use this marker to indicate further + * up that the material is transparent and therefore any geometries + * using it should be put into the {@link Bucket#Transparent transparent + * bucket}. + * + * @param transparent the transparent value marker. + */ + public void setTransparent(boolean transparent) { + this.transparent = transparent; + } + + /** + * Check if the material should receive shadows or not. + * + * @return True if the material should receive shadows. + * + * @see GlMaterial#setReceivesShadows(boolean) + */ + public boolean isReceivesShadows() { + return receivesShadows; + } + + /** + * Set if the material should receive shadows or not. + * + *

This value is merely a marker, by itself it does nothing. + * Generally model loaders will use this marker to indicate + * the material should receive shadows and therefore any + * geometries using it should have {@link com.jme3.renderer.queue.RenderQueue.ShadowMode#Receive} set + * on them. + * + * @param receivesShadows if the material should receive shadows or not. + */ + public void setReceivesShadows(boolean receivesShadows) { + this.receivesShadows = receivesShadows; + } + + /** + * Acquire the additional {@link RenderState render state} to apply + * for this material. + * + *

The first call to this method will create an additional render + * state which can be modified by the user to apply any render + * states in addition to the ones used by the renderer. Only render + * states which are modified in the additional render state will be applied. + * + * @return The additional render state. + */ + public RenderState getAdditionalRenderState() { + if (additionalState == null) { + additionalState = RenderState.ADDITIONAL.clone(); + } + return additionalState; + } + + /** + * Get the material definition (.j3md file info) that this + * material is implementing. + * + * @return the material definition this material implements. + */ + public MaterialDef getMaterialDef() { + return def; + } + + /** + * Returns the parameter set on this material with the given name, + * returns null if the parameter is not set. + * + * @param name The parameter name to look up. + * @return The MatParam if set, or null if not set. + */ + public MatParam getMatParam(String name) { + return paramValues.get(name); + } + + /** + * Returns the current parameter's value. + * + * @param the expected type of the parameter value + * @param name the parameter name to look up. + * @return current value or null if the parameter wasn't set. + */ + @SuppressWarnings("unchecked") + public T getParamValue(final String name) { + final MatParam param = paramValues.get(name); + return param == null ? null : (T) param.getValue(); + } + + /** + * Returns the texture parameter set on this material with the given name, + * returns null if the parameter is not set. + * + * @param name The parameter name to look up. + * @return The MatParamTexture if set, or null if not set. + */ + public MatParamTexture getTextureParam(String name) { + MatParam param = paramValues.get(name); + if (param instanceof MatParamTexture) { + return (MatParamTexture) param; + } + return null; + } + + /** + * Returns a collection of all parameters set on this material. + * + * @return a collection of all parameters set on this material. + * + * @see #setParam(java.lang.String, com.jme3.shader.VarType, java.lang.Object) + */ + public Collection getParams() { + return paramValues.values(); + } + + /** + * Returns the ListMap of all parameters set on this material. + * + * @return a ListMap of all parameters set on this material. + * + * @see #setParam(java.lang.String, com.jme3.shader.VarType, java.lang.Object) + */ + public ListMap getParamsMap() { + return paramValues; + } + + /** + * Check if setting the parameter given the type and name is allowed. + * @param type The type that the "set" function is designed to set + * @param name The name of the parameter + */ + private void checkSetParam(VarType type, String name) { + MatParam paramDef = def.getMaterialParam(name); + if (paramDef == null) { + throw new IllegalArgumentException("Material parameter is not defined: " + name); + } + if (type != null && paramDef.getVarType() != type) { + logger.log(Level.WARNING, "Material parameter being set: {0} with " + + "type {1} doesn''t match definition types {2}", new Object[]{name, type.name(), paramDef.getVarType()}); + } + } + + /** + * Pass a parameter to the material shader. + * + * @param name the name of the parameter defined in the material definition (.j3md) + * @param type the type of the parameter {@link VarType} + * @param value the value of the parameter + */ + public void setParam(String name, VarType type, Object value) { + checkSetParam(type, name); + + if (type.isTextureType()) { + setTextureParam(name, type, (GlTexture)value); + } else { + MatParam val = getMatParam(name); + if (val == null) { + paramValues.put(name, new MatParam(type, name, value)); + } else { + val.setValue(value); + } + + if (technique != null) { + technique.notifyParamChanged(name, type, value); + } + if (type.isImageType()) { + // recompute sort id + sortingId = -1; + } + } + } + + /** + * Pass a parameter to the material shader. + * + * @param name the name of the parameter defined in the material definition (j3md) + * @param value the value of the parameter + */ + @Override + public void setParam(String name, Object value) { + MatParam p = getMaterialDef().getMaterialParam(name); + setParam(name, p.getVarType(), value); + } + + /** + * Clear a parameter from this material. The parameter must exist + * @param name the name of the parameter to clear + */ + @Override + public void clearParam(String name) { + checkSetParam(null, name); + MatParam matParam = getMatParam(name); + if (matParam == null) { + return; + } + + paramValues.remove(name); + if (matParam instanceof MatParamTexture) { + sortingId = -1; + } + if (technique != null) { + technique.notifyParamChanged(name, null, null); + } + } + + /** + * Set a texture parameter. + * + * @param name The name of the parameter + * @param type The variable type {@link VarType} + * @param value The texture value of the parameter. + * + * @throws IllegalArgumentException is value is null + */ + public void setTextureParam(String name, VarType type, GlTexture value) { + if (value == null) { + throw new IllegalArgumentException(); + } + + checkSetParam(type, name); + MatParamTexture param = getTextureParam(name); + + checkTextureParamColorSpace(name, value); + ColorSpace colorSpace = value.getImage() != null ? value.getImage().getColorSpace() : null; + + if (param == null) { + param = new MatParamTexture(type, name, value, colorSpace); + paramValues.put(name, param); + } else { + param.setTextureValue(value); + param.setColorSpace(colorSpace); + } + + if (technique != null) { + technique.notifyParamChanged(name, type, value); + } + + // need to recompute sort ID + sortingId = -1; + } + + private void checkTextureParamColorSpace(String name, GlTexture value) { + MatParamTexture paramDef = (MatParamTexture) def.getMaterialParam(name); + if (paramDef.getColorSpace() != null && paramDef.getColorSpace() != value.getImage().getColorSpace()) { + value.getImage().setColorSpace(paramDef.getColorSpace()); + if (logger.isLoggable(Level.FINE)) { + logger.log(Level.FINE, "Material parameter {0} needs a {1} texture, " + + "texture {2} was switched to {3} color space.", + new Object[]{name, paramDef.getColorSpace().toString(), + value.getName(), + value.getImage().getColorSpace().name()}); + } + } else if (paramDef.getColorSpace() == null && value.getName() != null && value.getImage().getColorSpace() == ColorSpace.Linear) { + logger.log(Level.WARNING, + "The texture {0} has linear color space, but the material " + + "parameter {2} specifies no color space requirement, this may " + + "lead to unexpected behavior.\nCheck if the image " + + "was not set to another material parameter with a linear " + + "color space, or that you did not set the ColorSpace to " + + "Linear using texture.getImage.setColorSpace().", + new Object[]{value.getName(), value.getImage().getColorSpace().name(), name}); + } + } + + @Override + @NotFullyImplemented + public void setUniform(String name, GpuBuffer buffer) { + throw new UnsupportedOperationException("To be implemented."); + } + + @Override + @NotFullyImplemented + public void clearParam(String uniform, String param) { + clearParam(param); + } + + @Override + public T getParam(String uniform, String name) { + return getParam(name); + } + + @Override + @SuppressWarnings("unchecked") + public T getParam(String param) { + MatParam p = getMatParam(param); + return p != null ? (T)p.getValue() : null; + } + + @Override + public Texture getTexture(String name) { + MatParamTexture param = getTextureParam(name); + return param != null ? param.getTextureValue() : null; + } + + /** + * Pass a texture to the material shader. + * + * @param name the name of the texture defined in the material definition + * (.j3md) (e.g. Texture for Lighting.j3md) + * @param value the Texture object previously loaded by the asset manager + */ + @Override + public void setTexture(String name, Texture value) { + if (value == null) { + // clear it + clearParam(name); + return; + } + if (!(value instanceof GlTexture)) { + throw new IllegalArgumentException("Must be a GlTexture."); + } + + VarType paramType = null; + GlTexture.Type type = ((GlTexture)value).getType(); + switch (type) { + case TwoDimensional: + paramType = VarType.Texture2D; + break; + case TwoDimensionalArray: + paramType = VarType.TextureArray; + break; + case ThreeDimensional: + paramType = VarType.Texture3D; + break; + case CubeMap: + paramType = VarType.TextureCubeMap; + break; + default: + throw new UnsupportedOperationException("Unknown texture type: " + type); + } + + setTextureParam(name, paramType, (GlTexture)value); + } + + /** + * Pass a Matrix4f to the material shader. + * + * @param name the name of the matrix defined in the material definition (j3md) + * @param value the Matrix4f object + */ + @Override + public void setMatrix4(String name, Matrix4f value) { + setParam(name, VarType.Matrix4, value); + } + + /** + * Pass a boolean to the material shader. + * + * @param name the name of the boolean defined in the material definition (j3md) + * @param value the boolean value + */ + @Override + public void setBoolean(String name, boolean value) { + setParam(name, VarType.Boolean, value); + } + + /** + * Pass a float to the material shader. + * + * @param name the name of the float defined in the material definition (j3md) + * @param value the float value + */ + @Override + public void setFloat(String name, float value) { + setParam(name, VarType.Float, value); + } + + /** + * Pass a float to the material shader. This version avoids auto-boxing + * if the value is already a Float. + * + * @param name the name of the float defined in the material definition (j3md) + * @param value the float value + */ + @Override + public void setFloat(String name, Float value) { + setParam(name, VarType.Float, value); + } + + /** + * Pass an int to the material shader. + * + * @param name the name of the int defined in the material definition (j3md) + * @param value the int value + */ + @Override + public void setInt(String name, int value) { + setParam(name, VarType.Int, value); + } + + /** + * Pass a Color to the material shader. + * + * @param name the name of the color defined in the material definition (j3md) + * @param value the ColorRGBA value + */ + @Override + public void setColor(String name, ColorRGBA value) { + setParam(name, VarType.Vector4, value); + } + + /** + * Pass a uniform buffer object to the material shader. + * + * @param name the name of the buffer object defined in the material definition (j3md). + * @param value the buffer object. + */ + public void setUniformBufferObject(final String name, final BufferObject value) { + setParam(name, VarType.UniformBufferObject, value); + } + + /** + * Pass a shader storage buffer object to the material shader. + * + * @param name the name of the buffer object defined in the material definition (j3md). + * @param value the buffer object. + */ + public void setShaderStorageBufferObject(final String name, final BufferObject value) { + setParam(name, VarType.ShaderStorageBufferObject, value); + } + + /** + * Pass a Vector2f to the material shader. + * + * @param name the name of the Vector2f defined in the material definition (j3md) + * @param value the Vector2f value + */ + @Override + public void setVector2(String name, Vector2f value) { + setParam(name, VarType.Vector2, value); + } + + /** + * Pass a Vector3f to the material shader. + * + * @param name the name of the Vector3f defined in the material definition (j3md) + * @param value the Vector3f value + */ + @Override + public void setVector3(String name, Vector3f value) { + setParam(name, VarType.Vector3, value); + } + + /** + * Pass a Vector4f to the material shader. + * + * @param name the name of the Vector4f defined in the material definition (j3md) + * @param value the Vector4f value + */ + @Override + public void setVector4(String name, Vector4f value) { + setParam(name, VarType.Vector4, value); + } + + /** + * Select the technique to use for rendering this material. + *

+ * Any candidate technique for selection (either default or named) + * must be verified to be compatible with the system, for that, the + * renderManager is queried for capabilities. + * + * @param name The name of the technique to select, pass + * {@link TechniqueDef#DEFAULT_TECHNIQUE_NAME} to select one of the default + * techniques. + * @param renderManager The {@link RenderManager render manager} + * to query for capabilities. + * + * @throws IllegalArgumentException If no technique exists with the given + * name. + * @throws UnsupportedOperationException If no candidate technique supports + * the system capabilities. + */ + public Technique selectTechnique(String name, final RenderManager renderManager) { + // if null, use the currently selected technique, or the default technique + if (name == null || technique == null) { + if (technique != null) { + return technique; + } else { + name = TechniqueDef.DEFAULT_TECHNIQUE_NAME; + } + } + // check if already created + Technique tech = techniques.get(name); + // When choosing technique, we choose one that + // supports all the caps. + if (tech == null) { + EnumSet rendererCaps = renderManager.getRenderer().getCaps(); + List techDefs = def.getTechniqueDefs(name); + if (techDefs == null || techDefs.isEmpty()) { + throw new IllegalArgumentException( + String.format("The requested technique %s is not available on material %s", name, def.getName())); + } + + TechniqueDef lastTech = null; + float weight = 0; + for (TechniqueDef techDef : techDefs) { + if (rendererCaps.containsAll(techDef.getRequiredCaps())) { + float techWeight = techDef.getWeight() + (techDef.getLightMode() == renderManager.getPreferredLightMode() ? 10f : 0); + if (techWeight > weight) { + tech = new Technique(this, techDef); + techniques.put(name, tech); + weight = techWeight; + } + } + lastTech = techDef; + } + if (tech == null) { + throw new UnsupportedOperationException( + String.format("No technique '%s' on material " + + "'%s' is supported by the video hardware. " + + "The capabilities %s are required.", + name, def.getName(), lastTech.getRequiredCaps())); + } + if (logger.isLoggable(Level.FINE)) { + logger.log(Level.FINE, this.getMaterialDef().getName() + " selected technique def " + tech.getDef()); + } + } else if (technique == tech) { + // attempting to switch to an already + // active technique. + return technique; + } + + technique = tech; + tech.notifyTechniqueSwitched(); + + // shader was changed + sortingId = -1; + + return technique; + } + + private void applyOverrides(Renderer renderer, Shader shader, SafeArrayList overrides, BindUnits bindUnits) { + for (MatParamOverride override : overrides.getArray()) { + VarType type = override.getVarType(); + + MatParam paramDef = def.getMaterialParam(override.getName()); + + if (paramDef == null || paramDef.getVarType() != type || !override.isEnabled()) { + continue; + } + + Uniform uniform = shader.getUniform(override.getPrefixedName()); + + if (override.getValue() != null) { + updateShaderMaterialParameter(renderer, type, shader, override, bindUnits, true); + } else { + uniform.clearValue(); + } + } + } + + private void updateShaderMaterialParameter(Renderer renderer, VarType type, Shader shader, MatParam param, BindUnits unit, boolean override) { + if (type == VarType.UniformBufferObject || type == VarType.ShaderStorageBufferObject) { + ShaderBufferBlock bufferBlock = shader.getBufferBlock(param.getPrefixedName()); + BufferObject bufferObject = (BufferObject) param.getValue(); + + ShaderBufferBlock.BufferType btype; + if (type == VarType.ShaderStorageBufferObject) { + btype = ShaderBufferBlock.BufferType.ShaderStorageBufferObject; + bufferBlock.setBufferObject(btype, bufferObject); + renderer.setShaderStorageBufferObject(unit.bufferUnit, bufferObject); // TODO: probably not needed + } else { + btype = ShaderBufferBlock.BufferType.UniformBufferObject; + bufferBlock.setBufferObject(btype, bufferObject); + renderer.setUniformBufferObject(unit.bufferUnit, bufferObject); // TODO: probably not needed + } + unit.bufferUnit++; + } else { + Uniform uniform = shader.getUniform(param.getPrefixedName()); + if (!override && uniform.isSetByCurrentMaterial()) + return; + + if (type.isTextureType() || type.isImageType()) { + try { + if (type.isTextureType()) { + renderer.setTexture(unit.textureUnit, (GlTexture) param.getValue()); + } else { + renderer.setTextureImage(unit.textureUnit, (TextureImage) param.getValue()); + } + } catch (TextureUnitException ex) { + int numTexParams = unit.textureUnit + 1; + String message = "Too many texture parameters (" + numTexParams + ") assigned\n to " + this.toString(); + throw new IllegalStateException(message); + } + uniform.setValue(VarType.Int, unit.textureUnit); + unit.textureUnit++; + } else { + uniform.setValue(type, param.getValue()); + } + } + } + + public BindUnits updateShaderMaterialParameters(Renderer renderer, Shader shader, + SafeArrayList worldOverrides, + SafeArrayList forcedOverrides) { + + bindUnits.textureUnit = 0; + bindUnits.bufferUnit = 0; + + if (worldOverrides != null) { + applyOverrides(renderer, shader, worldOverrides, bindUnits); + } + if (forcedOverrides != null) { + applyOverrides(renderer, shader, forcedOverrides, bindUnits); + } + + for (int i = 0; i < paramValues.size(); i++) { + MatParam param = paramValues.getValue(i); + VarType type = param.getVarType(); + updateShaderMaterialParameter(renderer, type, shader, param, bindUnits, false); + } + + // TODO: HACKY HACK remove this when texture unit is handled by the uniform. + return bindUnits; + } + + private void updateRenderState(Geometry geometry, RenderManager renderManager, Renderer renderer, TechniqueDef techniqueDef) { + RenderState finalRenderState; + if (renderManager.getForcedRenderState() != null) { + finalRenderState = mergedRenderState.copyFrom(renderManager.getForcedRenderState()); + } else if (techniqueDef.getRenderState() != null) { + finalRenderState = mergedRenderState.copyFrom(RenderState.DEFAULT); + finalRenderState = techniqueDef.getRenderState().copyMergedTo(additionalState, finalRenderState); + } else { + finalRenderState = mergedRenderState.copyFrom(RenderState.DEFAULT); + finalRenderState = RenderState.DEFAULT.copyMergedTo(additionalState, finalRenderState); + } + // test if the face cull mode should be flipped before render + if (finalRenderState.isFaceCullFlippable() && isNormalsBackward(geometry.getWorldScale())) { + finalRenderState.flipFaceCull(); + } + renderer.applyRenderState(finalRenderState); + } + + /** + * Returns true if the geometry world scale indicates that normals will be backward. + * + * @param scalar The geometry's world scale vector. + * @return true if the normals are effectively backward; false otherwise. + */ + private boolean isNormalsBackward(Vector3f scalar) { + // count number of negative scalar vector components + int n = 0; + if (scalar.x < 0) n++; + if (scalar.y < 0) n++; + if (scalar.z < 0) n++; + // An odd number of negative components means the normal vectors + // are backward to what they should be. + return n == 1 || n == 3; + } + + /** + * Preloads this material for the given render manager. + *

+ * Preloading the material can ensure that when the material is first + * used for rendering, there won't be any delay since the material has + * been already been setup for rendering. + * + * @param renderManager The render manager to preload for + * @param geometry to determine the applicable parameter overrides, if any + */ + @Deprecated // this isn't used anywhere... + public void preload(RenderManager renderManager, Geometry geometry) { + if (technique == null) { + selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager); + } + TechniqueDef techniqueDef = technique.getDef(); + Renderer renderer = renderManager.getRenderer(); + EnumSet rendererCaps = renderer.getCaps(); + + if (techniqueDef.isNoRender()) { + return; + } + // Get world overrides + SafeArrayList overrides = geometry.getWorldMatParamOverrides(); + + Shader shader = technique.makeCurrent(renderManager, overrides, null, null, rendererCaps); + updateShaderMaterialParameters(renderer, shader, overrides, null); + renderManager.getRenderer().setShader(shader); + } + + private void clearUniformsSetByCurrent(Shader shader) { + ListMap uniforms = shader.getUniformMap(); + int size = uniforms.size(); + for (int i = 0; i < size; i++) { + Uniform u = uniforms.getValue(i); + u.clearSetByCurrentMaterial(); + } + } + + private void resetUniformsNotSetByCurrent(Shader shader) { + ListMap uniforms = shader.getUniformMap(); + int size = uniforms.size(); + for (int i = 0; i < size; i++) { + Uniform u = uniforms.getValue(i); + if (!u.isSetByCurrentMaterial()) { + if (u.getName().charAt(0) != 'g') { + // Don't reset world globals! + // The benefits gained from this are very minimal + // and cause lots of matrix -> FloatBuffer conversions. + u.clearValue(); + } + } + } + } + + /** + * Called by {@link RenderManager} to render the geometry by + * using this material. + *

+ * The material is rendered as follows: + *

    + *
  • Determine which technique to use to render the material - + * either what the user selected via + * {@link #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) + * Material.selectTechnique()}, + * or the first default technique that the renderer supports + * (based on the technique's {@link TechniqueDef#getRequiredCaps() requested rendering capabilities})
      + *
    • If the technique has been changed since the last frame, then it is notified via + * {@link Technique#makeCurrent(com.jme3.renderer.RenderManager, com.jme3.util.SafeArrayList, com.jme3.util.SafeArrayList, com.jme3.light.LightList, java.util.EnumSet) + * Technique.makeCurrent()}. + * If the technique wants to use a shader to render the model, it should load it at this part - + * the shader should have all the proper defines as declared in the technique definition, + * including those that are bound to material parameters. + * The technique can re-use the shader from the last frame if + * no changes to the defines occurred.
    + *
  • Set the {@link RenderState} to use for rendering. The render states are + * applied in this order (later RenderStates override earlier RenderStates):
      + *
    1. {@link TechniqueDef#getRenderState() Technique Definition's RenderState} + * - i.e. specific RenderState that is required for the shader.
    2. + *
    3. {@link #getAdditionalRenderState() Material Instance Additional RenderState} + * - i.e. ad-hoc RenderState set per model
    4. + *
    5. {@link RenderManager#getForcedRenderState() RenderManager's Forced RenderState} + * - i.e. RenderState requested by a {@link com.jme3.post.SceneProcessor} or + * post-processing filter.
    + *
  • If the technique uses a shader, then the uniforms of the shader must be updated.
      + *
    • Uniforms bound to material parameters are updated based on the current material parameter values.
    • + *
    • Uniforms bound to world parameters are updated from the RenderManager. + * Internally {@link UniformBindingManager} is used for this task.
    • + *
    • Uniforms bound to textures will cause the texture to be uploaded as necessary. + * The uniform is set to the texture unit where the texture is bound.
    + *
  • If the technique uses a shader, the model is then rendered according + * to the lighting mode specified on the technique definition.
      + *
    • {@link LightMode#SinglePass single pass light mode} fills the shader's light uniform arrays + * with the first 4 lights and renders the model once.
    • + *
    • {@link LightMode#MultiPass multi pass light mode} light mode renders the model multiple times, + * for the first light it is rendered opaque, on subsequent lights it is + * rendered with {@link BlendMode#AlphaAdditive alpha-additive} blending and depth writing disabled.
    • + *
    + *
  • For techniques that do not use shaders, + * fixed function OpenGL is used to render the model (see {@link com.jme3.renderer.opengl.GLRenderer} interface):
      + *
    • OpenGL state that is bound to material parameters is updated.
    • + *
    • The texture set on the material is uploaded and bound. + * Currently only 1 texture is supported for fixed function techniques.
    • + *
    • If the technique uses lighting, then OpenGL lighting state is updated + * based on the light list on the geometry, otherwise OpenGL lighting is disabled.
    • + *
    • The mesh is uploaded and rendered.
    • + *
    + *
+ * + * @param geometry The geometry to render + * @param lights Presorted and filtered light list to use for rendering + * @param renderManager The render manager requesting the rendering + */ + public void render(Geometry geometry, LightList lights, RenderManager renderManager) { + if (technique == null) { + selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager); + } + + if (!(geometry.getMesh() instanceof GlMesh)) { + throw new ClassCastException("Cannot render " + geometry.getMesh().getClass() + " in an OpenGL context."); + } + + TechniqueDef techniqueDef = technique.getDef(); + Renderer renderer = renderManager.getRenderer(); + EnumSet rendererCaps = renderer.getCaps(); + + if (techniqueDef.isNoRender()) { + return; + } + + // Apply render state + updateRenderState(geometry, renderManager, renderer, techniqueDef); + + // Get world overrides + SafeArrayList overrides = geometry.getWorldMatParamOverrides(); + + // Select shader to use + Shader shader = technique.makeCurrent(renderManager, overrides, renderManager.getForcedMatParams(), lights, rendererCaps); + + // Begin tracking which uniforms were changed by material. + clearUniformsSetByCurrent(shader); + + // Set uniform bindings + renderManager.updateUniformBindings(shader); + + // Set material parameters + BindUnits units = updateShaderMaterialParameters(renderer, shader, overrides, renderManager.getForcedMatParams()); + + // Clear any uniforms not changed by material. + resetUniformsNotSetByCurrent(shader); + + // Delegate rendering to the technique + technique.render(renderManager, shader, geometry, (GlMesh)geometry.getMesh(), lights, units); + } + + @Override + public String toString() { + return "Material[name=" + name + + ", def=" + (def != null ? def.getName() : null) + + ", tech=" + (technique != null && technique.getDef() != null ? technique.getDef().getName() : null) + + "]"; + } + + @Override + public void write(JmeExporter ex) throws IOException { + OutputCapsule oc = ex.getCapsule(this); + oc.write(def.getAssetName(), "material_def", null); + oc.write(additionalState, "render_state", null); + oc.write(transparent, "is_transparent", false); + oc.write(name, "name", null); + oc.writeStringSavableMap(paramValues, "parameters", null); + } + + @Override + @SuppressWarnings("unchecked") + public void read(JmeImporter im) throws IOException { + InputCapsule ic = im.getCapsule(this); + + name = ic.readString("name", null); + additionalState = (RenderState) ic.readSavable("render_state", null); + transparent = ic.readBoolean("is_transparent", false); + + // Load the material def + String defName = ic.readString("material_def", null); + HashMap params = (HashMap) ic.readStringSavableMap("parameters", null); + + boolean enableVertexColor = false; + boolean separateTexCoord = false; + boolean applyDefaultValues = false; + boolean guessRenderStateApply = false; + + int ver = ic.getSavableVersion(Material.class); + if (ver < 1) { + applyDefaultValues = true; + } + if (ver < 2) { + guessRenderStateApply = true; + } + if (im.getFormatVersion() == 0) { + // Enable compatibility with old models + if (defName.equalsIgnoreCase("Common/MatDefs/Misc/VertexColor.j3md")) { + // Using VertexColor, switch to Unshaded and set VertexColor=true + enableVertexColor = true; + defName = "Common/MatDefs/Misc/Unshaded.j3md"; + } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/SimpleTextured.j3md") + || defName.equalsIgnoreCase("Common/MatDefs/Misc/SolidColor.j3md")) { + // Using SimpleTextured/SolidColor, just switch to Unshaded + defName = "Common/MatDefs/Misc/Unshaded.j3md"; + } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/WireColor.j3md")) { + // Using WireColor, set wireframe render state = true and use Unshaded + getAdditionalRenderState().setWireframe(true); + defName = "Common/MatDefs/Misc/Unshaded.j3md"; + } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/Unshaded.j3md")) { + // Uses unshaded, ensure that the proper param is set + MatParam value = params.get("SeperateTexCoord"); + if (value != null && ((Boolean) value.getValue()) == true) { + params.remove("SeperateTexCoord"); + separateTexCoord = true; + } + } + assert applyDefaultValues && guessRenderStateApply; + } + + def = im.getAssetManager().loadAsset(new AssetKey(defName)); + paramValues = new ListMap(); + + // load the textures and update nextTexUnit + for (Map.Entry entry : params.entrySet()) { + MatParam param = entry.getValue(); + if (param instanceof MatParamTexture) { + MatParamTexture texVal = (MatParamTexture) param; + // the texture failed to load for this param + // do not add to param values + if (texVal.getTextureValue() == null || texVal.getTextureValue().getImage() == null) { + continue; + } + checkTextureParamColorSpace(texVal.getName(), texVal.getTextureValue()); + } + + if (im.getFormatVersion() == 0 && param.getName().startsWith("m_")) { + // Ancient version of jME3 ... + param.setName(param.getName().substring(2)); + } + + if (def.getMaterialParam(param.getName()) == null) { + logger.log(Level.WARNING, "The material parameter is not defined: {0}. Ignoring..", + param.getName()); + } else { + checkSetParam(param.getVarType(), param.getName()); + paramValues.put(param.getName(), param); + } + } + + if (applyDefaultValues) { + // compatibility with old versions where default vars were not available + for (MatParam param : def.getMaterialParams()) { + if (param.getValue() != null && paramValues.get(param.getName()) == null) { + setParam(param.getName(), param.getVarType(), param.getValue()); + } + } + } + if (guessRenderStateApply && additionalState != null) { + // Try to guess values of "apply" render state based on defaults + // if value != default then set apply to true + additionalState.applyPolyOffset = additionalState.offsetEnabled; + additionalState.applyBlendMode = additionalState.blendMode != BlendMode.Off; + additionalState.applyColorWrite = !additionalState.colorWrite; + additionalState.applyCullMode = additionalState.cullMode != FaceCullMode.Back; + additionalState.applyDepthTest = !additionalState.depthTest; + additionalState.applyDepthWrite = !additionalState.depthWrite; + additionalState.applyStencilTest = additionalState.stencilTest; + additionalState.applyWireFrame = additionalState.wireframe; + } + if (enableVertexColor) { + setBoolean("VertexColor", true); + } + if (separateTexCoord) { + setBoolean("SeparateTexCoord", true); + } + } +} + diff --git a/jme3-core/src/main/java/com/jme3/material/MatParam.java b/jme3-core/src/main/java/com/jme3/material/MatParam.java index 8e4168133e..cc5fa51566 100644 --- a/jme3-core/src/main/java/com/jme3/material/MatParam.java +++ b/jme3-core/src/main/java/com/jme3/material/MatParam.java @@ -48,8 +48,8 @@ import com.jme3.math.Vector3f; import com.jme3.math.Vector4f; import com.jme3.shader.VarType; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; /** * Describes a material parameter. This is used for both defining a name and type @@ -283,7 +283,7 @@ public String getValueAsString() { case TextureArray: case TextureBuffer: case TextureCubeMap: - Texture texVal = (Texture) value; + GlTexture texVal = (GlTexture) value; TextureKey texKey = (TextureKey) texVal.getKey(); if (texKey == null) { // throw new UnsupportedOperationException("The specified MatParam cannot be represented in J3M"); @@ -299,20 +299,20 @@ public String getValueAsString() { } // Wrap mode - ret += getWrapMode(texVal, Texture.WrapAxis.S); - ret += getWrapMode(texVal, Texture.WrapAxis.T); - ret += getWrapMode(texVal, Texture.WrapAxis.R); + ret += getWrapMode(texVal, GlTexture.WrapAxis.S); + ret += getWrapMode(texVal, GlTexture.WrapAxis.T); + ret += getWrapMode(texVal, GlTexture.WrapAxis.R); // Min and Mag filter - Texture.MinFilter def = Texture.MinFilter.BilinearNoMipMaps; + GlTexture.MinFilter def = GlTexture.MinFilter.BilinearNoMipMaps; if (texVal.getImage().hasMipmaps() || texKey.isGenerateMips()) { - def = Texture.MinFilter.Trilinear; + def = GlTexture.MinFilter.Trilinear; } if (texVal.getMinFilter() != def) { ret += "Min" + texVal.getMinFilter().name() + " "; } - if (texVal.getMagFilter() != Texture.MagFilter.Bilinear) { + if (texVal.getMagFilter() != GlTexture.MagFilter.Bilinear) { ret += "Mag" + texVal.getMagFilter().name() + " "; } @@ -322,7 +322,7 @@ public String getValueAsString() { } } - private String getWrapMode(Texture texVal, Texture.WrapAxis axis) { + private String getWrapMode(GlTexture texVal, GlTexture.WrapAxis axis) { WrapMode mode = WrapMode.EdgeClamp; try { mode = texVal.getWrap(axis); diff --git a/jme3-core/src/main/java/com/jme3/material/MatParamTexture.java b/jme3-core/src/main/java/com/jme3/material/MatParamTexture.java index 58bd44418d..e75137422b 100644 --- a/jme3-core/src/main/java/com/jme3/material/MatParamTexture.java +++ b/jme3-core/src/main/java/com/jme3/material/MatParamTexture.java @@ -36,7 +36,7 @@ import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; import com.jme3.shader.VarType; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import com.jme3.texture.image.ColorSpace; import java.io.IOException; @@ -57,7 +57,7 @@ public class MatParamTexture extends MatParam { * @param texture the texture associated with this parameter * @param colorSpace the required color space for the texture */ - public MatParamTexture(VarType type, String name, Texture texture, ColorSpace colorSpace) { + public MatParamTexture(VarType type, String name, GlTexture texture, ColorSpace colorSpace) { super(type, name, texture); this.colorSpace = colorSpace; } @@ -73,17 +73,17 @@ public MatParamTexture() { * * @return the texture object */ - public Texture getTextureValue() { - return (Texture) getValue(); + public GlTexture getTextureValue() { + return (GlTexture) getValue(); } /** * Sets the texture associated with this material parameter. * * @param value the texture object to set - * @throws RuntimeException if the provided value is not a {@link Texture} + * @throws RuntimeException if the provided value is not a {@link GlTexture} */ - public void setTextureValue(Texture value) { + public void setTextureValue(GlTexture value) { setValue(value); } @@ -113,7 +113,7 @@ public void write(JmeExporter ex) throws IOException { oc.write(colorSpace, "colorSpace", null); // For backwards compatibility oc.write(0, "texture_unit", -1); - oc.write((Texture) value, "texture", null); + oc.write((GlTexture) value, "texture", null); } @Override diff --git a/jme3-core/src/main/java/com/jme3/material/Material.java b/jme3-core/src/main/java/com/jme3/material/Material.java index da88ff30e9..5cf1c77cf0 100644 --- a/jme3-core/src/main/java/com/jme3/material/Material.java +++ b/jme3-core/src/main/java/com/jme3/material/Material.java @@ -31,46 +31,11 @@ */ package com.jme3.material; -import com.jme3.asset.AssetKey; -import com.jme3.asset.AssetManager; -import com.jme3.asset.CloneableSmartAsset; -import com.jme3.export.InputCapsule; -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.OutputCapsule; import com.jme3.export.Savable; -import com.jme3.light.LightList; -import com.jme3.material.RenderState.BlendMode; -import com.jme3.material.RenderState.FaceCullMode; -import com.jme3.material.TechniqueDef.LightMode; -import com.jme3.math.ColorRGBA; -import com.jme3.math.Matrix4f; -import com.jme3.math.Vector2f; -import com.jme3.math.Vector3f; -import com.jme3.math.Vector4f; -import com.jme3.renderer.Caps; -import com.jme3.renderer.RenderManager; -import com.jme3.renderer.Renderer; -import com.jme3.renderer.TextureUnitException; -import com.jme3.renderer.queue.RenderQueue.Bucket; +import com.jme3.math.*; import com.jme3.scene.Geometry; -import com.jme3.shader.*; -import com.jme3.shader.bufferobject.BufferObject; -import com.jme3.texture.Image; import com.jme3.texture.Texture; -import com.jme3.texture.TextureImage; -import com.jme3.texture.image.ColorSpace; -import com.jme3.util.ListMap; -import com.jme3.util.SafeArrayList; - -import java.io.IOException; -import java.util.Collection; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; +import com.jme3.vulkan.buffers.GpuBuffer; /** * Material describes the rendering style for a given @@ -81,1184 +46,76 @@ * shader. *

* - * @author Kirill Vainer + * @author codex */ -public class Material implements CloneableSmartAsset, Cloneable, Savable { - - // Version #2: Fixed issue with RenderState.apply*** flags not getting exported - public static final int SAVABLE_VERSION = 2; - private static final Logger logger = Logger.getLogger(Material.class.getName()); - - private AssetKey key; - private String name; - private MaterialDef def; - private ListMap paramValues = new ListMap<>(); - private Technique technique; - private HashMap techniques = new HashMap<>(); - private RenderState additionalState = null; - private final RenderState mergedRenderState = new RenderState(); - private boolean transparent = false; - private boolean receivesShadows = false; - private int sortingId = -1; - - /** - * Manages and tracks texture and buffer binding units for rendering. - * Used internally by the Material class. - */ - public static class BindUnits { - /** The current texture unit counter. */ - public int textureUnit = 0; - /** The current buffer unit counter. */ - public int bufferUnit = 0; - } - private BindUnits bindUnits = new BindUnits(); - - /** - * Constructs a new Material instance based on a provided MaterialDef. - * The material's parameters will be initialized with default values from the definition. - * - * @param def The material definition to use (cannot be null). - * @throws IllegalArgumentException if def is null. - */ - public Material(MaterialDef def) { - if (def == null) { - throw new IllegalArgumentException("Material definition cannot be null"); - } - this.def = def; - - // Load default values from definition (if any) - for (MatParam param : def.getMaterialParams()) { - if (param.getValue() != null) { - setParam(param.getName(), param.getVarType(), param.getValue()); - } - } - } - - /** - * Constructs a new Material by loading its MaterialDef from the asset manager. - * - * @param assetManager The asset manager to load the MaterialDef from. - * @param defName The asset path of the .j3md file. - */ - public Material(AssetManager assetManager, String defName) { - this(assetManager.loadAsset(new AssetKey(defName))); - } - - /** - * For serialization only. Do not use. - */ - public Material() { - } - - /** - * Returns the asset key name of the asset from which this material was loaded. - *

This value will be null unless this material was loaded from a .j3m file.

- * - * @return Asset key name of the .j3m file, or null if not loaded from a file. - */ - public String getAssetName() { - return key != null ? key.getName() : null; - } - - /** - * Returns the user-defined name of the material. - * This name is distinct from the asset name and may be null or not unique. - * - * @return The name of the material, or null. - */ - public String getName() { - return name; - } - - /** - * Sets the user-defined name of the material. - * The name is not the same as the asset name. - * It can be null, and there is no guarantee of its uniqueness. - * - * @param name The name of the material. - */ - public void setName(String name) { - this.name = name; - } +public interface Material extends Savable { - @Override - public void setKey(AssetKey key) { - this.key = key; - } + String DEFAULT_UNIFORM_BUFFER = "DefaultUniformBuffer"; - @Override - public AssetKey getKey() { - return key; - } - - /** - * Returns the sorting ID or sorting index for this material. - * - *

The sorting ID is used internally by the system to sort rendering - * of geometries. It sorted to reduce shader switches, if the shaders - * are equal, then it is sorted by textures. - * - * @return The sorting ID used for sorting geometries for rendering. - */ - public int getSortId() { - if (sortingId == -1 && technique != null) { - sortingId = technique.getSortId() << 16; - int texturesSortId = 17; - for (int i = 0; i < paramValues.size(); i++) { - MatParam param = paramValues.getValue(i); - if (!param.getVarType().isTextureType()) { - continue; - } - Texture texture = (Texture) param.getValue(); - if (texture == null) { - continue; - } - Image image = texture.getImage(); - if (image == null) { - continue; - } - int textureId = image.getId(); - if (textureId == -1) { - textureId = 0; - } - texturesSortId = texturesSortId * 23 + textureId; - } - sortingId |= texturesSortId & 0xFFFF; - } - return sortingId; - } + void setUniform(String name, GpuBuffer buffer); - /** - * Clones this material. The result is returned. - */ - @Override - public Material clone() { - try { - Material mat = (Material) super.clone(); - - if (additionalState != null) { - mat.additionalState = additionalState.clone(); - } - mat.technique = null; - mat.techniques = new HashMap(); - - mat.paramValues = new ListMap(); - for (int i = 0; i < paramValues.size(); i++) { - Map.Entry entry = paramValues.getEntry(i); - mat.paramValues.put(entry.getKey(), entry.getValue().clone()); - } - - mat.sortingId = -1; - - return mat; - } catch (CloneNotSupportedException ex) { - throw new AssertionError(ex); - } - } + void setTexture(String name, Texture texture); - /** - * Compares two materials for content equality. - * This methods compare definition, parameters, additional render states. - * Since materials are mutable objects, implementing equals() properly is not possible, - * hence the name contentEquals(). - * - * @param otherObj the material to compare to this material - * @return true if the materials are equal. - */ - public boolean contentEquals(Object otherObj) { - if (!(otherObj instanceof Material)) { - return false; - } + void setParam(String uniform, String param, Object value); - Material other = (Material) otherObj; + void clearParam(String uniform, String param); - // Early exit if the material are the same object - if (this == other) { - return true; - } + T getParam(String uniform, String name); - // Check material definition - if (this.getMaterialDef() != other.getMaterialDef()) { - return false; - } + Texture getTexture(String name); - // Early exit if the size of the params is different - if (this.paramValues.size() != other.paramValues.size()) { - return false; - } + /* ----- COMPATABILITY WITH OLD MATERIAL ----- */ - // Checking technique - if (this.technique != null || other.technique != null) { - // Techniques are considered equal if their names are the same - // E.g. if user chose custom technique for one material but - // uses default technique for other material, the materials - // are not equal. - String thisDefName = this.technique != null - ? this.technique.getDef().getName() - : TechniqueDef.DEFAULT_TECHNIQUE_NAME; - - String otherDefName = other.technique != null - ? other.technique.getDef().getName() - : TechniqueDef.DEFAULT_TECHNIQUE_NAME; - - if (!thisDefName.equals(otherDefName)) { - return false; - } - } - - // Comparing parameters - for (String paramKey : paramValues.keySet()) { - MatParam thisParam = this.getParam(paramKey); - MatParam otherParam = other.getParam(paramKey); - - // This param does not exist in compared mat - if (otherParam == null) { - return false; - } - - if (!otherParam.equals(thisParam)) { - return false; - } - } - - // Comparing additional render states - if (additionalState == null) { - if (other.additionalState != null) { - return false; - } - } else { - if (!additionalState.equals(other.additionalState)) { - return false; - } - } - - return true; + default int getSortId() { + return 0; } - /** - * Works like {@link Object#hashCode() } except it may change together with the material as the material is mutable by definition. - * - * @return value for use in hashing - */ - public int contentHashCode() { - int hash = 7; - hash = 29 * hash + (this.def != null ? this.def.hashCode() : 0); - hash = 29 * hash + (this.paramValues != null ? this.paramValues.hashCode() : 0); - hash = 29 * hash + (this.technique != null ? this.technique.getDef().getName().hashCode() : 0); - hash = 29 * hash + (this.additionalState != null ? this.additionalState.contentHashCode() : 0); - return hash; + default void clearParam(String param) { + clearParam(DEFAULT_UNIFORM_BUFFER, param); } - /** - * Returns the currently active technique. - *

- * The technique is selected automatically by the {@link RenderManager} - * based on system capabilities. Users may select their own - * technique by using - * {@link #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) }. - * - * @return the currently active technique. - * - * @see #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) - */ - public Technique getActiveTechnique() { - return technique; + default void setParam(String param, Object value) { + setParam(DEFAULT_UNIFORM_BUFFER, param, value); } - /** - * Check if the transparent value marker is set on this material. - * @return True if the transparent value marker is set on this material. - * @see #setTransparent(boolean) - */ - public boolean isTransparent() { - return transparent; + default void setBoolean(String param, boolean value) { + setParam(param, value); } - /** - * Set the transparent value marker. - * - *

This value is merely a marker, by itself it does nothing. - * Generally model loaders will use this marker to indicate further - * up that the material is transparent and therefore any geometries - * using it should be put into the {@link Bucket#Transparent transparent - * bucket}. - * - * @param transparent the transparent value marker. - */ - public void setTransparent(boolean transparent) { - this.transparent = transparent; + default void setInt(String param, int value) { + setParam(param, value); } - /** - * Check if the material should receive shadows or not. - * - * @return True if the material should receive shadows. - * - * @see Material#setReceivesShadows(boolean) - */ - public boolean isReceivesShadows() { - return receivesShadows; + default void setFloat(String param, float value) { + setParam(param, value); } - /** - * Set if the material should receive shadows or not. - * - *

This value is merely a marker, by itself it does nothing. - * Generally model loaders will use this marker to indicate - * the material should receive shadows and therefore any - * geometries using it should have {@link com.jme3.renderer.queue.RenderQueue.ShadowMode#Receive} set - * on them. - * - * @param receivesShadows if the material should receive shadows or not. - */ - public void setReceivesShadows(boolean receivesShadows) { - this.receivesShadows = receivesShadows; + default void setFloat(String param, Float value) { + setParam(param, value); } - /** - * Acquire the additional {@link RenderState render state} to apply - * for this material. - * - *

The first call to this method will create an additional render - * state which can be modified by the user to apply any render - * states in addition to the ones used by the renderer. Only render - * states which are modified in the additional render state will be applied. - * - * @return The additional render state. - */ - public RenderState getAdditionalRenderState() { - if (additionalState == null) { - additionalState = RenderState.ADDITIONAL.clone(); - } - return additionalState; + default void setColor(String param, ColorRGBA value) { + setParam(param, value); } - /** - * Get the material definition (.j3md file info) that this - * material is implementing. - * - * @return the material definition this material implements. - */ - public MaterialDef getMaterialDef() { - return def; + default void setVector2(String param, Vector2f value) { + setParam(param, value); } - /** - * Returns the parameter set on this material with the given name, - * returns null if the parameter is not set. - * - * @param name The parameter name to look up. - * @return The MatParam if set, or null if not set. - */ - public MatParam getParam(String name) { - return paramValues.get(name); + default void setVector3(String param, Vector3f value) { + setParam(param, value); } - /** - * Returns the current parameter's value. - * - * @param the expected type of the parameter value - * @param name the parameter name to look up. - * @return current value or null if the parameter wasn't set. - */ - @SuppressWarnings("unchecked") - public T getParamValue(final String name) { - final MatParam param = paramValues.get(name); - return param == null ? null : (T) param.getValue(); + default void setVector4(String param, Vector4f value) { + setParam(param, value); } - /** - * Returns the texture parameter set on this material with the given name, - * returns null if the parameter is not set. - * - * @param name The parameter name to look up. - * @return The MatParamTexture if set, or null if not set. - */ - public MatParamTexture getTextureParam(String name) { - MatParam param = paramValues.get(name); - if (param instanceof MatParamTexture) { - return (MatParamTexture) param; - } - return null; - } - - /** - * Returns a collection of all parameters set on this material. - * - * @return a collection of all parameters set on this material. - * - * @see #setParam(java.lang.String, com.jme3.shader.VarType, java.lang.Object) - */ - public Collection getParams() { - return paramValues.values(); + default void setMatrix4(String param, Matrix4f value) { + setParam(param, value); } - /** - * Returns the ListMap of all parameters set on this material. - * - * @return a ListMap of all parameters set on this material. - * - * @see #setParam(java.lang.String, com.jme3.shader.VarType, java.lang.Object) - */ - public ListMap getParamsMap() { - return paramValues; + default T getParam(String param) { + return getParam(DEFAULT_UNIFORM_BUFFER, param); } - /** - * Check if setting the parameter given the type and name is allowed. - * @param type The type that the "set" function is designed to set - * @param name The name of the parameter - */ - private void checkSetParam(VarType type, String name) { - MatParam paramDef = def.getMaterialParam(name); - if (paramDef == null) { - throw new IllegalArgumentException("Material parameter is not defined: " + name); - } - if (type != null && paramDef.getVarType() != type) { - logger.log(Level.WARNING, "Material parameter being set: {0} with " - + "type {1} doesn''t match definition types {2}", new Object[]{name, type.name(), paramDef.getVarType()}); - } - } - - /** - * Pass a parameter to the material shader. - * - * @param name the name of the parameter defined in the material definition (.j3md) - * @param type the type of the parameter {@link VarType} - * @param value the value of the parameter - */ - public void setParam(String name, VarType type, Object value) { - checkSetParam(type, name); - - if (type.isTextureType()) { - setTextureParam(name, type, (Texture)value); - } else { - MatParam val = getParam(name); - if (val == null) { - paramValues.put(name, new MatParam(type, name, value)); - } else { - val.setValue(value); - } - - if (technique != null) { - technique.notifyParamChanged(name, type, value); - } - if (type.isImageType()) { - // recompute sort id - sortingId = -1; - } - } - } - - /** - * Pass a parameter to the material shader. - * - * @param name the name of the parameter defined in the material definition (j3md) - * @param value the value of the parameter - */ - public void setParam(String name, Object value) { - MatParam p = getMaterialDef().getMaterialParam(name); - setParam(name, p.getVarType(), value); - } - - /** - * Clear a parameter from this material. The parameter must exist - * @param name the name of the parameter to clear - */ - public void clearParam(String name) { - checkSetParam(null, name); - MatParam matParam = getParam(name); - if (matParam == null) { - return; - } - - paramValues.remove(name); - if (matParam instanceof MatParamTexture) { - sortingId = -1; - } - if (technique != null) { - technique.notifyParamChanged(name, null, null); - } - } - - /** - * Set a texture parameter. - * - * @param name The name of the parameter - * @param type The variable type {@link VarType} - * @param value The texture value of the parameter. - * - * @throws IllegalArgumentException is value is null - */ - public void setTextureParam(String name, VarType type, Texture value) { - if (value == null) { - throw new IllegalArgumentException(); - } - - checkSetParam(type, name); - MatParamTexture param = getTextureParam(name); - - checkTextureParamColorSpace(name, value); - ColorSpace colorSpace = value.getImage() != null ? value.getImage().getColorSpace() : null; - - if (param == null) { - param = new MatParamTexture(type, name, value, colorSpace); - paramValues.put(name, param); - } else { - param.setTextureValue(value); - param.setColorSpace(colorSpace); - } - - if (technique != null) { - technique.notifyParamChanged(name, type, value); - } - - // need to recompute sort ID - sortingId = -1; - } - - private void checkTextureParamColorSpace(String name, Texture value) { - MatParamTexture paramDef = (MatParamTexture) def.getMaterialParam(name); - if (paramDef.getColorSpace() != null && paramDef.getColorSpace() != value.getImage().getColorSpace()) { - value.getImage().setColorSpace(paramDef.getColorSpace()); - if (logger.isLoggable(Level.FINE)) { - logger.log(Level.FINE, "Material parameter {0} needs a {1} texture, " - + "texture {2} was switched to {3} color space.", - new Object[]{name, paramDef.getColorSpace().toString(), - value.getName(), - value.getImage().getColorSpace().name()}); - } - } else if (paramDef.getColorSpace() == null && value.getName() != null && value.getImage().getColorSpace() == ColorSpace.Linear) { - logger.log(Level.WARNING, - "The texture {0} has linear color space, but the material " - + "parameter {2} specifies no color space requirement, this may " - + "lead to unexpected behavior.\nCheck if the image " - + "was not set to another material parameter with a linear " - + "color space, or that you did not set the ColorSpace to " - + "Linear using texture.getImage.setColorSpace().", - new Object[]{value.getName(), value.getImage().getColorSpace().name(), name}); - } - } - - /** - * Pass a texture to the material shader. - * - * @param name the name of the texture defined in the material definition - * (.j3md) (e.g. Texture for Lighting.j3md) - * @param value the Texture object previously loaded by the asset manager - */ - public void setTexture(String name, Texture value) { - if (value == null) { - // clear it - clearParam(name); - return; - } - - VarType paramType = null; - switch (value.getType()) { - case TwoDimensional: - paramType = VarType.Texture2D; - break; - case TwoDimensionalArray: - paramType = VarType.TextureArray; - break; - case ThreeDimensional: - paramType = VarType.Texture3D; - break; - case CubeMap: - paramType = VarType.TextureCubeMap; - break; - default: - throw new UnsupportedOperationException("Unknown texture type: " + value.getType()); - } - - setTextureParam(name, paramType, value); - } - - /** - * Pass a Matrix4f to the material shader. - * - * @param name the name of the matrix defined in the material definition (j3md) - * @param value the Matrix4f object - */ - public void setMatrix4(String name, Matrix4f value) { - setParam(name, VarType.Matrix4, value); - } - - /** - * Pass a boolean to the material shader. - * - * @param name the name of the boolean defined in the material definition (j3md) - * @param value the boolean value - */ - public void setBoolean(String name, boolean value) { - setParam(name, VarType.Boolean, value); - } - - /** - * Pass a float to the material shader. - * - * @param name the name of the float defined in the material definition (j3md) - * @param value the float value - */ - public void setFloat(String name, float value) { - setParam(name, VarType.Float, value); - } - - /** - * Pass a float to the material shader. This version avoids auto-boxing - * if the value is already a Float. - * - * @param name the name of the float defined in the material definition (j3md) - * @param value the float value - */ - public void setFloat(String name, Float value) { - setParam(name, VarType.Float, value); - } - - /** - * Pass an int to the material shader. - * - * @param name the name of the int defined in the material definition (j3md) - * @param value the int value - */ - public void setInt(String name, int value) { - setParam(name, VarType.Int, value); - } - - /** - * Pass a Color to the material shader. - * - * @param name the name of the color defined in the material definition (j3md) - * @param value the ColorRGBA value - */ - public void setColor(String name, ColorRGBA value) { - setParam(name, VarType.Vector4, value); - } - - /** - * Pass a uniform buffer object to the material shader. - * - * @param name the name of the buffer object defined in the material definition (j3md). - * @param value the buffer object. - */ - public void setUniformBufferObject(final String name, final BufferObject value) { - setParam(name, VarType.UniformBufferObject, value); - } - - /** - * Pass a shader storage buffer object to the material shader. - * - * @param name the name of the buffer object defined in the material definition (j3md). - * @param value the buffer object. - */ - public void setShaderStorageBufferObject(final String name, final BufferObject value) { - setParam(name, VarType.ShaderStorageBufferObject, value); - } - - /** - * Pass a Vector2f to the material shader. - * - * @param name the name of the Vector2f defined in the material definition (j3md) - * @param value the Vector2f value - */ - public void setVector2(String name, Vector2f value) { - setParam(name, VarType.Vector2, value); - } - - /** - * Pass a Vector3f to the material shader. - * - * @param name the name of the Vector3f defined in the material definition (j3md) - * @param value the Vector3f value - */ - public void setVector3(String name, Vector3f value) { - setParam(name, VarType.Vector3, value); - } - - /** - * Pass a Vector4f to the material shader. - * - * @param name the name of the Vector4f defined in the material definition (j3md) - * @param value the Vector4f value - */ - public void setVector4(String name, Vector4f value) { - setParam(name, VarType.Vector4, value); - } - - /** - * Select the technique to use for rendering this material. - *

- * Any candidate technique for selection (either default or named) - * must be verified to be compatible with the system, for that, the - * renderManager is queried for capabilities. - * - * @param name The name of the technique to select, pass - * {@link TechniqueDef#DEFAULT_TECHNIQUE_NAME} to select one of the default - * techniques. - * @param renderManager The {@link RenderManager render manager} - * to query for capabilities. - * - * @throws IllegalArgumentException If no technique exists with the given - * name. - * @throws UnsupportedOperationException If no candidate technique supports - * the system capabilities. - */ - public void selectTechnique(String name, final RenderManager renderManager) { - // check if already created - Technique tech = techniques.get(name); - // When choosing technique, we choose one that - // supports all the caps. - if (tech == null) { - EnumSet rendererCaps = renderManager.getRenderer().getCaps(); - List techDefs = def.getTechniqueDefs(name); - if (techDefs == null || techDefs.isEmpty()) { - throw new IllegalArgumentException( - String.format("The requested technique %s is not available on material %s", name, def.getName())); - } - - TechniqueDef lastTech = null; - float weight = 0; - for (TechniqueDef techDef : techDefs) { - if (rendererCaps.containsAll(techDef.getRequiredCaps())) { - float techWeight = techDef.getWeight() + (techDef.getLightMode() == renderManager.getPreferredLightMode() ? 10f : 0); - if (techWeight > weight) { - tech = new Technique(this, techDef); - techniques.put(name, tech); - weight = techWeight; - } - } - lastTech = techDef; - } - if (tech == null) { - throw new UnsupportedOperationException( - String.format("No technique '%s' on material " - + "'%s' is supported by the video hardware. " - + "The capabilities %s are required.", - name, def.getName(), lastTech.getRequiredCaps())); - } - if (logger.isLoggable(Level.FINE)) { - logger.log(Level.FINE, this.getMaterialDef().getName() + " selected technique def " + tech.getDef()); - } - } else if (technique == tech) { - // attempting to switch to an already - // active technique. - return; - } - - technique = tech; - tech.notifyTechniqueSwitched(); - - // shader was changed - sortingId = -1; - } - - private void applyOverrides(Renderer renderer, Shader shader, SafeArrayList overrides, BindUnits bindUnits) { - for (MatParamOverride override : overrides.getArray()) { - VarType type = override.getVarType(); - - MatParam paramDef = def.getMaterialParam(override.getName()); - - if (paramDef == null || paramDef.getVarType() != type || !override.isEnabled()) { - continue; - } - - Uniform uniform = shader.getUniform(override.getPrefixedName()); - - if (override.getValue() != null) { - updateShaderMaterialParameter(renderer, type, shader, override, bindUnits, true); - } else { - uniform.clearValue(); - } - } - } - - private void updateShaderMaterialParameter(Renderer renderer, VarType type, Shader shader, MatParam param, BindUnits unit, boolean override) { - if (type == VarType.UniformBufferObject || type == VarType.ShaderStorageBufferObject) { - ShaderBufferBlock bufferBlock = shader.getBufferBlock(param.getPrefixedName()); - BufferObject bufferObject = (BufferObject) param.getValue(); - - ShaderBufferBlock.BufferType btype; - if (type == VarType.ShaderStorageBufferObject) { - btype = ShaderBufferBlock.BufferType.ShaderStorageBufferObject; - bufferBlock.setBufferObject(btype, bufferObject); - renderer.setShaderStorageBufferObject(unit.bufferUnit, bufferObject); // TODO: probably not needed - } else { - btype = ShaderBufferBlock.BufferType.UniformBufferObject; - bufferBlock.setBufferObject(btype, bufferObject); - renderer.setUniformBufferObject(unit.bufferUnit, bufferObject); // TODO: probably not needed - } - unit.bufferUnit++; - } else { - Uniform uniform = shader.getUniform(param.getPrefixedName()); - if (!override && uniform.isSetByCurrentMaterial()) - return; - - if (type.isTextureType() || type.isImageType()) { - try { - if (type.isTextureType()) { - renderer.setTexture(unit.textureUnit, (Texture) param.getValue()); - } else { - renderer.setTextureImage(unit.textureUnit, (TextureImage) param.getValue()); - } - } catch (TextureUnitException ex) { - int numTexParams = unit.textureUnit + 1; - String message = "Too many texture parameters (" + numTexParams + ") assigned\n to " + this.toString(); - throw new IllegalStateException(message); - } - uniform.setValue(VarType.Int, unit.textureUnit); - unit.textureUnit++; - } else { - uniform.setValue(type, param.getValue()); - } - } - } - - private BindUnits updateShaderMaterialParameters(Renderer renderer, Shader shader, - SafeArrayList worldOverrides, SafeArrayList forcedOverrides) { - - bindUnits.textureUnit = 0; - bindUnits.bufferUnit = 0; - - if (worldOverrides != null) { - applyOverrides(renderer, shader, worldOverrides, bindUnits); - } - if (forcedOverrides != null) { - applyOverrides(renderer, shader, forcedOverrides, bindUnits); - } - - for (int i = 0; i < paramValues.size(); i++) { - MatParam param = paramValues.getValue(i); - VarType type = param.getVarType(); - updateShaderMaterialParameter(renderer, type, shader, param, bindUnits, false); - } - - // TODO: HACKY HACK remove this when texture unit is handled by the uniform. - return bindUnits; - } - - private void updateRenderState(Geometry geometry, RenderManager renderManager, Renderer renderer, TechniqueDef techniqueDef) { - RenderState finalRenderState; - if (renderManager.getForcedRenderState() != null) { - finalRenderState = mergedRenderState.copyFrom(renderManager.getForcedRenderState()); - } else if (techniqueDef.getRenderState() != null) { - finalRenderState = mergedRenderState.copyFrom(RenderState.DEFAULT); - finalRenderState = techniqueDef.getRenderState().copyMergedTo(additionalState, finalRenderState); - } else { - finalRenderState = mergedRenderState.copyFrom(RenderState.DEFAULT); - finalRenderState = RenderState.DEFAULT.copyMergedTo(additionalState, finalRenderState); - } - // test if the face cull mode should be flipped before render - if (finalRenderState.isFaceCullFlippable() && isNormalsBackward(geometry.getWorldScale())) { - finalRenderState.flipFaceCull(); - } - renderer.applyRenderState(finalRenderState); - } - - /** - * Returns true if the geometry world scale indicates that normals will be backward. - * - * @param scalar The geometry's world scale vector. - * @return true if the normals are effectively backward; false otherwise. - */ - private boolean isNormalsBackward(Vector3f scalar) { - // count number of negative scalar vector components - int n = 0; - if (scalar.x < 0) n++; - if (scalar.y < 0) n++; - if (scalar.z < 0) n++; - // An odd number of negative components means the normal vectors - // are backward to what they should be. - return n == 1 || n == 3; - } - - /** - * Preloads this material for the given render manager. - *

- * Preloading the material can ensure that when the material is first - * used for rendering, there won't be any delay since the material has - * been already been setup for rendering. - * - * @param renderManager The render manager to preload for - * @param geometry to determine the applicable parameter overrides, if any - */ - public void preload(RenderManager renderManager, Geometry geometry) { - if (technique == null) { - selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager); - } - TechniqueDef techniqueDef = technique.getDef(); - Renderer renderer = renderManager.getRenderer(); - EnumSet rendererCaps = renderer.getCaps(); - - if (techniqueDef.isNoRender()) { - return; - } - // Get world overrides - SafeArrayList overrides = geometry.getWorldMatParamOverrides(); - - Shader shader = technique.makeCurrent(renderManager, overrides, null, null, rendererCaps); - updateShaderMaterialParameters(renderer, shader, overrides, null); - renderManager.getRenderer().setShader(shader); - } - - private void clearUniformsSetByCurrent(Shader shader) { - ListMap uniforms = shader.getUniformMap(); - int size = uniforms.size(); - for (int i = 0; i < size; i++) { - Uniform u = uniforms.getValue(i); - u.clearSetByCurrentMaterial(); - } - } - - private void resetUniformsNotSetByCurrent(Shader shader) { - ListMap uniforms = shader.getUniformMap(); - int size = uniforms.size(); - for (int i = 0; i < size; i++) { - Uniform u = uniforms.getValue(i); - if (!u.isSetByCurrentMaterial()) { - if (u.getName().charAt(0) != 'g') { - // Don't reset world globals! - // The benefits gained from this are very minimal - // and cause lots of matrix -> FloatBuffer conversions. - u.clearValue(); - } - } - } - } - - /** - * Called by {@link RenderManager} to render the geometry by - * using this material. - *

- * The material is rendered as follows: - *

    - *
  • Determine which technique to use to render the material - - * either what the user selected via - * {@link #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) - * Material.selectTechnique()}, - * or the first default technique that the renderer supports - * (based on the technique's {@link TechniqueDef#getRequiredCaps() requested rendering capabilities})
      - *
    • If the technique has been changed since the last frame, then it is notified via - * {@link Technique#makeCurrent(com.jme3.renderer.RenderManager, com.jme3.util.SafeArrayList, com.jme3.util.SafeArrayList, com.jme3.light.LightList, java.util.EnumSet) - * Technique.makeCurrent()}. - * If the technique wants to use a shader to render the model, it should load it at this part - - * the shader should have all the proper defines as declared in the technique definition, - * including those that are bound to material parameters. - * The technique can re-use the shader from the last frame if - * no changes to the defines occurred.
    - *
  • Set the {@link RenderState} to use for rendering. The render states are - * applied in this order (later RenderStates override earlier RenderStates):
      - *
    1. {@link TechniqueDef#getRenderState() Technique Definition's RenderState} - * - i.e. specific RenderState that is required for the shader.
    2. - *
    3. {@link #getAdditionalRenderState() Material Instance Additional RenderState} - * - i.e. ad-hoc RenderState set per model
    4. - *
    5. {@link RenderManager#getForcedRenderState() RenderManager's Forced RenderState} - * - i.e. RenderState requested by a {@link com.jme3.post.SceneProcessor} or - * post-processing filter.
    - *
  • If the technique uses a shader, then the uniforms of the shader must be updated.
      - *
    • Uniforms bound to material parameters are updated based on the current material parameter values.
    • - *
    • Uniforms bound to world parameters are updated from the RenderManager. - * Internally {@link UniformBindingManager} is used for this task.
    • - *
    • Uniforms bound to textures will cause the texture to be uploaded as necessary. - * The uniform is set to the texture unit where the texture is bound.
    - *
  • If the technique uses a shader, the model is then rendered according - * to the lighting mode specified on the technique definition.
      - *
    • {@link LightMode#SinglePass single pass light mode} fills the shader's light uniform arrays - * with the first 4 lights and renders the model once.
    • - *
    • {@link LightMode#MultiPass multi pass light mode} light mode renders the model multiple times, - * for the first light it is rendered opaque, on subsequent lights it is - * rendered with {@link BlendMode#AlphaAdditive alpha-additive} blending and depth writing disabled.
    • - *
    - *
  • For techniques that do not use shaders, - * fixed function OpenGL is used to render the model (see {@link com.jme3.renderer.opengl.GLRenderer} interface):
      - *
    • OpenGL state that is bound to material parameters is updated.
    • - *
    • The texture set on the material is uploaded and bound. - * Currently only 1 texture is supported for fixed function techniques.
    • - *
    • If the technique uses lighting, then OpenGL lighting state is updated - * based on the light list on the geometry, otherwise OpenGL lighting is disabled.
    • - *
    • The mesh is uploaded and rendered.
    • - *
    - *
- * - * @param geometry The geometry to render - * @param lights Presorted and filtered light list to use for rendering - * @param renderManager The render manager requesting the rendering - */ - public void render(Geometry geometry, LightList lights, RenderManager renderManager) { - if (technique == null) { - selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager); - } - - TechniqueDef techniqueDef = technique.getDef(); - Renderer renderer = renderManager.getRenderer(); - EnumSet rendererCaps = renderer.getCaps(); - - if (techniqueDef.isNoRender()) { - return; - } - - // Apply render state - updateRenderState(geometry, renderManager, renderer, techniqueDef); - - // Get world overrides - SafeArrayList overrides = geometry.getWorldMatParamOverrides(); - - // Select shader to use - Shader shader = technique.makeCurrent(renderManager, overrides, renderManager.getForcedMatParams(), lights, rendererCaps); - - // Begin tracking which uniforms were changed by material. - clearUniformsSetByCurrent(shader); - - // Set uniform bindings - renderManager.updateUniformBindings(shader); - - // Set material parameters - BindUnits units = updateShaderMaterialParameters(renderer, shader, overrides, renderManager.getForcedMatParams()); - - // Clear any uniforms not changed by material. - resetUniformsNotSetByCurrent(shader); - - // Delegate rendering to the technique - technique.render(renderManager, shader, geometry, lights, units); - } - - /** - * Called by {@link RenderManager} to render the geometry by - * using this material. - * - * Note that this version of the render method - * does not perform light filtering. - * - * @param geom The geometry to render - * @param rm The render manager requesting the rendering - */ - public void render(Geometry geom, RenderManager rm) { - render(geom, geom.getWorldLightList(), rm); - } - - @Override - public String toString() { - return "Material[name=" + name + - ", def=" + (def != null ? def.getName() : null) + - ", tech=" + (technique != null && technique.getDef() != null ? technique.getDef().getName() : null) + - "]"; - } - - @Override - public void write(JmeExporter ex) throws IOException { - OutputCapsule oc = ex.getCapsule(this); - oc.write(def.getAssetName(), "material_def", null); - oc.write(additionalState, "render_state", null); - oc.write(transparent, "is_transparent", false); - oc.write(name, "name", null); - oc.writeStringSavableMap(paramValues, "parameters", null); - } - - @Override - @SuppressWarnings("unchecked") - public void read(JmeImporter im) throws IOException { - InputCapsule ic = im.getCapsule(this); - - name = ic.readString("name", null); - additionalState = (RenderState) ic.readSavable("render_state", null); - transparent = ic.readBoolean("is_transparent", false); - - // Load the material def - String defName = ic.readString("material_def", null); - HashMap params = (HashMap) ic.readStringSavableMap("parameters", null); - - boolean enableVertexColor = false; - boolean separateTexCoord = false; - boolean applyDefaultValues = false; - boolean guessRenderStateApply = false; - - int ver = ic.getSavableVersion(Material.class); - if (ver < 1) { - applyDefaultValues = true; - } - if (ver < 2) { - guessRenderStateApply = true; - } - if (im.getFormatVersion() == 0) { - // Enable compatibility with old models - if (defName.equalsIgnoreCase("Common/MatDefs/Misc/VertexColor.j3md")) { - // Using VertexColor, switch to Unshaded and set VertexColor=true - enableVertexColor = true; - defName = "Common/MatDefs/Misc/Unshaded.j3md"; - } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/SimpleTextured.j3md") - || defName.equalsIgnoreCase("Common/MatDefs/Misc/SolidColor.j3md")) { - // Using SimpleTextured/SolidColor, just switch to Unshaded - defName = "Common/MatDefs/Misc/Unshaded.j3md"; - } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/WireColor.j3md")) { - // Using WireColor, set wireframe render state = true and use Unshaded - getAdditionalRenderState().setWireframe(true); - defName = "Common/MatDefs/Misc/Unshaded.j3md"; - } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/Unshaded.j3md")) { - // Uses unshaded, ensure that the proper param is set - MatParam value = params.get("SeperateTexCoord"); - if (value != null && ((Boolean) value.getValue()) == true) { - params.remove("SeperateTexCoord"); - separateTexCoord = true; - } - } - assert applyDefaultValues && guessRenderStateApply; - } - - def = im.getAssetManager().loadAsset(new AssetKey(defName)); - paramValues = new ListMap(); - - // load the textures and update nextTexUnit - for (Map.Entry entry : params.entrySet()) { - MatParam param = entry.getValue(); - if (param instanceof MatParamTexture) { - MatParamTexture texVal = (MatParamTexture) param; - // the texture failed to load for this param - // do not add to param values - if (texVal.getTextureValue() == null || texVal.getTextureValue().getImage() == null) { - continue; - } - checkTextureParamColorSpace(texVal.getName(), texVal.getTextureValue()); - } - - if (im.getFormatVersion() == 0 && param.getName().startsWith("m_")) { - // Ancient version of jME3 ... - param.setName(param.getName().substring(2)); - } - - if (def.getMaterialParam(param.getName()) == null) { - logger.log(Level.WARNING, "The material parameter is not defined: {0}. Ignoring..", - param.getName()); - } else { - checkSetParam(param.getVarType(), param.getName()); - paramValues.put(param.getName(), param); - } - } - - if (applyDefaultValues) { - // compatibility with old versions where default vars were not available - for (MatParam param : def.getMaterialParams()) { - if (param.getValue() != null && paramValues.get(param.getName()) == null) { - setParam(param.getName(), param.getVarType(), param.getValue()); - } - } - } - if (guessRenderStateApply && additionalState != null) { - // Try to guess values of "apply" render state based on defaults - // if value != default then set apply to true - additionalState.applyPolyOffset = additionalState.offsetEnabled; - additionalState.applyBlendMode = additionalState.blendMode != BlendMode.Off; - additionalState.applyColorWrite = !additionalState.colorWrite; - additionalState.applyCullMode = additionalState.cullMode != FaceCullMode.Back; - additionalState.applyDepthTest = !additionalState.depthTest; - additionalState.applyDepthWrite = !additionalState.depthWrite; - additionalState.applyStencilTest = additionalState.stencilTest; - additionalState.applyWireFrame = additionalState.wireframe; - } - if (enableVertexColor) { - setBoolean("VertexColor", true); - } - if (separateTexCoord) { - setBoolean("SeparateTexCoord", true); - } - } } diff --git a/jme3-core/src/main/java/com/jme3/material/MaterialDef.java b/jme3-core/src/main/java/com/jme3/material/MaterialDef.java index 9cbd482e00..deccf713ca 100644 --- a/jme3-core/src/main/java/com/jme3/material/MaterialDef.java +++ b/jme3-core/src/main/java/com/jme3/material/MaterialDef.java @@ -38,7 +38,7 @@ import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; /** * Describes a J3MD (Material definition). @@ -133,7 +133,7 @@ public void addMaterialParam(VarType type, String name, Object value) { * @param value Default value of the parameter * @see ColorSpace */ - public void addMaterialParamTexture(VarType type, String name, ColorSpace colorSpace,Texture value) { + public void addMaterialParamTexture(VarType type, String name, ColorSpace colorSpace, GlTexture value) { matParams.put(name, new MatParamTexture(type, name, value, colorSpace)); } diff --git a/jme3-core/src/main/java/com/jme3/material/RenderState.java b/jme3-core/src/main/java/com/jme3/material/RenderState.java index 7f21e8abfa..4c49a3f271 100644 --- a/jme3-core/src/main/java/com/jme3/material/RenderState.java +++ b/jme3-core/src/main/java/com/jme3/material/RenderState.java @@ -32,8 +32,21 @@ package com.jme3.material; import com.jme3.export.*; +import com.jme3.scene.GlMesh; import com.jme3.scene.Mesh; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.mesh.MeshDescription; +import com.jme3.vulkan.pipeline.Pipeline; +import com.jme3.vulkan.pipeline.cache.PipelineCache; +import com.jme3.vulkan.pipeline.states.BasePipelineState; +import com.jme3.vulkan.pipeline.states.IShaderState; +import com.jme3.vulkan.shader.ShaderModule; +import org.lwjgl.system.MemoryStack; + import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; /** * RenderState specifies material rendering properties that cannot @@ -982,7 +995,7 @@ public void setDepthFunc(TestFunction depthFunc) { /** * Sets the mesh line width. * Use this in conjunction with {@link #setWireframe(boolean)} or with a mesh in - * {@link com.jme3.scene.Mesh.Mode#Lines} mode. + * {@link com.jme3.scene.GlMesh.Mode#Lines} mode. * Note: this does not work in OpenGL core profile. It only works in * compatibility profile. * @@ -1444,8 +1457,6 @@ public float getLineWidth() { return lineWidth; } - - public boolean isApplyBlendMode() { return applyBlendMode; } diff --git a/jme3-core/src/main/java/com/jme3/material/Technique.java b/jme3-core/src/main/java/com/jme3/material/Technique.java index 5091028a85..b890220a7e 100644 --- a/jme3-core/src/main/java/com/jme3/material/Technique.java +++ b/jme3-core/src/main/java/com/jme3/material/Technique.java @@ -32,27 +32,34 @@ package com.jme3.material; import com.jme3.asset.AssetManager; +import com.jme3.dev.NotFullyImplemented; import com.jme3.light.LightList; -import com.jme3.material.Material.BindUnits; +import com.jme3.material.GlMaterial.BindUnits; import com.jme3.material.TechniqueDef.LightMode; import com.jme3.material.logic.TechniqueDefLogic; import com.jme3.renderer.Caps; import com.jme3.renderer.RenderManager; import com.jme3.scene.Geometry; +import com.jme3.scene.GlMesh; import com.jme3.shader.DefineList; import com.jme3.shader.Shader; import com.jme3.shader.VarType; import com.jme3.util.ListMap; import com.jme3.util.SafeArrayList; +import com.jme3.vulkan.commands.CommandBuffer; +import com.jme3.vulkan.pipeline.Pipeline; +import com.jme3.vulkan.pipeline.PipelineBindPoint; +import com.jme3.vulkan.pipeline.states.BasePipelineState; + import java.util.EnumSet; /** * Represents a technique instance. */ -public final class Technique { +public final class Technique implements Pipeline { private final TechniqueDef def; - private final Material owner; + private final GlMaterial owner; private final DefineList paramDefines; private final DefineList dynamicDefines; @@ -63,7 +70,7 @@ public final class Technique { * @param owner The material that will own this technique * @param def The technique definition being implemented. */ - public Technique(Material owner, TechniqueDef def) { + public Technique(GlMaterial owner, TechniqueDef def) { this.owner = owner; this.def = def; this.paramDefines = def.createDefineList(); @@ -134,7 +141,7 @@ private void applyOverrides(DefineList defineList, SafeArrayList worldOverrides, + public Shader makeCurrent(RenderManager renderManager, SafeArrayList worldOverrides, SafeArrayList forcedOverrides, LightList lights, EnumSet rendererCaps) { TechniqueDefLogic logic = def.getLogic(); @@ -155,17 +162,18 @@ Shader makeCurrent(RenderManager renderManager, SafeArrayList /** * Render the technique according to its {@link TechniqueDefLogic}. - * + * * @param renderManager The render manager to perform the rendering against. - * @param shader The shader that was selected in - * {@link #makeCurrent(RenderManager, SafeArrayList, SafeArrayList, LightList, EnumSet)}. - * @param geometry The geometry to render - * @param lights Lights which influence the geometry. - * @param lastTexUnit the index of the most recently used texture unit + * @param shader The shader that was selected in + * {@link #makeCurrent(RenderManager, SafeArrayList, SafeArrayList, LightList, EnumSet)}. + * @param geometry The geometry to render + * @param mesh The mesh to render + * @param lights Lights which influence the geometry. + * @param lastBindUnits the index of the most recently used texture unit */ - void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, BindUnits lastBindUnits) { + public void render(RenderManager renderManager, Shader shader, Geometry geometry, GlMesh mesh, LightList lights, BindUnits lastBindUnits) { TechniqueDefLogic logic = def.getLogic(); - logic.render(renderManager, shader, geometry, lights, lastBindUnits); + logic.render(renderManager, shader, geometry, mesh, lights, lastBindUnits); } /** @@ -191,13 +199,40 @@ public DefineList getDynamicDefines() { public DefineList getAllDefines() { throw new UnsupportedOperationException(); } - + + @Override + public BasePipelineState getState() { + return null; + } + + @Override + @NotFullyImplemented + public void bind(CommandBuffer cmd) { + + } + + @Override + public boolean isMaterialEquivalent(Pipeline other) { + return false; + } + + @Override + public PipelineBindPoint getBindPoint() { + return PipelineBindPoint.Graphics; + } + + @Override + public Pipeline getParent() { + return null; + } + /** * Compute the sort ID. Similar to {@link Object#hashCode()} but used * for sorting geometries for rendering. * * @return the sort ID for this technique instance. */ + @Override public int getSortId() { int hash = 17; hash = hash * 23 + def.getSortId(); diff --git a/jme3-core/src/main/java/com/jme3/material/logic/DefaultTechniqueDefLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/DefaultTechniqueDefLogic.java index ca8f7d1efa..29d08b308d 100644 --- a/jme3-core/src/main/java/com/jme3/material/logic/DefaultTechniqueDefLogic.java +++ b/jme3-core/src/main/java/com/jme3/material/logic/DefaultTechniqueDefLogic.java @@ -33,14 +33,14 @@ import com.jme3.asset.AssetManager; import com.jme3.light.*; +import com.jme3.material.GlMaterial; import com.jme3.material.TechniqueDef; -import com.jme3.material.Material.BindUnits; import com.jme3.math.ColorRGBA; import com.jme3.renderer.Caps; import com.jme3.renderer.RenderManager; import com.jme3.renderer.Renderer; import com.jme3.scene.Geometry; -import com.jme3.scene.Mesh; +import com.jme3.scene.GlMesh; import com.jme3.scene.instancing.InstancedGeometry; import com.jme3.shader.DefineList; import com.jme3.shader.Shader; @@ -60,17 +60,16 @@ public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager return techniqueDef.getShader(assetManager, rendererCaps, defines); } - public static void renderMeshFromGeometry(Renderer renderer, Geometry geom) { - Mesh mesh = geom.getMesh(); + public static void renderMeshFromGeometry(Renderer renderer, Geometry geom, GlMesh mesh, GlMesh.Mode mode) { int lodLevel = geom.getLodLevel(); if (geom instanceof InstancedGeometry) { InstancedGeometry instGeom = (InstancedGeometry) geom; int numVisibleInstances = instGeom.getNumVisibleInstances(); if (numVisibleInstances > 0) { - renderer.renderMesh(mesh, lodLevel, numVisibleInstances, instGeom.getAllInstanceData()); + renderer.renderMesh(mesh, mode, lodLevel, numVisibleInstances, instGeom.getAllInstanceData()); } } else { - renderer.renderMesh(mesh, lodLevel, 1, null); + renderer.renderMesh(mesh, mode, lodLevel, 1, null); } } @@ -92,9 +91,9 @@ protected static ColorRGBA getAmbientColor(LightList lightList, boolean removeLi @Override - public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, BindUnits lastBindUnits) { + public void render(RenderManager renderManager, Shader shader, Geometry geometry, GlMesh mesh, LightList lights, GlMaterial.BindUnits lastBindUnits) { Renderer renderer = renderManager.getRenderer(); renderer.setShader(shader); - renderMeshFromGeometry(renderer, geometry); + renderMeshFromGeometry(renderer, geometry, mesh, mode); } } diff --git a/jme3-core/src/main/java/com/jme3/material/logic/MultiPassLightingLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/MultiPassLightingLogic.java index 9340d3560d..f04c59a03f 100644 --- a/jme3-core/src/main/java/com/jme3/material/logic/MultiPassLightingLogic.java +++ b/jme3-core/src/main/java/com/jme3/material/logic/MultiPassLightingLogic.java @@ -36,9 +36,9 @@ import com.jme3.light.LightList; import com.jme3.light.PointLight; import com.jme3.light.SpotLight; +import com.jme3.material.GlMaterial; import com.jme3.material.RenderState; import com.jme3.material.TechniqueDef; -import com.jme3.material.Material.BindUnits; import com.jme3.math.ColorRGBA; import com.jme3.math.Quaternion; import com.jme3.math.Vector3f; @@ -46,6 +46,7 @@ import com.jme3.renderer.RenderManager; import com.jme3.renderer.Renderer; import com.jme3.scene.Geometry; +import com.jme3.scene.GlMesh; import com.jme3.shader.Shader; import com.jme3.shader.Uniform; import com.jme3.shader.VarType; @@ -68,7 +69,7 @@ public MultiPassLightingLogic(TechniqueDef techniqueDef) { } @Override - public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, BindUnits lastBindUnits) { + public void render(RenderManager renderManager, Shader shader, Geometry geometry, GlMesh mesh, LightList lights, GlMaterial.BindUnits lastBindUnits) { Renderer r = renderManager.getRenderer(); Uniform lightDir = shader.getUniform("g_LightDirection"); Uniform lightColor = shader.getUniform("g_LightColor"); @@ -157,7 +158,7 @@ public void render(RenderManager renderManager, Shader shader, Geometry geometry } vars.release(); r.setShader(shader); - renderMeshFromGeometry(r, geometry); + renderMeshFromGeometry(r, geometry, mesh, mode); } if (isFirstLight) { @@ -167,7 +168,7 @@ public void render(RenderManager renderManager, Shader shader, Geometry geometry lightColor.setValue(VarType.Vector4, ColorRGBA.BlackNoAlpha); lightPos.setValue(VarType.Vector4, NULL_DIR_LIGHT); r.setShader(shader); - renderMeshFromGeometry(r, geometry); + renderMeshFromGeometry(r, geometry, mesh, mode); } } } diff --git a/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java index 8e38f2e6ca..55a8599818 100644 --- a/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java +++ b/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java @@ -34,11 +34,11 @@ import com.jme3.asset.AssetManager; import com.jme3.light.*; import com.jme3.material.*; -import com.jme3.material.Material.BindUnits; import com.jme3.material.RenderState.BlendMode; import com.jme3.math.*; import com.jme3.renderer.*; import com.jme3.scene.Geometry; +import com.jme3.scene.GlMesh; import com.jme3.shader.*; import com.jme3.texture.TextureCubeMap; import com.jme3.util.TempVars; @@ -108,7 +108,6 @@ public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager * attenuation)

* * @param shader the Shader being used - * @param g the Geometry being rendered * @param lightList the list of lights * @param numLights the number of lights to upload * @param rm to manage rendering @@ -116,7 +115,7 @@ public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager * @param lastTexUnit the index of the most recently-used texture unit * @return the next starting index in the LightList */ - protected int updateLightListUniforms(Shader shader, Geometry g, LightList lightList, int numLights, RenderManager rm, int startIndex, int lastTexUnit) { + private int updateLightListUniforms(Shader shader, LightList lightList, int numLights, RenderManager rm, int startIndex, int lastTexUnit) { if (numLights == 0) { // this shader does not do lighting, ignore. return 0; } @@ -263,24 +262,24 @@ private int setProbeData(RenderManager rm, int lastTexUnit, Uniform lightProbeDa } @Override - public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, BindUnits lastBindUnits) { + public void render(RenderManager renderManager, Shader shader, Geometry geometry, GlMesh mesh, LightList lights, GlMaterial.BindUnits lastBindUnits) { int nbRenderedLights = 0; Renderer renderer = renderManager.getRenderer(); int batchSize = renderManager.getSinglePassLightBatchSize(); if (lights.size() == 0) { - updateLightListUniforms(shader, geometry, lights, batchSize, renderManager, 0, lastBindUnits.textureUnit); + updateLightListUniforms(shader, lights, batchSize, renderManager, 0, lastBindUnits.textureUnit); renderer.setShader(shader); - renderMeshFromGeometry(renderer, geometry); + renderMeshFromGeometry(renderer, geometry, mesh, mode); } else { while (nbRenderedLights < lights.size()) { - nbRenderedLights = updateLightListUniforms(shader, geometry, lights, batchSize, renderManager, nbRenderedLights, lastBindUnits.textureUnit); + nbRenderedLights = updateLightListUniforms(shader, lights, batchSize, renderManager, nbRenderedLights, lastBindUnits.textureUnit); renderer.setShader(shader); - renderMeshFromGeometry(renderer, geometry); + renderMeshFromGeometry(renderer, geometry, mesh, mode); } } } - protected void extractIndirectLights(LightList lightList, boolean removeLights) { + private void extractIndirectLights(LightList lightList, boolean removeLights) { ambientLightColor.set(0, 0, 0, 1); useAmbientLight = false; for (int j = 0; j < lightList.size(); j++) { diff --git a/jme3-core/src/main/java/com/jme3/material/logic/SinglePassLightingLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/SinglePassLightingLogic.java index 58240569ef..bb627a1a36 100644 --- a/jme3-core/src/main/java/com/jme3/material/logic/SinglePassLightingLogic.java +++ b/jme3-core/src/main/java/com/jme3/material/logic/SinglePassLightingLogic.java @@ -37,10 +37,10 @@ import com.jme3.light.LightList; import com.jme3.light.PointLight; import com.jme3.light.SpotLight; +import com.jme3.material.GlMaterial; import com.jme3.material.RenderState; import com.jme3.material.RenderState.BlendMode; import com.jme3.material.TechniqueDef; -import com.jme3.material.Material.BindUnits; import com.jme3.math.ColorRGBA; import com.jme3.math.Vector3f; import com.jme3.math.Vector4f; @@ -48,6 +48,7 @@ import com.jme3.renderer.RenderManager; import com.jme3.renderer.Renderer; import com.jme3.scene.Geometry; +import com.jme3.scene.GlMesh; import com.jme3.shader.DefineList; import com.jme3.shader.Shader; import com.jme3.shader.Uniform; @@ -99,14 +100,13 @@ public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager * attenuation)

* * @param shader the Shader being used - * @param g the Geometry being rendered * @param lightList the list of lights * @param numLights the number of lights to upload * @param rm to manage rendering * @param startIndex the starting index in the LightList * @return the next starting index in the LightList */ - protected int updateLightListUniforms(Shader shader, Geometry g, LightList lightList, int numLights, RenderManager rm, int startIndex) { + private int updateLightListUniforms(Shader shader, LightList lightList, int numLights, RenderManager rm, int startIndex) { if (numLights == 0) { // this shader does not do lighting, ignore. return 0; } @@ -207,19 +207,19 @@ protected int updateLightListUniforms(Shader shader, Geometry g, LightList light } @Override - public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, BindUnits lastBindUnits) { + public void render(RenderManager renderManager, Shader shader, Geometry geometry, GlMesh mesh, LightList lights, GlMaterial.BindUnits lastBindUnits) { int nbRenderedLights = 0; Renderer renderer = renderManager.getRenderer(); int batchSize = renderManager.getSinglePassLightBatchSize(); if (lights.size() == 0) { - updateLightListUniforms(shader, geometry, lights, batchSize, renderManager, 0); + updateLightListUniforms(shader, lights, batchSize, renderManager, 0); renderer.setShader(shader); - renderMeshFromGeometry(renderer, geometry); + renderMeshFromGeometry(renderer, geometry, mesh, mode); } else { while (nbRenderedLights < lights.size()) { - nbRenderedLights = updateLightListUniforms(shader, geometry, lights, batchSize, renderManager, nbRenderedLights); + nbRenderedLights = updateLightListUniforms(shader, lights, batchSize, renderManager, nbRenderedLights); renderer.setShader(shader); - renderMeshFromGeometry(renderer, geometry); + renderMeshFromGeometry(renderer, geometry, mesh, mode); } } } diff --git a/jme3-core/src/main/java/com/jme3/material/logic/StaticPassLightingLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/StaticPassLightingLogic.java index fe35dc4c4d..e0e9051d7e 100644 --- a/jme3-core/src/main/java/com/jme3/material/logic/StaticPassLightingLogic.java +++ b/jme3-core/src/main/java/com/jme3/material/logic/StaticPassLightingLogic.java @@ -37,8 +37,8 @@ import com.jme3.light.LightList; import com.jme3.light.PointLight; import com.jme3.light.SpotLight; +import com.jme3.material.GlMaterial; import com.jme3.material.TechniqueDef; -import com.jme3.material.Material.BindUnits; import com.jme3.math.ColorRGBA; import com.jme3.math.Matrix4f; import com.jme3.math.Vector3f; @@ -46,6 +46,7 @@ import com.jme3.renderer.RenderManager; import com.jme3.renderer.Renderer; import com.jme3.scene.Geometry; +import com.jme3.scene.GlMesh; import com.jme3.shader.DefineList; import com.jme3.shader.Shader; import com.jme3.shader.Uniform; @@ -172,12 +173,12 @@ private void updateLightListUniforms(Matrix4f viewMatrix, Shader shader, LightLi } @Override - public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, BindUnits lastBindUnits) { + public void render(RenderManager renderManager, Shader shader, Geometry geometry, GlMesh mesh, LightList lights, GlMaterial.BindUnits lastBindUnits) { Renderer renderer = renderManager.getRenderer(); Matrix4f viewMatrix = renderManager.getCurrentCamera().getViewMatrix(); updateLightListUniforms(viewMatrix, shader, lights); renderer.setShader(shader); - renderMeshFromGeometry(renderer, geometry); + renderMeshFromGeometry(renderer, geometry, mesh, mode); } } diff --git a/jme3-core/src/main/java/com/jme3/material/logic/TechniqueDefLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/TechniqueDefLogic.java index 31f970a176..dc544278ac 100644 --- a/jme3-core/src/main/java/com/jme3/material/logic/TechniqueDefLogic.java +++ b/jme3-core/src/main/java/com/jme3/material/logic/TechniqueDefLogic.java @@ -33,14 +33,15 @@ import com.jme3.asset.AssetManager; import com.jme3.light.LightList; -import com.jme3.material.Material.BindUnits; +import com.jme3.material.GlMaterial; import com.jme3.renderer.Caps; import com.jme3.renderer.RenderManager; import com.jme3.scene.Geometry; +import com.jme3.scene.GlMesh; import com.jme3.shader.DefineList; import com.jme3.shader.Shader; import com.jme3.shader.Uniform; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import java.util.EnumSet; /** @@ -73,25 +74,26 @@ public interface TechniqueDefLogic { * * @return The shader to use for rendering. */ - public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager, + Shader makeCurrent(AssetManager assetManager, RenderManager renderManager, EnumSet rendererCaps, LightList lights, DefineList defines); /** * Requests that the TechniqueDefLogic renders the given geometry. - * - * Fixed material functionality such as {@link com.jme3.material.RenderState}, - * {@link com.jme3.material.MatParam material parameters}, and + *

+ * Fixed material functionality such as {@link com.jme3.material.RenderState}, + * {@link com.jme3.material.MatParam material parameters}, and * {@link com.jme3.shader.UniformBinding uniform bindings} - * have already been applied by the material, however, - * {@link com.jme3.material.RenderState}, {@link Uniform uniforms}, {@link Texture textures}, + * have already been applied by the material, however, + * {@link com.jme3.material.RenderState}, {@link Uniform uniforms}, {@link GlTexture textures}, * can still be overridden. - * + * * @param renderManager The render manager to perform the rendering against. - * @param shader The shader that was selected by this logic in - * {@link #makeCurrent(com.jme3.asset.AssetManager, com.jme3.renderer.RenderManager, java.util.EnumSet, com.jme3.light.LightList, com.jme3.shader.DefineList)}. - * @param geometry The geometry to render - * @param lights Lights which influence the geometry. - * @param lastTexUnit the index of the most recently used texture unit + * @param shader The shader that was selected by this logic in + * {@link #makeCurrent(AssetManager, RenderManager, EnumSet, LightList, DefineList)}. + * @param geometry The geometry to render + * @param lights Lights which influence the geometry. + * @param lastBindUnits the index of the most recently used texture unit */ - public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, BindUnits lastBindUnits); + void render(RenderManager renderManager, Shader shader, Geometry geometry, GlMesh mesh, + LightList lights, GlMaterial.BindUnits lastBindUnits); } diff --git a/jme3-core/src/main/java/com/jme3/math/ColorRGBA.java b/jme3-core/src/main/java/com/jme3/math/ColorRGBA.java index 03cde0991d..a32d078f83 100644 --- a/jme3-core/src/main/java/com/jme3/math/ColorRGBA.java +++ b/jme3-core/src/main/java/com/jme3/math/ColorRGBA.java @@ -31,7 +31,10 @@ package com.jme3.math; import com.jme3.export.*; +import org.lwjgl.system.MemoryStack; + import java.io.IOException; +import java.nio.FloatBuffer; /** * ColorRGBA defines a color made from a collection of red, green @@ -188,7 +191,7 @@ public ColorRGBA(Vector3f vec3) { this.r = vec3.x; this.g = vec3.y; this.b = vec3.z; - } + } /** * set sets the RGBA values of this ColorRGBA. @@ -825,4 +828,20 @@ public ColorRGBA getAsSrgb() { srgb.a = a; return srgb; } + + /** + * Creates a new FloatBuffer under the MemoryStack containing + * 4 floats for R, G, B, and A. + * + * @param stack stack to allocate from + * @return FloatBuffer containing the color data + */ + public FloatBuffer toBuffer(MemoryStack stack) { + return stack.floats(r, g, b, a); + } + + public static ColorRGBA storage(ColorRGBA store) { + return store != null ? store : new ColorRGBA(); + } + } diff --git a/jme3-core/src/main/java/com/jme3/math/Matrix3f.java b/jme3-core/src/main/java/com/jme3/math/Matrix3f.java index 5b4d6d2634..8c1684d36e 100644 --- a/jme3-core/src/main/java/com/jme3/math/Matrix3f.java +++ b/jme3-core/src/main/java/com/jme3/math/Matrix3f.java @@ -34,7 +34,10 @@ import com.jme3.export.*; import com.jme3.util.BufferUtils; import com.jme3.util.TempVars; +import com.jme3.vulkan.buffers.BufferMember; + import java.io.IOException; +import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.util.logging.Logger; @@ -55,7 +58,7 @@ * @author Mark Powell * @author Joshua Slack */ -public final class Matrix3f implements Savable, Cloneable, java.io.Serializable { +public final class Matrix3f implements BufferMember, Savable, Cloneable, java.io.Serializable { static final long serialVersionUID = 1; @@ -1500,4 +1503,17 @@ public Matrix3f clone() { throw new AssertionError(); // can not happen } } + + @Override + public void fillBuffer(ByteBuffer buffer) { + buffer.putFloat(m00).putFloat(m10).putFloat(m20) + .putFloat(m01).putFloat(m11).putFloat(m21) + .putFloat(m02).putFloat(m12).putFloat(m22); + } + + @Override + public int getSizeInBytes() { + return 9 * Float.BYTES; + } + } diff --git a/jme3-core/src/main/java/com/jme3/math/Matrix4f.java b/jme3-core/src/main/java/com/jme3/math/Matrix4f.java index 4f9a6e2f35..e097de818b 100644 --- a/jme3-core/src/main/java/com/jme3/math/Matrix4f.java +++ b/jme3-core/src/main/java/com/jme3/math/Matrix4f.java @@ -2560,4 +2560,16 @@ public Matrix4f clone() { throw new AssertionError(); // can not happen } } + + /** + * Flips the sign of the Y scalar component (m11) to make Vulkan renderings + * appear upright in clip space. + * + * @return this instance + */ + public Matrix4f flipYScalarForVulkan() { + m11 = -m11; + return this; + } + } diff --git a/jme3-core/src/main/java/com/jme3/math/Vector2f.java b/jme3-core/src/main/java/com/jme3/math/Vector2f.java index e32871d75b..19ed8197f1 100644 --- a/jme3-core/src/main/java/com/jme3/math/Vector2f.java +++ b/jme3-core/src/main/java/com/jme3/math/Vector2f.java @@ -906,4 +906,9 @@ public void rotateAroundOrigin(float angle, boolean cw) { x = newX; y = newY; } + + public static Vector2f storage(Vector2f store) { + return store != null ? store : new Vector2f(); + } + } diff --git a/jme3-core/src/main/java/com/jme3/math/Vector3f.java b/jme3-core/src/main/java/com/jme3/math/Vector3f.java index fa5a8539cd..4ca951711d 100644 --- a/jme3-core/src/main/java/com/jme3/math/Vector3f.java +++ b/jme3-core/src/main/java/com/jme3/math/Vector3f.java @@ -1223,4 +1223,9 @@ public void set(int index, float value) { } throw new IllegalArgumentException("index must be either 0, 1 or 2"); } + + public static Vector3f storage(Vector3f store) { + return store != null ? store : new Vector3f(); + } + } diff --git a/jme3-core/src/main/java/com/jme3/math/Vector4f.java b/jme3-core/src/main/java/com/jme3/math/Vector4f.java index cf18c4be50..d88794a411 100644 --- a/jme3-core/src/main/java/com/jme3/math/Vector4f.java +++ b/jme3-core/src/main/java/com/jme3/math/Vector4f.java @@ -143,6 +143,19 @@ public Vector4f(Vector4f copy) { this.set(copy); } + public ColorRGBA toColor() { + return toColor(null); + } + + public ColorRGBA toColor(ColorRGBA store) { + store = ColorRGBA.storage(store); + store.r = x; + store.g = y; + store.b = z; + store.a = w; + return store; + } + /** * set sets the x,y,z,w values of the vector based on passed * parameters. @@ -1178,4 +1191,9 @@ public void set(int index, float value) { } throw new IllegalArgumentException("index must be either 0, 1, 2 or 3"); } + + public static Vector4f storage(Vector4f store) { + return store != null ? store : new Vector4f(); + } + } diff --git a/jme3-core/src/main/java/com/jme3/opencl/Buffer.java b/jme3-core/src/main/java/com/jme3/opencl/Buffer.java index bbe9a8c1c9..f5a64118bf 100644 --- a/jme3-core/src/main/java/com/jme3/opencl/Buffer.java +++ b/jme3-core/src/main/java/com/jme3/opencl/Buffer.java @@ -31,6 +31,8 @@ */ package com.jme3.opencl; +import com.jme3.scene.GlVertexBuffer; + import java.nio.ByteBuffer; /** @@ -454,7 +456,7 @@ public ByteBuffer getBuffer() { /** * Acquires this buffer object for using. Only call this method if this buffer * represents a shared object from OpenGL, created with e.g. - * {@link Context#bindVertexBuffer(com.jme3.scene.VertexBuffer, com.jme3.opencl.MemoryAccess) }. + * {@link Context#bindVertexBuffer(GlVertexBuffer, com.jme3.opencl.MemoryAccess) }. * This method must be called before the buffer is used. After the work is * done, the buffer must be released by calling * {@link #releaseBufferForSharingAsync(com.jme3.opencl.CommandQueue) } @@ -468,7 +470,7 @@ public ByteBuffer getBuffer() { /** * Acquires this buffer object for using. Only call this method if this buffer * represents a shared object from OpenGL, created with e.g. - * {@link Context#bindVertexBuffer(com.jme3.scene.VertexBuffer, com.jme3.opencl.MemoryAccess) }. + * {@link Context#bindVertexBuffer(GlVertexBuffer, com.jme3.opencl.MemoryAccess) }. * This method must be called before the buffer is used. After the work is * done, the buffer must be released by calling * {@link #releaseBufferForSharingAsync(com.jme3.opencl.CommandQueue) } diff --git a/jme3-core/src/main/java/com/jme3/opencl/Context.java b/jme3-core/src/main/java/com/jme3/opencl/Context.java index e25f893e24..33b392e121 100644 --- a/jme3-core/src/main/java/com/jme3/opencl/Context.java +++ b/jme3-core/src/main/java/com/jme3/opencl/Context.java @@ -38,9 +38,10 @@ import com.jme3.opencl.Image.ImageDescriptor; import com.jme3.opencl.Image.ImageFormat; import com.jme3.opencl.Image.ImageType; -import com.jme3.scene.VertexBuffer; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Texture; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -206,7 +207,7 @@ public Buffer createBufferFromHost(ByteBuffer data) { * @param access the memory access for the kernel * @return the new buffer */ - public abstract Buffer bindVertexBuffer(VertexBuffer vb, MemoryAccess access); + public abstract Buffer bindVertexBuffer(GlVertexBuffer vb, MemoryAccess access); /** * Creates a shared image object from a jME3-image. @@ -229,7 +230,7 @@ public Buffer createBufferFromHost(ByteBuffer data) { * @param access the allowed memory access for kernels * @return the OpenCL image */ - public abstract Image bindImage(com.jme3.texture.Image image, Texture.Type textureType, int miplevel, MemoryAccess access); + public abstract Image bindImage(GlImage image, GlTexture.Type textureType, int miplevel, MemoryAccess access); /** * Creates a shared image object from a jME3 texture. @@ -254,19 +255,19 @@ public Buffer createBufferFromHost(ByteBuffer data) { * @param access the allowed memory access for kernels * @return the OpenCL image */ - public Image bindImage(Texture texture, int miplevel, MemoryAccess access) { + public Image bindImage(GlTexture texture, int miplevel, MemoryAccess access) { return bindImage(texture.getImage(), texture.getType(), miplevel, access); } /** - * Alternative version to {@link #bindImage(com.jme3.texture.Texture, int, com.jme3.opencl.MemoryAccess) }, + * Alternative version to {@link #bindImage(GlTexture, int, com.jme3.opencl.MemoryAccess) }, * uses {@code miplevel=0}. * * @param texture the jME3 texture * @param access the allowed memory access for kernels * @return the OpenCL image */ - public Image bindImage(Texture texture, MemoryAccess access) { + public Image bindImage(GlTexture texture, MemoryAccess access) { return bindImage(texture, 0, access); } @@ -290,7 +291,7 @@ public Image bindImage(Texture texture, MemoryAccess access) { * @param access the kernel access permissions * @return an image */ - public Image bindRenderBuffer(FrameBuffer.RenderBuffer buffer, MemoryAccess access) { + public Image bindRenderBuffer(GlFrameBuffer.RenderBuffer buffer, MemoryAccess access) { if (buffer.getTexture() == null) { return bindPureRenderBuffer(buffer, access); } else { @@ -298,7 +299,7 @@ public Image bindRenderBuffer(FrameBuffer.RenderBuffer buffer, MemoryAccess acce } } - protected abstract Image bindPureRenderBuffer(FrameBuffer.RenderBuffer buffer, MemoryAccess access); + protected abstract Image bindPureRenderBuffer(GlFrameBuffer.RenderBuffer buffer, MemoryAccess access); /** * Creates a program object from the provided source code. diff --git a/jme3-core/src/main/java/com/jme3/opencl/Image.java b/jme3-core/src/main/java/com/jme3/opencl/Image.java index 5b07d0f376..026fa516f5 100644 --- a/jme3-core/src/main/java/com/jme3/opencl/Image.java +++ b/jme3-core/src/main/java/com/jme3/opencl/Image.java @@ -32,6 +32,9 @@ package com.jme3.opencl; import com.jme3.math.ColorRGBA; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; + import java.nio.ByteBuffer; import java.util.Objects; @@ -48,7 +51,7 @@ * An image is created from scratch using * {@link Context#createImage(com.jme3.opencl.MemoryAccess, com.jme3.opencl.Image.ImageFormat, com.jme3.opencl.Image.ImageDescriptor) } * or from OpenGL by - * {@link Context#bindImage(com.jme3.texture.Image, com.jme3.texture.Texture.Type, int, com.jme3.opencl.MemoryAccess) } + * {@link Context#bindImage(GlImage, GlTexture.Type, int, com.jme3.opencl.MemoryAccess) } * (and alternative versions). * *

@@ -504,7 +507,7 @@ public ImageMapping(ByteBuffer buffer, long rowPitch, long slicePitch) { /** * Acquires this image object for using. Only call this method if this image * represents a shared object from OpenGL, created with e.g. - * {@link Context#bindImage(com.jme3.texture.Image, com.jme3.texture.Texture.Type, int, com.jme3.opencl.MemoryAccess) } + * {@link Context#bindImage(GlImage, GlTexture.Type, int, com.jme3.opencl.MemoryAccess) } * or variations. * This method must be called before the image is used. After the work is * done, the image must be released by calling @@ -519,7 +522,7 @@ public ImageMapping(ByteBuffer buffer, long rowPitch, long slicePitch) { /** * Acquires this image object for using. Only call this method if this image * represents a shared object from OpenGL, created with e.g. - * {@link Context#bindImage(com.jme3.texture.Image, com.jme3.texture.Texture.Type, int, com.jme3.opencl.MemoryAccess) } + * {@link Context#bindImage(GlImage, GlTexture.Type, int, com.jme3.opencl.MemoryAccess) } * or variations. * This method must be called before the image is used. After the work is * done, the image must be released by calling diff --git a/jme3-core/src/main/java/com/jme3/opencl/package-info.java b/jme3-core/src/main/java/com/jme3/opencl/package-info.java index 0a19e3a24a..bc9e1269fa 100644 --- a/jme3-core/src/main/java/com/jme3/opencl/package-info.java +++ b/jme3-core/src/main/java/com/jme3/opencl/package-info.java @@ -103,14 +103,14 @@ *

* Interoperability between OpenCL and jME3:
* This Wrapper allows sharing jME3 Images and VertexBuffers with OpenCL.
- * {@link com.jme3.scene.VertexBuffer} objects can be shared with OpenCL - * by calling {@link com.jme3.opencl.Context#bindVertexBuffer(com.jme3.scene.VertexBuffer, com.jme3.opencl.MemoryAccess) } + * {@link com.jme3.scene.GlVertexBuffer} objects can be shared with OpenCL + * by calling {@link com.jme3.opencl.Context#bindVertexBuffer(GlVertexBuffer, com.jme3.opencl.MemoryAccess) } * resulting in a {@link com.jme3.opencl.Buffer} object. This buffer object * can then be used as usual, allowing e.g. the dynamic modification of position buffers for particle systems.
- * {@link com.jme3.texture.Image} and {@link com.jme3.texture.Texture} objects can be used in OpenCL with the method - * {@link com.jme3.opencl.Context#bindImage(com.jme3.texture.Texture, com.jme3.opencl.MemoryAccess) } - * or variations of this method. The same holds for {@link com.jme3.texture.FrameBuffer.RenderBuffer} objects - * using {@link com.jme3.opencl.Context#bindRenderBuffer(com.jme3.texture.FrameBuffer.RenderBuffer, com.jme3.opencl.MemoryAccess) }. + * {@link com.jme3.texture.GlImage} and {@link com.jme3.texture.GlTexture} objects can be used in OpenCL with the method + * {@link com.jme3.opencl.Context#bindImage(GlTexture, com.jme3.opencl.MemoryAccess) } + * or variations of this method. The same holds for {@link com.jme3.texture.GlFrameBuffer.RenderBuffer} objects + * using {@link com.jme3.opencl.Context#bindRenderBuffer(GlFrameBuffer.RenderBuffer, com.jme3.opencl.MemoryAccess) }. * These methods result in an OpenCL-Image. Usages are e.g. animated textures, * terrain based on height maps, post-processing effects, and so forth. *
@@ -157,3 +157,7 @@ package com.jme3.opencl; //TODO: add profiling to Kernel, CommandQueue + +import com.jme3.scene.GlVertexBuffer; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlTexture; \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/post/Filter.java b/jme3-core/src/main/java/com/jme3/post/Filter.java index efdb19b04a..eac188fce9 100644 --- a/jme3-core/src/main/java/com/jme3/post/Filter.java +++ b/jme3-core/src/main/java/com/jme3/post/Filter.java @@ -39,11 +39,11 @@ import com.jme3.renderer.Renderer; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; -import com.jme3.texture.FrameBuffer.FrameBufferTarget; +import com.jme3.texture.GlFrameBuffer.FrameBufferTarget; import java.io.IOException; import java.util.Collection; import java.util.Iterator; @@ -81,7 +81,7 @@ public Filter(String name) { */ public class Pass { - protected FrameBuffer renderFrameBuffer; + protected GlFrameBuffer renderFrameBuffer; protected Texture2D renderedTexture; protected Texture2D depthTexture; protected Material passMaterial; @@ -108,7 +108,7 @@ public Pass() { public void init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat, int numSamples, boolean renderDepth) { Collection caps = renderer.getCaps(); if (numSamples > 1 && caps.contains(Caps.FrameBufferMultisample) && caps.contains(Caps.OpenGL31)) { - renderFrameBuffer = new FrameBuffer(width, height, numSamples); + renderFrameBuffer = new GlFrameBuffer(width, height, numSamples); renderedTexture = new Texture2D(width, height, numSamples, textureFormat); renderFrameBuffer.setDepthTarget(FrameBufferTarget.newTarget(depthBufferFormat)); if (renderDepth) { @@ -116,7 +116,7 @@ public void init(Renderer renderer, int width, int height, Format textureFormat, renderFrameBuffer.setDepthTarget(FrameBufferTarget.newTarget(depthTexture)); } } else { - renderFrameBuffer = new FrameBuffer(width, height, 1); + renderFrameBuffer = new GlFrameBuffer(width, height, 1); renderedTexture = new Texture2D(width, height, textureFormat); renderFrameBuffer.setDepthTarget(FrameBufferTarget.newTarget(depthBufferFormat)); if (renderDepth) { @@ -174,11 +174,11 @@ public boolean requiresDepthAsTexture() { public void beforeRender() { } - public FrameBuffer getRenderFrameBuffer() { + public GlFrameBuffer getRenderFrameBuffer() { return renderFrameBuffer; } - public void setRenderFrameBuffer(FrameBuffer renderFrameBuffer) { + public void setRenderFrameBuffer(GlFrameBuffer renderFrameBuffer) { this.renderFrameBuffer = renderFrameBuffer; } @@ -306,7 +306,7 @@ protected void cleanUpFilter(Renderer r) { * * @param depthTexture the desired Texture */ - protected void setDepthTexture(Texture depthTexture){ + protected void setDepthTexture(GlTexture depthTexture){ getMaterial().setTexture("DepthTexture", depthTexture); } @@ -335,7 +335,7 @@ protected void preFrame(float tpf) { * @param prevFilterBuffer the FrameBuffer of the previous filter * @param sceneBuffer the FrameBuffer of the scene */ - protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) { + protected void postFrame(RenderManager renderManager, ViewPort viewPort, GlFrameBuffer prevFilterBuffer, GlFrameBuffer sceneBuffer) { } /** @@ -385,7 +385,7 @@ public void setName(String name) { * returns the default pass frame buffer * @return the pre-existing buffer */ - protected FrameBuffer getRenderFrameBuffer() { + protected GlFrameBuffer getRenderFrameBuffer() { return defaultPass.renderFrameBuffer; } @@ -394,7 +394,7 @@ protected FrameBuffer getRenderFrameBuffer() { * * @param renderFrameBuffer the buffer to use (alias created) */ - protected void setRenderFrameBuffer(FrameBuffer renderFrameBuffer) { + protected void setRenderFrameBuffer(GlFrameBuffer renderFrameBuffer) { this.defaultPass.renderFrameBuffer = renderFrameBuffer; } @@ -490,6 +490,6 @@ protected void setProcessor(FilterPostProcessor proc) { * @param r the renderer * @param buffer the framebuffer on which the filter has been rendered. */ - protected void postFilter(Renderer r, FrameBuffer buffer){ + protected void postFilter(Renderer r, GlFrameBuffer buffer){ } } diff --git a/jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java b/jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java index aa8896a951..8bc98c041a 100644 --- a/jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java +++ b/jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java @@ -37,11 +37,11 @@ import com.jme3.profile.*; import com.jme3.renderer.*; import com.jme3.renderer.queue.RenderQueue; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; -import com.jme3.texture.FrameBuffer.FrameBufferTarget; +import com.jme3.texture.GlFrameBuffer.FrameBufferTarget; import com.jme3.ui.Picture; import com.jme3.util.SafeArrayList; import java.io.IOException; @@ -62,16 +62,16 @@ public class FilterPostProcessor implements SceneProcessor, Savable { private RenderManager renderManager; private Renderer renderer; private ViewPort viewPort; - private FrameBuffer renderFrameBufferMS; + private GlFrameBuffer renderFrameBufferMS; private int numSamples = 1; - private FrameBuffer renderFrameBuffer; + private GlFrameBuffer renderFrameBuffer; private Texture2D filterTexture; private Texture2D depthTexture; private SafeArrayList filters = new SafeArrayList<>(Filter.class); private AssetManager assetManager; private Picture fsQuad; private boolean computeDepth = false; - private FrameBuffer outputBuffer; + private GlFrameBuffer outputBuffer; private int width; private int height; private float bottom; @@ -201,15 +201,11 @@ private void initFilter(Filter filter, ViewPort vp) { * @param buff * @param mat */ - private void renderProcessing(Renderer r, FrameBuffer buff, Material mat) { + private void renderProcessing(Renderer r, GlFrameBuffer buff, Material mat) { if (buff == outputBuffer) { viewPort.getCamera().resize(originalWidth, originalHeight, false); viewPort.getCamera().setViewPort(left, right, bottom, top); - // update is redundant because resize and setViewPort will both - // run the appropriate (and same) onXXXChange methods. - // Also, update() updates some things that don't need to be updated. - //viewPort.getCamera().update(); - renderManager.setCamera( viewPort.getCamera(), false); + renderManager.setCamera(viewPort.getCamera(), false); if (mat.getAdditionalRenderState().isDepthWrite()) { mat.getAdditionalRenderState().setDepthTest(false); mat.getAdditionalRenderState().setDepthWrite(false); @@ -217,16 +213,11 @@ private void renderProcessing(Renderer r, FrameBuffer buff, Material mat) { } else { viewPort.getCamera().resize(buff.getWidth(), buff.getHeight(), false); viewPort.getCamera().setViewPort(0, 1, 0, 1); - // update is redundant because resize and setViewPort will both - // run the appropriate (and same) onXXXChange methods. - // Also, update() updates some things that don't need to be updated. - //viewPort.getCamera().update(); - renderManager.setCamera( viewPort.getCamera(), false); + renderManager.setCamera(viewPort.getCamera(), false); mat.getAdditionalRenderState().setDepthTest(true); mat.getAdditionalRenderState().setDepthWrite(true); } - fsQuad.setMaterial(mat); fsQuad.updateGeometricState(); @@ -255,9 +246,9 @@ public void postQueue(RenderQueue rq) { * @param r * @param sceneFb */ - private void renderFilterChain(Renderer r, FrameBuffer sceneFb) { + private void renderFilterChain(Renderer r, GlFrameBuffer sceneFb) { Texture2D tex = filterTexture; - FrameBuffer buff = sceneFb; + GlFrameBuffer buff = sceneFb; boolean msDepth = depthTexture != null && depthTexture.getImage().getMultiSamples() > 1; for (int i = 0; i < filters.size(); i++) { Filter filter = filters.get(i); @@ -307,8 +298,8 @@ private void renderFilterChain(Renderer r, FrameBuffer sceneFb) { boolean wantsBilinear = filter.isRequiresBilinear(); if (wantsBilinear) { - tex.setMagFilter(Texture.MagFilter.Bilinear); - tex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); + tex.setMagFilter(GlTexture.MagFilter.Bilinear); + tex.setMinFilter(GlTexture.MinFilter.BilinearNoMipMaps); } buff = outputBuffer; @@ -323,17 +314,17 @@ private void renderFilterChain(Renderer r, FrameBuffer sceneFb) { filter.postFilter(r, buff); if (wantsBilinear) { - tex.setMagFilter(Texture.MagFilter.Nearest); - tex.setMinFilter(Texture.MinFilter.NearestNoMipMaps); + tex.setMagFilter(GlTexture.MagFilter.Nearest); + tex.setMinFilter(GlTexture.MinFilter.NearestNoMipMaps); } } } } @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { - FrameBuffer sceneBuffer = renderFrameBuffer; + GlFrameBuffer sceneBuffer = renderFrameBuffer; if (renderFrameBufferMS != null && !renderer.getCaps().contains(Caps.OpenGL32)) { renderer.copyFrameBuffer(renderFrameBufferMS, renderFrameBuffer, true, true); } else if (renderFrameBufferMS != null) { @@ -487,7 +478,7 @@ public void reshape(ViewPort vp, int w, int h) { //antialiasing on filters only supported in opengl 3 due to depth read problem if (numSamples > 1 && caps.contains(Caps.FrameBufferMultisample)) { - renderFrameBufferMS = new FrameBuffer(width, height, numSamples); + renderFrameBufferMS = new GlFrameBuffer(width, height, numSamples); if (caps.contains(Caps.OpenGL32)) { Texture2D msColor = new Texture2D(width, height, numSamples, fbFormat); Texture2D msDepth = new Texture2D(width, height, numSamples, depthFormat); @@ -502,7 +493,7 @@ public void reshape(ViewPort vp, int w, int h) { } if (numSamples <= 1 || !caps.contains(Caps.OpenGL32) || !caps.contains(Caps.FrameBufferMultisample)) { - renderFrameBuffer = new FrameBuffer(width, height, 1); + renderFrameBuffer = new GlFrameBuffer(width, height, 1); renderFrameBuffer.setDepthTarget(FrameBufferTarget.newTarget(depthFormat)); filterTexture = new Texture2D(width, height, fbFormat); renderFrameBuffer.addColorTarget(FrameBufferTarget.newTarget(filterTexture)); diff --git a/jme3-core/src/main/java/com/jme3/post/HDRRenderer.java b/jme3-core/src/main/java/com/jme3/post/HDRRenderer.java index 857e78dbc3..b629a8daa4 100644 --- a/jme3-core/src/main/java/com/jme3/post/HDRRenderer.java +++ b/jme3-core/src/main/java/com/jme3/post/HDRRenderer.java @@ -37,12 +37,12 @@ import com.jme3.profile.AppProfiler; import com.jme3.renderer.*; import com.jme3.renderer.queue.RenderQueue; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.MagFilter; -import com.jme3.texture.Texture.MinFilter; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.MagFilter; +import com.jme3.texture.GlTexture.MinFilter; import com.jme3.texture.Texture2D; import com.jme3.ui.Picture; import java.util.Collection; @@ -64,15 +64,15 @@ public class HDRRenderer implements SceneProcessor { private static final Logger logger = Logger.getLogger(HDRRenderer.class.getName()); private Camera fbCam = new Camera(1, 1); - private FrameBuffer msFB; + private GlFrameBuffer msFB; - private FrameBuffer mainSceneFB; + private GlFrameBuffer mainSceneFB; private Texture2D mainScene; - private FrameBuffer scene64FB; + private GlFrameBuffer scene64FB; private Texture2D scene64; - private FrameBuffer scene8FB; + private GlFrameBuffer scene8FB; private Texture2D scene8; - private FrameBuffer scene1FB[] = new FrameBuffer[2]; + private GlFrameBuffer scene1FB[] = new GlFrameBuffer[2]; private Texture2D scene1[] = new Texture2D[2]; private Material hdr64; @@ -91,7 +91,7 @@ public class HDRRenderer implements SceneProcessor { private float whiteLevel = 100f; private float throttle = -1; private int maxIterations = -1; - private Image.Format bufFormat = Format.RGB8; + private GlImage.Format bufFormat = Format.RGB8; private MinFilter fbMinFilter = MinFilter.BilinearNoMipMaps; private MagFilter fbMagFilter = MagFilter.Bilinear; @@ -172,7 +172,7 @@ public Picture createDisplayQuad(/*int mode, Texture tex*/) { } private Material createLumShader(int srcW, int srcH, int bufW, int bufH, int mode, - int iters, Texture tex) { + int iters, GlTexture tex) { Material mat = new Material(manager, "Common/MatDefs/Hdr/LogLum.j3md"); Vector2f blockSize = new Vector2f(1f / bufW, 1f / bufH); @@ -218,7 +218,7 @@ private int opposite(int i) { return i == 1 ? 0 : 1; } - private void renderProcessing(Renderer r, FrameBuffer dst, Material mat) { + private void renderProcessing(Renderer r, GlFrameBuffer dst, Material mat) { if (dst == null) { fsQuad.setWidth(mainSceneFB.getWidth()); fsQuad.setHeight(mainSceneFB.getHeight()); @@ -237,7 +237,7 @@ private void renderProcessing(Renderer r, FrameBuffer dst, Material mat) { renderManager.renderGeometry(fsQuad); } - private void renderToneMap(Renderer r, FrameBuffer out) { + private void renderToneMap(Renderer r, GlFrameBuffer out) { tone.setFloat("A", exposure); tone.setFloat("White", whiteLevel); tone.setTexture("Lum", scene1[oppSrc]); @@ -263,7 +263,7 @@ public void reshape(ViewPort vp, int w, int h) { renderer.deleteFrameBuffer(mainSceneFB); } - mainSceneFB = new FrameBuffer(w, h, 1); + mainSceneFB = new GlFrameBuffer(w, h, 1); mainScene = new Texture2D(w, h, bufFormat); mainSceneFB.setDepthBuffer(Format.Depth); mainSceneFB.setColorTexture(mainScene); @@ -278,7 +278,7 @@ public void reshape(ViewPort vp, int w, int h) { Collection caps = renderer.getCaps(); if (numSamples > 1 && caps.contains(Caps.FrameBufferMultisample)){ - msFB = new FrameBuffer(w, h, numSamples); + msFB = new GlFrameBuffer(w, h, numSamples); msFB.setDepthBuffer(Format.Depth); msFB.setColorBuffer(bufFormat); vp.setOutputFrameBuffer(msFB); @@ -305,23 +305,23 @@ public void initialize(RenderManager rm, ViewPort vp) { fsQuad = new Picture("HDR Fullscreen Quad"); Format lumFmt = Format.RGB8; - scene64FB = new FrameBuffer(64, 64, 1); + scene64FB = new GlFrameBuffer(64, 64, 1); scene64 = new Texture2D(64, 64, lumFmt); scene64FB.setColorTexture(scene64); scene64.setMagFilter(fbMagFilter); scene64.setMinFilter(fbMinFilter); - scene8FB = new FrameBuffer(8, 8, 1); + scene8FB = new GlFrameBuffer(8, 8, 1); scene8 = new Texture2D(8, 8, lumFmt); scene8FB.setColorTexture(scene8); scene8.setMagFilter(fbMagFilter); scene8.setMinFilter(fbMinFilter); - scene1FB[0] = new FrameBuffer(1, 1, 1); + scene1FB[0] = new GlFrameBuffer(1, 1, 1); scene1[0] = new Texture2D(1, 1, lumFmt); scene1FB[0].setColorTexture(scene1[0]); - scene1FB[1] = new FrameBuffer(1, 1, 1); + scene1FB[1] = new GlFrameBuffer(1, 1, 1); scene1[1] = new Texture2D(1, 1, lumFmt); scene1FB[1].setColorTexture(scene1[1]); @@ -350,7 +350,7 @@ public void postQueue(RenderQueue rq) { } @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { if (!enabled) return; diff --git a/jme3-core/src/main/java/com/jme3/post/PreDepthProcessor.java b/jme3-core/src/main/java/com/jme3/post/PreDepthProcessor.java index 1a7a7be94f..d51caf8534 100644 --- a/jme3-core/src/main/java/com/jme3/post/PreDepthProcessor.java +++ b/jme3-core/src/main/java/com/jme3/post/PreDepthProcessor.java @@ -39,7 +39,7 @@ import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; -import com.jme3.texture.FrameBuffer; +import com.jme3.texture.GlFrameBuffer; /** * Processor that lays depth first, this can improve performance in complex @@ -93,7 +93,7 @@ public void postQueue(RenderQueue rq) { } @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { rm.setForcedRenderState(null); } diff --git a/jme3-core/src/main/java/com/jme3/post/SceneProcessor.java b/jme3-core/src/main/java/com/jme3/post/SceneProcessor.java index 1fc437b758..9a27ade592 100644 --- a/jme3-core/src/main/java/com/jme3/post/SceneProcessor.java +++ b/jme3-core/src/main/java/com/jme3/post/SceneProcessor.java @@ -35,7 +35,7 @@ import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; -import com.jme3.texture.FrameBuffer; +import com.jme3.texture.GlFrameBuffer; /** * Scene processors are used to compute/render things before and after the classic render of the scene. @@ -98,7 +98,7 @@ public default void rescale(ViewPort vp, float x, float y) { * * @param out The FB to which the scene was rendered. */ - public void postFrame(FrameBuffer out); + public void postFrame(GlFrameBuffer out); /** * Called when the SP is removed from the RM. diff --git a/jme3-core/src/main/java/com/jme3/renderer/Camera.java b/jme3-core/src/main/java/com/jme3/renderer/Camera.java index b05c3bd86a..138919256a 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Camera.java +++ b/jme3-core/src/main/java/com/jme3/renderer/Camera.java @@ -46,10 +46,18 @@ import com.jme3.math.Vector3f; import com.jme3.math.Vector4f; import com.jme3.util.TempVars; +import com.jme3.vulkan.commands.CommandBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkRect2D; +import org.lwjgl.vulkan.VkViewport; + import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; +import static org.lwjgl.vulkan.VK10.vkCmdSetScissor; +import static org.lwjgl.vulkan.VK10.vkCmdSetViewport; + /** * A standalone, purely mathematical class for doing * camera-related computations. @@ -197,6 +205,14 @@ public enum FrustumIntersect { * Default is 0. Must be less than {@code viewPortTop}. */ protected float viewPortBottom; + /** + * Minimum depth this camera will render to. + */ + protected float minDepth = 0f; + /** + * Maximum depth this camera will render to. + */ + protected float maxDepth = 1f; /** * Array holding the planes that this camera will check for culling. */ @@ -323,6 +339,9 @@ public Camera clone() { cam.viewProjectionMatrix = viewProjectionMatrix.clone(); cam.guiBounding = (BoundingBox) guiBounding.clone(); + cam.minDepth = minDepth; + cam.maxDepth = maxDepth; + cam.update(); return cam; @@ -386,6 +405,9 @@ public void copyFrom(Camera cam) { this.guiBounding.setCenter(cam.guiBounding.getCenter()); this.guiBounding.setCheckPlane(cam.guiBounding.getCheckPlane()); + this.minDepth = cam.minDepth; + this.maxDepth = cam.maxDepth; + this.name = cam.name; } @@ -1248,7 +1270,6 @@ public void updateViewProjection() { if (overrideProjection) { viewProjectionMatrix.set(projectionMatrixOverride).multLocal(viewMatrix); } else { - //viewProjectionMatrix.set(viewMatrix).multLocal(projectionMatrix); viewProjectionMatrix.set(projectionMatrix).multLocal(viewMatrix); } } @@ -1358,7 +1379,9 @@ public void onFrustumChange() { projectionMatrix.fromFrustum(frustumNear, frustumFar, frustumLeft, frustumRight, frustumTop, frustumBottom, parallelProjection); -// projectionMatrix.transposeLocal(); + + // for Vulkan rendering + projectionMatrix.flipYScalarForVulkan(); // The frame is affected by the frustum values // update it as well @@ -1588,6 +1611,41 @@ public int getHeight() { return height; } + public float getMaxDepth() { + return maxDepth; + } + + public float getMinDepth() { + return minDepth; + } + + public void setMinDepth(float minDepth) { + this.minDepth = minDepth; + } + + public void setMaxDepth(float maxDepth) { + this.maxDepth = maxDepth; + } + + public void setViewPortAndScissor(MemoryStack stack, CommandBuffer cmd) { + float x = viewPortLeft * width; + float y = viewPortTop * height; + float w = (viewPortRight * width) - x; + float h = (viewPortBottom * height) - y; + VkViewport.Buffer vp = VkViewport.calloc(1, stack) + .x(x).y(y) + .width(w).height(h) + //.width(swapchain.getExtent().getX()) + //.height(swapchain.getExtent().getY()) + .minDepth(minDepth).maxDepth(maxDepth); + vkCmdSetViewport(cmd.getBuffer(), 0, vp); + VkRect2D.Buffer scissor = VkRect2D.calloc(1, stack); + scissor.offset().set((int)x, (int)y); + //scissor.extent(swapchain.getExtent().toStruct(stack)); + scissor.extent().set((int)w, (int)h); + vkCmdSetScissor(cmd.getBuffer(), 0, scissor); + } + @Override public String toString() { return "Camera[location=" + location + "\n, direction=" + getDirection() + "\n" @@ -1617,6 +1675,8 @@ public void write(JmeExporter e) throws IOException { capsule.write(width, "width", 0); capsule.write(height, "height", 0); capsule.write(name, "name", null); + capsule.write(minDepth, "minDepth", 0f); + capsule.write(maxDepth, "maxDepth", 1f); } @Override @@ -1641,6 +1701,8 @@ public void read(JmeImporter importer) throws IOException { width = capsule.readInt("width", 1); height = capsule.readInt("height", 1); name = capsule.readString("name", null); + minDepth = capsule.readFloat("minDepth", 0f); + maxDepth = capsule.readFloat("maxDepth", 1f); onFrustumChange(); onViewPortChange(); onFrameChange(); diff --git a/jme3-core/src/main/java/com/jme3/renderer/Caps.java b/jme3-core/src/main/java/com/jme3/renderer/Caps.java index 647d44a9a6..4a0589ae47 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Caps.java +++ b/jme3-core/src/main/java/com/jme3/renderer/Caps.java @@ -33,11 +33,11 @@ import com.jme3.shader.Shader; import com.jme3.shader.Shader.ShaderSource; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.FrameBuffer.RenderBuffer; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlFrameBuffer.RenderBuffer; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture; import java.util.Collection; /** @@ -49,7 +49,7 @@ public enum Caps { /** - * Supports {@link FrameBuffer FrameBuffers}. + * Supports {@link GlFrameBuffer FrameBuffers}. * *

OpenGL: Renderer exposes the GL_EXT_framebuffer_object extension.
* OpenGL ES: Renderer supports OpenGL ES 2.0. @@ -378,12 +378,12 @@ public enum Caps { * *

Use of NPOT textures is allowed iff: *

    - *
  • The {@link com.jme3.texture.Texture.WrapMode} is set to - * {@link com.jme3.texture.Texture.WrapMode#EdgeClamp}.
  • + *
  • The {@link GlTexture.WrapMode} is set to + * {@link GlTexture.WrapMode#EdgeClamp}.
  • *
  • Mip-mapping is not used, meaning - * {@link com.jme3.texture.Texture.MinFilter} is set to - * {@link com.jme3.texture.Texture.MinFilter#BilinearNoMipMaps} or - * {@link com.jme3.texture.Texture.MinFilter#NearestNoMipMaps}
  • + * {@link GlTexture.MinFilter} is set to + * {@link GlTexture.MinFilter#BilinearNoMipMaps} or + * {@link GlTexture.MinFilter#NearestNoMipMaps} *
*/ PartialNonPowerOfTwoTextures, @@ -474,18 +474,18 @@ public enum Caps { * @param tex The texture to check * @return True if it is supported, false otherwise. */ - public static boolean supports(Collection caps, Texture tex) { - if (tex.getType() == Texture.Type.TwoDimensionalArray + public static boolean supports(Collection caps, GlTexture tex) { + if (tex.getType() == GlTexture.Type.TwoDimensionalArray && !caps.contains(Caps.TextureArray)) { return false; } - Image img = tex.getImage(); + GlImage img = tex.getImage(); if (img == null) { return true; } - Format fmt = img.getFormat(); + Format fmt = img.getGlFormat(); switch (fmt) { case Depth24Stencil8: return caps.contains(Caps.PackedDepthStencilBuffer); @@ -540,7 +540,7 @@ private static boolean supportsColorBuffer(Collection caps, RenderBuffer c * @param fb The framebuffer to check * @return True if it is supported, false otherwise. */ - public static boolean supports(Collection caps, FrameBuffer fb) { + public static boolean supports(Collection caps, GlFrameBuffer fb) { if (!caps.contains(Caps.FrameBuffer)) { return false; } @@ -550,7 +550,7 @@ public static boolean supports(Collection caps, FrameBuffer fb) { return false; } - RenderBuffer depthBuf = fb.getDepthBuffer(); + RenderBuffer depthBuf = fb.getDepthTarget(); if (depthBuf != null) { Format depthFmt = depthBuf.getFormat(); if (!depthFmt.isDepthFormat()) { @@ -567,8 +567,8 @@ public static boolean supports(Collection caps, FrameBuffer fb) { } } } - for (int i = 0; i < fb.getNumColorBuffers(); i++) { - if (!supportsColorBuffer(caps, fb.getColorBuffer(i))) { + for (RenderBuffer clr : fb.getColorTargets()) { + if (!supportsColorBuffer(caps, clr)) { return false; } } diff --git a/jme3-core/src/main/java/com/jme3/renderer/RenderContext.java b/jme3-core/src/main/java/com/jme3/renderer/RenderContext.java index ea6088afb9..43f266cf6b 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/RenderContext.java +++ b/jme3-core/src/main/java/com/jme3/renderer/RenderContext.java @@ -33,12 +33,15 @@ import com.jme3.material.RenderState; import com.jme3.math.ColorRGBA; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlMesh; +import com.jme3.scene.Mesh; +import com.jme3.scene.GlVertexBuffer; import com.jme3.shader.Shader; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage; import java.lang.ref.WeakReference; import com.jme3.shader.bufferobject.BufferObject; +import com.jme3.texture.GlTexture; /** * Represents the current state of the graphics library. This class is used @@ -123,6 +126,8 @@ public class RenderContext { */ public float lineWidth; + public GlMesh.Mode meshMode; + /** * How to blend input pixels with those already in the color buffer. * @@ -204,21 +209,21 @@ public class RenderContext { /** * ID of the bound FrameBuffer. * - * @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer) + * @see Renderer#setFrameBuffer(GlFrameBuffer) */ public int boundFBO; /** * Currently bound FrameBuffer. * - * @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer) + * @see Renderer#setFrameBuffer(GlFrameBuffer) */ - public FrameBuffer boundFB; + public GlFrameBuffer boundFB; /** * Currently bound Renderbuffer. * - * @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer) + * @see Renderer#setFrameBuffer(GlFrameBuffer) */ public int boundRB; @@ -226,21 +231,21 @@ public class RenderContext { /** * Currently bound element array vertex buffer. * - * @see Renderer#renderMesh(com.jme3.scene.Mesh, int, int, com.jme3.scene.VertexBuffer[]) + * @see Renderer#renderMesh(Mesh, int, int, GlVertexBuffer[]) */ public int boundElementArrayVBO; /** * ID of the bound vertex array. * - * @see Renderer#renderMesh(com.jme3.scene.Mesh, int, int, com.jme3.scene.VertexBuffer[]) + * @see Renderer#renderMesh(Mesh, int, int, GlVertexBuffer[]) */ public int boundVertexArray; /** * Currently bound array vertex buffer. * - * @see Renderer#renderMesh(com.jme3.scene.Mesh, int, int, com.jme3.scene.VertexBuffer[]) + * @see Renderer#renderMesh(Mesh, int, int, GlVertexBuffer[]) */ public int boundArrayVBO; @@ -257,9 +262,9 @@ public class RenderContext { /** * Current bound texture IDs for each texture unit. * - * @see Renderer#setTexture(int, com.jme3.texture.Texture) + * @see Renderer#setTexture(int, GlTexture) */ - public final WeakReference boundTextures[] + public final WeakReference boundTextures[] = new WeakReference[maxTextureUnits]; @@ -274,14 +279,14 @@ public class RenderContext { /** * IDList for texture units. * - * @see Renderer#setTexture(int, com.jme3.texture.Texture) + * @see Renderer#setTexture(int, GlTexture) */ public final IDList textureIndexList = new IDList(); /** * Currently bound texture unit. * - * @see Renderer#setTexture(int, com.jme3.texture.Texture) + * @see Renderer#setTexture(int, GlTexture) */ public int boundTextureUnit; @@ -326,7 +331,7 @@ public class RenderContext { * Vertex attribs currently bound and enabled. If a slot is null, then * it is disabled. */ - public final WeakReference[] boundAttribs = new WeakReference[16]; + public final WeakReference[] boundAttribs = new WeakReference[16]; /** * IDList for vertex attributes. diff --git a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java index c59ffd9084..93a18c501e 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java +++ b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java @@ -31,6 +31,7 @@ */ package com.jme3.renderer; +import com.jme3.material.*; import com.jme3.renderer.pipeline.ForwardPipeline; import com.jme3.renderer.pipeline.DefaultPipelineContext; import com.jme3.renderer.pipeline.RenderPipeline; @@ -38,12 +39,6 @@ import com.jme3.light.DefaultLightFilter; import com.jme3.light.LightFilter; import com.jme3.light.LightList; -import com.jme3.material.MatParamOverride; -import com.jme3.material.Material; -import com.jme3.material.MaterialDef; -import com.jme3.material.RenderState; -import com.jme3.material.Technique; -import com.jme3.material.TechniqueDef; import com.jme3.math.Matrix4f; import com.jme3.post.SceneProcessor; import com.jme3.profile.AppProfiler; @@ -57,15 +52,15 @@ import com.jme3.scene.Mesh; import com.jme3.scene.Node; import com.jme3.scene.Spatial; -import com.jme3.scene.VertexBuffer; import com.jme3.shader.Shader; import com.jme3.shader.UniformBinding; import com.jme3.shader.UniformBindingManager; import com.jme3.shader.VarType; import com.jme3.system.NullRenderer; import com.jme3.system.Timer; -import com.jme3.texture.FrameBuffer; +import com.jme3.texture.GlFrameBuffer; import com.jme3.util.SafeArrayList; + import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -99,7 +94,7 @@ public class RenderManager { private final LinkedList usedPipelines = new LinkedList<>(); private RenderPipeline defaultPipeline = new ForwardPipeline(); private Camera prevCam = null; - private Material forcedMaterial = null; + private GlMaterial forcedMaterial = null; private String forcedTechnique = null; private RenderState forcedRenderState = null; private final SafeArrayList forcedOverrides @@ -157,9 +152,9 @@ public void setPipeline(RenderPipeline pipeline) { /** * Gets the default pipeline context registered under - * {@link PipelineContext#getClass()}. + * {@code PipelineContext.class}. * - * @return + * @return the default pipeline context */ public PipelineContext getDefaultContext() { return getContext(PipelineContext.class); @@ -539,7 +534,7 @@ public void notifyRescale(float x, float y) { * * @param mat The forced material to set, or null to return to normal */ - public void setForcedMaterial(Material mat) { + public void setForcedMaterial(GlMaterial mat) { forcedMaterial = mat; } @@ -612,7 +607,7 @@ public String getForcedTechnique() { * *

If the specified technique name is available on the geometry's * material, then it is used, otherwise, the - * {@link #setForcedMaterial(com.jme3.material.Material) forced material} is used. + * {@link #setForcedMaterial(com.jme3.material.GlMaterial) forced material} is used. * If a forced material is not set and the forced technique name cannot * be found on the material, the geometry will not be rendered. * @@ -704,7 +699,7 @@ public void setHandleTranslucentBucket(boolean handleTranslucentBucket) { /** * Internal use only. Sets the world matrix to use for future * rendering. This has no effect unless objects are rendered manually - * using {@link Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) }. + * using {@link GlMaterial#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) }. * Using {@link #renderGeometry(com.jme3.scene.Geometry) } will * override this value. * @@ -734,7 +729,7 @@ public void updateUniformBindings(Shader shader) { * geometry's {@link Geometry#getWorldMatrix() world transform matrix} is used. * *

Once the world matrix is applied, the proper material is chosen for rendering. - * If a {@link #setForcedMaterial(com.jme3.material.Material) forced material} is + * If a {@link #setForcedMaterial(com.jme3.material.GlMaterial) forced material} is * set on this RenderManager, then it is used for rendering the geometry, * otherwise, the {@link Geometry#getMaterial() geometry's material} is used. * @@ -753,33 +748,43 @@ public void updateUniformBindings(Shader shader) { * * @see Technique * @see RenderState - * @see com.jme3.material.Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) - * @see com.jme3.material.Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) + * @see com.jme3.material.GlMaterial#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) + * @see com.jme3.material.GlMaterial#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) */ public void renderGeometry(Geometry geom) { - if (renderFilter != null && !renderFilter.test(geom)) { return; } - - LightList lightList = geom.getWorldLightList(); - if (lightFilter != null) { - filteredLightList.clear(); - lightFilter.filterLights(geom, filteredLightList); - lightList = filteredLightList; + renderGeometry(geom, filterLights(geom, geom.getWorldLightList(), null)); + } + + /** + * + * + * @param geometry + * @param dst + * @return + */ + public LightList filterLights(Geometry geometry, LightList src, LightList dst) { + if (lightFilter == null) { + return geometry.getWorldLightList(); } - - renderGeometry(geom, lightList); - + if (dst == null) { + dst = filteredLightList; + } + dst.clear(); + lightFilter.filterLights(geometry, src, dst); + return dst; } /** * * @param geom - * @param lightList + * @param lightList + * @deprecated use {@link com.jme3.vulkan.render.GlGeometryBatch} instead */ public void renderGeometry(Geometry geom, LightList lightList) { - + if (renderFilter != null && !renderFilter.test(geom)) { return; } @@ -793,12 +798,16 @@ public void renderGeometry(Geometry geom, LightList lightList) { // Use material override to pass the current target index (used in api such as GL ES // that do not support glDrawBuffer) - FrameBuffer currentFb = this.renderer.getCurrentFrameBuffer(); + GlFrameBuffer currentFb = this.renderer.getCurrentFrameBuffer(); if (currentFb != null && !currentFb.isMultiTarget()) { this.boundDrawBufferId.setValue(currentFb.getTargetIndex()); } - Material material = geom.getMaterial(); + if (!(geom.getMaterial() instanceof GlMaterial)) { + throw new UnsupportedOperationException("Cannot render " + geom.getMaterial().getClass() + " in an OpenGL context."); + } + + GlMaterial material = (GlMaterial)geom.getMaterial(); // If forcedTechnique exists, we try to force it for the render. // If it does not exist in the mat def, we check for forcedMaterial and render the geom if not null. @@ -813,13 +822,13 @@ public void renderGeometry(Geometry geom, LightList lightList) { ? activeTechnique.getDef().getName() : TechniqueDef.DEFAULT_TECHNIQUE_NAME; - geom.getMaterial().selectTechnique(forcedTechnique, this); + material.selectTechnique(forcedTechnique, this); //saving forcedRenderState for future calls RenderState tmpRs = forcedRenderState; - if (geom.getMaterial().getActiveTechnique().getDef().getForcedRenderState() != null) { - //forcing forced technique renderState + if (material.getActiveTechnique().getDef().getForcedRenderState() != null) { + // forcing forced technique renderState forcedRenderState - = geom.getMaterial().getActiveTechnique().getDef().getForcedRenderState(); + = material.getActiveTechnique().getDef().getForcedRenderState(); } // use geometry's material material.render(geom, lightList, this); @@ -889,16 +898,18 @@ public void preloadScene(Spatial scene) { throw new IllegalStateException("No material is set for Geometry: " + gm.getName()); } - gm.getMaterial().preload(this, gm); + // fixme + //gm.getMaterial().preload(this, gm); Mesh mesh = gm.getMesh(); if (mesh != null && mesh.getVertexCount() != 0 && mesh.getTriangleCount() != 0) { - for (VertexBuffer vb : mesh.getBufferList().getArray()) { - if (vb.getData() != null && vb.getUsage() != VertexBuffer.Usage.CpuOnly) { - renderer.updateBufferData(vb); - } - } + // fixme +// for (VertexBuffer vb : mesh.getBufferList().getArray()) { +// if (vb.getData() != null && vb.getUsage() != VertexBuffer.Usage.CpuOnly) { +// renderer.updateBufferData(vb); +// } +// } } } } @@ -1237,6 +1248,7 @@ private void setViewProjection(Camera cam, boolean ortho) { * @param ortho True if to use orthographic projection (for GUI rendering), * false if to use the camera's view and projection matrices. */ + // todo: remove ortho argument public void setCamera(Camera cam, boolean ortho) { // Tell the light filter which camera to use for filtering. if (lightFilter != null) { diff --git a/jme3-core/src/main/java/com/jme3/renderer/Renderer.java b/jme3-core/src/main/java/com/jme3/renderer/Renderer.java index acd0d599e1..68453e1519 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Renderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/Renderer.java @@ -33,15 +33,15 @@ import com.jme3.material.RenderState; import com.jme3.math.ColorRGBA; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer; import com.jme3.shader.bufferobject.BufferObject; import com.jme3.shader.Shader; import com.jme3.shader.Shader.ShaderSource; import com.jme3.system.AppSettings; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; import com.jme3.texture.TextureImage; import com.jme3.util.NativeObject; import java.nio.ByteBuffer; @@ -199,11 +199,11 @@ public interface Renderer { * @param src the source FrameBuffer (unaffected) * @param dst the destination FrameBuffer (modified) * @param copyDepth true→copy depth info, false→don't copy it - * @deprecated Use {@link Renderer#copyFrameBuffer(com.jme3.texture.FrameBuffer, - * com.jme3.texture.FrameBuffer, boolean, boolean)}. + * @deprecated Use {@link Renderer#copyFrameBuffer(GlFrameBuffer, + * GlFrameBuffer, boolean, boolean)}. */ @Deprecated - public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth); + public void copyFrameBuffer(GlFrameBuffer src, GlFrameBuffer dst, boolean copyDepth); /** * Copies contents from src to dst, scaling if necessary. @@ -213,7 +213,7 @@ public interface Renderer { * @param copyColor true→copy color info, false→don't copy it * @param copyDepth true→copy depth info, false→don't copy it */ - public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyColor, boolean copyDepth); + public void copyFrameBuffer(GlFrameBuffer src, GlFrameBuffer dst, boolean copyColor, boolean copyDepth); /** * Sets the framebuffer that will be drawn to. @@ -223,7 +223,7 @@ public interface Renderer { * * @param fb The framebuffer to set */ - public void setFrameBuffer(FrameBuffer fb); + public void setFrameBuffer(GlFrameBuffer fb); /** * Sets the framebuffer that will be set instead of the main framebuffer @@ -231,7 +231,7 @@ public interface Renderer { * * @param fb The framebuffer to override the main framebuffer. */ - public void setMainFrameBufferOverride(FrameBuffer fb); + public void setMainFrameBufferOverride(GlFrameBuffer fb); /** * Reads the pixels currently stored in the specified framebuffer @@ -243,7 +243,7 @@ public interface Renderer { * @param fb The framebuffer to read from * @param byteBuf The bytebuffer to transfer color data to */ - public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf); + public void readFrameBuffer(GlFrameBuffer fb, ByteBuffer byteBuf); /** * Reads the pixels currently stored in the specified framebuffer @@ -256,14 +256,14 @@ public interface Renderer { * @param byteBuf The bytebuffer to transfer color data to * @param format the image format to use when reading the frameBuffer. */ - public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format); + public void readFrameBufferWithFormat(GlFrameBuffer fb, ByteBuffer byteBuf, GlImage.Format format); /** * Deletes a framebuffer and all attached renderbuffers. * * @param fb the FrameBuffer to be deleted */ - public void deleteFrameBuffer(FrameBuffer fb); + public void deleteFrameBuffer(GlFrameBuffer fb); /** * Assigns a Texture to the specified texture unit. @@ -272,7 +272,7 @@ public interface Renderer { * @param tex the Texture to assign * @throws TextureUnitException if the texture unit doesn't exist */ - public void setTexture(int unit, Texture tex) + public void setTexture(int unit, GlTexture tex) throws TextureUnitException; /** @@ -295,21 +295,21 @@ public void setTexture(int unit, Texture tex) * @param x the x position to put the image into the texture * @param y the y position to put the image into the texture */ - public void modifyTexture(Texture tex, Image pixels, int x, int y); + public void modifyTexture(GlTexture tex, GlImage pixels, int x, int y); /** * Deletes a texture from the GPU. * * @param image the texture to delete */ - public void deleteImage(Image image); + public void deleteImage(GlImage image); /** * Uploads a vertex buffer to the GPU. * * @param vb The vertex buffer to upload */ - public void updateBufferData(VertexBuffer vb); + public void updateBufferData(GlVertexBuffer vb); /** * Uploads data of the buffer object on the GPU. @@ -330,7 +330,7 @@ public void setTexture(int unit, Texture tex) * * @param vb The vertex buffer to delete */ - public void deleteBuffer(VertexBuffer vb); + public void deleteBuffer(GlVertexBuffer vb); /** * Deletes the buffer object from the GPU. @@ -351,12 +351,12 @@ public void setTexture(int unit, Texture tex) * per-instance vertex attribute to the shader. * * @param mesh The mesh to render - * @param lod The LOD level to use, see {@link Mesh#setLodLevels(com.jme3.scene.VertexBuffer[]) }. + * @param lod The LOD level to use * @param count Number of mesh instances to render * @param instanceData When count is greater than 1, these buffers provide * the per-instance attributes. */ - public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData); + public void renderMesh(GlMesh mesh, GlMesh.Mode mode, int lod, int count, GlVertexBuffer[] instanceData); /** * Resets all previously used {@link NativeObject Native Objects} on this Renderer. @@ -381,7 +381,7 @@ public void setTexture(int unit, Texture tex) * Sets the default anisotropic filter level for textures. * *

If the - * {@link Texture#setAnisotropicFilter(int) texture anisotropic filter} is + * {@link GlTexture#setAnisotropicFilter(int) texture anisotropic filter} is * set to 0, then the default level is used. Otherwise, if the texture level * is 1 or greater, then the texture's value overrides the default value. * @@ -413,7 +413,7 @@ public void setTexture(int unit, Texture tex) *

If enabled, color values rendered to the main framebuffer undergo * linear -> sRGB conversion. * - *

This is identical to {@link FrameBuffer#setSrgb(boolean)} except it is toggled + *

This is identical to {@link GlFrameBuffer#setSrgb(boolean)} except it is toggled * for the main framebuffer instead of an offscreen buffer. * *

This should be set together with {@link Renderer#setLinearizeSrgbImages(boolean)} @@ -426,25 +426,25 @@ public void setTexture(int unit, Texture tex) * @param srgb true for sRGB colorspace, false for linear colorspace * @throws RendererException If the GPU hardware does not support sRGB. * - * @see FrameBuffer#setSrgb(boolean) + * @see GlFrameBuffer#setSrgb(boolean) * @see Caps#Srgb */ public void setMainFrameBufferSrgb(boolean srgb); /** - * If enabled, all {@link Image images} with the - * {@link Image#setColorSpace(com.jme3.texture.image.ColorSpace) sRGB flag} + * If enabled, all {@link GlImage images} with the + * {@link GlImage#setColorSpace(com.jme3.texture.image.ColorSpace) sRGB flag} * set shall undergo an sRGB to linear RGB color conversion when read by a shader. * *

The conversion is performed for the following formats: - * - {@link com.jme3.texture.Image.Format#RGB8} - * - {@link com.jme3.texture.Image.Format#RGBA8} - * - {@link com.jme3.texture.Image.Format#Luminance8} - * - {@link com.jme3.texture.Image.Format#Luminance8Alpha8} - * - {@link com.jme3.texture.Image.Format#DXT1} - * - {@link com.jme3.texture.Image.Format#DXT1A} - * - {@link com.jme3.texture.Image.Format#DXT3} - * - {@link com.jme3.texture.Image.Format#DXT5} + * - {@link GlImage.Format#RGB8} + * - {@link GlImage.Format#RGBA8} + * - {@link GlImage.Format#Luminance8} + * - {@link GlImage.Format#Luminance8Alpha8} + * - {@link GlImage.Format#DXT1} + * - {@link GlImage.Format#DXT1A} + * - {@link GlImage.Format#DXT3} + * - {@link GlImage.Format#DXT5} * *

For all other formats, no conversion is performed. * @@ -548,7 +548,7 @@ public default void pushDebugGroup(String name) { * Returns the current FrameBuffer that is being rendered to. * @return the FrameBuffer or null if rendering to the screen. */ - public FrameBuffer getCurrentFrameBuffer(); + public GlFrameBuffer getCurrentFrameBuffer(); public void setShaderStorageBufferObject(int bindingPoint, BufferObject bufferObject) ; public void setUniformBufferObject(int bindingPoint, BufferObject bufferObject) ; diff --git a/jme3-core/src/main/java/com/jme3/renderer/Statistics.java b/jme3-core/src/main/java/com/jme3/renderer/Statistics.java index b2766df154..fa1bdf1453 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/Statistics.java +++ b/jme3-core/src/main/java/com/jme3/renderer/Statistics.java @@ -33,8 +33,8 @@ import com.jme3.scene.Mesh; import com.jme3.shader.Shader; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage; import com.jme3.util.IntMap; /** @@ -235,15 +235,15 @@ public void onUniformSet() { * @param image The image that was set * @param wasSwitched If true, the texture has required a state switch */ - public void onTextureUse(Image image, boolean wasSwitched) { - assert image.getId() >= 1; + public void onTextureUse(GlImage image, boolean wasSwitched) { + assert image.getNativeObject() >= 1; if (!enabled) { return; } - if (!texturesUsed.containsKey(image.getId())) { - texturesUsed.put(image.getId(), null); + if (!texturesUsed.containsKey(image.getNativeObject())) { + texturesUsed.put(image.getNativeObject(), null); } if (wasSwitched) { @@ -257,16 +257,16 @@ public void onTextureUse(Image image, boolean wasSwitched) { * @param fb The framebuffer that was set * @param wasSwitched If true, the framebuffer required a state switch */ - public void onFrameBufferUse(FrameBuffer fb, boolean wasSwitched) { + public void onFrameBufferUse(GlFrameBuffer fb, boolean wasSwitched) { if (!enabled) { return; } if (fb != null) { - assert fb.getId() >= 1; + assert fb.getNativeObject() >= 1; - if (!fbosUsed.containsKey(fb.getId())) { - fbosUsed.put(fb.getId(), null); + if (!fbosUsed.containsKey(fb.getNativeObject())) { + fbosUsed.put(fb.getNativeObject(), null); } } diff --git a/jme3-core/src/main/java/com/jme3/renderer/ViewPort.java b/jme3-core/src/main/java/com/jme3/renderer/ViewPort.java index f256405276..6627cddeb0 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/ViewPort.java +++ b/jme3-core/src/main/java/com/jme3/renderer/ViewPort.java @@ -37,12 +37,12 @@ import com.jme3.renderer.queue.RenderQueue; import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; -import com.jme3.texture.FrameBuffer; +import com.jme3.texture.GlFrameBuffer; import com.jme3.util.SafeArrayList; /** * Represents a view inside the display - * window or a {@link FrameBuffer} to which scenes will be rendered. + * window or a {@link GlFrameBuffer} to which scenes will be rendered. * *

A viewport has a {@link #ViewPort(java.lang.String, com.jme3.renderer.Camera) camera} * which is used to render a set of {@link #attachScene(com.jme3.scene.Spatial) scenes}. @@ -92,7 +92,7 @@ public class ViewPort { /** * FrameBuffer for output. */ - protected FrameBuffer out = null; + protected GlFrameBuffer out = null; /** * Color applied when the color buffer is cleared. @@ -289,9 +289,9 @@ public void setClearFlags(boolean color, boolean depth, boolean stencil) { * @return the framebuffer where this ViewPort's scenes are * rendered to. * - * @see #setOutputFrameBuffer(com.jme3.texture.FrameBuffer) + * @see #setOutputFrameBuffer(GlFrameBuffer) */ - public FrameBuffer getOutputFrameBuffer() { + public GlFrameBuffer getOutputFrameBuffer() { return out; } @@ -305,7 +305,7 @@ public FrameBuffer getOutputFrameBuffer() { * @param out The framebuffer to render scenes to, or null if to render * to the screen. */ - public void setOutputFrameBuffer(FrameBuffer out) { + public void setOutputFrameBuffer(GlFrameBuffer out) { this.out = out; } diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java index c73943c54e..31955b927a 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLImageFormats.java @@ -32,8 +32,8 @@ package com.jme3.renderer.opengl; import com.jme3.renderer.Caps; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; import java.util.EnumSet; /** @@ -45,21 +45,21 @@ public final class GLImageFormats { private GLImageFormats() { } - private static void format(GLImageFormat[][] formatToGL, Image.Format format, + private static void format(GLImageFormat[][] formatToGL, GlImage.Format format, int glInternalFormat, int glFormat, int glDataType){ formatToGL[0][format.ordinal()] = new GLImageFormat(glInternalFormat, glFormat, glDataType); } - private static void formatSwiz(GLImageFormat[][] formatToGL, Image.Format format, + private static void formatSwiz(GLImageFormat[][] formatToGL, GlImage.Format format, int glInternalFormat, int glFormat, int glDataType){ formatToGL[0][format.ordinal()] = new GLImageFormat(glInternalFormat, glFormat, glDataType, false, true); } - private static void formatSrgb(GLImageFormat[][] formatToGL, Image.Format format, + private static void formatSrgb(GLImageFormat[][] formatToGL, GlImage.Format format, int glInternalFormat, int glFormat, int glDataType) @@ -67,7 +67,7 @@ private static void formatSrgb(GLImageFormat[][] formatToGL, Image.Format format formatToGL[1][format.ordinal()] = new GLImageFormat(glInternalFormat, glFormat, glDataType); } - private static void formatSrgbSwiz(GLImageFormat[][] formatToGL, Image.Format format, + private static void formatSrgbSwiz(GLImageFormat[][] formatToGL, GlImage.Format format, int glInternalFormat, int glFormat, int glDataType) @@ -75,14 +75,14 @@ private static void formatSrgbSwiz(GLImageFormat[][] formatToGL, Image.Format fo formatToGL[1][format.ordinal()] = new GLImageFormat(glInternalFormat, glFormat, glDataType, false, true); } - private static void formatComp(GLImageFormat[][] formatToGL, Image.Format format, + private static void formatComp(GLImageFormat[][] formatToGL, GlImage.Format format, int glCompressedFormat, int glFormat, int glDataType){ formatToGL[0][format.ordinal()] = new GLImageFormat(glCompressedFormat, glFormat, glDataType, true); } - private static void formatCompSrgb(GLImageFormat[][] formatToGL, Image.Format format, + private static void formatCompSrgb(GLImageFormat[][] formatToGL, GlImage.Format format, int glCompressedFormat, int glFormat, int glDataType) @@ -101,7 +101,7 @@ private static void formatCompSrgb(GLImageFormat[][] formatToGL, Image.Format fo * @return An 2D array containing supported texture formats. */ public static GLImageFormat[][] getFormatsForCaps(EnumSet caps) { - GLImageFormat[][] formatToGL = new GLImageFormat[2][Image.Format.values().length]; + GLImageFormat[][] formatToGL = new GLImageFormat[2][GlImage.Format.values().length]; int halfFloatFormat = GLExt.GL_HALF_FLOAT_ARB; if (caps.contains(Caps.OpenGLES20)) { diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java index 09a560d4a6..7f03262bb5 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java @@ -39,12 +39,12 @@ import com.jme3.math.*; import com.jme3.opencl.OpenCLObjectManager; import com.jme3.renderer.*; -import com.jme3.scene.Mesh; -import com.jme3.scene.Mesh.Mode; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Format; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.VertexBuffer.Usage; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlMesh.Mode; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Format; +import com.jme3.scene.GlVertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Usage; import com.jme3.shader.*; import com.jme3.shader.Shader.ShaderSource; import com.jme3.shader.Shader.ShaderType; @@ -54,13 +54,13 @@ import com.jme3.shader.bufferobject.BufferObject; import com.jme3.shader.bufferobject.BufferRegion; import com.jme3.shader.bufferobject.DirtyRegionsIterator; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.FrameBuffer.RenderBuffer; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlFrameBuffer.RenderBuffer; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; -import com.jme3.texture.Texture.ShadowCompareMode; -import com.jme3.texture.Texture.WrapAxis; +import com.jme3.texture.GlTexture.ShadowCompareMode; +import com.jme3.texture.GlTexture.WrapAxis; import com.jme3.texture.TextureImage; import com.jme3.texture.image.LastTextureState; import com.jme3.util.BufferUtils; @@ -99,7 +99,7 @@ public final class GLRenderer implements Renderer { private final EnumSet caps = EnumSet.noneOf(Caps.class); private final EnumMap limits = new EnumMap<>(Limits.class); - private FrameBuffer mainFbOverride = null; + private GlFrameBuffer mainFbOverride = null; private int defaultFBO = 0; private final Statistics statistics = new Statistics(); private int vpX, vpY, vpW, vpH; @@ -108,6 +108,7 @@ public final class GLRenderer implements Renderer { private boolean linearizeSrgbImages; private HashSet extensions; private boolean generateMipmapsForFramebuffers = true; + //private boolean mainFbSrgb = false; private final GL gl; private final GL2 gl2; @@ -1024,6 +1025,7 @@ public void applyRenderState(RenderState state) { gl.glLineWidth(state.getLineWidth()); context.lineWidth = state.getLineWidth(); } + context.meshMode = state.getMeshMode(); } private void changeBlendMode(RenderState.BlendMode blendMode) { @@ -1805,17 +1807,17 @@ public void deleteShader(Shader shader) { * @param src the source buffer (unaffected) * @param dst the destination buffer */ - public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) { + public void copyFrameBuffer(GlFrameBuffer src, GlFrameBuffer dst) { copyFrameBuffer(src, dst, true, true); } @Override - public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) { + public void copyFrameBuffer(GlFrameBuffer src, GlFrameBuffer dst, boolean copyDepth) { copyFrameBuffer(src, dst, true, copyDepth); } @Override - public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyColor,boolean copyDepth) { + public void copyFrameBuffer(GlFrameBuffer src, GlFrameBuffer dst, boolean copyColor, boolean copyDepth) { if (caps.contains(Caps.FrameBufferBlit)) { int srcX0 = 0; int srcY0 = 0; @@ -1853,7 +1855,7 @@ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyColor, srcX1 = vpX + vpW; srcY1 = vpY + vpH; } else { - glfbo.glBindFramebufferEXT(GLFbo.GL_READ_FRAMEBUFFER_EXT, src.getId()); + glfbo.glBindFramebufferEXT(GLFbo.GL_READ_FRAMEBUFFER_EXT, src.getNativeObject()); srcX1 = src.getWidth(); srcY1 = src.getHeight(); } @@ -1864,7 +1866,7 @@ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyColor, dstX1 = vpX + vpW; dstY1 = vpY + vpH; } else { - glfbo.glBindFramebufferEXT(GLFbo.GL_DRAW_FRAMEBUFFER_EXT, dst.getId()); + glfbo.glBindFramebufferEXT(GLFbo.GL_DRAW_FRAMEBUFFER_EXT, dst.getNativeObject()); dstX1 = dst.getWidth(); dstY1 = dst.getHeight(); } @@ -1921,12 +1923,12 @@ private void checkFrameBufferError() { } } - private void updateRenderBuffer(FrameBuffer fb, RenderBuffer rb) { - int id = rb.getId(); + private void updateRenderBuffer(GlFrameBuffer fb, RenderBuffer rb) { + int id = rb.getRenderBufferId(); if (id == -1) { glfbo.glGenRenderbuffersEXT(intBuf1); id = intBuf1.get(0); - rb.setId(id); + rb.setRenderBufferId(id); } if (context.boundRB != id) { @@ -1963,9 +1965,9 @@ private void updateRenderBuffer(FrameBuffer fb, RenderBuffer rb) { private int convertAttachmentSlot(int attachmentSlot) { // can also add support for stencil here - if (attachmentSlot == FrameBuffer.SLOT_DEPTH) { + if (attachmentSlot == GlFrameBuffer.SLOT_DEPTH) { return GLFbo.GL_DEPTH_ATTACHMENT_EXT; - } else if (attachmentSlot == FrameBuffer.SLOT_DEPTH_STENCIL) { + } else if (attachmentSlot == GlFrameBuffer.SLOT_DEPTH_STENCIL) { // NOTE: Using depth stencil format requires GL3, this is already // checked via render caps. return GL3.GL_DEPTH_STENCIL_ATTACHMENT; @@ -1976,9 +1978,9 @@ private int convertAttachmentSlot(int attachmentSlot) { return GLFbo.GL_COLOR_ATTACHMENT0_EXT + attachmentSlot; } - public void updateRenderTexture(FrameBuffer fb, RenderBuffer rb) { - Texture tex = rb.getTexture(); - Image image = tex.getImage(); + public void updateRenderTexture(GlFrameBuffer fb, RenderBuffer rb) { + GlTexture tex = rb.getTexture(); + GlImage image = tex.getImage(); if (image.isUpdateNeeded()) { // Check NPOT requirements checkNonPowerOfTwo(tex); @@ -1995,22 +1997,22 @@ public void updateRenderTexture(FrameBuffer fb, RenderBuffer rb) { glfbo.glFramebufferTexture2DEXT(GLFbo.GL_FRAMEBUFFER_EXT, convertAttachmentSlot(rb.getSlot()), convertTextureType(tex.getType(), image.getMultiSamples(), rb.getFace()), - image.getId(), + image.getNativeObject(), rb.getLevel()); } else { glfbo.glFramebufferTextureLayerEXT(GLFbo.GL_FRAMEBUFFER_EXT, convertAttachmentSlot(rb.getSlot()), - image.getId(), + image.getNativeObject(), rb.getLevel(), rb.getLayer()); } } - public void updateFrameBufferAttachment(FrameBuffer fb, RenderBuffer rb) { + public void updateFrameBufferAttachment(GlFrameBuffer fb, RenderBuffer rb) { boolean needAttach; if (rb.getTexture() == null) { // if it hasn't been created yet, then attach is required. - needAttach = rb.getId() == -1; + needAttach = rb.getRenderBufferId() == -1; updateRenderBuffer(fb, rb); } else { needAttach = false; @@ -2020,11 +2022,11 @@ public void updateFrameBufferAttachment(FrameBuffer fb, RenderBuffer rb) { glfbo.glFramebufferRenderbufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, convertAttachmentSlot(rb.getSlot()), GLFbo.GL_RENDERBUFFER_EXT, - rb.getId()); + rb.getRenderBufferId()); } } - private void bindFrameBuffer(FrameBuffer fb) { + private void bindFrameBuffer(GlFrameBuffer fb) { if (fb == null) { if (context.boundFBO != defaultFBO) { glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, defaultFBO); @@ -2033,10 +2035,10 @@ private void bindFrameBuffer(FrameBuffer fb) { context.boundFB = null; } } else { - assert fb.getId() != -1 && fb.getId() != 0; - if (context.boundFBO != fb.getId()) { - glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, fb.getId()); - context.boundFBO = fb.getId(); + assert fb.getNativeObject() != -1 && fb.getNativeObject() != 0; + if (context.boundFBO != fb.getNativeObject()) { + glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, fb.getNativeObject()); + context.boundFBO = fb.getNativeObject(); context.boundFB = fb; statistics.onFrameBufferUse(fb, true); } else { @@ -2045,30 +2047,28 @@ private void bindFrameBuffer(FrameBuffer fb) { } } - public void updateFrameBuffer(FrameBuffer fb) { - if (fb.getNumColorBuffers() == 0 && fb.getDepthBuffer() == null) { + public void updateFrameBuffer(GlFrameBuffer fb) { + if (fb.getColorTargets().isEmpty() && fb.getDepthTarget() == null) { throw new IllegalArgumentException("The framebuffer: " + fb + "\nDoesn't have any color/depth buffers"); } - int id = fb.getId(); + int id = fb.getNativeObject(); if (id == -1) { glfbo.glGenFramebuffersEXT(intBuf1); id = intBuf1.get(0); - fb.setId(id); - objManager.registerObject(fb); + fb.setId(this, id); statistics.onNewFrameBuffer(); } bindFrameBuffer(fb); - FrameBuffer.RenderBuffer depthBuf = fb.getDepthBuffer(); + GlFrameBuffer.RenderBuffer depthBuf = fb.getDepthTarget(); if (depthBuf != null) { updateFrameBufferAttachment(fb, depthBuf); } - for (int i = 0; i < fb.getNumColorBuffers(); i++) { - FrameBuffer.RenderBuffer colorBuf = fb.getColorBuffer(i); + for (GlFrameBuffer.RenderBuffer colorBuf : fb.getColorTargets()) { updateFrameBufferAttachment(fb, colorBuf); } @@ -2078,7 +2078,7 @@ public void updateFrameBuffer(FrameBuffer fb) { fb.clearUpdateNeeded(); } - public Vector2f[] getFrameBufferSamplePositions(FrameBuffer fb) { + public Vector2f[] getFrameBufferSamplePositions(GlFrameBuffer fb) { if (fb.getSamples() <= 1) { throw new IllegalArgumentException("Framebuffer must be multisampled"); } @@ -2100,7 +2100,7 @@ public Vector2f[] getFrameBufferSamplePositions(FrameBuffer fb) { } @Override - public void setMainFrameBufferOverride(FrameBuffer fb) { + public void setMainFrameBufferOverride(GlFrameBuffer fb) { mainFbOverride = null; if (context.boundFBO == 0) { // Main FB is now set to fb, make sure its bound @@ -2111,31 +2111,27 @@ public void setMainFrameBufferOverride(FrameBuffer fb) { @Override - public FrameBuffer getCurrentFrameBuffer() { + public GlFrameBuffer getCurrentFrameBuffer() { if(mainFbOverride!=null){ return mainFbOverride; } return context.boundFB; } - public void setReadDrawBuffers(FrameBuffer fb) { + public void setReadDrawBuffers(GlFrameBuffer fb) { if (gl2 == null) { return; } - final int NONE = -2; - final int INITIAL = -1; - final int MRT_OFF = 100; - if (fb != null) { - if (fb.getNumColorBuffers() == 0) { + if (fb.getColorTargets().isEmpty()) { // make sure to select NONE as draw buf // no color buffer attached. gl2.glDrawBuffer(GL.GL_NONE); gl2.glReadBuffer(GL.GL_NONE); } else { - if (fb.getNumColorBuffers() > limits.get(Limits.FrameBufferAttachments)) { + if (fb.getColorTargets().size() > limits.get(Limits.FrameBufferAttachments)) { throw new RendererException("Framebuffer has more color " + "attachments than are supported" + " by the video hardware!"); @@ -2145,21 +2141,21 @@ public void setReadDrawBuffers(FrameBuffer fb) { throw new RendererException("Multiple render targets " + " are not supported by the video hardware"); } - if (fb.getNumColorBuffers() > limits.get(Limits.FrameBufferMrtAttachments)) { + if (fb.getColorTargets().size() > limits.get(Limits.FrameBufferMrtAttachments)) { throw new RendererException("Framebuffer has more" + " multi targets than are supported" + " by the video hardware!"); } intBuf16.clear(); - for (int i = 0; i < fb.getNumColorBuffers(); i++) { + for (int i = 0; i < fb.getColorTargets().size(); i++) { intBuf16.put(GLFbo.GL_COLOR_ATTACHMENT0_EXT + i); } intBuf16.flip(); glext.glDrawBuffers(intBuf16); } else { - RenderBuffer rb = fb.getColorBuffer(fb.getTargetIndex()); + RenderBuffer rb = fb.getColorTarget(fb.getTargetIndex()); // select this draw buffer gl2.glDrawBuffer(GLFbo.GL_COLOR_ATTACHMENT0_EXT + rb.getSlot()); // select this read buffer @@ -2171,7 +2167,7 @@ public void setReadDrawBuffers(FrameBuffer fb) { } @Override - public void setFrameBuffer(FrameBuffer fb) { + public void setFrameBuffer(GlFrameBuffer fb) { if (fb == null && mainFbOverride != null) { fb = mainFbOverride; } @@ -2182,6 +2178,12 @@ public void setFrameBuffer(FrameBuffer fb) { } } +// if (!mainFbSrgb && fb == null && context.boundFB != null) { +// gl.glDisable(GLExt.GL_FRAMEBUFFER_SRGB_EXT); +// } else { +// gl.glDisable(GLExt.GL_FRAMEBUFFER_SRGB_EXT); +// } + if (!caps.contains(Caps.FrameBuffer)) { throw new RendererException("Framebuffer objects are not supported" + " by the video hardware"); @@ -2189,9 +2191,8 @@ public void setFrameBuffer(FrameBuffer fb) { // generate mipmaps for last FB if needed if (context.boundFB != null && (context.boundFB.getMipMapsGenerationHint()!=null?context.boundFB.getMipMapsGenerationHint():generateMipmapsForFramebuffers)) { - for (int i = 0; i < context.boundFB.getNumColorBuffers(); i++) { - RenderBuffer rb = context.boundFB.getColorBuffer(i); - Texture tex = rb.getTexture(); + for (RenderBuffer rb : context.boundFB.getColorTargets()) { + GlTexture tex = rb.getTexture(); if (tex != null && tex.getMinFilter().usesMipMapLevels()) { try { final int textureUnitIndex = 0; @@ -2199,7 +2200,7 @@ public void setFrameBuffer(FrameBuffer fb) { } catch (TextureUnitException exception) { throw new RuntimeException("Renderer lacks texture units?"); } - if (tex.getType() == Texture.Type.CubeMap) { + if (tex.getType() == GlTexture.Type.CubeMap) { glfbo.glGenerateMipmapEXT(GL.GL_TEXTURE_CUBE_MAP); } else { int textureType = convertTextureType(tex.getType(), tex.getImage().getMultiSamples(), rb.getFace()); @@ -2221,24 +2222,24 @@ public void setFrameBuffer(FrameBuffer fb) { // update viewport to reflect framebuffer's resolution setViewPort(0, 0, fb.getWidth(), fb.getHeight()); - assert fb.getId() > 0; - assert context.boundFBO == fb.getId(); + assert fb.getNativeObject() > 0; + assert context.boundFBO == fb.getNativeObject(); context.boundFB = fb; if (debug && caps.contains(Caps.GLDebug)) { - if (fb.getName() != null) glext.glObjectLabel(GL3.GL_FRAMEBUFFER, fb.getId(), fb.getName()); + if (fb.getName() != null) glext.glObjectLabel(GL3.GL_FRAMEBUFFER, fb.getNativeObject(), fb.getName()); } } } @Override - public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) { + public void readFrameBuffer(GlFrameBuffer fb, ByteBuffer byteBuf) { readFrameBufferWithGLFormat(fb, byteBuf, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE); } - private void readFrameBufferWithGLFormat(FrameBuffer fb, ByteBuffer byteBuf, int glFormat, int dataType) { + private void readFrameBufferWithGLFormat(GlFrameBuffer fb, ByteBuffer byteBuf, int glFormat, int dataType) { if (fb != null) { - RenderBuffer rb = fb.getColorBuffer(); + RenderBuffer rb = fb.getColorTarget(); if (rb == null) { throw new IllegalArgumentException("Specified framebuffer" + " does not have a colorbuffer"); @@ -2255,32 +2256,32 @@ private void readFrameBufferWithGLFormat(FrameBuffer fb, ByteBuffer byteBuf, int } @Override - public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) { + public void readFrameBufferWithFormat(GlFrameBuffer fb, ByteBuffer byteBuf, GlImage.Format format) { GLImageFormat glFormat = texUtil.getImageFormatWithError(format, false); readFrameBufferWithGLFormat(fb, byteBuf, glFormat.format, glFormat.dataType); } - private void deleteRenderBuffer(FrameBuffer fb, RenderBuffer rb) { - intBuf1.put(0, rb.getId()); + private void deleteRenderBuffer(GlFrameBuffer fb, RenderBuffer rb) { + intBuf1.put(0, rb.getRenderBufferId()); glfbo.glDeleteRenderbuffersEXT(intBuf1); } @Override - public void deleteFrameBuffer(FrameBuffer fb) { - if (fb.getId() != -1) { - if (context.boundFBO == fb.getId()) { + public void deleteFrameBuffer(GlFrameBuffer fb) { + if (fb.getNativeObject() != -1) { + if (context.boundFBO == fb.getNativeObject()) { glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, 0); context.boundFBO = 0; } - if (fb.getDepthBuffer() != null) { - deleteRenderBuffer(fb, fb.getDepthBuffer()); + if (fb.getDepthTarget() != null) { + deleteRenderBuffer(fb, fb.getDepthTarget()); } - if (fb.getColorBuffer() != null) { - deleteRenderBuffer(fb, fb.getColorBuffer()); + if (fb.getColorTarget() != null) { + deleteRenderBuffer(fb, fb.getColorTarget()); } - intBuf1.put(0, fb.getId()); + intBuf1.put(0, fb.getNativeObject()); glfbo.glDeleteFramebuffersEXT(intBuf1); fb.resetObject(); @@ -2291,7 +2292,7 @@ public void deleteFrameBuffer(FrameBuffer fb) { /*********************************************************************\ |* Textures *| \*********************************************************************/ - private int convertTextureType(Texture.Type type, int samples, int face) { + private int convertTextureType(GlTexture.Type type, int samples, int face) { if (samples > 1 && !caps.contains(Caps.TextureMultisample)) { throw new RendererException("Multisample textures are not supported" + " by the video hardware."); @@ -2333,7 +2334,7 @@ private int convertTextureType(Texture.Type type, int samples, int face) { } } - private int convertMagFilter(Texture.MagFilter filter) { + private int convertMagFilter(GlTexture.MagFilter filter) { switch (filter) { case Bilinear: return GL.GL_LINEAR; @@ -2344,7 +2345,7 @@ private int convertMagFilter(Texture.MagFilter filter) { } } - private int convertMinFilter(Texture.MinFilter filter, boolean haveMips) { + private int convertMinFilter(GlTexture.MinFilter filter, boolean haveMips) { if (haveMips){ switch (filter) { case Trilinear: @@ -2378,7 +2379,7 @@ private int convertMinFilter(Texture.MinFilter filter, boolean haveMips) { } } - private int convertWrapMode(Texture.WrapMode mode) { + private int convertWrapMode(GlTexture.WrapMode mode) { switch (mode) { case BorderClamp: case Clamp: @@ -2395,8 +2396,8 @@ private int convertWrapMode(Texture.WrapMode mode) { } @SuppressWarnings("fallthrough") - private void setupTextureParams(int unit, Texture tex) { - Image image = tex.getImage(); + private void setupTextureParams(int unit, GlTexture tex) { + GlImage image = tex.getImage(); int samples = image != null ? image.getMultiSamples() : 1; int target = convertTextureType(tex.getType(), samples, -1); @@ -2492,7 +2493,7 @@ private void setupTextureParams(int unit, Texture tex) { * @param tex The texture to validate. * @throws RendererException If the texture is not supported by the hardware */ - private void checkNonPowerOfTwo(Texture tex) { + private void checkNonPowerOfTwo(GlTexture tex) { if (!tex.getImage().isNPOT()) { // Texture is power-of-2, safe to use. return; @@ -2519,15 +2520,15 @@ private void checkNonPowerOfTwo(Texture tex) { switch (tex.getType()) { case CubeMap: case ThreeDimensional: - if (tex.getWrap(WrapAxis.R) != Texture.WrapMode.EdgeClamp) { + if (tex.getWrap(WrapAxis.R) != GlTexture.WrapMode.EdgeClamp) { throw new RendererException("repeating non-power-of-2 textures " + "are not supported by the video hardware"); } // fallthrough intentional!!! case TwoDimensionalArray: case TwoDimensional: - if (tex.getWrap(WrapAxis.S) != Texture.WrapMode.EdgeClamp - || tex.getWrap(WrapAxis.T) != Texture.WrapMode.EdgeClamp) { + if (tex.getWrap(WrapAxis.S) != GlTexture.WrapMode.EdgeClamp + || tex.getWrap(WrapAxis.T) != GlTexture.WrapMode.EdgeClamp) { throw new RendererException("repeating non-power-of-2 textures " + "are not supported by the video hardware"); } @@ -2545,13 +2546,13 @@ private void checkNonPowerOfTwo(Texture tex) { * @param img The image texture to bind * @param unit At what unit to bind the texture. */ - private void bindTextureAndUnit(int target, Image img, int unit) { + private void bindTextureAndUnit(int target, GlImage img, int unit) { if (context.boundTextureUnit != unit) { gl.glActiveTexture(GL.GL_TEXTURE0 + unit); context.boundTextureUnit = unit; } if (context.boundTextures[unit]==null||context.boundTextures[unit].get() != img.getWeakRef().get()) { - gl.glBindTexture(target, img.getId()); + gl.glBindTexture(target, img.getNativeObject()); context.boundTextures[unit] = img.getWeakRef(); statistics.onTextureUse(img, true); } else { @@ -2567,13 +2568,13 @@ private void bindTextureAndUnit(int target, Image img, int unit) { * @param img The image texture to bind * @param unit At what unit to bind the texture. */ - private void bindTextureOnly(int target, Image img, int unit) { + private void bindTextureOnly(int target, GlImage img, int unit) { if (context.boundTextures[unit] == null || context.boundTextures[unit].get() != img.getWeakRef().get()) { if (context.boundTextureUnit != unit) { gl.glActiveTexture(GL.GL_TEXTURE0 + unit); context.boundTextureUnit = unit; } - gl.glBindTexture(target, img.getId()); + gl.glBindTexture(target, img.getNativeObject()); context.boundTextures[unit] = img.getWeakRef(); statistics.onTextureUse(img, true); } else { @@ -2590,14 +2591,14 @@ private void bindTextureOnly(int target, Image img, int unit) { * @param scaleToPot If true, the image will be scaled to power-of-2 dimensions * before being uploaded. */ - public void updateTexImageData(Image img, Texture.Type type, int unit, boolean scaleToPot) { - int texId = img.getId(); + public void updateTexImageData(GlImage img, GlTexture.Type type, int unit, boolean scaleToPot) { + int texId = img.getNativeObject(); if (texId == -1) { // create texture gl.glGenTextures(intBuf1); texId = intBuf1.get(0); - img.setId(texId); - objManager.registerObject(img); + img.setId(this, texId); // setId automatically updates the native state + //objManager.registerObject(img); statistics.onNewTexture(); } @@ -2640,7 +2641,7 @@ public void updateTexImageData(Image img, Texture.Type type, int unit, boolean s throw new RendererException("Multisample textures do not support mipmaps"); } - if (img.getFormat().isDepthFormat()) { + if (img.getGlFormat().isDepthFormat()) { img.setMultiSamples(Math.min(limits.get(Limits.DepthTextureSamples), imageSamples)); } else { img.setMultiSamples(Math.min(limits.get(Limits.ColorTextureSamples), imageSamples)); @@ -2650,7 +2651,7 @@ public void updateTexImageData(Image img, Texture.Type type, int unit, boolean s } // Check if graphics card doesn't support depth textures - if (img.getFormat().isDepthFormat() && !caps.contains(Caps.DepthTexture)) { + if (img.getGlFormat().isDepthFormat() && !caps.contains(Caps.DepthTexture)) { throw new RendererException("Depth textures are not supported by the video hardware"); } @@ -2670,7 +2671,7 @@ public void updateTexImageData(Image img, Texture.Type type, int unit, boolean s } } - Image imageForUpload; + GlImage imageForUpload; if (scaleToPot) { imageForUpload = MipMapGenerator.resizeToPowerOf2(img); } else { @@ -2720,12 +2721,12 @@ public void updateTexImageData(Image img, Texture.Type type, int unit, boolean s } @Override - public void setTexture(int unit, Texture tex) throws TextureUnitException { + public void setTexture(int unit, GlTexture tex) throws TextureUnitException { if (unit < 0 || unit >= RenderContext.maxTextureUnits) { throw new TextureUnitException(); } - Image image = tex.getImage(); + GlImage image = tex.getImage(); if (image.isUpdateNeeded() || (image.isGeneratedMipmapsRequired() && !image.isMipmapsGenerated())) { // Check NPOT requirements boolean scaleToPot = false; @@ -2747,12 +2748,12 @@ public void setTexture(int unit, Texture tex) throws TextureUnitException { updateTexImageData(image, tex.getType(), unit, scaleToPot); } - int texId = image.getId(); + int texId = image.getNativeObject(); assert texId != -1; setupTextureParams(unit, tex); if (debug && caps.contains(Caps.GLDebug)) { - if (tex.getName() != null) glext.glObjectLabel(GL.GL_TEXTURE, tex.getImage().getId(), tex.getName()); + if (tex.getName() != null) glext.glObjectLabel(GL.GL_TEXTURE, tex.getImage().getNativeObject(), tex.getName()); } } @@ -2761,7 +2762,7 @@ public void setTextureImage(int unit, TextureImage tex) throws TextureUnitExcept if (unit < 0 || unit >= RenderContext.maxTextureUnits) { throw new TextureUnitException(); } - WeakReference ref = context.boundTextures[unit]; + WeakReference ref = context.boundTextures[unit]; boolean bindRequired = tex.clearUpdateNeeded() || ref == null || ref.get() != tex.getImage().getWeakRef().get(); setTexture(unit, tex.getTexture()); if (gl4 != null && bindRequired) { @@ -2811,7 +2812,7 @@ public void setShaderStorageBufferObject(int bindingPoint, BufferObject bufferOb */ @Deprecated @Override - public void modifyTexture(Texture tex, Image pixels, int x, int y) { + public void modifyTexture(GlTexture tex, GlImage pixels, int x, int y) { final int textureUnitIndex = 0; try { setTexture(textureUnitIndex, tex); @@ -2819,7 +2820,7 @@ public void modifyTexture(Texture tex, Image pixels, int x, int y) { throw new RuntimeException("Renderer lacks texture units?"); } - if(caps.contains(Caps.OpenGLES20) && pixels.getFormat()!=tex.getImage().getFormat()) { + if(caps.contains(Caps.OpenGLES20) && pixels.getGlFormat()!=tex.getImage().getGlFormat()) { logger.log(Level.WARNING, "Incompatible texture subimage"); } int target = convertTextureType(tex.getType(), pixels.getMultiSamples(), -1); @@ -2838,8 +2839,8 @@ public void modifyTexture(Texture tex, Image pixels, int x, int y) { * @param areaW Width of the area to copy * @param areaH Height of the area to copy */ - public void modifyTexture(Texture2D dest, Image src, int destX, int destY, - int srcX, int srcY, int areaW, int areaH) { + public void modifyTexture(Texture2D dest, GlImage src, int destX, int destY, + int srcX, int srcY, int areaW, int areaH) { final int textureUnitIndex = 0; try { setTexture(textureUnitIndex, dest); @@ -2847,7 +2848,7 @@ public void modifyTexture(Texture2D dest, Image src, int destX, int destY, throw new RuntimeException("Renderer lacks texture units?"); } - if(caps.contains(Caps.OpenGLES20) && src.getFormat()!=dest.getImage().getFormat()) { + if(caps.contains(Caps.OpenGLES20) && src.getGlFormat()!=dest.getImage().getGlFormat()) { logger.log(Level.WARNING, "Incompatible texture subimage"); } int target = convertTextureType(dest.getType(), src.getMultiSamples(), -1); @@ -2856,8 +2857,8 @@ public void modifyTexture(Texture2D dest, Image src, int destX, int destY, } @Override - public void deleteImage(Image image) { - int texId = image.getId(); + public void deleteImage(GlImage image) { + int texId = image.getNativeObject(); if (texId != -1) { intBuf1.put(0, texId); intBuf1.position(0).limit(1); @@ -2909,40 +2910,28 @@ private int convertFormat(Format format) { } @Override - public void updateBufferData(VertexBuffer vb) { - int bufId = vb.getId(); - boolean created = false; + public void updateBufferData(GlVertexBuffer vb) { + int bufId = vb.getNativeObject(); if (bufId == -1) { // create buffer gl.glGenBuffers(intBuf1); bufId = intBuf1.get(0); - vb.setId(bufId); - objManager.registerObject(vb); - - //statistics.onNewVertexBuffer(); - - created = true; + vb.setId(this, bufId); } // bind buffer int target; - if (vb.getBufferType() == VertexBuffer.Type.Index) { + if (vb.getBufferType() == GlVertexBuffer.Type.Index) { target = GL.GL_ELEMENT_ARRAY_BUFFER; if (context.boundElementArrayVBO != bufId) { gl.glBindBuffer(target, bufId); context.boundElementArrayVBO = bufId; - //statistics.onVertexBufferUse(vb, true); - } else { - //statistics.onVertexBufferUse(vb, false); } } else { target = GL.GL_ARRAY_BUFFER; if (context.boundArrayVBO != bufId) { gl.glBindBuffer(target, bufId); context.boundArrayVBO = bufId; - //statistics.onVertexBufferUse(vb, true); - } else { - //statistics.onVertexBufferUse(vb, false); } } @@ -3068,16 +3057,14 @@ private void updateBufferData(int type, BufferObject bo) { } @Override - public void deleteBuffer(VertexBuffer vb) { - int bufId = vb.getId(); + public void deleteBuffer(GlVertexBuffer vb) { + int bufId = vb.getNativeObject(); if (bufId != -1) { // delete buffer intBuf1.put(0, bufId); intBuf1.position(0).limit(1); gl.glDeleteBuffers(intBuf1); vb.resetObject(); - - //statistics.onDeleteVertexBuffer(); } } @@ -3103,9 +3090,9 @@ public void clearVertexAttribs() { for (int i = 0; i < attribList.oldLen; i++) { int idx = attribList.oldList[i]; gl.glDisableVertexAttribArray(idx); - WeakReference ref = context.boundAttribs[idx]; + WeakReference ref = context.boundAttribs[idx]; if (ref != null) { - VertexBuffer buffer = ref.get(); + GlVertexBuffer buffer = ref.get(); if (buffer != null && buffer.isInstanced()) { glext.glVertexAttribDivisorARB(idx, 0); } @@ -3115,8 +3102,8 @@ public void clearVertexAttribs() { attribList.copyNewToOld(); } - public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) { - if (vb.getBufferType() == VertexBuffer.Type.Index) { + public void setVertexAttrib(GlVertexBuffer vb, GlVertexBuffer idb) { + if (vb.getBufferType() == GlVertexBuffer.Type.Index) { throw new IllegalArgumentException("Index buffers not allowed to be set to vertex attrib"); } @@ -3162,7 +3149,7 @@ public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) { updateBufferData(vb); } - WeakReference[] attribs = context.boundAttribs; + WeakReference[] attribs = context.boundAttribs; for (int i = 0; i < slotsRequired; i++) { if (!context.attribIndexList.moveToNew(loc + i)) { gl.glEnableVertexAttribArray(loc + i); @@ -3170,7 +3157,7 @@ public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) { } if (attribs[loc]==null||attribs[loc].get() != vb) { // NOTE: Use id from interleaved buffer if specified - int bufId = idb != null ? idb.getId() : vb.getId(); + int bufId = idb != null ? idb.getNativeObject() : vb.getNativeObject(); assert bufId != -1; if (context.boundArrayVBO != bufId) { gl.glBindBuffer(GL.GL_ARRAY_BUFFER, bufId); @@ -3219,15 +3206,15 @@ public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) { } } if (debug && caps.contains(Caps.GLDebug)) { - if (vb.getName() != null) glext.glObjectLabel(GLExt.GL_BUFFER, vb.getId(), vb.getName()); + if (vb.getName() != null) glext.glObjectLabel(GLExt.GL_BUFFER, vb.getNativeObject(), vb.getName()); } } - public void setVertexAttrib(VertexBuffer vb) { + public void setVertexAttrib(GlVertexBuffer vb) { setVertexAttrib(vb, null); } - public void drawTriangleArray(Mesh.Mode mode, int count, int vertCount) { + public void drawTriangleArray(GlMesh.Mode mode, int count, int vertCount) { boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing); if (useInstancing) { glext.glDrawArraysInstancedARB(convertElementMode(mode), 0, @@ -3237,8 +3224,8 @@ public void drawTriangleArray(Mesh.Mode mode, int count, int vertCount) { } } - public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) { - if (indexBuf.getBufferType() != VertexBuffer.Type.Index) { + public void drawTriangleList(GlVertexBuffer indexBuf, GlMesh mesh, int count) { + if (indexBuf.getBufferType() != GlVertexBuffer.Type.Index) { throw new IllegalArgumentException("Only index buffers are allowed as triangle lists."); } @@ -3262,7 +3249,7 @@ public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) { updateBufferData(indexBuf); } - int bufId = indexBuf.getId(); + int bufId = indexBuf.getNativeObject(); assert bufId != -1; if (context.boundElementArrayVBO != bufId) { @@ -3276,7 +3263,7 @@ public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) { int vertCount = mesh.getVertexCount(); boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing); - if (mesh.getMode() == Mode.Hybrid) { + if (context.meshMode == Mode.Hybrid) { int[] modeStart = mesh.getModeStart(); int[] elementLengths = mesh.getElementLengths(); @@ -3314,13 +3301,13 @@ public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) { } } else { if (useInstancing) { - glext.glDrawElementsInstancedARB(convertElementMode(mesh.getMode()), + glext.glDrawElementsInstancedARB(convertElementMode(context.meshMode), indexBuf.getData().limit(), convertFormat(indexBuf.getFormat()), 0, count); } else { - gl.glDrawRangeElements(convertElementMode(mesh.getMode()), + gl.glDrawRangeElements(convertElementMode(context.meshMode), 0, vertCount, indexBuf.getData().limit(), @@ -3340,7 +3327,7 @@ public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) { * @param mode input enum value (not null) * @return the corresponding GL value */ - public int convertElementMode(Mesh.Mode mode) { + public int convertElementMode(GlMesh.Mode mode) { switch (mode) { case Points: return GL.GL_POINTS; @@ -3363,7 +3350,7 @@ public int convertElementMode(Mesh.Mode mode) { } } - public void updateVertexArray(Mesh mesh, VertexBuffer instanceData) { + public void updateVertexArray(GlMesh mesh, GlVertexBuffer instanceData) { int id = mesh.getId(); if (id == -1) { IntBuffer temp = intBuf1; @@ -3377,7 +3364,7 @@ public void updateVertexArray(Mesh mesh, VertexBuffer instanceData) { context.boundVertexArray = id; } - VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); + GlVertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); if (interleavedData != null && interleavedData.isUpdateNeeded()) { updateBufferData(interleavedData); } @@ -3386,7 +3373,7 @@ public void updateVertexArray(Mesh mesh, VertexBuffer instanceData) { setVertexAttrib(instanceData, null); } - for (VertexBuffer vb : mesh.getBufferList().getArray()) { + for (GlVertexBuffer vb : mesh.getVertices()) { if (vb.getBufferType() == Type.InterleavedData || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers || vb.getBufferType() == Type.Index) { @@ -3403,57 +3390,48 @@ public void updateVertexArray(Mesh mesh, VertexBuffer instanceData) { } } - private void renderMeshDefault(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { + private void renderMeshDefault(GlMesh mesh, int lod, int count, GlVertexBuffer[] instanceData) { // Here while count is still passed in. Can be removed when/if // the method is collapsed again. -pspeed count = Math.max(mesh.getInstanceCount(), count); - VertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); + GlVertexBuffer interleavedData = mesh.getBuffer(Type.InterleavedData); if (interleavedData != null && interleavedData.isUpdateNeeded()) { updateBufferData(interleavedData); } - VertexBuffer indices; - if (mesh.getNumLodLevels() > 0) { - indices = mesh.getLodLevel(lod); - } else { - indices = mesh.getBuffer(Type.Index); - } - if (instanceData != null) { - for (VertexBuffer vb : instanceData) { + for (GlVertexBuffer vb : instanceData) { setVertexAttrib(vb, null); } } - for (VertexBuffer vb : mesh.getBufferList().getArray()) { + for (GlVertexBuffer vb : mesh.getVertices()) { if (vb.getBufferType() == Type.InterleavedData || vb.getUsage() == Usage.CpuOnly // ignore cpu-only buffers || vb.getBufferType() == Type.Index) { continue; } - if (vb.getStride() == 0) { - // not interleaved + if (vb.getStride() == 0) { // not interleaved setVertexAttrib(vb); - } else { - // interleaved + } else { // interleaved setVertexAttrib(vb, interleavedData); } } clearVertexAttribs(); - if (indices != null) { - drawTriangleList(indices, mesh, count); + if (mesh.getNumLodLevels() > 0) { + drawTriangleList(mesh.getIndexBuffer(lod), mesh, count); } else { - drawTriangleArray(mesh.getMode(), count, mesh.getVertexCount()); + drawTriangleArray(context.meshMode, count, mesh.getVertexCount()); } } @Override - public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { + public void renderMesh(GlMesh mesh, GlMesh.Mode mode, int lod, int count, GlVertexBuffer[] instanceData) { if (mesh.getVertexCount() == 0 || mesh.getTriangleCount() == 0 || count == 0) { return; } @@ -3462,20 +3440,17 @@ public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceDat throw new RendererException("Mesh instancing is not supported by the video hardware"); } - if (mesh.getLineWidth() != 1f && context.lineWidth != mesh.getLineWidth()) { - gl.glLineWidth(mesh.getLineWidth()); - context.lineWidth = mesh.getLineWidth(); - } + // line width for deprecated system: can be removed +// if (mesh.getLineWidth() != 1f && context.lineWidth != mesh.getLineWidth()) { +// gl.glLineWidth(mesh.getLineWidth()); +// context.lineWidth = mesh.getLineWidth(); +// } - if (gl4 != null && mesh.getMode().equals(Mode.Patch)) { + if (gl4 != null && context.meshMode.equals(Mode.Patch)) { gl4.glPatchParameter(mesh.getPatchVertexCount()); } statistics.onMeshDrawn(mesh, lod, count); -// if (ctxCaps.GL_ARB_vertex_array_object){ -// renderMeshVertexArray(mesh, lod, count); -// }else{ renderMeshDefault(mesh, lod, count, instanceData); -// } } @Override @@ -3483,8 +3458,7 @@ public void setMainFrameBufferSrgb(boolean enableSrgb) { // Gamma correction if (!caps.contains(Caps.Srgb) && enableSrgb) { // Not supported, sorry. - logger.warning("sRGB framebuffer is not supported " + - "by video hardware, but was requested."); + logger.warning("sRGB framebuffer is not supported by video hardware, but was requested."); return; } @@ -3499,12 +3473,13 @@ public void setMainFrameBufferSrgb(boolean enableSrgb) { ) { logger.warning("Driver claims that default framebuffer " + "is not sRGB capable. Enabling anyway."); } - gl.glEnable(GLExt.GL_FRAMEBUFFER_SRGB_EXT); + //mainFbSrgb = true; logger.log(Level.FINER, "SRGB FrameBuffer enabled (Gamma Correction)"); } else { - gl.glDisable(GLExt.GL_FRAMEBUFFER_SRGB_EXT); + gl.glDisable(GLExt.GL_FRAMEBUFFER_SRGB_EXT); + //mainFbSrgb = false; } } diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java index 7064f7ce2e..786856e1d4 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java @@ -33,8 +33,8 @@ import com.jme3.renderer.Caps; import com.jme3.renderer.RendererException; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; import com.jme3.texture.image.ColorSpace; import java.nio.ByteBuffer; import java.util.EnumSet; @@ -242,13 +242,13 @@ private void uploadTextureLevel(GLImageFormat format, int target, int level, int } } - public void uploadTexture(Image image, + public void uploadTexture(GlImage image, int target, int index, boolean linearizeSrgb) { boolean getSrgbFormat = image.getColorSpace() == ColorSpace.sRGB && linearizeSrgb; - Image.Format jmeFormat = image.getFormat(); + GlImage.Format jmeFormat = image.getGlFormat(); GLImageFormat oglFormat = getImageFormatWithError(jmeFormat, getSrgbFormat); ByteBuffer data = null; @@ -304,7 +304,7 @@ public void uploadTexture(Image image, * @deprecated Use uploadSubTexture(int target, Image src, int index,int targetX, int targetY,int srcX,int srcY, int areaWidth,int areaHeight, boolean linearizeSrgb) */ @Deprecated - public void uploadSubTexture(Image image, int target, int index, int x, int y, boolean linearizeSrgb) { + public void uploadSubTexture(GlImage image, int target, int index, int x, int y, boolean linearizeSrgb) { if (target != GL.GL_TEXTURE_2D || image.getDepth() > 1) { throw new UnsupportedOperationException("Updating non-2D texture is not supported"); } @@ -317,7 +317,7 @@ public void uploadSubTexture(Image image, int target, int index, int x, int y, b throw new UnsupportedOperationException("Updating multisampled images is not supported"); } - Image.Format jmeFormat = image.getFormat(); + GlImage.Format jmeFormat = image.getGlFormat(); if (jmeFormat.isCompressed()) { throw new UnsupportedOperationException("Updating compressed images is not supported"); @@ -345,7 +345,7 @@ public void uploadSubTexture(Image image, int target, int index, int x, int y, b oglFormat.format, oglFormat.dataType, data); } - public void uploadSubTexture(int target, Image src, int index, int targetX, int targetY, int areaX, int areaY, int areaWidth, int areaHeight, boolean linearizeSrgb) { + public void uploadSubTexture(int target, GlImage src, int index, int targetX, int targetY, int areaX, int areaY, int areaWidth, int areaHeight, boolean linearizeSrgb) { if (target != GL.GL_TEXTURE_2D || src.getDepth() > 1) { throw new UnsupportedOperationException("Updating non-2D texture is not supported"); } @@ -358,7 +358,7 @@ public void uploadSubTexture(int target, Image src, int index, int targetX, int throw new UnsupportedOperationException("Updating multisampled images is not supported"); } - Image.Format jmeFormat = src.getFormat(); + GlImage.Format jmeFormat = src.getGlFormat(); if (jmeFormat.isCompressed()) { throw new UnsupportedOperationException("Updating compressed images is not supported"); @@ -375,7 +375,7 @@ public void uploadSubTexture(int target, Image src, int index, int targetX, int throw new IndexOutOfBoundsException("The image index " + index + " is not valid for the given image"); } - int Bpp = src.getFormat().getBitsPerPixel() / 8; + int Bpp = src.getGlFormat().getBitsPerPixel() / 8; int srcWidth = src.getWidth(); int cpos = data.position(); diff --git a/jme3-core/src/main/java/com/jme3/renderer/queue/OpaqueComparator.java b/jme3-core/src/main/java/com/jme3/renderer/queue/OpaqueComparator.java index 1896ae9ace..a1ca857cf4 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/queue/OpaqueComparator.java +++ b/jme3-core/src/main/java/com/jme3/renderer/queue/OpaqueComparator.java @@ -76,7 +76,7 @@ public int compare(Geometry o1, Geometry o2) { Material m2 = o2.getMaterial(); int compareResult = Integer.compare(m1.getSortId(), m2.getSortId()); - if (compareResult == 0){ + if (compareResult == 0) { // use the same shader. // sort front-to-back then. float d1 = distanceToCam(o1); diff --git a/jme3-core/src/main/java/com/jme3/renderer/vulkan/VulkanRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/vulkan/VulkanRenderer.java new file mode 100644 index 0000000000..a41424268c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/renderer/vulkan/VulkanRenderer.java @@ -0,0 +1,10 @@ +package com.jme3.renderer.vulkan; + +import com.jme3.system.NullRenderer; + +/** + * Temporary placeholder. + */ +public class VulkanRenderer extends NullRenderer { + +} diff --git a/jme3-core/src/main/java/com/jme3/renderer/vulkan/VulkanUtils.java b/jme3-core/src/main/java/com/jme3/renderer/vulkan/VulkanUtils.java new file mode 100644 index 0000000000..c251de6b5a --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/renderer/vulkan/VulkanUtils.java @@ -0,0 +1,179 @@ +package com.jme3.renderer.vulkan; + +import com.jme3.util.natives.Native; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.Struct; +import org.lwjgl.system.StructBuffer; +import org.lwjgl.vulkan.VkInstance; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.nio.LongBuffer; +import java.util.Collection; +import java.util.Iterator; +import java.util.function.*; +import java.util.stream.Stream; + +import static org.lwjgl.vulkan.VK10.*; + +public class VulkanUtils { + + public static final int UINT32_MAX = 0xFFFFFFFF; + + public static int check(int vulkanCode, String message) { + if (vulkanCode != VK_SUCCESS) { + throw new RuntimeException(message); + } + return vulkanCode; + } + + public static int check(int vulkanCode) { + return check(vulkanCode, "Operation unsuccessful: " + vulkanCode); + } + + public static long nonNull(long id, String message) { + if (id == MemoryUtil.NULL) { + throw new NullPointerException(message); + } + return id; + } + + public static long nonNull(long id) { + return nonNull(id, "Pointer ID is NULL."); + } + + public static T enumerateBuffer(MemoryStack stack, IntFunction factory, BiConsumer fetch) { + IntBuffer count = stack.callocInt(1); + fetch.accept(count, null); + if (count.get(0) <= 0) { + return null; + } + T buffer = factory.apply(count.get(0)); + fetch.accept(count, buffer); + return buffer; + } + + public static T enumerateBuffer(IntFunction factory, BiConsumer fetch) { + IntBuffer count = MemoryUtil.memAllocInt(1); + fetch.accept(count, null); + if (count.get(0) <= 0) { + return null; + } + T buffer = factory.apply(count.get(0)); + fetch.accept(count, buffer); + MemoryUtil.memFree(count); + return buffer; + } + + public static PointerBuffer toPointers(MemoryStack stack, Stream stream, int count, Function toBytes) { + PointerBuffer ptrs = stack.mallocPointer(count); + stream.map(toBytes).forEach(ptrs::put); + return ptrs.rewind(); + } + + public static PointerBuffer toPointers(MemoryStack stack, Collection collection, Function toBytes) { + return toPointers(stack, collection.stream(), collection.size(), toBytes); + } + + public static long getPointer(MemoryStack stack, Consumer action) { + PointerBuffer ptr = stack.mallocPointer(1); + action.accept(ptr); + return ptr.get(0); + } + + public static long getLong(MemoryStack stack, Consumer action) { + LongBuffer l = stack.mallocLong(1); + action.accept(l); + return l.get(0); + } + + public static int getInt(MemoryStack stack, Consumer action) { + IntBuffer i = stack.mallocInt(1); + action.accept(i); + return i.get(0); + } + + public static PointerBuffer gatherPointers(MemoryStack stack, Collection pointers) { + int size = 0; + for (PointerBuffer p : pointers) { + size += p.limit(); + } + PointerBuffer gather = stack.mallocPointer(size); + for (PointerBuffer p : pointers) { + gather.put(p); + } + return gather.rewind(); + } + + public static void printListed(String label, Iterable list, Function toString) { + System.out.println(label); + for (T o : list) { + System.out.println(" " + toString.apply(o)); + } + } + + public static void verifyExtensionMethod(VkInstance instance, String name) { + if (vkGetInstanceProcAddr(instance, name) == MemoryUtil.NULL) { + throw new NullPointerException("Extension method " + name + " does not exist."); + } + } + + public static NativeIterator iteratePointers(PointerBuffer buffer, LongFunction func) { + return new NativeIterator<>(buffer, func); + } + + public static boolean isBitSet(int n, int bit) { + return (n & bit) > 0; + } + + public static int sharingMode(boolean concurrent) { + return concurrent ? VK_SHARING_MODE_CONCURRENT : VK_SHARING_MODE_EXCLUSIVE; + } + + public static , E extends Struct> T accumulate(Collection collection, IntFunction allocate) { + T buffer = allocate.apply(collection.size()); + for (E e : collection) { + buffer.put(e); + } + return buffer.rewind(); + } + + public static LongBuffer accumulate(MemoryStack stack, Native... natives) { + LongBuffer buf = stack.mallocLong(natives.length); + for (int i = 0; i < buf.limit(); i++) { + buf.put(i, natives[i].getNativeObject()); + } + return buf; + } + + public static class NativeIterator implements Iterable, Iterator { + + private final PointerBuffer pointers; + private final LongFunction func; + private int index = 0; + + public NativeIterator(PointerBuffer pointers, LongFunction func) { + this.pointers = pointers; + this.func = func; + } + + @Override + public Iterator iterator() { + return this; + } + + @Override + public boolean hasNext() { + return index < pointers.limit(); + } + + @Override + public T next() { + return func.apply(pointers.get(index++)); + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/scene/BatchNode.java b/jme3-core/src/main/java/com/jme3/scene/BatchNode.java index aa5d62a02a..4f1abfdf21 100644 --- a/jme3-core/src/main/java/com/jme3/scene/BatchNode.java +++ b/jme3-core/src/main/java/com/jme3/scene/BatchNode.java @@ -41,6 +41,10 @@ import com.jme3.util.TempVars; import com.jme3.util.clone.Cloner; import com.jme3.util.clone.JmeCloneable; +import com.jme3.vulkan.mesh.AttributeModifier; +import com.jme3.vulkan.mesh.VertexReader; +import com.jme3.vulkan.mesh.VertexWriter; + import java.nio.Buffer; import java.nio.FloatBuffer; import java.util.ArrayList; @@ -130,40 +134,20 @@ protected void updateSubBatch(Geometry bg) { if (batch != null) { Mesh mesh = batch.geometry.getMesh(); Mesh origMesh = bg.getMesh(); - - VertexBuffer pvb = mesh.getBuffer(VertexBuffer.Type.Position); - VertexBuffer nvb = mesh.getBuffer(VertexBuffer.Type.Normal); - VertexBuffer tvb = mesh.getBuffer(VertexBuffer.Type.Tangent); - - VertexBuffer opvb = origMesh.getBuffer(VertexBuffer.Type.Position); - VertexBuffer onvb = origMesh.getBuffer(VertexBuffer.Type.Normal); - VertexBuffer otvb = origMesh.getBuffer(VertexBuffer.Type.Tangent); - - FloatBuffer posBuf = getFloatBuffer(pvb); - FloatBuffer normBuf = getFloatBuffer(nvb); - FloatBuffer tanBuf = getFloatBuffer(tvb); - - FloatBuffer oposBuf = getFloatBuffer(opvb); - FloatBuffer onormBuf = getFloatBuffer(onvb); - FloatBuffer otanBuf = getFloatBuffer(otvb); - Matrix4f transformMat = getTransformMatrix(bg); - doTransforms(oposBuf, onormBuf, otanBuf, posBuf, normBuf, tanBuf, bg.startIndex, bg.startIndex + bg.getVertexCount(), transformMat); - - pvb.updateData(posBuf); - - if (nvb != null) { - nvb.updateData(normBuf); - } - if (tvb != null) { - tvb.updateData(tanBuf); + try (AttributeModifier position = mesh.modify(GlVertexBuffer.Type.Position); + AttributeModifier normal = mesh.modify(GlVertexBuffer.Type.Normal); + AttributeModifier tangent = mesh.modify(GlVertexBuffer.Type.Tangent); + AttributeModifier bindPos = origMesh.modify(GlVertexBuffer.Type.Position); + AttributeModifier bindNorm = origMesh.modify(GlVertexBuffer.Type.Normal); + AttributeModifier bindTan = origMesh.modify(GlVertexBuffer.Type.Tangent)) { + doTransforms(bindPos, bindNorm, bindTan, position, normal, tangent, bg.startIndex, bg.startIndex + bg.getVertexCount(), transformMat); } - batch.geometry.updateModelBound(); } } - private FloatBuffer getFloatBuffer(VertexBuffer vb) { + private FloatBuffer getFloatBuffer(GlVertexBuffer vb) { if (vb == null) { return null; } @@ -202,7 +186,7 @@ protected void doBatch() { } for (Map.Entry> entry : matMap.entrySet()) { - Mesh m = new Mesh(); + GlMesh m = new GlMesh(); Material material = entry.getKey(); List list = entry.getValue(); nbGeoms += list.size(); @@ -229,13 +213,12 @@ protected void doBatch() { batch.geometry.setMaterial(material); this.attachChild(batch.geometry); - + m.updateCounts(); batch.geometry.setMesh(m); - batch.geometry.getMesh().updateCounts(); batch.geometry.updateModelBound(); batches.add(batch); } - if (batches.size() > 0) { + if (!batches.isEmpty()) { needsFullRebatch = false; } @@ -306,7 +289,7 @@ private void gatherGeometries(Map> map, Spatial n, bool } } if (list == null) { - list = new ArrayList(); + list = new ArrayList<>(); map.put(g.getMaterial(), list); } g.setTransformRefresh(); @@ -378,22 +361,22 @@ public Material getMaterial() { * @param outMesh */ private void mergeGeometries(Mesh outMesh, List geometries) { - int[] compsForBuf = new int[VertexBuffer.Type.values().length]; - VertexBuffer.Format[] formatForBuf = new VertexBuffer.Format[compsForBuf.length]; - boolean[] normForBuf = new boolean[VertexBuffer.Type.values().length]; + int[] compsForBuf = new int[GlVertexBuffer.Type.values().length]; + GlVertexBuffer.Format[] formatForBuf = new GlVertexBuffer.Format[compsForBuf.length]; + boolean[] normForBuf = new boolean[GlVertexBuffer.Type.values().length]; int totalVerts = 0; int totalTris = 0; int totalLodLevels = 0; int maxWeights = -1; - Mesh.Mode mode = null; + GlMesh.Mode mode = null; for (Geometry geom : geometries) { totalVerts += geom.getVertexCount(); totalTris += geom.getTriangleCount(); totalLodLevels = Math.min(totalLodLevels, geom.getMesh().getNumLodLevels()); - Mesh.Mode listMode; + GlMesh.Mode listMode; int components; switch (geom.getMesh().getMode()) { case Points: @@ -410,16 +393,16 @@ private void mergeGeometries(Mesh outMesh, List geometries) { case TriangleFan: case TriangleStrip: case Triangles: - listMode = Mesh.Mode.Triangles; + listMode = GlMesh.Mode.Triangles; components = 3; break; default: throw new UnsupportedOperationException(); } - for (VertexBuffer vb : geom.getMesh().getBufferList().getArray()) { + for (GlVertexBuffer vb : geom.getMesh().getBufferList().getArray()) { int currentCompsForBuf = compsForBuf[vb.getBufferType().ordinal()]; - if (vb.getBufferType() != VertexBuffer.Type.Index && currentCompsForBuf != 0 && currentCompsForBuf != vb.getNumComponents()) { + if (vb.getBufferType() != GlVertexBuffer.Type.Index && currentCompsForBuf != 0 && currentCompsForBuf != vb.getNumComponents()) { throw new UnsupportedOperationException("The geometry " + geom + " buffer " + vb.getBufferType() + " has different number of components than the rest of the meshes " + "(this: " + vb.getNumComponents() + ", expected: " + currentCompsForBuf + ")"); @@ -436,7 +419,7 @@ private void mergeGeometries(Mesh outMesh, List geometries) { + " primitive types: " + mode + " != " + listMode); } mode = listMode; - compsForBuf[VertexBuffer.Type.Index.ordinal()] = components; + compsForBuf[GlVertexBuffer.Type.Index.ordinal()] = components; } outMesh.setMaxNumWeights(maxWeights); @@ -444,9 +427,9 @@ private void mergeGeometries(Mesh outMesh, List geometries) { //outMesh.setLineWidth(lineWidth); if (totalVerts >= 65536) { // Make sure we create an UnsignedInt buffer, so we can fit all of the meshes. - formatForBuf[VertexBuffer.Type.Index.ordinal()] = VertexBuffer.Format.UnsignedInt; + formatForBuf[GlVertexBuffer.Type.Index.ordinal()] = GlVertexBuffer.Format.UnsignedInt; } else { - formatForBuf[VertexBuffer.Type.Index.ordinal()] = VertexBuffer.Format.UnsignedShort; + formatForBuf[GlVertexBuffer.Type.Index.ordinal()] = GlVertexBuffer.Format.UnsignedShort; } // generate output buffers based on retrieved info @@ -456,14 +439,14 @@ private void mergeGeometries(Mesh outMesh, List geometries) { } Buffer data; - if (i == VertexBuffer.Type.Index.ordinal()) { - data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalTris); + if (i == GlVertexBuffer.Type.Index.ordinal()) { + data = GlVertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalTris); } else { - data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalVerts); + data = GlVertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalVerts); } - VertexBuffer vb = new VertexBuffer(VertexBuffer.Type.values()[i]); - vb.setupData(VertexBuffer.Usage.Dynamic, compsForBuf[i], formatForBuf[i], data); + GlVertexBuffer vb = new GlVertexBuffer(GlVertexBuffer.Type.values()[i]); + vb.setupData(GlVertexBuffer.Usage.Dynamic, compsForBuf[i], formatForBuf[i], data); vb.setNormalized(normForBuf[i]); outMesh.setBuffer(vb); } @@ -481,15 +464,15 @@ private void mergeGeometries(Mesh outMesh, List geometries) { int geomTriCount = inMesh.getTriangleCount(); for (int bufType = 0; bufType < compsForBuf.length; bufType++) { - VertexBuffer inBuf = inMesh.getBuffer(VertexBuffer.Type.values()[bufType]); + GlVertexBuffer inBuf = inMesh.getBuffer(GlVertexBuffer.Type.values()[bufType]); - VertexBuffer outBuf = outMesh.getBuffer(VertexBuffer.Type.values()[bufType]); + GlVertexBuffer outBuf = outMesh.getBuffer(GlVertexBuffer.Type.values()[bufType]); if (outBuf == null) { continue; } - if (VertexBuffer.Type.Index.ordinal() == bufType) { + if (GlVertexBuffer.Type.Index.ordinal() == bufType) { int components = compsForBuf[bufType]; IndexBuffer inIdx = inMesh.getIndicesAsList(); @@ -501,15 +484,15 @@ private void mergeGeometries(Mesh outMesh, List geometries) { outIdx.put((globalTriIndex + tri) * components + comp, idx); } } - } else if (VertexBuffer.Type.Position.ordinal() == bufType) { + } else if (GlVertexBuffer.Type.Position.ordinal() == bufType) { FloatBuffer inPos = (FloatBuffer) inBuf.getData(); FloatBuffer outPos = (FloatBuffer) outBuf.getData(); doCopyBuffer(inPos, globalVertIndex, outPos, 3); - } else if (VertexBuffer.Type.Normal.ordinal() == bufType || VertexBuffer.Type.Tangent.ordinal() == bufType) { + } else if (GlVertexBuffer.Type.Normal.ordinal() == bufType || GlVertexBuffer.Type.Tangent.ordinal() == bufType) { FloatBuffer inPos = (FloatBuffer) inBuf.getData(); FloatBuffer outPos = (FloatBuffer) outBuf.getData(); doCopyBuffer(inPos, globalVertIndex, outPos, compsForBuf[bufType]); - if (VertexBuffer.Type.Tangent.ordinal() == bufType) { + if (GlVertexBuffer.Type.Tangent.ordinal() == bufType) { useTangents = true; } } else { @@ -528,7 +511,9 @@ private void mergeGeometries(Mesh outMesh, List geometries) { } } - private void doTransforms(FloatBuffer bindBufPos, FloatBuffer bindBufNorm, FloatBuffer bindBufTangents, FloatBuffer bufPos, FloatBuffer bufNorm, FloatBuffer bufTangents, int start, int end, Matrix4f transform) { + private void doTransforms(VertexReader bindBufPos, VertexReader bindBufNorm, VertexReader bindBufTangents, + VertexWriter bufPos, VertexWriter bufNorm, VertexWriter bufTangents, + int start, int end, Matrix4f transform) { TempVars vars = TempVars.get(); Vector3f pos = vars.vect1; Vector3f norm = vars.vect2; diff --git a/jme3-core/src/main/java/com/jme3/scene/Geometry.java b/jme3-core/src/main/java/com/jme3/scene/Geometry.java index 008e987f1d..b183087d79 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Geometry.java +++ b/jme3-core/src/main/java/com/jme3/scene/Geometry.java @@ -42,11 +42,14 @@ import com.jme3.material.Material; import com.jme3.math.Matrix4f; import com.jme3.renderer.Camera; -import com.jme3.scene.VertexBuffer.Type; import com.jme3.scene.mesh.MorphTarget; import com.jme3.util.TempVars; import com.jme3.util.clone.Cloner; import com.jme3.util.clone.IdentityCloneFunction; +import com.jme3.vulkan.buffers.GpuBuffer; +import com.jme3.vulkan.material.MatrixTransformMaterial; +import com.jme3.vulkan.material.NewMaterial; + import java.io.IOException; import java.util.Queue; import java.util.logging.Level; @@ -68,6 +71,7 @@ public class Geometry extends Spatial { protected Mesh mesh; protected transient int lodLevel = 0; protected Material material; + protected MatrixTransformMaterial transforms; // stores the matrices unique to this geometry /** * When true, the geometry's transform will not be applied. */ @@ -136,6 +140,11 @@ public Geometry(String name, Mesh mesh) { this.mesh = mesh; } + public Geometry(String name, Mesh mesh, MatrixTransformMaterial transforms) { + this(name, mesh); + this.transforms = transforms; + } + /** * Create a geometry node with mesh data and material. * @@ -143,11 +152,18 @@ public Geometry(String name, Mesh mesh) { * @param mesh The mesh data for this geometry * @param material The material for this geometry */ - public Geometry(String name, Mesh mesh, Material material) { + public Geometry(String name, Mesh mesh, NewMaterial material) { this(name, mesh); setMaterial(material); } + public void updateMatrixTransforms(Camera cam) { + Matrix4f worldViewProjection = cam.getViewProjectionMatrix().mult(worldTransform.toTransformMatrix()); + GpuBuffer matBuffer = transforms.getTransforms().get(); + worldViewProjection.fillFloatBuffer(matBuffer.mapFloats(), true); + matBuffer.unmap(); + } + @Override public boolean checkCulling(Camera cam) { if (isGrouped()) { @@ -177,7 +193,7 @@ public void setIgnoreTransform(boolean ignoreTransform) { * Sets the LOD level to use when rendering the mesh of this geometry. * Level 0 indicates that the default index buffer should be used, * levels [1, LodLevels + 1] represent the levels set on the mesh - * with {@link Mesh#setLodLevels(com.jme3.scene.VertexBuffer[]) }. + * with {@link GlMesh#setLodLevels(GlVertexBuffer[]) }. * * @param lod The lod level to set */ @@ -256,7 +272,7 @@ public void setMesh(Mesh mesh) { * * @return the mesh to use for this geometry * - * @see #setMesh(com.jme3.scene.Mesh) + * @see #setMesh(Mesh) */ public Mesh getMesh() { return mesh; @@ -281,7 +297,7 @@ public void setMaterial(Material material) { * * @return the material that is used for this geometry * - * @see #setMaterial(com.jme3.material.Material) + * @see #setMaterial(Material) */ public Material getMaterial() { return material; @@ -480,7 +496,7 @@ public int collideWith(Collidable other, CollisionResults results) { // NOTE: BIHTree in mesh already checks collision with the // mesh's bound int prevSize = results.size(); - int added = mesh.collideWith(other, cachedWorldMat, worldBound, results); + int added = mesh.collideWith(other, this, results); int newSize = results.size(); for (int i = prevSize; i < newSize; i++) { results.getCollisionDirect(i).setGeometry(this); @@ -551,10 +567,13 @@ public Spatial deepClone() { return super.deepClone(); } + @Deprecated public Spatial oldDeepClone() { Geometry geomClone = clone(true); - geomClone.mesh = mesh.deepClone(); - return geomClone; + // fixme + //geomClone.mesh = mesh.deepClone(); + //return geomClone; + throw new UnsupportedOperationException("Mesh deep clone not yet supported."); } /** @@ -589,9 +608,11 @@ public void cloneFields(Cloner cloner, Object original) { // See if we clone the mesh using the special animation // semi-deep cloning - if (shallowClone && mesh != null && mesh.getBuffer(Type.BindPosePosition) != null) { + // fixme + if (shallowClone && mesh != null && false /*mesh.getBuffer(Type.BindPosePosition) != null*/) { // Then we need to clone the mesh a little deeper - this.mesh = mesh.cloneForAnim(); + //this.mesh = mesh.cloneForAnim(); + throw new UnsupportedOperationException("Animation cloning not yet supported."); } else { // Do whatever the cloner wants to do about it this.mesh = cloner.clone(mesh); @@ -600,18 +621,20 @@ public void cloneFields(Cloner cloner, Object original) { this.material = cloner.clone(material); } - public void setMorphState(float[] state) { - if (mesh == null || mesh.getMorphTargets().length == 0) { - return; - } - - int nbMorphTargets = mesh.getMorphTargets().length; + // todo: fix morph animations - if (morphState == null) { - morphState = new float[nbMorphTargets]; - } - System.arraycopy(state, 0, morphState, 0, morphState.length); - this.dirtyMorph = true; + public void setMorphState(float[] state) { +// if (mesh == null || mesh.getMorphTargets().length == 0) { +// return; +// } +// +// int nbMorphTargets = mesh.getMorphTargets().length; +// +// if (morphState == null) { +// morphState = new float[nbMorphTargets]; +// } +// System.arraycopy(state, 0, morphState, 0, morphState.length); +// this.dirtyMorph = true; } /** @@ -623,11 +646,11 @@ public void setMorphState(float[] state) { * @param state The state to set the morph to */ public void setMorphState(String morphTarget, float state) { - int index = mesh.getMorphIndex(morphTarget); - if (index >= 0) { - morphState[index] = state; - this.dirtyMorph = true; - } +// int index = mesh.getMorphIndex(morphTarget); +// if (index >= 0) { +// morphState[index] = state; +// this.dirtyMorph = true; +// } } /** @@ -656,8 +679,10 @@ public void setDirtyMorph(boolean dirtyMorph) { * @return an array */ public float[] getMorphState() { + // fixme if (morphState == null) { - morphState = new float[mesh.getMorphTargets().length]; + //morphState = new float[mesh.getMorphTargets().length]; + morphState = new float[0]; } return morphState; } @@ -669,7 +694,9 @@ public float[] getMorphState() { * @return the state of the morph, or -1 if the morph is not found */ public float getMorphState(String morphTarget) { - int index = mesh.getMorphIndex(morphTarget); + // fixme + //int index = mesh.getMorphIndex(morphTarget); + int index = -1; if (index < 0) { return -1; } else { @@ -720,10 +747,11 @@ public void write(JmeExporter ex) throws IOException { super.write(ex); OutputCapsule oc = ex.getCapsule(this); oc.write(mesh, "mesh", null); - if (material != null) { - oc.write(material.getAssetName(), "materialName", null); - } - oc.write(material, "material", null); + // fixme +// if (material != null) { +// oc.write(material.getAssetName(), "materialName", null); +// } +// oc.write(material, "material", null); oc.write(ignoreTransform, "ignoreTransform", false); } @@ -732,14 +760,14 @@ public void read(JmeImporter im) throws IOException { super.read(im); InputCapsule ic = im.getCapsule(this); mesh = (Mesh) ic.readSavable("mesh", null); - material = null; String matName = ic.readString("materialName", null); if (matName != null) { // Material name is set, // Attempt to load material via J3M try { - material = im.getAssetManager().loadMaterial(matName); + // fixme + //material = im.getAssetManager().loadMaterial(matName); } catch (AssetNotFoundException ex) { // Cannot find J3M file. if (logger.isLoggable(Level.FINE)) { @@ -750,7 +778,8 @@ public void read(JmeImporter im) throws IOException { } // If material is NULL, try to load it from the geometry if (material == null) { - material = (Material) ic.readSavable("material", null); + // fixme + material = (NewMaterial) ic.readSavable("material", null); } ignoreTransform = ic.readBoolean("ignoreTransform", false); @@ -758,9 +787,17 @@ public void read(JmeImporter im) throws IOException { // Fix shared mesh (if set) Mesh sharedMesh = getUserData(UserData.JME_SHAREDMESH); if (sharedMesh != null) { - getMesh().extractVertexData(sharedMesh); + // fixme + //getMesh().extractVertexData(sharedMesh); setUserData(UserData.JME_SHAREDMESH, null); + throw new UnsupportedOperationException("Shared meshes not yet supported."); } } } + + @Override + protected void findNextIteration(GraphIterator iterator) { + iterator.moveUp(); + } + } diff --git a/jme3-core/src/main/java/com/jme3/scene/GeometryGroupNode.java b/jme3-core/src/main/java/com/jme3/scene/GeometryGroupNode.java index db245392ca..be5e0f0492 100644 --- a/jme3-core/src/main/java/com/jme3/scene/GeometryGroupNode.java +++ b/jme3-core/src/main/java/com/jme3/scene/GeometryGroupNode.java @@ -57,7 +57,7 @@ public GeometryGroupNode(String name) { /** * Called by {@link Geometry geom} to specify that its - * {@link Geometry#setMesh(com.jme3.scene.Mesh) mesh} + * {@link Geometry#setMesh(Mesh) mesh} * has been changed. * * This is also called when the geometry's diff --git a/jme3-core/src/main/java/com/jme3/scene/GlMesh.java b/jme3-core/src/main/java/com/jme3/scene/GlMesh.java new file mode 100644 index 0000000000..22df7b267c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/scene/GlMesh.java @@ -0,0 +1,1538 @@ +/* + * Copyright (c) 2009-2022 jMonkeyEngine + * 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 'jMonkeyEngine' 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 com.jme3.scene; + +import com.jme3.bounding.BoundingBox; +import com.jme3.bounding.BoundingVolume; +import com.jme3.collision.Collidable; +import com.jme3.collision.CollisionResults; +import com.jme3.collision.bih.BIHTree; +import com.jme3.dev.NotFullyImplemented; +import com.jme3.export.*; +import com.jme3.material.RenderState; +import com.jme3.math.*; +import com.jme3.scene.GlVertexBuffer.*; +import com.jme3.scene.mesh.*; +import com.jme3.util.*; +import com.jme3.vulkan.buffers.GpuBuffer; +import com.jme3.vulkan.mesh.DataAccess; +import com.jme3.vulkan.mesh.exp.GlVertex; +import com.jme3.vulkan.pipeline.Topology; +import com.jme3.vulkan.util.IntEnum; + +import java.io.IOException; +import java.nio.*; +import java.util.ArrayList; +import java.util.List; + +/** + * Mesh is used to store rendering data. + *

+ * All visible elements in a scene are represented by meshes. + * Meshes may contain three types of geometric primitives: + *

    + *
  • Points - Every vertex represents a single point in space. + *
  • Lines - 2 vertices represent a line segment, with the width specified + * via {@link com.jme3.material.GlMaterial#getAdditionalRenderState()} and {@link RenderState#setLineWidth(float)}.
  • + *
  • Triangles - 3 vertices represent a solid triangle primitive.
  • + *
+ * + * @author Kirill Vainer + */ +public class GlMesh implements Mesh, Savable { + + /** + * The mode of the Mesh specifies both the type of primitive represented + * by the mesh and how the data should be interpreted. + */ + public enum Mode implements IntEnum { + /** + * A primitive is a single point in space. The size of {@link Mode#Points points} are + * determined via the vertex shader's gl_PointSize output. + */ + Points(true), + /** + * A primitive is a line segment. Every two vertices specify + * a single line. {@link com.jme3.material.GlMaterial#getAdditionalRenderState()} + * and {@link RenderState#setLineWidth(float)} can be used + * to set the width of the lines. + */ + Lines(true), + /** + * A primitive is a line segment. The first two vertices specify + * a single line, while subsequent vertices are combined with the + * previous vertex to make a line. {@link com.jme3.material.GlMaterial#getAdditionalRenderState()} + * and {@link RenderState#setLineWidth(float)} can + * be used to set the width of the lines. + */ + LineStrip(false), + /** + * Identical to {@link #LineStrip} except that at the end + * the last vertex is connected with the first to form a line. + * {@link com.jme3.material.GlMaterial#getAdditionalRenderState()} + * and {@link RenderState#setLineWidth(float)} can be used + * to set the width of the lines. + */ + LineLoop(false), + /** + * A primitive is a triangle. Each 3 vertices specify a single + * triangle. + */ + Triangles(true), + /** + * Similar to {@link #Triangles}, the first 3 vertices + * specify a triangle, while subsequent vertices are combined with + * the previous two to form a triangle. + */ + TriangleStrip(false), + /** + * Similar to {@link #Triangles}, the first 3 vertices + * specify a triangle, each 2 subsequent vertices are combined + * with the very first vertex to make a triangle. + */ + TriangleFan(false), + /** + * A combination of various triangle modes. It is best to avoid + * using this mode as it may not be supported by all renderers. + * The {@link GlMesh#setModeStart(int[]) mode start points} and + * {@link GlMesh#setElementLengths(int[]) element lengths} must + * be specified for this mode. + */ + Hybrid(false), + /** + * Used for Tessellation only. Requires to set the number of vertices + * for each patch (default is 3 for triangle tessellation) + */ + Patch(true); + + private boolean listMode = false; + + private Mode(boolean listMode) { + this.listMode = listMode; + } + + /** + * Returns true if the specified mode is a list mode (meaning + * ,it specifies the indices as a linear list and not some special + * format). + * Will return true for the types {@link #Points}, {@link #Lines} and + * {@link #Triangles}. + * + * @return true if the mode is a list type mode + */ + public boolean isListMode() { + return listMode; + } + + @Override + public int getEnum() { + return 0; + } + + } + + /** + * Default Variables + */ + private static final int DEFAULT_VERTEX_ARRAY_ID = -1; + private static final CollisionData DEFAULT_COLLISION_TREE = null; + + private static final float DEFAULT_POINT_SIZE = 1.0f; + private static final float DEFAULT_LINE_WIDTH = 1.0f; + + private static final int DEFAULT_VERT_COUNT = -1; + private static final int DEFAULT_ELEMENT_COUNT = -1; + private static final int DEFAULT_INSTANCE_COUNT = -1; + private static final int DEFAULT_PATCH_VERTEX_COUNT = 3; + private static final int DEFAULT_MAX_NUM_WEIGHTS = -1; + + /** + * The bounding volume that contains the mesh entirely. + * By default a BoundingBox (AABB). + */ + private BoundingVolume meshBound = new BoundingBox(); + + private CollisionData collisionTree = DEFAULT_COLLISION_TREE; + + //private SafeArrayList buffersList = new SafeArrayList<>(GlVertexBuffer.class); + //private IntMap buffers = new IntMap<>(); + private GlVertex vertices = new GlVertex(); + private List indices = new ArrayList<>(1); + + private float pointSize = DEFAULT_POINT_SIZE; + + private transient int vertexArrayID = DEFAULT_VERTEX_ARRAY_ID; + + private int vertCount = DEFAULT_VERT_COUNT; + private int elementCount = DEFAULT_ELEMENT_COUNT; + private int instanceCount = DEFAULT_INSTANCE_COUNT; + private int patchVertexCount = DEFAULT_PATCH_VERTEX_COUNT; //only used for tessellation + private int maxNumWeights = DEFAULT_MAX_NUM_WEIGHTS; // only if using skeletal animation + + private int[] elementLengths; + private int[] modeStart; + + private Mode mode = Mode.Triangles; + private SafeArrayList morphTargets; + + /** + * Creates a new mesh with no {@link GlVertexBuffer vertex buffers}. + */ + public GlMesh() { + } + + // Why are there so many cloning methods??? + + /** + * Create a shallow clone of this Mesh. The {@link GlVertexBuffer vertex + * buffers} are shared between this and the clone mesh, the rest + * of the data is cloned. + * + * @return A shallow clone of the mesh + */ + @Override + public GlMesh clone() { + try { + GlMesh clone = (GlMesh) super.clone(); + clone.meshBound = meshBound.clone(); + clone.collisionTree = collisionTree != null ? collisionTree : null; + clone.vertices = vertices.copy(); + clone.vertexArrayID = DEFAULT_VERTEX_ARRAY_ID; + if (elementLengths != null) { + clone.elementLengths = elementLengths.clone(); + } + if (modeStart != null) { + clone.modeStart = modeStart.clone(); + } + return clone; + } catch (CloneNotSupportedException ex) { + throw new AssertionError(); + } + } + + /** + * Creates a deep clone of this mesh. + * The {@link GlVertexBuffer vertex buffers} and the data inside them + * is cloned. + * + * @return a deep clone of this mesh. + */ + public GlMesh deepClone() { + try { + GlMesh clone = (GlMesh) super.clone(); + clone.meshBound = meshBound != null ? meshBound.clone() : null; + + // TODO: Collision tree cloning + //clone.collisionTree = collisionTree != null ? collisionTree : null; + clone.collisionTree = DEFAULT_COLLISION_TREE; // it will get re-generated in any case + + clone.vertices = vertices.copy(); + + clone.vertexArrayID = DEFAULT_VERTEX_ARRAY_ID; + clone.vertCount = vertCount; + clone.elementCount = elementCount; + clone.instanceCount = instanceCount; + + // although this could change + // if the bone weight/index buffers are modified + clone.maxNumWeights = maxNumWeights; + + clone.elementLengths = elementLengths != null ? elementLengths.clone() : null; + clone.modeStart = modeStart != null ? modeStart.clone() : null; + return clone; + } catch (CloneNotSupportedException ex) { + throw new AssertionError(); + } + } + + /** + * Clone the mesh for animation use. + * This creates a shallow clone of the mesh, sharing most + * of the {@link GlVertexBuffer vertex buffer} data, however the + * {@link Type#Position}, {@link Type#Normal}, and {@link Type#Tangent} buffers + * are deeply cloned. + * + * @return A clone of the mesh for animation use. + */ + public GlMesh cloneForAnim() { + GlMesh clone = clone(); + if (getBuffer(Type.BindPosePosition) != null) { + GlVertexBuffer oldPos = getBuffer(Type.Position); + + // NOTE: creates deep clone + GlVertexBuffer newPos = oldPos.clone(); + clone.clearBuffer(Type.Position); + clone.setBuffer(newPos); + + if (getBuffer(Type.BindPoseNormal) != null) { + GlVertexBuffer oldNorm = getBuffer(Type.Normal); + GlVertexBuffer newNorm = oldNorm.clone(); + clone.clearBuffer(Type.Normal); + clone.setBuffer(newNorm); + + if (getBuffer(Type.BindPoseTangent) != null) { + GlVertexBuffer oldTang = getBuffer(Type.Tangent); + GlVertexBuffer newTang = oldTang.clone(); + clone.clearBuffer(Type.Tangent); + clone.setBuffer(newTang); + } + } + } + return clone; + } + + /** + * @param forSoftwareAnim ignored + * @deprecated use generateBindPose(); + */ + @Deprecated + public void generateBindPose(boolean forSoftwareAnim) { + generateBindPose(); + } + + @Override + public void setTopology(IntEnum topology) { + + } + + @Override + public IntEnum getTopology() { + return null; + } + + /** + * Generates the {@link Type#BindPosePosition}, {@link Type#BindPoseNormal}, + * and {@link Type#BindPoseTangent} + * buffers for this mesh by duplicating them based on the position and normal + * buffers already set on the mesh. + * This method does nothing if the mesh has no bone weight or index + * buffers. + */ + public void generateBindPose() { + GlVertexBuffer pos = getBuffer(Type.Position); + if (pos == null || getBuffer(Type.BoneIndex) == null) { + // ignore, this mesh doesn't have positional data + // or it doesn't have bone-vertex assignments, so it's not animated + return; + } + + GlVertexBuffer bindPos = new GlVertexBuffer(Type.BindPosePosition); + bindPos.setupData(Usage.CpuOnly, + pos.getNumComponents(), + pos.getFormat(), + BufferUtils.clone(pos.getData())); + setBuffer(bindPos); + + // XXX: note that this method also sets stream mode + // so that animation is faster. this is not needed for hardware skinning + pos.setUsage(Usage.Stream); + + GlVertexBuffer norm = getBuffer(Type.Normal); + if (norm != null) { + GlVertexBuffer bindNorm = new GlVertexBuffer(Type.BindPoseNormal); + bindNorm.setupData(Usage.CpuOnly, + norm.getNumComponents(), + norm.getFormat(), + norm.getData()); + setBuffer(bindNorm); + norm.setUsage(Usage.Stream); + } + + GlVertexBuffer tangents = getBuffer(Type.Tangent); + if (tangents != null) { + GlVertexBuffer bindTangents = new GlVertexBuffer(Type.BindPoseTangent); + bindTangents.setupData(Usage.CpuOnly, + tangents.getNumComponents(), + tangents.getFormat(), + tangents.getData()); + setBuffer(bindTangents); + tangents.setUsage(Usage.Stream); + }// else hardware setup does nothing, mesh already in bind pose + + } + + public void prepareSoftwareAnim() { + // convert indices to ubytes on the heap + GlVertexBuffer indices = getBuffer(Type.BoneIndex); + indices.setUsage(Usage.CpuOnly); + + // convert weights on the heap + GlVertexBuffer weights = getBuffer(Type.BoneWeight); + weights.setUsage(Usage.CpuOnly); + + // position, normal, and tangent buffers to be in "Stream" mode + GlVertexBuffer positions = getBuffer(Type.Position); + GlVertexBuffer normals = getBuffer(Type.Normal); + GlVertexBuffer tangents = getBuffer(Type.Tangent); + positions.setUsage(Usage.Stream); + if (normals != null) { + normals.setUsage(Usage.Stream); + } + if (tangents != null) { + tangents.setUsage(Usage.Stream); + } + } + + public void prepareHardwareAnim() { + //if HWBoneIndex and HWBoneWeight are empty, we set up them as direct + //buffers with software anim buffers data + GlVertexBuffer indicesHW = getBuffer(Type.HWBoneIndex); + Buffer result; + if (indicesHW.getData() == null) { + GlVertexBuffer indices = getBuffer(Type.BoneIndex); + indicesHW.setupData(Usage.Static, indices.getNumComponents(), + indices.getFormat(), indices.getData()); + } + + GlVertexBuffer weightsHW = getBuffer(Type.HWBoneWeight); + if (weightsHW.getData() == null) { + GlVertexBuffer weights = getBuffer(Type.BoneWeight); + weightsHW.setupData(Usage.Static, weights.getNumComponents(), + weights.getFormat(), weights.getData()); + } + + // position, normal, and tangent buffers to be in "Static" mode + GlVertexBuffer positions = getBuffer(Type.Position); + GlVertexBuffer normals = getBuffer(Type.Normal); + GlVertexBuffer tangents = getBuffer(Type.Tangent); + + GlVertexBuffer positionsBP = getBuffer(Type.BindPosePosition); + GlVertexBuffer normalsBP = getBuffer(Type.BindPoseNormal); + GlVertexBuffer tangentsBP = getBuffer(Type.BindPoseTangent); + + positions.setUsage(Usage.Static); + positionsBP.copyElements(0, positions, 0, positionsBP.getNumElements()); + positions.setUpdateNeeded(); + + if (normals != null) { + normals.setUsage(Usage.Static); + normalsBP.copyElements(0, normals, 0, normalsBP.getNumElements()); + normals.setUpdateNeeded(); + } + + if (tangents != null) { + tangents.setUsage(Usage.Static); + tangentsBP.copyElements(0, tangents, 0, tangentsBP.getNumElements()); + tangents.setUpdateNeeded(); + } + } + + /** + * Prepares the mesh for software skinning by converting the bone index + * and weight buffers to heap buffers. + * + * @param forSoftwareAnim Should be true to enable the conversion. + * @deprecated use {@link #prepareSoftwareAnim()} or {@link #prepareHardwareAnim()} instead + */ + @Deprecated + public void prepareForAnim(boolean forSoftwareAnim) { + if (forSoftwareAnim) { + prepareSoftwareAnim(); + } else { + prepareHardwareAnim(); + } + } + + /** + * @return The number of LOD levels set on this mesh, including the main + * index buffer, returns zero if there are no lod levels. + */ + @Override + public int getNumLodLevels() { + return indices.size(); + } + + /** + * Get the element lengths for {@link Mode#Hybrid} mesh mode. + * + * @return element lengths + */ + public int[] getElementLengths() { + return elementLengths; + } + + /** + * Set the element lengths for {@link Mode#Hybrid} mesh mode. + * + * @param elementLengths The element lengths to set + */ + public void setElementLengths(int[] elementLengths) { + this.elementLengths = elementLengths; + } + + /** + * Set the mode start indices for {@link Mode#Hybrid} mesh mode. + * + * @return mode start indices + */ + public int[] getModeStart() { + return modeStart; + } + + /** + * Get the mode start indices for {@link Mode#Hybrid} mesh mode. + * + * @param modeStart the pre-existing array + */ + public void setModeStart(int[] modeStart) { + this.modeStart = modeStart; + } + + /** + * Returns the mesh mode + * + * @return the mesh mode + * + * @see #setMode(GlMesh.Mode) + */ + @Override + public Mode getMode() { + return mode; + } + + /** + * Change the Mesh's mode. By default the mode is {@link Mode#Triangles}. + * + * @param mode The new mode to set + * + * @see Mode + */ + public void setMode(Mode mode) { + this.mode = mode; + updateCounts(); + } + + /** + * Returns the maximum number of weights per vertex on this mesh. + * + * @return maximum number of weights per vertex + * + * @see #setMaxNumWeights(int) + */ + public int getMaxNumWeights() { + return maxNumWeights; + } + + /** + * Set the maximum number of weights per vertex on this mesh. + * Only relevant if this mesh has bone index/weight buffers. + * This value should be between 0 and 4. + * + * @param maxNumWeights the desired number (between 0 and 4, inclusive) + */ + public void setMaxNumWeights(int maxNumWeights) { + this.maxNumWeights = maxNumWeights; + } + + /** + * Indicates to the GPU that this mesh will not be modified (a hint). + * Sets the usage mode to {@link Usage#Static} + * for all {@link GlVertexBuffer vertex buffers} on this Mesh. + */ + public void setStatic() { + vertices.setAllAccess(DataAccess.Static); + } + + /** + * Indicates to the GPU that this mesh will be modified occasionally (a hint). + * Sets the usage mode to {@link Usage#Dynamic} + * for all {@link GlVertexBuffer vertex buffers} on this Mesh. + */ + public void setDynamic() { + vertices.setAllAccess(DataAccess.Dynamic); + } + + /** + * Indicates to the GPU that this mesh will be modified every frame (a hint). + * Sets the usage mode to {@link Usage#Stream} + * for all {@link GlVertexBuffer vertex buffers} on this Mesh. + */ + public void setStreamed() { + vertices.setAllAccess(DataAccess.Stream); + } + + private int computeNumElements(int bufSize) { + switch (mode) { + case Triangles: + return bufSize / 3; + case TriangleFan: + case TriangleStrip: + return bufSize - 2; + case Points: + return bufSize; + case Lines: + return bufSize / 2; + case LineLoop: + return bufSize; + case LineStrip: + return bufSize - 1; + case Patch: + return bufSize / patchVertexCount; + default: + throw new UnsupportedOperationException(); + } + } + + private int computeInstanceCount() { + // Whatever the max of the base instance counts + int max = 0; + for (GlVertexBuffer vb : vertices) { + if (vb.getBaseInstanceCount() > max) { + max = vb.getBaseInstanceCount(); + } + } + return max; + } + + /** + * Update the {@link #getVertexCount() vertex} and + * {@link #getTriangleCount() triangle} counts for this mesh + * based on the current data. This method should be called + * after the {@link Buffer#capacity() capacities} of the mesh's + * {@link GlVertexBuffer vertex buffers} has been altered. + */ + public void updateCounts() { + if (getBuffer(Type.InterleavedData) != null) { + throw new IllegalStateException("Should update counts before interleave"); + } + + GlVertexBuffer pb = getBuffer(Type.Position); + GlVertexBuffer ib = getBuffer(Type.Index); + if (pb != null) { + vertCount = pb.getData().limit() / pb.getNumComponents(); + } + if (ib != null) { + elementCount = computeNumElements(ib.getData().limit()); + } else { + elementCount = computeNumElements(vertCount); + } + instanceCount = computeInstanceCount(); + } + + /** + * Returns the triangle count for the given LOD level. + * + * @param lod The lod level to look up + * @return The triangle count for that LOD level + */ + @Override + public int getTriangleCount(int lod) { + if (lod < 0) { + throw new IllegalArgumentException("LOD level cannot be < 0"); + } + if (lod >= indices.size()) { + throw new IllegalArgumentException("LOD level " + lod + " does not exist!"); + } + return computeNumElements(indices.get(lod).getData().limit()); + } + + /** + * Returns how many triangles or elements are on this Mesh. + * This value is only updated when {@link #updateCounts() } is called. + * If the mesh mode is not a triangle mode, then this returns the + * number of elements/primitives, e.g. how many lines or how many points, + * instead of how many triangles. + * + * @return how many triangles/elements are on this Mesh. + */ + @Override + public int getTriangleCount() { + return elementCount; + } + + @Override + public GlMeshModifier modify(String attributeName) { + return new GlMeshModifier(getBuffer(Type.valueOf(attributeName))); + } + + @Override + public boolean attributeExists(String attributeName) { + return getBuffer(Type.valueOf(attributeName)) != null; + } + + @Override + public GpuBuffer getIndices(int lod) { + return indices.get(lod).getBuffer(); + } + + @Override + public void setAccessFrequency(String attributeName, DataAccess hint) { + getBuffer(Type.valueOf(attributeName)).setAccess(hint); + } + + @Override + public void setVertexCount(int vertices) { + for (GlVertexBuffer buf : this.vertices) { + buf.setNumVertices(vertices); + } + } + + @Override + public void setTriangleCount(int lod, int triangles) { + throw new UnsupportedOperationException("To be implemented."); + } + + @Override + public void setInstanceCount(int instances) { + throw new UnsupportedOperationException("To be implemented."); + } + + /** + * Returns the number of vertices on this mesh. + * The value is computed based on the position buffer, which + * must be set on all meshes. + * + * @return Number of vertices on the mesh + */ + @Override + public int getVertexCount() { + return vertCount; + } + + /** + * Returns the number of instances this mesh contains. The instance + * count is based on any VertexBuffers with instancing set. + * + * @return the number of instances + */ + @Override + public int getInstanceCount() { + return instanceCount; + } + + @Override + public int collideWith(Collidable other, Geometry geometry, CollisionResults results) { + return 0; + } + + /** + * Gets the triangle vertex positions at the given triangle index + * and stores them into the v1, v2, v3 arguments. + * + * @param index The index of the triangle. + * Should be between 0 and {@link #getTriangleCount()}. + * + * @param v1 Vector to contain first vertex position + * @param v2 Vector to contain second vertex position + * @param v3 Vector to contain third vertex position + */ + public void getTriangle(int index, Vector3f v1, Vector3f v2, Vector3f v3) { + GlVertexBuffer pb = getBuffer(Type.Position); + IndexBuffer ib = getIndicesAsList(); + if (pb != null && pb.getFormat() == Format.Float && pb.getNumComponents() == 3) { + FloatBuffer fpb = (FloatBuffer) pb.getData(); + + // acquire triangle's vertex indices + int vertIndex = index * 3; + int vert1 = ib.get(vertIndex); + int vert2 = ib.get(vertIndex + 1); + int vert3 = ib.get(vertIndex + 2); + + BufferUtils.populateFromBuffer(v1, fpb, vert1); + BufferUtils.populateFromBuffer(v2, fpb, vert2); + BufferUtils.populateFromBuffer(v3, fpb, vert3); + } else { + throw new UnsupportedOperationException("Position buffer not set or " + + " has incompatible format"); + } + } + + /** + * Gets the triangle vertex positions at the given triangle index + * and stores them into the {@link Triangle} argument. + * Also sets the triangle index to the index argument. + * + * @param index The index of the triangle. + * Should be between 0 and {@link #getTriangleCount()}. + * + * @param tri The triangle to store the positions in + */ + @Override + public Triangle getTriangle(int index, Triangle tri) { + if (tri == null) { + tri = new Triangle(); + } + getTriangle(index, tri.get1(), tri.get2(), tri.get3()); + tri.setIndex(index); + tri.setCenter(null); // invalidate previously cached centroid, if any + tri.setNormal(null); + return tri; + } + + /** + * Gets the triangle vertex indices at the given triangle index + * and stores them into the given int array. + * + * @param index The index of the triangle. + * Should be between 0 and {@link #getTriangleCount()}. + * + * @param indices Indices of the triangle's vertices + */ + @Override + public int[] getTriangle(int index, int[] indices) { + IndexBuffer ib = getIndicesAsList(); + if (indices == null) { + indices = new int[3]; + } else if (indices.length < 3) { + throw new IllegalArgumentException("Index storage array must have at least 3 elements."); + } + // acquire triangle's vertex indices + int vertIndex = index * 3; + indices[0] = ib.get(vertIndex); + indices[1] = ib.get(vertIndex + 1); + indices[2] = ib.get(vertIndex + 2); + return indices; + } + + /** + * Returns the mesh's VAO ID. Internal use only. + * + * @return the array ID + */ + public int getId() { + return vertexArrayID; + } + + /** + * Sets the mesh's VAO ID. Internal use only. + * + * @param id the array ID + */ + public void setId(int id) { + if (vertexArrayID != DEFAULT_VERTEX_ARRAY_ID) { + throw new IllegalStateException("ID has already been set."); + } + + vertexArrayID = id; + } + + public GlVertexBuffer getIndexBuffer(int lod) { + return indices.get(lod); + } + + /** + * Generates a collision tree for the mesh. + * Called automatically by {@link #collideWith(com.jme3.collision.Collidable, + * com.jme3.math.Matrix4f, + * com.jme3.bounding.BoundingVolume, + * com.jme3.collision.CollisionResults) }. + */ + public void createCollisionData() { + throw new UnsupportedOperationException("Collision tree not supported."); +// BIHTree tree = new BIHTree(this); +// tree.construct(); +// collisionTree = tree; + } + + /** + * Clears any previously generated collision data. Use this if + * the mesh has changed in some way that invalidates any previously + * generated BIHTree. + */ + public void clearCollisionData() { + collisionTree = DEFAULT_COLLISION_TREE; + } + + /** + * Handles collision detection, internal use only. + * User code should only use collideWith() on scene + * graph elements such as {@link Spatial}s. + * + * @param other the other Collidable + * @param worldMatrix the world matrix + * @param worldBound the world bound + * @param results storage for the results + * @return the number of collisions detected (≥0) + */ + public int collideWith(Collidable other, + Matrix4f worldMatrix, + BoundingVolume worldBound, + CollisionResults results) { + + switch (mode) { + case Points: + case Lines: + case LineStrip: + case LineLoop: + /* + * Collisions can be detected only with triangles, + * and there are no triangles in this mesh. + */ + return 0; + } + + if (getVertexCount() == 0) { + return 0; + } + + if (collisionTree == null) { + createCollisionData(); + } + + return collisionTree.collideWith(other, worldMatrix, worldBound, results); + } + + /** + * Sets the {@link GlVertexBuffer} on the mesh. + * This will update the vertex/triangle counts if needed. + * + * @param vb The buffer to set + * @throws IllegalArgumentException If the buffer type is already set + */ + @Deprecated + public void setBuffer(GlVertexBuffer vb) { +// if (buffers.containsKey(vb.getBufferType().ordinal())) { +// throw new IllegalArgumentException("Buffer type already set: " + vb.getBufferType()); +// } +// +// buffers.put(vb.getBufferType().ordinal(), vb); +// buffersList.add(vb); + updateCounts(); + } + + /** + * Unsets the {@link GlVertexBuffer} set on this mesh + * with the given type. Does nothing if the vertex buffer type is not set + * initially. + * + * @param type The buffer type to remove + */ + public void clearBuffer(GlVertexBuffer.Type type) { + if (vertices.clearBuffer(type) != null) { + updateCounts(); + } + } + + /** + * Creates a {@link GlVertexBuffer} for the mesh or modifies + * the existing one per the parameters given. + * + * @param type The type of the buffer + * @param components Number of components + * @param format Data format + * @param buf The buffer data + * + * @throws UnsupportedOperationException If the buffer already set is + * incompatible with the parameters given. + */ + public void setBuffer(Type type, int components, Format format, Buffer buf) { + GlVertexBuffer vb = vertices.getVertexBuffer(type); + if (vb == null) { + vb = new GlVertexBuffer(type); + vb.setupData(Usage.Dynamic, components, format, buf); + setBuffer(vb); + } else { + if (vb.getNumComponents() != components || vb.getFormat() != format) { + throw new UnsupportedOperationException("The buffer already set " + + "is incompatible with the given parameters"); + } + vb.updateData(buf); + updateCounts(); + } + } + + /** + * Set a floating point {@link GlVertexBuffer} on the mesh. + * + * @param type The type of {@link GlVertexBuffer}, + * e.g. {@link Type#Position}, {@link Type#Normal}, etc. + * + * @param components Number of components on the vertex buffer, should + * be between 1 and 4. + * + * @param buf The floating point data to contain + */ + public void setBuffer(Type type, int components, FloatBuffer buf) { + setBuffer(type, components, Format.Float, buf); + } + + public void setBuffer(Type type, int components, float[] buf) { + setBuffer(type, components, BufferUtils.createFloatBuffer(buf)); + } + + public void setBuffer(Type type, int components, IntBuffer buf) { + setBuffer(type, components, Format.UnsignedInt, buf); + } + + public void setBuffer(Type type, int components, int[] buf) { + setBuffer(type, components, BufferUtils.createIntBuffer(buf)); + } + + public void setBuffer(Type type, int components, ShortBuffer buf) { + setBuffer(type, components, Format.UnsignedShort, buf); + } + + public void setBuffer(Type type, int components, byte[] buf) { + setBuffer(type, components, BufferUtils.createByteBuffer(buf)); + } + + public void setBuffer(Type type, int components, ByteBuffer buf) { + setBuffer(type, components, Format.UnsignedByte, buf); + } + + public void setBuffer(Type type, int components, short[] buf) { + setBuffer(type, components, BufferUtils.createShortBuffer(buf)); + } + + /** + * Get the {@link GlVertexBuffer} stored on this mesh with the given + * type. + * + * @param type The type of VertexBuffer + * @return the VertexBuffer data, or null if not set + */ + public GlVertexBuffer getBuffer(Type type) { + return vertices.getVertexBuffer(type); + } + + /** + * Get the {@link GlVertexBuffer} data stored on this mesh in float + * format. + * + * @param type The type of VertexBuffer + * @return the VertexBuffer data, or null if not set + */ + public FloatBuffer getFloatBuffer(Type type) { + GlVertexBuffer vb = getBuffer(type); + if (vb == null) { + return null; + } + + return (FloatBuffer) vb.getData(); + } + + /** + * Get the {@link GlVertexBuffer} data stored on this mesh in short + * format. + * + * @param type The type of VertexBuffer + * @return the VertexBuffer data, or null if not set + */ + public ShortBuffer getShortBuffer(Type type) { + GlVertexBuffer vb = getBuffer(type); + if (vb == null) { + return null; + } + + return (ShortBuffer) vb.getData(); + } + + /** + * Acquires an index buffer that will read the vertices on the mesh + * as a list. + * + * @return A virtual or wrapped index buffer to read the data as a list + */ + public IndexBuffer getIndicesAsList() { + if (mode == Mode.Hybrid) { + throw new UnsupportedOperationException("Hybrid mode not supported"); + } + + IndexBuffer ib = getIndexBuffer(); + if (ib != null) { + if (mode.isListMode()) { + // already in list mode + return ib; + } else { + // not in list mode but it does have an index buffer + // wrap it so the data is converted to list format + return new WrappedIndexBuffer(this); + } + } else { + // return a virtual index buffer that will supply + // "fake" indices in list format + return new VirtualIndexBuffer(vertCount, mode); + } + } + + /** + * Get the index buffer for this mesh. + * Will return null if no index buffer is set. + * + * @return The index buffer of this mesh. + * + * @see Type#Index + */ + public IndexBuffer getIndexBuffer() { + GlVertexBuffer vb = getBuffer(Type.Index); + if (vb == null) { + return null; + } + + return IndexBuffer.wrapIndexBuffer(vb.getData()); + } + + /** + * Extracts the vertex attributes from the given mesh into + * this mesh, by using this mesh's {@link #getIndexBuffer() index buffer} + * to index into the attributes of the other mesh. + * Note that this will also change this mesh's index buffer so that + * the references to the vertex data match the new indices. + * + * @param other The mesh to extract the vertex data from + */ + public void extractVertexData(GlMesh other) { + // Determine the number of unique vertices need to + // be created. Also determine the mappings + // between old indices to new indices (since we avoid duplicating + // vertices, this is a map and not an array). + GlVertexBuffer oldIdxBuf = getBuffer(Type.Index); + IndexBuffer indexBuf = getIndexBuffer(); + int numIndices = indexBuf.size(); + + IntMap oldIndicesToNewIndices = new IntMap<>(numIndices); + ArrayList newIndicesToOldIndices = new ArrayList<>(); + int newIndex = 0; + + for (int i = 0; i < numIndices; i++) { + int oldIndex = indexBuf.get(i); + + if (!oldIndicesToNewIndices.containsKey(oldIndex)) { + // this vertex has not been added, so allocate a + // new index for it and add it to the map + oldIndicesToNewIndices.put(oldIndex, newIndex); + newIndicesToOldIndices.add(oldIndex); + + // increment to have the next index + newIndex++; + } + } + + // Number of unique verts to be created now available + int newNumVerts = newIndicesToOldIndices.size(); + + if (newIndex != newNumVerts) { + throw new AssertionError(); + } + + // Create the new index buffer. + // Do not overwrite the old one because we might be able to + // convert from int index buffer to short index buffer + IndexBuffer newIndexBuf; + if (newNumVerts >= 65536) { + newIndexBuf = new IndexIntBuffer(BufferUtils.createIntBuffer(numIndices)); + } else { + newIndexBuf = new IndexShortBuffer(BufferUtils.createShortBuffer(numIndices)); + } + + for (int i = 0; i < numIndices; i++) { + // Map the old indices to the new indices + int oldIndex = indexBuf.get(i); + newIndex = oldIndicesToNewIndices.get(oldIndex); + + newIndexBuf.put(i, newIndex); + } + + GlVertexBuffer newIdxBuf = new GlVertexBuffer(Type.Index); + newIdxBuf.setupData(oldIdxBuf.getUsage(), + oldIdxBuf.getNumComponents(), + newIndexBuf instanceof IndexIntBuffer ? Format.UnsignedInt : Format.UnsignedShort, + newIndexBuf.getBuffer()); + clearBuffer(Type.Index); + setBuffer(newIdxBuf); + + // Now, create the vertex buffers + for (GlVertexBuffer oldVb : vertices) { + if (oldVb.getBufferType() == GlVertexBuffer.Type.Index) { + // ignore the index buffer + continue; + } + + GlVertexBuffer newVb = new GlVertexBuffer(oldVb.getBufferType()); + newVb.setNormalized(oldVb.isNormalized()); + //check for data before copying, some buffers are just empty shells + //for caching purpose (HW skinning buffers), and will be filled when + //needed + if (oldVb.getData() != null) { + // Create a new vertex buffer with similar configuration, but + // with the capacity of number of unique vertices + Buffer buffer = GlVertexBuffer.createBuffer(oldVb.getFormat(), + oldVb.getNumComponents(), newNumVerts); + newVb.setupData(oldVb.getUsage(), oldVb.getNumComponents(), + oldVb.getFormat(), buffer); + + // Copy the vertex data from the old buffer into the new buffer + for (int i = 0; i < newNumVerts; i++) { + int oldIndex = newIndicesToOldIndices.get(i); + + // Copy the vertex attribute from the old index + // to the new index + oldVb.copyElement(oldIndex, newVb, i); + } + } + + // Set the buffer on the mesh + clearBuffer(newVb.getBufferType()); + setBuffer(newVb); + } + + // Copy max weights per vertex as well + setMaxNumWeights(other.getMaxNumWeights()); + + // The data has been copied over, update information + updateCounts(); + updateBound(); + } + + /** + * Scales the texture coordinate buffer on this mesh by the given scale + * factor. + *

+ * Note that values above 1 will cause the + * texture to tile, while values below 1 will cause the texture + * to stretch. + *

+ * + * @param scaleFactor The scale factor to scale by. Every texture + * coordinate is multiplied by this vector to get the result. + * + * @throws IllegalStateException If there's no texture coordinate + * buffer on the mesh + * @throws UnsupportedOperationException If the texture coordinate + * buffer is not in 2D float format. + */ + public void scaleTextureCoordinates(Vector2f scaleFactor) { + GlVertexBuffer tc = getBuffer(Type.TexCoord); + if (tc == null) { + throw new IllegalStateException("The mesh has no texture coordinates"); + } + + if (tc.getFormat() != GlVertexBuffer.Format.Float) { + throw new UnsupportedOperationException("Only float texture coord format is supported"); + } + + if (tc.getNumComponents() != 2) { + throw new UnsupportedOperationException("Only 2D texture coords are supported"); + } + + FloatBuffer fb = (FloatBuffer) tc.getData(); + fb.clear(); + for (int i = 0; i < fb.limit() / 2; i++) { + float x = fb.get(); + float y = fb.get(); + fb.position(fb.position() - 2); + x *= scaleFactor.getX(); + y *= scaleFactor.getY(); + fb.put(x).put(y); + } + fb.clear(); + tc.updateData(fb); + } + + /** + * Updates the bounding volume of this mesh. + * The method does nothing if the mesh has no {@link Type#Position} buffer. + * It is expected that the position buffer is a float buffer with 3 components. + */ + @Override + public void updateBound() { + GlVertexBuffer posBuf = getBuffer(GlVertexBuffer.Type.Position); + if (meshBound != null && posBuf != null) { + meshBound.computeFromPoints((FloatBuffer) posBuf.getData()); + } + } + + /** + * Returns the {@link BoundingVolume} of this Mesh. + * By default the bounding volume is a {@link BoundingBox}. + * + * @return the bounding volume of this mesh + */ + @Override + public BoundingVolume getBound() { + return meshBound; + } + + /** + * Sets the {@link BoundingVolume} for this Mesh. + * The bounding volume is recomputed by calling {@link #updateBound() }. + * + * @param modelBound The model bound to set + */ + @Override + public void setBound(BoundingVolume modelBound) { + meshBound = modelBound; + } + + @Override + public GlVertex getVertices() { + return vertices; + } + + /** + * @deprecated use isAnimatedByJoint + * @param boneIndex the bone's index in its skeleton + * @return true if animated by that bone, otherwise false + */ + @Deprecated + public boolean isAnimatedByBone(int boneIndex) { + return isAnimatedByJoint(boneIndex); + } + + /** + * Test whether the specified bone animates this mesh. + * + * @param jointIndex the bone's index in its skeleton + * @return true if the specified bone animates this mesh, otherwise false + */ + @Override + public boolean isAnimatedByJoint(int jointIndex) { + GlVertexBuffer biBuf = getBuffer(GlVertexBuffer.Type.BoneIndex); + GlVertexBuffer wBuf = getBuffer(GlVertexBuffer.Type.BoneWeight); + if (biBuf == null || wBuf == null) { + return false; // no bone animation data + } + + IndexBuffer boneIndexBuffer = IndexBuffer.wrapIndexBuffer(biBuf.getData()); + boneIndexBuffer.rewind(); + int numBoneIndices = boneIndexBuffer.remaining(); + assert numBoneIndices % 4 == 0 : numBoneIndices; + int numVertices = boneIndexBuffer.remaining() / 4; + + FloatBuffer weightBuffer = (FloatBuffer) wBuf.getData(); + weightBuffer.rewind(); + int numWeights = weightBuffer.remaining(); + assert numWeights == numVertices * 4 : numWeights; + /* + * Test each vertex to determine whether the bone affects it. + */ + int biByte = jointIndex; + for (int vIndex = 0; vIndex < numVertices; vIndex++) { + for (int wIndex = 0; wIndex < 4; wIndex++) { + int bIndex = boneIndexBuffer.get(); + float weight = weightBuffer.get(); + if (wIndex < maxNumWeights && bIndex == biByte && weight != 0f) { + return true; + } + } + } + return false; + } + + /** + * Sets the count of vertices used for each tessellation patch + * + * @param patchVertexCount the desired count + */ + public void setPatchVertexCount(int patchVertexCount) { + this.patchVertexCount = patchVertexCount; + } + + /** + * Gets the amount of vertices used for each patch; + * + * @return the count (≥0) + */ + public int getPatchVertexCount() { + return patchVertexCount; + } + + public void addMorphTarget(MorphTarget target) { + if (morphTargets == null) { + morphTargets = new SafeArrayList<>(MorphTarget.class); + } + morphTargets.add(target); + } + + /** + * Remove the given MorphTarget from the Mesh + * @param target The MorphTarget to remove + * @return If the MorphTarget was removed + */ + public boolean removeMorphTarget(MorphTarget target) { + return morphTargets != null ? morphTargets.remove(target) : false; + } + + /** + * Remove the MorphTarget from the Mesh at the given index + * @throws IndexOutOfBoundsException if the index outside the number of morph targets + * @param index Index of the MorphTarget to remove + * @return The MorphTarget that was removed + */ + public MorphTarget removeMorphTarget(int index) { + if (morphTargets == null) { + throw new IndexOutOfBoundsException("Index:" + index + ", Size:0"); + } + return morphTargets.remove(index); + } + + /** + * Get the MorphTarget at the given index + * @throws IndexOutOfBoundsException if the index outside the number of morph targets + * @param index The index of the morph target to get + * @return The MorphTarget at the index + */ + public MorphTarget getMorphTarget(int index) { + if (morphTargets == null) { + throw new IndexOutOfBoundsException("Index:" + index + ", Size:0"); + } + return morphTargets.get(index); + } + + public MorphTarget[] getMorphTargets() { + if (morphTargets == null) { + return new MorphTarget[0]; + } else { + return morphTargets.getArray(); + } + } + + /** + * Get the name of all morphs in order. + * Morphs without names will be null + * @return an array + */ + public String[] getMorphTargetNames() { + MorphTarget[] nbMorphTargets = getMorphTargets(); + if (nbMorphTargets.length == 0) { + return new String[0]; + } + String[] targets = new String[nbMorphTargets.length]; + + for (int index = 0; index < nbMorphTargets.length; index++) { + targets[index] = nbMorphTargets[index].getName(); + } + return targets; + } + + public boolean hasMorphTargets() { + return morphTargets != null && !morphTargets.isEmpty(); + } + + /** + * Get the index of the morph that has the given name. + * + * @param morphName The name of the morph to search for + * @return The index of the morph, or -1 if not found. + */ + public int getMorphIndex(String morphName) { + int index = -1; + MorphTarget[] nbMorphTargets = getMorphTargets(); + for (int i = 0; i < nbMorphTargets.length; i++) { + if (nbMorphTargets[i].getName().equals(morphName)) { + index = i; + break; + } + } + return index; + } + + @Override + @NotFullyImplemented + public void write(JmeExporter ex) throws IOException { + OutputCapsule out = ex.getCapsule(this); + + out.write(meshBound, "modelBound", null); + out.write(vertCount, "vertCount", DEFAULT_VERT_COUNT); + out.write(elementCount, "elementCount", DEFAULT_ELEMENT_COUNT); + out.write(instanceCount, "instanceCount", DEFAULT_INSTANCE_COUNT); + out.write(maxNumWeights, "max_num_weights", DEFAULT_MAX_NUM_WEIGHTS); + out.write(mode, "mode", Mode.Triangles); + out.write(collisionTree, "collisionTree", DEFAULT_COLLISION_TREE); + out.write(elementLengths, "elementLengths", null); + out.write(modeStart, "modeStart", null); + out.write(pointSize, "pointSize", DEFAULT_POINT_SIZE); + + //Removing HW skinning buffers to not save them + GlVertexBuffer hwBoneIndex = getBuffer(Type.HWBoneIndex); + if (hwBoneIndex != null) { + vertices.clearBuffer(Type.HWBoneIndex); + } + GlVertexBuffer hwBoneWeight = getBuffer(Type.HWBoneWeight); + if (hwBoneWeight != null) { + vertices.clearBuffer(Type.HWBoneWeight); + } + + // this code is NOT dead +// out.writeIntSavableMap(buffers, "buffers", null); +// +// //restoring Hw skinning buffers. +// if (hwBoneIndex != null) { +// buffers.put(hwBoneIndex.getBufferType().ordinal(), hwBoneIndex); +// } +// if (hwBoneWeight != null) { +// buffers.put(hwBoneWeight.getBufferType().ordinal(), hwBoneWeight); +// } + //out.write(lodLevels, "lodLevels", null); + + if (morphTargets != null) { + out.writeSavableArrayList(new ArrayList(morphTargets), "morphTargets", null); + } + } + + @Override + @NotFullyImplemented + public void read(JmeImporter im) throws IOException { + InputCapsule in = im.getCapsule(this); + meshBound = (BoundingVolume) in.readSavable("modelBound", null); + vertCount = in.readInt("vertCount", DEFAULT_VERT_COUNT); + elementCount = in.readInt("elementCount", DEFAULT_ELEMENT_COUNT); + instanceCount = in.readInt("instanceCount", DEFAULT_INSTANCE_COUNT); + maxNumWeights = in.readInt("max_num_weights", DEFAULT_MAX_NUM_WEIGHTS); + mode = in.readEnum("mode", Mode.class, Mode.Triangles); + elementLengths = in.readIntArray("elementLengths", null); + modeStart = in.readIntArray("modeStart", null); + collisionTree = (BIHTree) in.readSavable("collisionTree", DEFAULT_COLLISION_TREE); + elementLengths = in.readIntArray("elementLengths", null); + modeStart = in.readIntArray("modeStart", null); + pointSize = in.readFloat("pointSize", DEFAULT_POINT_SIZE); + + // this code is NOT dead +// in.readStringSavableMap("buffers", null); +// buffers = (IntMap) in.readIntSavableMap("buffers", null); +// for (Entry entry : buffers) { +// buffersList.add(entry.getValue()); +// } + + //creating hw animation buffers empty so that they are put in the cache + if (isAnimated()) { + GlVertexBuffer hwBoneIndex = new GlVertexBuffer(Type.HWBoneIndex); + hwBoneIndex.setUsage(Usage.CpuOnly); + setBuffer(hwBoneIndex); + GlVertexBuffer hwBoneWeight = new GlVertexBuffer(Type.HWBoneWeight); + hwBoneWeight.setUsage(Usage.CpuOnly); + setBuffer(hwBoneWeight); + } + + Savable[] lodLevelsSavable = in.readSavableArray("lodLevels", null); + // this code is NOT dead +// if (lodLevelsSavable != null) { +// lodLevels = new GlVertexBuffer[lodLevelsSavable.length]; +// System.arraycopy(lodLevelsSavable, 0, lodLevels, 0, lodLevels.length); +// } + + ArrayList l = in.readSavableArrayList("morphTargets", null); + if (l != null) { + morphTargets = new SafeArrayList(MorphTarget.class, l); + } + } +} diff --git a/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java b/jme3-core/src/main/java/com/jme3/scene/GlVertexBuffer.java similarity index 56% rename from jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java rename to jme3-core/src/main/java/com/jme3/scene/GlVertexBuffer.java index 8c0a388bb7..84d0d2f69b 100644 --- a/jme3-core/src/main/java/com/jme3/scene/VertexBuffer.java +++ b/jme3-core/src/main/java/com/jme3/scene/GlVertexBuffer.java @@ -31,13 +31,23 @@ */ package com.jme3.scene; +import com.jme3.dev.NotFullyImplemented; import com.jme3.export.*; -import com.jme3.math.FastMath; -import com.jme3.renderer.Renderer; -import com.jme3.util.BufferUtils; -import com.jme3.util.NativeObject; +import com.jme3.util.*; +import com.jme3.util.natives.GlNative; +import com.jme3.vulkan.buffers.AsyncBufferHandler; +import com.jme3.vulkan.buffers.GlBuffer; +import com.jme3.vulkan.buffers.GpuBuffer; +import com.jme3.vulkan.memory.MemorySize; +import com.jme3.vulkan.mesh.DataAccess; +import com.jme3.vulkan.mesh.InputRate; +import com.jme3.vulkan.mesh.VertexBinding; +import com.jme3.vulkan.mesh.attribute.Attribute; +import com.jme3.vulkan.util.IntEnum; + import java.io.IOException; import java.nio.*; +import java.util.Objects; /** * A VertexBuffer contains a particular type of geometry @@ -53,11 +63,13 @@ * For a 3D vector, a single component is one of the dimensions, X, Y or Z. * */ -public class VertexBuffer extends NativeObject implements Savable, Cloneable { +public class GlVertexBuffer extends GlNative implements VertexBinding, Savable, Cloneable { + /** * Type of buffer. Specifies the actual attribute it defines. */ - public static enum Type { + @Deprecated + public enum Type { /** * Position of the vertex (3 floats) */ @@ -93,7 +105,9 @@ public static enum Type { * Specifies the source data for various vertex buffers * when interleaving is used. By default, the format is * byte. + * @deprecated use individual buffers instead */ + @Deprecated InterleavedData, /** * Do not use. @@ -225,32 +239,74 @@ public static enum Type { MorphTarget10, MorphTarget11, MorphTarget12, - MorphTarget13, + MorphTarget13; + + private final String name; + + Type() { + this.name = name(); + } + + Type(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static Type getTexCoord(int channel) { + assert channel >= 0 : "TexCoord channel must be non-negative."; + assert channel < 8 : "Only 8 TexCoord channels exist."; + if (channel == 0) return TexCoord; + else return values()[TexCoord2.ordinal() + channel - 1]; + } + } /** * The usage of the VertexBuffer, specifies how often the buffer * is used. This can determine if a vertex buffer is placed in VRAM - * or held in video memory, but no guarantees are made- it's only a hint. + * or held in video memory, but no guarantees are made; it's only a hint. */ - public static enum Usage { + public enum Usage { /** * Mesh data is sent once and very rarely updated. */ - Static, + Static(DataAccess.Static), /** * Mesh data is updated occasionally (once per frame or less). */ - Dynamic, + Dynamic(DataAccess.Dynamic), /** * Mesh data is updated every frame. */ - Stream, + Stream(DataAccess.Stream), /** * Mesh data is not sent to GPU at all. It is only * used by the CPU. */ - CpuOnly + CpuOnly(null); + + private final DataAccess access; + + Usage(DataAccess access) { + this.access = access; + } + + public DataAccess getAccess() { + return access; + } + + public static Usage fromDataAccess(DataAccess access) { + for (Usage u : values()) { + if (u.access == access) { + return u; + } + } + return Usage.CpuOnly; + } + } /** @@ -261,7 +317,7 @@ public static enum Usage { * For the {@link Format#Half} type, {@link ByteBuffer}s should * be used. */ - public static enum Format { + public enum Format { /** * Half precision floating point. 2 bytes, signed. */ @@ -317,7 +373,6 @@ public int getComponentSize() { } protected int offset = 0; - protected int lastLimit = 0; protected int stride = 0; protected int components = 0; @@ -325,88 +380,56 @@ public int getComponentSize() { * derived from components * format.getComponentSize() */ protected transient int componentsLength = 0; - protected Buffer data = null; + protected final AsyncBufferHandler data = new AsyncBufferHandler<>(); protected Usage usage; - protected Type bufType; protected Format format; protected boolean normalized = false; protected int instanceSpan = 0; - protected transient boolean dataSizeChanged = false; protected String name; + private final Attribute attribute; + /** * Creates an empty, uninitialized buffer. * Must call setupData() to initialize. * * @param type the type of VertexBuffer, such as Position or Binormal */ - public VertexBuffer(Type type) { + @Deprecated + @NotFullyImplemented + public GlVertexBuffer(Type type) { super(); - this.bufType = type; + attribute = null; + } + + public GlVertexBuffer(Attribute attribute) { + super(); + this.attribute = attribute; } /** * Serialization only. Do not use. */ - protected VertexBuffer() { + @NotFullyImplemented + protected GlVertexBuffer() { super(); + attribute = null; } - protected VertexBuffer(int id) { + @NotFullyImplemented + protected GlVertexBuffer(int id) { super(id); + attribute = null; } - public boolean invariant() { - // Does the VB hold any data? - if (data == null) { - throw new AssertionError(); - } - // Position must be 0. - if (data.position() != 0) { - throw new AssertionError(); - } - // Is the size of the VB == 0? - if (data.limit() == 0) { - throw new AssertionError(); - } - // Does offset exceed buffer limit or negative? - if (offset > data.limit() || offset < 0) { - throw new AssertionError(); - } - // Are components between 1 and 4? - - // Are components between 1 and 4 and not InstanceData? - if (bufType != Type.InstanceData) { - if (components < 1 || components > 4) { - throw new AssertionError(); - } - } - - // Does usage comply with buffer directness? - //if (usage == Usage.CpuOnly && data.isDirect()) { - // throw new AssertionError(); - /*} else*/ if (usage != Usage.CpuOnly && !data.isDirect()) { - throw new AssertionError(); - } + @Override + public AsyncBufferHandler getBuffer() { + return null; + } - // Double/Char/Long buffers are not supported for VertexBuffers. - // For the rest, ensure they comply with the "Format" value. - if (data instanceof DoubleBuffer) { - throw new AssertionError(); - } else if (data instanceof CharBuffer) { - throw new AssertionError(); - } else if (data instanceof LongBuffer) { - throw new AssertionError(); - } else if (data instanceof FloatBuffer && format != Format.Float) { - throw new AssertionError(); - } else if (data instanceof IntBuffer && format != Format.Int && format != Format.UnsignedInt) { - throw new AssertionError(); - } else if (data instanceof ShortBuffer && format != Format.Short && format != Format.UnsignedShort) { - throw new AssertionError(); - } else if (data instanceof ByteBuffer && format != Format.Byte && format != Format.UnsignedByte) { - throw new AssertionError(); - } - return true; + @Override + public T mapAttribute(String name, int vertices) { + return null; } /** @@ -414,10 +437,15 @@ public boolean invariant() { * * @see #setOffset(int) */ - public int getOffset() { + @Override + public long getOffset() { return offset; } + public Attribute getAttribute() { + return attribute; + } + /** * @param offset Specify the offset (in bytes) from the start of the buffer * after which the data is sent to the GPU. @@ -431,10 +459,16 @@ public void setOffset(int offset) { * * @see #setStride(int) */ + @Override public int getStride() { return stride; } + @Override + public IntEnum getInputRate() { + return null; + } + /** * Set the stride (in bytes) for the data. *

@@ -459,48 +493,7 @@ public void setStride(int stride) { * @return A native buffer, in the specified {@link Format format}. */ public Buffer getData() { - return data; - } - - /** - * Returns a safe read-only version of this VertexBuffer's data. The - * contents of the buffer will reflect whatever changes are made on - * other threads (eventually) but these should not be used in that way. - * This method provides a read-only buffer that is safe to _read_ from - * a separate thread since it has its own book-keeping state (position, limit, etc.) - * - * @return A rewound native buffer in the specified {@link Format format} - * that is safe to read from a separate thread from other readers. - */ - public Buffer getDataReadOnly() { - if (data == null) { - return null; - } - - // Create a read-only duplicate(). Note: this does not copy - // the underlying memory, it just creates a new read-only wrapper - // with its own buffer position state. - // Unfortunately, this is not 100% straight forward since Buffer - // does not have an asReadOnlyBuffer() method. - Buffer result; - if (data instanceof ByteBuffer) { - result = ((ByteBuffer) data).asReadOnlyBuffer(); - } else if (data instanceof FloatBuffer) { - result = ((FloatBuffer) data).asReadOnlyBuffer(); - } else if (data instanceof ShortBuffer) { - result = ((ShortBuffer) data).asReadOnlyBuffer(); - } else if (data instanceof IntBuffer) { - result = ((IntBuffer) data).asReadOnlyBuffer(); - } else { - throw new UnsupportedOperationException("Cannot get read-only view of buffer type:" + data); - } - - // Make sure the caller gets a consistent view since we may - // have grabbed this buffer while another thread was reading - // the raw data. - result.rewind(); - - return result; + return data.getBuffer().getBuffer(); } /** @@ -508,19 +501,20 @@ public Buffer getDataReadOnly() { * information. */ public Usage getUsage() { - return usage; + return attribute.getUsage(); } /** * @param usage The usage of this buffer. See {@link Usage} for more * information. */ + @Deprecated public void setUsage(Usage usage) { // if (id != -1) // throw new UnsupportedOperationException("Data has already been sent. Cannot set usage."); - this.usage = usage; - this.setUpdateNeeded(); + //this.usage = usage; + //this.setUpdateNeeded(); } /** @@ -537,7 +531,7 @@ public void setNormalized(boolean normalized) { /** * @return True if integer components should be converted to the range 0-1. - * @see VertexBuffer#setNormalized(boolean) + * @see GlVertexBuffer#setNormalized(boolean) */ public boolean isNormalized() { return normalized; @@ -582,13 +576,6 @@ public int getInstanceSpan() { return instanceSpan; } - /** - * @return The type of information that this buffer has. - */ - public Type getBufferType() { - return bufType; - } - /** * @return The {@link Format format}, or data type of the data. */ @@ -608,10 +595,10 @@ public int getNumComponents() { * @return The total number of data elements in the data buffer. */ public int getNumElements() { - if (data == null) { + if (!data.hasBuffer()) { return 0; } - int elements = data.limit() / components; + int elements = data.getBuffer().getBuffer().limit() / components; if (format == Format.Half) { elements /= 2; } @@ -634,6 +621,23 @@ public int getBaseInstanceCount() { return getNumElements() * instanceSpan; } + /** + * Initializes this vertex buffer with a new empty data buffer. + * + * @param usage access frequency of the data buffer + * @param format component format + * @param vertices number of vertices + * @param components components per vertex + * @param padding extra vertices to allocate space for + */ + public void initDataBuffer(Usage usage, Format format, int vertices, int components, int padding) { + this.usage = usage; + this.format = format; + this.components = components; + this.componentsLength = components * format.getComponentSize(); + data.setBuffer(new GlBuffer(new MemorySize(vertices * components, format.getComponentSize()), padding * components)); + } + /** * Called to initialize the data in the VertexBuffer. Must only * be called once. @@ -647,7 +651,7 @@ public int getBaseInstanceCount() { * argument. */ public void setupData(Usage usage, int components, Format format, Buffer data) { - if (id != -1) { + if (object != -1) { throw new UnsupportedOperationException("Data has already been sent. Cannot setupData again."); } @@ -655,29 +659,31 @@ public void setupData(Usage usage, int components, Format format, Buffer data) { throw new IllegalArgumentException("None of the arguments can be null"); } - if (data.isReadOnly()) { - throw new IllegalArgumentException("VertexBuffer data cannot be read-only."); - } + // not sure if this requirement should still be kept. To be deleted. +// if (bufType != Type.InstanceData) { +// if (components < 1 || components > 4) { +// throw new IllegalArgumentException("components must be between 1 and 4"); +// } +// } - if (bufType != Type.InstanceData) { - if (components < 1 || components > 4) { - throw new IllegalArgumentException("components must be between 1 and 4"); - } + //this.data.setBuffer(data); + if (!this.data.hasBuffer()) { + this.data.setBuffer(new GlBuffer(data)); + } else { + this.data.resize(data.limit()); } - - this.data = data; + this.data.copy(data); this.components = components; this.usage = usage; this.format = format; this.componentsLength = components * format.getComponentSize(); - this.lastLimit = data.limit(); setUpdateNeeded(); } /** * Called to update the data in the buffer with new data. Can only - * be called after {@link VertexBuffer#setupData( - * com.jme3.scene.VertexBuffer.Usage, int, com.jme3.scene.VertexBuffer.Format, java.nio.Buffer) } + * be called after {@link GlVertexBuffer#setupData( + * GlVertexBuffer.Usage, int, GlVertexBuffer.Format, java.nio.Buffer) } * has been called. Note that it is fine to call this method on the * data already set, e.g. vb.updateData(vb.getData()), this will just * set the proper update flag indicating the data should be sent to the GPU @@ -690,211 +696,26 @@ public void setupData(Usage usage, int components, Format format, Buffer data) { * @param data The data buffer to set */ public void updateData(Buffer data) { - if (id != -1) { - // request to update data is okay - } - - // Check if the data buffer is read-only which is a sign - // of a bug on the part of the caller - if (data != null && data.isReadOnly()) { - throw new IllegalArgumentException("VertexBuffer data cannot be read-only."); - } - - // will force renderer to call glBufferData again - if (data != null && (this.data.getClass() != data.getClass() || data.limit() != lastLimit)) { - dataSizeChanged = true; - lastLimit = data.limit(); - } - - this.data = data; + Objects.requireNonNull(data, "Vertex data buffer cannot be null."); + this.data.setBufferIfAbsent(() -> new GlBuffer(data)); + this.data.resize(data.limit()); + this.data.copy(data); setUpdateNeeded(); } - /** - * Returns true if the data size of the VertexBuffer has changed. - * Internal use only. - * - * @return true if the data size has changed - */ - public boolean hasDataSizeChanged() { - return dataSizeChanged; + @Override + public boolean isUpdateNeeded() { + if (attribute.getUsage() != usage) { + usage = attribute.getUsage(); + setUpdateNeeded(); + } + return data.getBuffer().isUpdateNeeded() || super.isUpdateNeeded(); } @Override public void clearUpdateNeeded() { super.clearUpdateNeeded(); - dataSizeChanged = false; - } - - /** - * Converts single floating-point data to {@link Format#Half half} floating-point data. - */ - public void convertToHalf() { - if (id != -1) { - throw new UnsupportedOperationException("Data has already been sent."); - } - - if (format != Format.Float) { - throw new IllegalStateException("Format must be float!"); - } - - int numElements = data.limit() / components; - format = Format.Half; - this.componentsLength = components * format.getComponentSize(); - - ByteBuffer halfData = BufferUtils.createByteBuffer(componentsLength * numElements); - halfData.rewind(); - - FloatBuffer floatData = (FloatBuffer) data; - floatData.rewind(); - - for (int i = 0; i < floatData.limit(); i++) { - float f = floatData.get(i); - short half = FastMath.convertFloatToHalf(f); - halfData.putShort(half); - } - this.data = halfData; - setUpdateNeeded(); - dataSizeChanged = true; - } - - /** - * Reduces the capacity of the buffer to the given amount - * of elements, any elements at the end of the buffer are truncated - * as necessary. - * - * @param numElements The number of elements to reduce to. - */ - public void compact(int numElements) { - int total = components * numElements; - data.clear(); - switch (format) { - case Byte: - case UnsignedByte: - case Half: - ByteBuffer bbuf = (ByteBuffer) data; - bbuf.limit(total); - ByteBuffer bnewBuf = BufferUtils.createByteBuffer(total); - bnewBuf.put(bbuf); - data = bnewBuf; - break; - case Short: - case UnsignedShort: - ShortBuffer sbuf = (ShortBuffer) data; - sbuf.limit(total); - ShortBuffer snewBuf = BufferUtils.createShortBuffer(total); - snewBuf.put(sbuf); - data = snewBuf; - break; - case Int: - case UnsignedInt: - IntBuffer ibuf = (IntBuffer) data; - ibuf.limit(total); - IntBuffer inewBuf = BufferUtils.createIntBuffer(total); - inewBuf.put(ibuf); - data = inewBuf; - break; - case Float: - FloatBuffer fbuf = (FloatBuffer) data; - fbuf.limit(total); - FloatBuffer fnewBuf = BufferUtils.createFloatBuffer(total); - fnewBuf.put(fbuf); - data = fnewBuf; - break; - default: - throw new UnsupportedOperationException("Unrecognized buffer format: " + format); - } - data.clear(); - setUpdateNeeded(); - dataSizeChanged = true; - } - - /** - * Modify a component inside an element. - * The val parameter must be in the buffer's format: - * {@link Format}. - * - * @param elementIndex The element index to modify - * @param componentIndex The component index to modify - * @param val The value to set, either byte, short, int or float depending - * on the {@link Format}. - */ - public void setElementComponent(int elementIndex, int componentIndex, Object val) { - int inPos = elementIndex * components; - int elementPos = componentIndex; - - if (format == Format.Half) { - inPos *= 2; - elementPos *= 2; - } - - data.clear(); - - switch (format) { - case Byte: - case UnsignedByte: - case Half: - ByteBuffer bin = (ByteBuffer) data; - bin.put(inPos + elementPos, (Byte) val); - break; - case Short: - case UnsignedShort: - ShortBuffer sin = (ShortBuffer) data; - sin.put(inPos + elementPos, (Short) val); - break; - case Int: - case UnsignedInt: - IntBuffer iin = (IntBuffer) data; - iin.put(inPos + elementPos, (Integer) val); - break; - case Float: - FloatBuffer fin = (FloatBuffer) data; - fin.put(inPos + elementPos, (Float) val); - break; - default: - throw new UnsupportedOperationException("Unrecognized buffer format: " + format); - } - } - - /** - * Get the component inside an element. - * - * @param elementIndex The element index - * @param componentIndex The component index - * @return The component, as one of the primitive types, byte, short, - * int or float. - */ - public Object getElementComponent(int elementIndex, int componentIndex) { - int inPos = elementIndex * components; - int elementPos = componentIndex; - - if (format == Format.Half) { - inPos *= 2; - elementPos *= 2; - } - - Buffer srcData = getDataReadOnly(); - - switch (format) { - case Byte: - case UnsignedByte: - case Half: - ByteBuffer bin = (ByteBuffer) srcData; - return bin.get(inPos + elementPos); - case Short: - case UnsignedShort: - ShortBuffer sin = (ShortBuffer) srcData; - return sin.get(inPos + elementPos); - case Int: - case UnsignedInt: - IntBuffer iin = (IntBuffer) srcData; - return iin.get(inPos + elementPos); - case Float: - FloatBuffer fin = (FloatBuffer) srcData; - return fin.get(inPos + elementPos); - default: - throw new UnsupportedOperationException("Unrecognized buffer format: " + format); - } + data.getBuffer().update(); } /** @@ -908,7 +729,8 @@ public Object getElementComponent(int elementIndex, int componentIndex) { * @throws IllegalArgumentException If the formats of the buffers do not * match. */ - public void copyElement(int inIndex, VertexBuffer outVb, int outIndex) { + @Deprecated + public void copyElement(int inIndex, GlVertexBuffer outVb, int outIndex) { copyElements(inIndex, outVb, outIndex, 1); } @@ -924,67 +746,24 @@ public void copyElement(int inIndex, VertexBuffer outVb, int outIndex) { * @throws IllegalArgumentException If the formats of the buffers do not * match. */ - public void copyElements(int inIndex, VertexBuffer outVb, int outIndex, int len) { - if (outVb.format != format || outVb.components != components) { - throw new IllegalArgumentException("Buffer format mismatch. Cannot copy"); - } - - int inPos = inIndex * components; - int outPos = outIndex * components; - int elementSz = components; - if (format == Format.Half) { - // because half is stored as ByteBuffer, but it's 2 bytes long - inPos *= 2; - outPos *= 2; - elementSz *= 2; + @Deprecated + public void copyElements(int inIndex, GlVertexBuffer outVb, int outIndex, int len) { + if (!data.hasBuffer()) { + throw new NullPointerException("No data present to copy."); } - - // Make sure to grab a read-only copy in case some other - // thread is also accessing the buffer and messing with its - // position() - Buffer srcData = getDataReadOnly(); - outVb.data.clear(); - - switch (format) { - case Byte: - case UnsignedByte: - case Half: - ByteBuffer bin = (ByteBuffer) srcData; - ByteBuffer bout = (ByteBuffer) outVb.data; - bin.position(inPos).limit(inPos + elementSz * len); - bout.position(outPos).limit(outPos + elementSz * len); - bout.put(bin); - break; - case Short: - case UnsignedShort: - ShortBuffer sin = (ShortBuffer) srcData; - ShortBuffer sout = (ShortBuffer) outVb.data; - sin.position(inPos).limit(inPos + elementSz * len); - sout.position(outPos).limit(outPos + elementSz * len); - sout.put(sin); - break; - case Int: - case UnsignedInt: - IntBuffer iin = (IntBuffer) srcData; - IntBuffer iout = (IntBuffer) outVb.data; - iin.position(inPos).limit(inPos + elementSz * len); - iout.position(outPos).limit(outPos + elementSz * len); - iout.put(iin); - break; - case Float: - FloatBuffer fin = (FloatBuffer) srcData; - FloatBuffer fout = (FloatBuffer) outVb.data; - fin.position(inPos).limit(inPos + elementSz * len); - fout.position(outPos).limit(outPos + elementSz * len); - fout.put(fin); - break; - default: - throw new UnsupportedOperationException("Unrecognized buffer format: " + format); + if (!outVb.data.hasBuffer()) { + outVb.data.setBuffer(new GlBuffer(data.size(), data.getBuffer().getPadding())); + } else if (outVb.data.size().getBytesPerElement() != data.size().getBytesPerElement()) { + throw new IllegalArgumentException("Buffer element size mismatch."); + } else { + outVb.data.resize(data.size().getElements()); } - - // Clear the output buffer to rewind it and reset its - // limit from where we shortened it above. - outVb.data.clear(); + inIndex *= data.size().getBytesPerElement() * getNumElements(); + outIndex *= data.size().getBytesPerElement() * getNumElements(); + len *= data.size().getBytesPerElement() * getNumElements(); + outVb.data.copy(data); + outVb.data.unmap(); + data.unmap(); } /** @@ -1027,23 +806,23 @@ public static Buffer createBuffer(Format format, int components, int numElements } /** - * Creates a deep clone of the {@link VertexBuffer}. + * Creates a deep clone of the {@link GlVertexBuffer}. * * @return Deep clone of this buffer */ @Override - public VertexBuffer clone() { + public GlVertexBuffer clone() { // NOTE: Superclass GLObject automatically creates shallow clone // e.g. re-use ID. - VertexBuffer vb = (VertexBuffer) super.clone(); - vb.handleRef = new Object(); - vb.id = -1; - if (data != null) { + GlVertexBuffer vb = (GlVertexBuffer) super.clone(); + vb.object = -1; + if (data.hasBuffer()) { // Make sure to pass a read-only buffer to clone so that // the position information doesn't get clobbered by another // reading thread during cloning (and vice versa) since this is // a purely read-only operation. - vb.updateData(BufferUtils.clone(getDataReadOnly())); + vb.updateData(data.mapBytes()); + data.unmap(); } return vb; @@ -1056,19 +835,16 @@ public VertexBuffer clone() { * @param overrideType The type of the cloned VertexBuffer * @return A deep clone of the buffer */ - public VertexBuffer clone(Type overrideType) { - VertexBuffer vb = new VertexBuffer(overrideType); + public GlVertexBuffer clone(Type overrideType) { + GlVertexBuffer vb = new GlVertexBuffer(overrideType); vb.components = components; vb.componentsLength = componentsLength; - - // Make sure to pass a read-only buffer to clone so that - // the position information doesn't get clobbered by another - // reading thread during cloning (and vice versa) since this is - // a purely read-only operation. - vb.data = BufferUtils.clone(getDataReadOnly()); + if (data.hasBuffer()) { + vb.data.setBuffer(new GlBuffer(data.getBuffer())); + vb.data.copy(data); + } vb.format = format; - vb.handleRef = new Object(); - vb.id = -1; + vb.object = -1; vb.normalized = normalized; vb.instanceSpan = instanceSpan; vb.offset = offset; @@ -1081,11 +857,10 @@ public VertexBuffer clone(Type overrideType) { @Override public String toString() { String dataTxt = null; - if (data != null) { - dataTxt = ", elements=" + data.limit(); + if (data.hasBuffer()) { + dataTxt = ", elements=" + data.getBuffer().getBuffer().limit(); } return getClass().getSimpleName() + "[fmt=" + format.name() - + ", type=" + bufType.name() + ", usage=" + usage.name() + dataTxt + "]"; } @@ -1093,30 +868,16 @@ public String toString() { @Override public void resetObject() { // assert this.id != -1; - this.id = -1; + this.object = -1; setUpdateNeeded(); } @Override - public void deleteObject(Object rendererObject) { - ((Renderer) rendererObject).deleteBuffer(this); - } - - @Override - protected void deleteNativeBuffers() { - if (data != null) { - BufferUtils.destroyDirectBuffer(data); - } - } + public Runnable createNativeDestroyer() { + return () -> { + renderer.deleteBuffer(new GlVertexBuffer(object)); - @Override - public NativeObject createDestructableClone() { - return new VertexBuffer(id); - } - - @Override - public long getUniqueId() { - return ((long) OBJTYPE_VERTEXBUFFER << 32) | (0xffffffffL & (long) id); + }; } @Override @@ -1124,34 +885,13 @@ public void write(JmeExporter ex) throws IOException { OutputCapsule oc = ex.getCapsule(this); oc.write(components, "components", 0); oc.write(usage, "usage", Usage.Dynamic); - oc.write(bufType, "buffer_type", null); oc.write(format, "format", Format.Float); oc.write(normalized, "normalized", false); oc.write(offset, "offset", 0); oc.write(stride, "stride", 0); oc.write(instanceSpan, "instanceSpan", 0); - - String dataName = "data" + format.name(); - Buffer roData = getDataReadOnly(); - switch (format) { - case Float: - oc.write((FloatBuffer) roData, dataName, null); - break; - case Short: - case UnsignedShort: - oc.write((ShortBuffer) roData, dataName, null); - break; - case UnsignedByte: - case Byte: - case Half: - oc.write((ByteBuffer) roData, dataName, null); - break; - case Int: - case UnsignedInt: - oc.write((IntBuffer) roData, dataName, null); - break; - default: - throw new IOException("Unsupported export buffer format: " + format); + if (data.hasBuffer()) { + oc.write(data.mapBytes(), "data", null); } } @@ -1160,45 +900,21 @@ public void read(JmeImporter im) throws IOException { InputCapsule ic = im.getCapsule(this); components = ic.readInt("components", 0); usage = ic.readEnum("usage", Usage.class, Usage.Dynamic); - bufType = ic.readEnum("buffer_type", Type.class, null); format = ic.readEnum("format", Format.class, Format.Float); normalized = ic.readBoolean("normalized", false); offset = ic.readInt("offset", 0); stride = ic.readInt("stride", 0); instanceSpan = ic.readInt("instanceSpan", 0); componentsLength = components * format.getComponentSize(); - - String dataName = "data" + format.name(); - switch (format) { - case Float: - data = ic.readFloatBuffer(dataName, null); - break; - case Short: - case UnsignedShort: - data = ic.readShortBuffer(dataName, null); - break; - case UnsignedByte: - case Byte: - case Half: - data = ic.readByteBuffer(dataName, null); - break; - case Int: - case UnsignedInt: - data = ic.readIntBuffer(dataName, null); - break; - default: - throw new IOException("Unsupported import buffer format: " + format); - } - } - - public String getName() { - if (name == null) { - name = getClass().getSimpleName() + "(" + getBufferType().name() + ")"; + ByteBuffer data = ic.readByteBuffer("data", null); + if (data != null) { + this.data.setBuffer(new GlBuffer(MemorySize.dynamic(data.capacity(), format.getComponentSize()))); + this.data.copy(data); } - return name; } public void setName(String name) { this.name = name; } + } diff --git a/jme3-core/src/main/java/com/jme3/scene/Mesh.java b/jme3-core/src/main/java/com/jme3/scene/Mesh.java index 2819c2838f..46ac0463c1 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Mesh.java +++ b/jme3-core/src/main/java/com/jme3/scene/Mesh.java @@ -31,1692 +31,262 @@ */ package com.jme3.scene; -import com.jme3.bounding.BoundingBox; import com.jme3.bounding.BoundingVolume; import com.jme3.collision.Collidable; import com.jme3.collision.CollisionResults; -import com.jme3.collision.bih.BIHTree; import com.jme3.export.*; -import com.jme3.material.Material; -import com.jme3.material.RenderState; -import com.jme3.math.*; -import com.jme3.scene.VertexBuffer.*; -import com.jme3.scene.mesh.*; -import com.jme3.util.*; -import com.jme3.util.IntMap.Entry; -import com.jme3.util.clone.Cloner; -import com.jme3.util.clone.JmeCloneable; -import java.io.IOException; -import java.nio.*; -import java.util.ArrayList; +import com.jme3.math.Triangle; +import com.jme3.vulkan.buffers.GpuBuffer; +import com.jme3.vulkan.mesh.AttributeModifier; +import com.jme3.vulkan.mesh.DataAccess; +import com.jme3.vulkan.mesh.exp.Vertex; +import com.jme3.vulkan.pipeline.Topology; +import com.jme3.vulkan.util.IntEnum; /** - * Mesh is used to store rendering data. - *

- * All visible elements in a scene are represented by meshes. - * Meshes may contain three types of geometric primitives: - *

    - *
  • Points - Every vertex represents a single point in space. - *
  • Lines - 2 vertices represent a line segment, with the width specified - * via {@link Material#getAdditionalRenderState()} and {@link RenderState#setLineWidth(float)}.
  • - *
  • Triangles - 3 vertices represent a solid triangle primitive.
  • - *
+ * Stores the vertex, index, and instance data for drawing indexed meshes. * - * @author Kirill Vainer + *

Each mesh is made up of at least one vertex buffer, at least one + * index buffer, and at least one vertex attribute. Vertex attributes are + * stored in vertex buffers and are identified by string. Implementations + * are free to format the vertex attributes among the vertex buffers however + * they choose, but the format and number of each vertex attributes' components + * must be as previously or externally specified.

+ * + * @author codex */ -public class Mesh implements Savable, Cloneable, JmeCloneable { - - /** - * The mode of the Mesh specifies both the type of primitive represented - * by the mesh and how the data should be interpreted. - */ - public enum Mode { - /** - * A primitive is a single point in space. The size of {@link Mode#Points points} are - * determined via the vertex shader's gl_PointSize output. - */ - Points(true), - /** - * A primitive is a line segment. Every two vertices specify - * a single line. {@link Material#getAdditionalRenderState()} - * and {@link RenderState#setLineWidth(float)} can be used - * to set the width of the lines. - */ - Lines(true), - /** - * A primitive is a line segment. The first two vertices specify - * a single line, while subsequent vertices are combined with the - * previous vertex to make a line. {@link Material#getAdditionalRenderState()} - * and {@link RenderState#setLineWidth(float)} can - * be used to set the width of the lines. - */ - LineStrip(false), - /** - * Identical to {@link #LineStrip} except that at the end - * the last vertex is connected with the first to form a line. - * {@link Material#getAdditionalRenderState()} - * and {@link RenderState#setLineWidth(float)} can be used - * to set the width of the lines. - */ - LineLoop(false), - /** - * A primitive is a triangle. Each 3 vertices specify a single - * triangle. - */ - Triangles(true), - /** - * Similar to {@link #Triangles}, the first 3 vertices - * specify a triangle, while subsequent vertices are combined with - * the previous two to form a triangle. - */ - TriangleStrip(false), - /** - * Similar to {@link #Triangles}, the first 3 vertices - * specify a triangle, each 2 subsequent vertices are combined - * with the very first vertex to make a triangle. - */ - TriangleFan(false), - /** - * A combination of various triangle modes. It is best to avoid - * using this mode as it may not be supported by all renderers. - * The {@link Mesh#setModeStart(int[]) mode start points} and - * {@link Mesh#setElementLengths(int[]) element lengths} must - * be specified for this mode. - */ - Hybrid(false), - /** - * Used for Tessellation only. Requires to set the number of vertices - * for each patch (default is 3 for triangle tessellation) - */ - Patch(true); - - private boolean listMode = false; - - private Mode(boolean listMode) { - this.listMode = listMode; - } - - /** - * Returns true if the specified mode is a list mode (meaning - * ,it specifies the indices as a linear list and not some special - * format). - * Will return true for the types {@link #Points}, {@link #Lines} and - * {@link #Triangles}. - * - * @return true if the mode is a list type mode - */ - public boolean isListMode() { - return listMode; - } - } - - /** - * Default Variables - */ - private static final int DEFAULT_VERTEX_ARRAY_ID = -1; - private static final CollisionData DEFAULT_COLLISION_TREE = null; - - private static final float DEFAULT_POINT_SIZE = 1.0f; - private static final float DEFAULT_LINE_WIDTH = 1.0f; - - private static final int DEFAULT_VERT_COUNT = -1; - private static final int DEFAULT_ELEMENT_COUNT = -1; - private static final int DEFAULT_INSTANCE_COUNT = -1; - private static final int DEFAULT_PATCH_VERTEX_COUNT = 3; - private static final int DEFAULT_MAX_NUM_WEIGHTS = -1; - - /** - * The bounding volume that contains the mesh entirely. - * By default a BoundingBox (AABB). - */ - private BoundingVolume meshBound = new BoundingBox(); - - private CollisionData collisionTree = DEFAULT_COLLISION_TREE; - - private SafeArrayList buffersList = new SafeArrayList<>(VertexBuffer.class); - private IntMap buffers = new IntMap<>(); - private VertexBuffer[] lodLevels; - - private float pointSize = DEFAULT_POINT_SIZE; - private float lineWidth = DEFAULT_LINE_WIDTH; - - private transient int vertexArrayID = DEFAULT_VERTEX_ARRAY_ID; - - private int vertCount = DEFAULT_VERT_COUNT; - private int elementCount = DEFAULT_ELEMENT_COUNT; - private int instanceCount = DEFAULT_INSTANCE_COUNT; - private int patchVertexCount = DEFAULT_PATCH_VERTEX_COUNT; //only used for tessellation - private int maxNumWeights = DEFAULT_MAX_NUM_WEIGHTS; // only if using skeletal animation - - private int[] elementLengths; - private int[] modeStart; - - private Mode mode = Mode.Triangles; - - private SafeArrayList morphTargets; - - /** - * Creates a new mesh with no {@link VertexBuffer vertex buffers}. - */ - public Mesh() { - } - - /** - * Create a shallow clone of this Mesh. The {@link VertexBuffer vertex - * buffers} are shared between this and the clone mesh, the rest - * of the data is cloned. - * - * @return A shallow clone of the mesh - */ - @Override - public Mesh clone() { - try { - Mesh clone = (Mesh) super.clone(); - clone.meshBound = meshBound.clone(); - clone.collisionTree = collisionTree != null ? collisionTree : null; - clone.buffers = buffers.clone(); - clone.buffersList = new SafeArrayList<>(VertexBuffer.class, buffersList); - clone.vertexArrayID = DEFAULT_VERTEX_ARRAY_ID; - if (elementLengths != null) { - clone.elementLengths = elementLengths.clone(); - } - if (modeStart != null) { - clone.modeStart = modeStart.clone(); - } - return clone; - } catch (CloneNotSupportedException ex) { - throw new AssertionError(); - } - } - - /** - * Creates a deep clone of this mesh. - * The {@link VertexBuffer vertex buffers} and the data inside them - * is cloned. - * - * @return a deep clone of this mesh. - */ - public Mesh deepClone() { - try { - Mesh clone = (Mesh) super.clone(); - clone.meshBound = meshBound != null ? meshBound.clone() : null; - - // TODO: Collision tree cloning - //clone.collisionTree = collisionTree != null ? collisionTree : null; - clone.collisionTree = DEFAULT_COLLISION_TREE; // it will get re-generated in any case - - clone.buffers = new IntMap<>(); - clone.buffersList = new SafeArrayList<>(VertexBuffer.class); - for (VertexBuffer vb : buffersList.getArray()) { - VertexBuffer bufClone = vb.clone(); - clone.buffers.put(vb.getBufferType().ordinal(), bufClone); - clone.buffersList.add(bufClone); - } - - clone.vertexArrayID = DEFAULT_VERTEX_ARRAY_ID; - clone.vertCount = vertCount; - clone.elementCount = elementCount; - clone.instanceCount = instanceCount; - - // although this could change - // if the bone weight/index buffers are modified - clone.maxNumWeights = maxNumWeights; - - clone.elementLengths = elementLengths != null ? elementLengths.clone() : null; - clone.modeStart = modeStart != null ? modeStart.clone() : null; - return clone; - } catch (CloneNotSupportedException ex) { - throw new AssertionError(); - } - } - - /** - * Clone the mesh for animation use. - * This creates a shallow clone of the mesh, sharing most - * of the {@link VertexBuffer vertex buffer} data, however the - * {@link Type#Position}, {@link Type#Normal}, and {@link Type#Tangent} buffers - * are deeply cloned. - * - * @return A clone of the mesh for animation use. - */ - public Mesh cloneForAnim() { - Mesh clone = clone(); - if (getBuffer(Type.BindPosePosition) != null) { - VertexBuffer oldPos = getBuffer(Type.Position); - - // NOTE: creates deep clone - VertexBuffer newPos = oldPos.clone(); - clone.clearBuffer(Type.Position); - clone.setBuffer(newPos); - - if (getBuffer(Type.BindPoseNormal) != null) { - VertexBuffer oldNorm = getBuffer(Type.Normal); - VertexBuffer newNorm = oldNorm.clone(); - clone.clearBuffer(Type.Normal); - clone.setBuffer(newNorm); - - if (getBuffer(Type.BindPoseTangent) != null) { - VertexBuffer oldTang = getBuffer(Type.Tangent); - VertexBuffer newTang = oldTang.clone(); - clone.clearBuffer(Type.Tangent); - clone.setBuffer(newTang); - } - } - } - return clone; - } - - /** - * Called internally by com.jme3.util.clone.Cloner. Do not call directly. - */ - @Override - public Mesh jmeClone() { - try { - Mesh clone = (Mesh) super.clone(); - clone.vertexArrayID = DEFAULT_VERTEX_ARRAY_ID; - return clone; - } catch (CloneNotSupportedException ex) { - throw new AssertionError(); - } - } - - /** - * Called internally by com.jme3.util.clone.Cloner. Do not call directly. - */ - @Override - public void cloneFields(Cloner cloner, Object original) { - // Probably could clone this now but it will get regenerated anyway. - this.collisionTree = DEFAULT_COLLISION_TREE; - - this.meshBound = cloner.clone(meshBound); - this.buffersList = cloner.clone(buffersList); - this.buffers = cloner.clone(buffers); - this.lodLevels = cloner.clone(lodLevels); - this.elementLengths = cloner.clone(elementLengths); - this.modeStart = cloner.clone(modeStart); - } - - /** - * @param forSoftwareAnim ignored - * @deprecated use generateBindPose(); - */ - @Deprecated - public void generateBindPose(boolean forSoftwareAnim) { - generateBindPose(); - } - - /** - * Generates the {@link Type#BindPosePosition}, {@link Type#BindPoseNormal}, - * and {@link Type#BindPoseTangent} - * buffers for this mesh by duplicating them based on the position and normal - * buffers already set on the mesh. - * This method does nothing if the mesh has no bone weight or index - * buffers. - */ - public void generateBindPose() { - VertexBuffer pos = getBuffer(Type.Position); - if (pos == null || getBuffer(Type.BoneIndex) == null) { - // ignore, this mesh doesn't have positional data - // or it doesn't have bone-vertex assignments, so it's not animated - return; - } - - VertexBuffer bindPos = new VertexBuffer(Type.BindPosePosition); - bindPos.setupData(Usage.CpuOnly, - pos.getNumComponents(), - pos.getFormat(), - BufferUtils.clone(pos.getData())); - setBuffer(bindPos); - - // XXX: note that this method also sets stream mode - // so that animation is faster. this is not needed for hardware skinning - pos.setUsage(Usage.Stream); - - VertexBuffer norm = getBuffer(Type.Normal); - if (norm != null) { - VertexBuffer bindNorm = new VertexBuffer(Type.BindPoseNormal); - bindNorm.setupData(Usage.CpuOnly, - norm.getNumComponents(), - norm.getFormat(), - BufferUtils.clone(norm.getData())); - setBuffer(bindNorm); - norm.setUsage(Usage.Stream); - } - - VertexBuffer tangents = getBuffer(Type.Tangent); - if (tangents != null) { - VertexBuffer bindTangents = new VertexBuffer(Type.BindPoseTangent); - bindTangents.setupData(Usage.CpuOnly, - tangents.getNumComponents(), - tangents.getFormat(), - BufferUtils.clone(tangents.getData())); - setBuffer(bindTangents); - tangents.setUsage(Usage.Stream); - }// else hardware setup does nothing, mesh already in bind pose - - } - - /** - * Prepares the mesh for software skinning by converting the bone index - * and weight buffers to heap buffers. - * - * @param forSoftwareAnim Should be true to enable the conversion. - */ - public void prepareForAnim(boolean forSoftwareAnim) { - if (forSoftwareAnim) { - // convert indices to ubytes on the heap - VertexBuffer indices = getBuffer(Type.BoneIndex); - if (!indices.getData().hasArray()) { - if (indices.getFormat() == Format.UnsignedByte) { - ByteBuffer originalIndex = (ByteBuffer) indices.getData(); - ByteBuffer arrayIndex = ByteBuffer.allocate(originalIndex.capacity()); - originalIndex.clear(); - arrayIndex.put(originalIndex); - indices.updateData(arrayIndex); - } else { - //bone indices can be stored in an UnsignedShort buffer - ShortBuffer originalIndex = (ShortBuffer) indices.getData(); - ShortBuffer arrayIndex = ShortBuffer.allocate(originalIndex.capacity()); - originalIndex.clear(); - arrayIndex.put(originalIndex); - indices.updateData(arrayIndex); - } - } - indices.setUsage(Usage.CpuOnly); - - // convert weights on the heap - VertexBuffer weights = getBuffer(Type.BoneWeight); - if (!weights.getData().hasArray()) { - FloatBuffer originalWeight = (FloatBuffer) weights.getData(); - FloatBuffer arrayWeight = FloatBuffer.allocate(originalWeight.capacity()); - originalWeight.clear(); - arrayWeight.put(originalWeight); - weights.updateData(arrayWeight); - } - weights.setUsage(Usage.CpuOnly); - // position, normal, and tangent buffers to be in "Stream" mode - VertexBuffer positions = getBuffer(Type.Position); - VertexBuffer normals = getBuffer(Type.Normal); - VertexBuffer tangents = getBuffer(Type.Tangent); - positions.setUsage(Usage.Stream); - if (normals != null) { - normals.setUsage(Usage.Stream); - } - if (tangents != null) { - tangents.setUsage(Usage.Stream); - } - } else { - //if HWBoneIndex and HWBoneWeight are empty, we setup them as direct - //buffers with software anim buffers data - VertexBuffer indicesHW = getBuffer(Type.HWBoneIndex); - Buffer result; - if (indicesHW.getData() == null) { - VertexBuffer indices = getBuffer(Type.BoneIndex); - if (indices.getFormat() == Format.UnsignedByte) { - ByteBuffer originalIndex = (ByteBuffer) indices.getData(); - ByteBuffer directIndex - = BufferUtils.createByteBuffer(originalIndex.capacity()); - originalIndex.clear(); - directIndex.put(originalIndex); - result = directIndex; - } else { - //bone indices can be stored in an UnsignedShort buffer - ShortBuffer originalIndex = (ShortBuffer) indices.getData(); - ShortBuffer directIndex - = BufferUtils.createShortBuffer(originalIndex.capacity()); - originalIndex.clear(); - directIndex.put(originalIndex); - result = directIndex; - } - indicesHW.setupData(Usage.Static, indices.getNumComponents(), - indices.getFormat(), result); - } - - VertexBuffer weightsHW = getBuffer(Type.HWBoneWeight); - if (weightsHW.getData() == null) { - VertexBuffer weights = getBuffer(Type.BoneWeight); - FloatBuffer originalWeight = (FloatBuffer) weights.getData(); - FloatBuffer directWeight - = BufferUtils.createFloatBuffer(originalWeight.capacity()); - originalWeight.clear(); - directWeight.put(originalWeight); - weightsHW.setupData(Usage.Static, weights.getNumComponents(), - weights.getFormat(), directWeight); - } - - // position, normal, and tangent buffers to be in "Static" mode - VertexBuffer positions = getBuffer(Type.Position); - VertexBuffer normals = getBuffer(Type.Normal); - VertexBuffer tangents = getBuffer(Type.Tangent); - - VertexBuffer positionsBP = getBuffer(Type.BindPosePosition); - VertexBuffer normalsBP = getBuffer(Type.BindPoseNormal); - VertexBuffer tangentsBP = getBuffer(Type.BindPoseTangent); - - positions.setUsage(Usage.Static); - positionsBP.copyElements(0, positions, 0, positionsBP.getNumElements()); - positions.setUpdateNeeded(); - - if (normals != null) { - normals.setUsage(Usage.Static); - normalsBP.copyElements(0, normals, 0, normalsBP.getNumElements()); - normals.setUpdateNeeded(); - } - - if (tangents != null) { - tangents.setUsage(Usage.Static); - tangentsBP.copyElements(0, tangents, 0, tangentsBP.getNumElements()); - tangents.setUpdateNeeded(); - } - } - } - - /** - * Set the LOD (level of detail) index buffers on this mesh. - * - * @param lodLevels The LOD levels to set - */ - public void setLodLevels(VertexBuffer[] lodLevels) { - this.lodLevels = lodLevels; - } - - /** - * @return The number of LOD levels set on this mesh, including the main - * index buffer, returns zero if there are no lod levels. - */ - public int getNumLodLevels() { - return lodLevels != null ? lodLevels.length : 0; - } - - /** - * Returns the lod level at the given index. - * - * @param lod The lod level index, this does not include - * the main index buffer. - * @return The LOD index buffer at the index - * - * @throws IndexOutOfBoundsException If the index is outside of the - * range [0, {@link #getNumLodLevels()}]. - * - * @see #setLodLevels(com.jme3.scene.VertexBuffer[]) - */ - public VertexBuffer getLodLevel(int lod) { - return lodLevels[lod]; - } - - /** - * Get the element lengths for {@link Mode#Hybrid} mesh mode. - * - * @return element lengths - */ - public int[] getElementLengths() { - return elementLengths; - } - - /** - * Set the element lengths for {@link Mode#Hybrid} mesh mode. - * - * @param elementLengths The element lengths to set - */ - public void setElementLengths(int[] elementLengths) { - this.elementLengths = elementLengths; - } - - /** - * Set the mode start indices for {@link Mode#Hybrid} mesh mode. - * - * @return mode start indices - */ - public int[] getModeStart() { - return modeStart; - } - - /** - * Get the mode start indices for {@link Mode#Hybrid} mesh mode. - * - * @param modeStart the pre-existing array - */ - public void setModeStart(int[] modeStart) { - this.modeStart = modeStart; - } - - /** - * Returns the mesh mode - * - * @return the mesh mode - * - * @see #setMode(com.jme3.scene.Mesh.Mode) - */ - public Mode getMode() { - return mode; - } - - /** - * Change the Mesh's mode. By default the mode is {@link Mode#Triangles}. - * - * @param mode The new mode to set - * - * @see Mode - */ - public void setMode(Mode mode) { - this.mode = mode; - updateCounts(); - } - - /** - * Returns the maximum number of weights per vertex on this mesh. - * - * @return maximum number of weights per vertex - * - * @see #setMaxNumWeights(int) - */ - public int getMaxNumWeights() { - return maxNumWeights; - } - - /** - * Set the maximum number of weights per vertex on this mesh. - * Only relevant if this mesh has bone index/weight buffers. - * This value should be between 0 and 4. - * - * @param maxNumWeights the desired number (between 0 and 4, inclusive) - */ - public void setMaxNumWeights(int maxNumWeights) { - this.maxNumWeights = maxNumWeights; - } - - /** - * @deprecated Always returns 1.0 since point size is - * determined in the vertex shader. - * - * @return 1.0 - */ - @Deprecated - public float getPointSize() { - return DEFAULT_POINT_SIZE; - } - - /** - * Returns the line width for line meshes. - * - * @return the line width - * @deprecated use {@link Material#getAdditionalRenderState()} - * and {@link RenderState#getLineWidth()} - */ - @Deprecated - public float getLineWidth() { - return lineWidth; - } - - /** - * Specify the line width for meshes of the line modes, such - * as {@link Mode#Lines}. The line width is specified as on-screen pixels, - * the default value is 1.0. - * - * @param lineWidth The line width - * @deprecated use {@link Material#getAdditionalRenderState()} - * and {@link RenderState#setLineWidth(float)} - */ - @Deprecated - public void setLineWidth(float lineWidth) { - if (lineWidth < 1f) { - throw new IllegalArgumentException("lineWidth must be greater than or equal to 1.0"); - } - this.lineWidth = lineWidth; - } - - /** - * Indicates to the GPU that this mesh will not be modified (a hint). - * Sets the usage mode to {@link Usage#Static} - * for all {@link VertexBuffer vertex buffers} on this Mesh. - */ - public void setStatic() { - for (VertexBuffer vb : buffersList.getArray()) { - vb.setUsage(Usage.Static); - } - } - - /** - * Indicates to the GPU that this mesh will be modified occasionally (a hint). - * Sets the usage mode to {@link Usage#Dynamic} - * for all {@link VertexBuffer vertex buffers} on this Mesh. - */ - public void setDynamic() { - for (VertexBuffer vb : buffersList.getArray()) { - vb.setUsage(Usage.Dynamic); - } - } - - /** - * Indicates to the GPU that this mesh will be modified every frame (a hint). - * Sets the usage mode to {@link Usage#Stream} - * for all {@link VertexBuffer vertex buffers} on this Mesh. - */ - public void setStreamed() { - for (VertexBuffer vb : buffersList.getArray()) { - vb.setUsage(Usage.Stream); - } - } - - /** - * Interleaves the data in this mesh. This operation cannot be reversed. - * Some GPUs may prefer the data in this format, however it is a good idea - * to avoid using this method as it disables some engine features. - */ - @Deprecated - public void setInterleaved() { - ArrayList vbs = new ArrayList<>(); - vbs.addAll(buffersList); - -// ArrayList vbs = new ArrayList(buffers.values()); - // index buffer not included when interleaving - vbs.remove(getBuffer(Type.Index)); +public interface Mesh extends Savable { - int stride = 0; // aka bytes per vertex - for (int i = 0; i < vbs.size(); i++) { - VertexBuffer vb = vbs.get(i); -// if (vb.getFormat() != Format.Float){ -// throw new UnsupportedOperationException("Cannot interleave vertex buffer.\n" + -// "Contains not-float data."); -// } - stride += vb.componentsLength; - vb.getData().clear(); // reset position & limit (used later) - } - - VertexBuffer allData = new VertexBuffer(Type.InterleavedData); - ByteBuffer dataBuf = BufferUtils.createByteBuffer(stride * getVertexCount()); - allData.setupData(Usage.Static, 1, Format.UnsignedByte, dataBuf); - - // adding buffer directly so that no update counts is forced - buffers.put(Type.InterleavedData.ordinal(), allData); - buffersList.add(allData); - - for (int vert = 0; vert < getVertexCount(); vert++) { - for (int i = 0; i < vbs.size(); i++) { - VertexBuffer vb = vbs.get(i); - switch (vb.getFormat()) { - case Float: - FloatBuffer fb = (FloatBuffer) vb.getData(); - for (int comp = 0; comp < vb.components; comp++) { - dataBuf.putFloat(fb.get()); - } - break; - case Byte: - case UnsignedByte: - ByteBuffer bb = (ByteBuffer) vb.getData(); - for (int comp = 0; comp < vb.components; comp++) { - dataBuf.put(bb.get()); - } - break; - case Half: - case Short: - case UnsignedShort: - ShortBuffer sb = (ShortBuffer) vb.getData(); - for (int comp = 0; comp < vb.components; comp++) { - dataBuf.putShort(sb.get()); - } - break; - case Int: - case UnsignedInt: - IntBuffer ib = (IntBuffer) vb.getData(); - for (int comp = 0; comp < vb.components; comp++) { - dataBuf.putInt(ib.get()); - } - break; - case Double: - DoubleBuffer db = (DoubleBuffer) vb.getData(); - for (int comp = 0; comp < vb.components; comp++) { - dataBuf.putDouble(db.get()); - } - break; - } - } - } - - int offset = 0; - for (VertexBuffer vb : vbs) { - vb.setOffset(offset); - vb.setStride(stride); - - vb.updateData(null); - //vb.setupData(vb.usage, vb.components, vb.format, null); - offset += vb.componentsLength; - } - } - - private int computeNumElements(int bufSize) { - switch (mode) { - case Triangles: - return bufSize / 3; - case TriangleFan: - case TriangleStrip: - return bufSize - 2; - case Points: - return bufSize; - case Lines: - return bufSize / 2; - case LineLoop: - return bufSize; - case LineStrip: - return bufSize - 1; - case Patch: - return bufSize / patchVertexCount; - default: - throw new UnsupportedOperationException(); - } - } - - private int computeInstanceCount() { - // Whatever the max of the base instance counts - int max = 0; - for (VertexBuffer vb : buffersList) { - if (vb.getBaseInstanceCount() > max) { - max = vb.getBaseInstanceCount(); - } - } - return max; - } + Vertex getVertices(); /** - * Update the {@link #getVertexCount() vertex} and - * {@link #getTriangleCount() triangle} counts for this mesh - * based on the current data. This method should be called - * after the {@link Buffer#capacity() capacities} of the mesh's - * {@link VertexBuffer vertex buffers} has been altered. + * Creates an AttributeModifier with which to modify the vertex data + * of the named attribute. If the named attribute has not been initialized, + * it will be initialized here if possible. * - * @throws IllegalStateException If this mesh is in - * {@link #setInterleaved() interleaved} format. - */ - public void updateCounts() { - if (getBuffer(Type.InterleavedData) != null) { - throw new IllegalStateException("Should update counts before interleave"); - } - - VertexBuffer pb = getBuffer(Type.Position); - VertexBuffer ib = getBuffer(Type.Index); - if (pb != null) { - vertCount = pb.getData().limit() / pb.getNumComponents(); - } - if (ib != null) { - elementCount = computeNumElements(ib.getData().limit()); - } else { - elementCount = computeNumElements(vertCount); - } - instanceCount = computeInstanceCount(); - } - - /** - * Returns the triangle count for the given LOD level. + *

The returned modifier must be properly closed after use.

* - * @param lod The lod level to look up - * @return The triangle count for that LOD level + * @param attributeName attribute to modify + * @return modifier + * @throws IllegalArgumentException if {@code namedAttribute} does not + * correspond to an existing attribute and the implementation is unable + * to create a new attribute. */ - public int getTriangleCount(int lod) { - if (lodLevels != null) { - if (lod < 0) { - throw new IllegalArgumentException("LOD level cannot be < 0"); - } + AttributeModifier modify(String attributeName); - if (lod >= lodLevels.length) { - throw new IllegalArgumentException("LOD level " + lod + " does not exist!"); - } - - return computeNumElements(lodLevels[lod].getData().limit()); - } else if (lod == 0) { - return elementCount; - } else { - throw new IllegalArgumentException("There are no LOD levels on the mesh!"); - } - } + boolean attributeExists(String attributeName); /** - * Returns how many triangles or elements are on this Mesh. - * This value is only updated when {@link #updateCounts() } is called. - * If the mesh mode is not a triangle mode, then this returns the - * number of elements/primitives, e.g. how many lines or how many points, - * instead of how many triangles. + * Gets the index buffer for the specified level of detail (LOD). * - * @return how many triangles/elements are on this Mesh. + * @param lod the level of detail to fetch from + * @return index buffer for {@code lod}, or null if no such buffer exists */ - public int getTriangleCount() { - return elementCount; - } + GpuBuffer getIndices(int lod); /** - * Returns the number of vertices on this mesh. - * The value is computed based on the position buffer, which - * must be set on all meshes. + * Hints at how often the named attribute will be modified by the host. + * If {@code hint's} ordinal is less than the attribute's vertex buffer's + * current access hint ordinal (indicating more accesses), then the vertex + * buffer is replaced with one optimized for higher frequency accesses. * - * @return Number of vertices on the mesh + * @param attributeName name of the attribute the hint applies to + * @param hint access frequency hint (lower ordinal is more optimized + * for frequent accesses) */ - public int getVertexCount() { - return vertCount; - } + void setAccessFrequency(String attributeName, DataAccess hint); /** - * Returns the number of instances this mesh contains. The instance - * count is based on any VertexBuffers with instancing set. + * Sets the minimum number of vertices that must be supported by + * this mesh's vertex buffers. * - * @return the number of instances - */ - public int getInstanceCount() { - return instanceCount; - } - - /** - * Gets the triangle vertex positions at the given triangle index - * and stores them into the v1, v2, v3 arguments. + *

If the current vertex buffers are unable to support the minimum + * count as a result of this method, then the current vertex data is + * copied to a functionally identical set of larger vertex buffers. + * Implementations are free to anticipate demand for more vertices + * by creating larger vertex buffers than are currently necessary.

* - * @param index The index of the triangle. - * Should be between 0 and {@link #getTriangleCount()}. - * - * @param v1 Vector to contain first vertex position - * @param v2 Vector to contain second vertex position - * @param v3 Vector to contain third vertex position + * @param vertices minimum number of vertices that must be supported */ - public void getTriangle(int index, Vector3f v1, Vector3f v2, Vector3f v3) { - VertexBuffer pb = getBuffer(Type.Position); - IndexBuffer ib = getIndicesAsList(); - if (pb != null && pb.getFormat() == Format.Float && pb.getNumComponents() == 3) { - FloatBuffer fpb = (FloatBuffer) pb.getData(); - - // acquire triangle's vertex indices - int vertIndex = index * 3; - int vert1 = ib.get(vertIndex); - int vert2 = ib.get(vertIndex + 1); - int vert3 = ib.get(vertIndex + 2); - - BufferUtils.populateFromBuffer(v1, fpb, vert1); - BufferUtils.populateFromBuffer(v2, fpb, vert2); - BufferUtils.populateFromBuffer(v3, fpb, vert3); - } else { - throw new UnsupportedOperationException("Position buffer not set or " - + " has incompatible format"); - } - } + void setVertexCount(int vertices); /** - * Gets the triangle vertex positions at the given triangle index - * and stores them into the {@link Triangle} argument. - * Also sets the triangle index to the index argument. + * Sets the minimum number of triangles that must be supported + * by the LOD's index buffer. For rendering purposes, only + * {@code triangles} number of triangles are drawn. * - * @param index The index of the triangle. - * Should be between 0 and {@link #getTriangleCount()}. + *

If the current index buffer is unable to support the minimum + * count as a result of this method, then the current index data is + * copied to a larger (but functionally identical) index buffer. + * Implementations are free to anticipate demand for more triangles + * by creating larger index buffers than are currently necessary.

* - * @param tri The triangle to store the positions in + * @param lod level of detail (LOD) index + * @param triangles minimum number of triangles that must be supported */ - public void getTriangle(int index, Triangle tri) { - getTriangle(index, tri.get1(), tri.get2(), tri.get3()); - tri.setIndex(index); - tri.setCenter(null); // invalidate previously cached centroid, if any - tri.setNormal(null); - } + void setTriangleCount(int lod, int triangles); /** - * Gets the triangle vertex indices at the given triangle index - * and stores them into the given int array. - * - * @param index The index of the triangle. - * Should be between 0 and {@link #getTriangleCount()}. + * Sets the minimum number of instances that must be supported by this + * mesh's {@link com.jme3.vulkan.mesh.InputRate#Instance per-instance} + * vertex buffers. * - * @param indices Indices of the triangle's vertices + * @param instances number of instances on this mesh */ - public void getTriangle(int index, int[] indices) { - IndexBuffer ib = getIndicesAsList(); - - // acquire triangle's vertex indices - int vertIndex = index * 3; - indices[0] = ib.get(vertIndex); - indices[1] = ib.get(vertIndex + 1); - indices[2] = ib.get(vertIndex + 2); - } + void setInstanceCount(int instances); /** - * Returns the mesh's VAO ID. Internal use only. + * Gets the vertex count as specified by {@link #setVertexCount(int)}. * - * @return the array ID + * @return vertex count */ - public int getId() { - return vertexArrayID; - } + int getVertexCount(); /** - * Sets the mesh's VAO ID. Internal use only. + * Gets the triangle count for the specified LOD. If the LOD + * does not exist, zero is returned. * - * @param id the array ID - */ - public void setId(int id) { - if (vertexArrayID != DEFAULT_VERTEX_ARRAY_ID) { - throw new IllegalStateException("ID has already been set."); - } - - vertexArrayID = id; - } - - /** - * Generates a collision tree for the mesh. - * Called automatically by {@link #collideWith(com.jme3.collision.Collidable, - * com.jme3.math.Matrix4f, - * com.jme3.bounding.BoundingVolume, - * com.jme3.collision.CollisionResults) }. + * @param lod level of detail + * @return number of triangles for that LOD */ - public void createCollisionData() { - BIHTree tree = new BIHTree(this); - tree.construct(); - collisionTree = tree; - } + int getTriangleCount(int lod); /** - * Clears any previously generated collision data. Use this if - * the mesh has changed in some way that invalidates any previously - * generated BIHTree. - */ - public void clearCollisionData() { - collisionTree = DEFAULT_COLLISION_TREE; - } - - /** - * Handles collision detection, internal use only. - * User code should only use collideWith() on scene - * graph elements such as {@link Spatial}s. + * Gets the instance count as specified by {@link #setInstanceCount(int)}. * - * @param other the other Collidable - * @param worldMatrix the world matrix - * @param worldBound the world bound - * @param results storage for the results - * @return the number of collisions detected (≥0) + * @return instance count */ - public int collideWith(Collidable other, - Matrix4f worldMatrix, - BoundingVolume worldBound, - CollisionResults results) { - - switch (mode) { - case Points: - case Lines: - case LineStrip: - case LineLoop: - /* - * Collisions can be detected only with triangles, - * and there are no triangles in this mesh. - */ - return 0; - } - - if (getVertexCount() == 0) { - return 0; - } - - if (collisionTree == null) { - createCollisionData(); - } - - return collisionTree.collideWith(other, worldMatrix, worldBound, results); - } + int getInstanceCount(); /** - * Sets the {@link VertexBuffer} on the mesh. - * This will update the vertex/triangle counts if needed. + * Collides with {@code other} in the context of {@code geometry}. Collisions + * are stored in {@code results}. * - * @param vb The buffer to set - * @throws IllegalArgumentException If the buffer type is already set + * @param other collidable to test collisions with + * @param geometry geometry specifying the world bound and matrix + * @param results collision results to store the collisions in + * @return number of collisions that occured */ - public void setBuffer(VertexBuffer vb) { - if (buffers.containsKey(vb.getBufferType().ordinal())) { - throw new IllegalArgumentException("Buffer type already set: " + vb.getBufferType()); - } - - buffers.put(vb.getBufferType().ordinal(), vb); - buffersList.add(vb); - updateCounts(); - } + int collideWith(Collidable other, Geometry geometry, CollisionResults results); /** - * Unsets the {@link VertexBuffer} set on this mesh - * with the given type. Does nothing if the vertex buffer type is not set - * initially. - * - * @param type The buffer type to remove + * Updates the bounding volume to contain all vertices of this mesh in model space. */ - public void clearBuffer(VertexBuffer.Type type) { - VertexBuffer vb = buffers.remove(type.ordinal()); - if (vb != null) { - buffersList.remove(vb); - updateCounts(); - } - } + void updateBound(); /** - * Creates a {@link VertexBuffer} for the mesh or modifies - * the existing one per the parameters given. + * Sets the bounding volume to be used by this mesh. * - * @param type The type of the buffer - * @param components Number of components - * @param format Data format - * @param buf The buffer data - * - * @throws UnsupportedOperationException If the buffer already set is - * incompatible with the parameters given. + * @param volume volume to use */ - public void setBuffer(Type type, int components, Format format, Buffer buf) { - VertexBuffer vb = buffers.get(type.ordinal()); - if (vb == null) { - vb = new VertexBuffer(type); - vb.setupData(Usage.Dynamic, components, format, buf); - setBuffer(vb); - } else { - if (vb.getNumComponents() != components || vb.getFormat() != format) { - throw new UnsupportedOperationException("The buffer already set " - + "is incompatible with the given parameters"); - } - vb.updateData(buf); - updateCounts(); - } - } + void setBound(BoundingVolume volume); /** - * Set a floating point {@link VertexBuffer} on the mesh. - * - * @param type The type of {@link VertexBuffer}, - * e.g. {@link Type#Position}, {@link Type#Normal}, etc. + * Gets the bounding volume used by this mesh. * - * @param components Number of components on the vertex buffer, should - * be between 1 and 4. - * - * @param buf The floating point data to contain + * @return bounding volume */ - public void setBuffer(Type type, int components, FloatBuffer buf) { - setBuffer(type, components, Format.Float, buf); - } - - public void setBuffer(Type type, int components, float[] buf) { - setBuffer(type, components, BufferUtils.createFloatBuffer(buf)); - } - - public void setBuffer(Type type, int components, IntBuffer buf) { - setBuffer(type, components, Format.UnsignedInt, buf); - } - - public void setBuffer(Type type, int components, int[] buf) { - setBuffer(type, components, BufferUtils.createIntBuffer(buf)); - } - - public void setBuffer(Type type, int components, ShortBuffer buf) { - setBuffer(type, components, Format.UnsignedShort, buf); - } - - public void setBuffer(Type type, int components, byte[] buf) { - setBuffer(type, components, BufferUtils.createByteBuffer(buf)); - } - - public void setBuffer(Type type, int components, ByteBuffer buf) { - setBuffer(type, components, Format.UnsignedByte, buf); - } - - public void setBuffer(Type type, int components, short[] buf) { - setBuffer(type, components, BufferUtils.createShortBuffer(buf)); - } + BoundingVolume getBound(); /** - * Get the {@link VertexBuffer} stored on this mesh with the given - * type. + * Sets the topology format of the index buffers. * - * @param type The type of VertexBuffer - * @return the VertexBuffer data, or null if not set + * @param topology topology */ - public VertexBuffer getBuffer(Type type) { - return buffers.get(type.ordinal()); - } + void setTopology(IntEnum topology); /** - * Get the {@link VertexBuffer} data stored on this mesh in float - * format. * - * @param type The type of VertexBuffer - * @return the VertexBuffer data, or null if not set + * @return topology */ - public FloatBuffer getFloatBuffer(Type type) { - VertexBuffer vb = getBuffer(type); - if (vb == null) { - return null; - } - - return (FloatBuffer) vb.getData(); - } + IntEnum getTopology(); /** - * Get the {@link VertexBuffer} data stored on this mesh in short - * format. + * Gets the number of defined LOD levels. * - * @param type The type of VertexBuffer - * @return the VertexBuffer data, or null if not set + * @return number of LOD levels */ - public ShortBuffer getShortBuffer(Type type) { - VertexBuffer vb = getBuffer(type); - if (vb == null) { - return null; - } - - return (ShortBuffer) vb.getData(); - } + int getNumLodLevels(); /** - * Acquires an index buffer that will read the vertices on the mesh - * as a list. + * Fetches that positional data of the requested triangle. * - * @return A virtual or wrapped index buffer to read the data as a list + * @param triangleIndex triangle to fetch + * @param store stores the result (if null, a new Triangle is created) + * @return triangle respresenting the mesh triangle */ - public IndexBuffer getIndicesAsList() { - if (mode == Mode.Hybrid) { - throw new UnsupportedOperationException("Hybrid mode not supported"); - } - - IndexBuffer ib = getIndexBuffer(); - if (ib != null) { - if (mode.isListMode()) { - // already in list mode - return ib; - } else { - // not in list mode but it does have an index buffer - // wrap it so the data is converted to list format - return new WrappedIndexBuffer(this); - } - } else { - // return a virtual index buffer that will supply - // "fake" indices in list format - return new VirtualIndexBuffer(vertCount, mode); - } - } + Triangle getTriangle(int triangleIndex, Triangle store); /** - * Get the index buffer for this mesh. - * Will return null if no index buffer is set. - * - * @return The index buffer of this mesh. + * Gets the indices pointing to the vertices of the requested triangle. * - * @see Type#Index + * @param triangleIndex triangle to fetch + * @param store array storing the indices (must have at least 3 elements or be null) + * @return array storing the indices */ - public IndexBuffer getIndexBuffer() { - VertexBuffer vb = getBuffer(Type.Index); - if (vb == null) { - return null; - } + int[] getTriangle(int triangleIndex, int[] store); - return IndexBuffer.wrapIndexBuffer(vb.getData()); - } + /* ----- DEFAULTS ----- */ /** - * Extracts the vertex attributes from the given mesh into - * this mesh, by using this mesh's {@link #getIndexBuffer() index buffer} - * to index into the attributes of the other mesh. - * Note that this will also change this mesh's index buffer so that - * the references to the vertex data match the new indices. + * Modifies the named attribute. * - * @param other The mesh to extract the vertex data from + * @param name name of the attribute + * @return modifier + * @see #modify(String) */ - public void extractVertexData(Mesh other) { - // Determine the number of unique vertices need to - // be created. Also determine the mappings - // between old indices to new indices (since we avoid duplicating - // vertices, this is a map and not an array). - VertexBuffer oldIdxBuf = getBuffer(Type.Index); - IndexBuffer indexBuf = getIndexBuffer(); - int numIndices = indexBuf.size(); - - IntMap oldIndicesToNewIndices = new IntMap<>(numIndices); - ArrayList newIndicesToOldIndices = new ArrayList<>(); - int newIndex = 0; - - for (int i = 0; i < numIndices; i++) { - int oldIndex = indexBuf.get(i); - - if (!oldIndicesToNewIndices.containsKey(oldIndex)) { - // this vertex has not been added, so allocate a - // new index for it and add it to the map - oldIndicesToNewIndices.put(oldIndex, newIndex); - newIndicesToOldIndices.add(oldIndex); - - // increment to have the next index - newIndex++; - } - } - - // Number of unique verts to be created now available - int newNumVerts = newIndicesToOldIndices.size(); - - if (newIndex != newNumVerts) { - throw new AssertionError(); - } - - // Create the new index buffer. - // Do not overwrite the old one because we might be able to - // convert from int index buffer to short index buffer - IndexBuffer newIndexBuf; - if (newNumVerts >= 65536) { - newIndexBuf = new IndexIntBuffer(BufferUtils.createIntBuffer(numIndices)); - } else { - newIndexBuf = new IndexShortBuffer(BufferUtils.createShortBuffer(numIndices)); - } - - for (int i = 0; i < numIndices; i++) { - // Map the old indices to the new indices - int oldIndex = indexBuf.get(i); - newIndex = oldIndicesToNewIndices.get(oldIndex); - - newIndexBuf.put(i, newIndex); - } - - VertexBuffer newIdxBuf = new VertexBuffer(Type.Index); - newIdxBuf.setupData(oldIdxBuf.getUsage(), - oldIdxBuf.getNumComponents(), - newIndexBuf instanceof IndexIntBuffer ? Format.UnsignedInt : Format.UnsignedShort, - newIndexBuf.getBuffer()); - clearBuffer(Type.Index); - setBuffer(newIdxBuf); - - // Now, create the vertex buffers - SafeArrayList oldVertexData = other.getBufferList(); - for (VertexBuffer oldVb : oldVertexData) { - if (oldVb.getBufferType() == VertexBuffer.Type.Index) { - // ignore the index buffer - continue; - } - - VertexBuffer newVb = new VertexBuffer(oldVb.getBufferType()); - newVb.setNormalized(oldVb.isNormalized()); - //check for data before copying, some buffers are just empty shells - //for caching purpose (HW skinning buffers), and will be filled when - //needed - if (oldVb.getData() != null) { - // Create a new vertex buffer with similar configuration, but - // with the capacity of number of unique vertices - Buffer buffer = VertexBuffer.createBuffer(oldVb.getFormat(), - oldVb.getNumComponents(), newNumVerts); - newVb.setupData(oldVb.getUsage(), oldVb.getNumComponents(), - oldVb.getFormat(), buffer); - - // Copy the vertex data from the old buffer into the new buffer - for (int i = 0; i < newNumVerts; i++) { - int oldIndex = newIndicesToOldIndices.get(i); - - // Copy the vertex attribute from the old index - // to the new index - oldVb.copyElement(oldIndex, newVb, i); - } - } - - // Set the buffer on the mesh - clearBuffer(newVb.getBufferType()); - setBuffer(newVb); - } - - // Copy max weights per vertex as well - setMaxNumWeights(other.getMaxNumWeights()); - - // The data has been copied over, update information - updateCounts(); - updateBound(); + default AttributeModifier modify(GlVertexBuffer.Type name) { + return modify(name.getName()); } /** - * Scales the texture coordinate buffer on this mesh by the given scale - * factor. - *

- * Note that values above 1 will cause the - * texture to tile, while values below 1 will cause the texture - * to stretch. - *

- * - * @param scaleFactor The scale factor to scale by. Every texture - * coordinate is multiplied by this vector to get the result. + * Modifies the position attribute. * - * @throws IllegalStateException If there's no texture coordinate - * buffer on the mesh - * @throws UnsupportedOperationException If the texture coordinate - * buffer is not in 2D float format. - */ - public void scaleTextureCoordinates(Vector2f scaleFactor) { - VertexBuffer tc = getBuffer(Type.TexCoord); - if (tc == null) { - throw new IllegalStateException("The mesh has no texture coordinates"); - } - - if (tc.getFormat() != VertexBuffer.Format.Float) { - throw new UnsupportedOperationException("Only float texture coord format is supported"); - } - - if (tc.getNumComponents() != 2) { - throw new UnsupportedOperationException("Only 2D texture coords are supported"); - } - - FloatBuffer fb = (FloatBuffer) tc.getData(); - fb.clear(); - for (int i = 0; i < fb.limit() / 2; i++) { - float x = fb.get(); - float y = fb.get(); - fb.position(fb.position() - 2); - x *= scaleFactor.getX(); - y *= scaleFactor.getY(); - fb.put(x).put(y); - } - fb.clear(); - tc.updateData(fb); - } - - /** - * Updates the bounding volume of this mesh. - * The method does nothing if the mesh has no {@link Type#Position} buffer. - * It is expected that the position buffer is a float buffer with 3 components. + * @return modifier + * @see #modify(String) */ - public void updateBound() { - VertexBuffer posBuf = getBuffer(VertexBuffer.Type.Position); - if (meshBound != null && posBuf != null) { - meshBound.computeFromPoints((FloatBuffer) posBuf.getData()); - } + default AttributeModifier modifyPosition() { + return modify(GlVertexBuffer.Type.Position.name()); } /** - * Returns the {@link BoundingVolume} of this Mesh. - * By default the bounding volume is a {@link BoundingBox}. + * Gets the triangle count for the base LOD level (0). * - * @return the bounding volume of this mesh + * @return triangel count for the base LOD */ - public BoundingVolume getBound() { - return meshBound; + default int getTriangleCount() { + return getTriangleCount(0); } - /** - * Sets the {@link BoundingVolume} for this Mesh. - * The bounding volume is recomputed by calling {@link #updateBound() }. - * - * @param modelBound The model bound to set - */ - public void setBound(BoundingVolume modelBound) { - meshBound = modelBound; + default boolean attributeExists(GlVertexBuffer.Type type) { + return attributeExists(type.name()); } - /** - * Returns a map of all {@link VertexBuffer vertex buffers} on this Mesh. - * The integer key for the map is the {@link Enum#ordinal() ordinal} - * of the vertex buffer's {@link Type}. - * Note that the returned map is a reference to the map used internally, - * modifying it will cause undefined results. - * - * @return map of vertex buffers on this mesh. - */ - public IntMap getBuffers() { - return buffers; + default boolean isAnimated() { + return attributeExists(GlVertexBuffer.Type.BoneIndex) + || attributeExists(GlVertexBuffer.Type.HWBoneIndex); } - /** - * Returns a list of all {@link VertexBuffer vertex buffers} on this Mesh. - * Using a list instead an IntMap via the {@link #getBuffers() } method is - * better for iteration as there's no need to create an iterator instance. - * Note that the returned list is a reference to the list used internally, - * modifying it will cause undefined results. - * - * @return list of vertex buffers on this mesh. - */ - public SafeArrayList getBufferList() { - return buffersList; - } + /* ----- COMPATIBILITY WITH OLD MESH ----- */ - /** - * Determines if the mesh uses bone animation. - * - * A mesh uses bone animation if it has bone index / weight buffers - * such as {@link Type#BoneIndex} or {@link Type#HWBoneIndex}. - * - * @return true if the mesh uses bone animation, false otherwise - */ - public boolean isAnimated() { - return getBuffer(Type.BoneIndex) != null - || getBuffer(Type.HWBoneIndex) != null; + @Deprecated + default void setMode(IntEnum topology) { + setTopology(topology); } - /** - * @deprecated use isAnimatedByJoint - * @param boneIndex the bone's index in its skeleton - * @return true if animated by that bone, otherwise false - */ @Deprecated - public boolean isAnimatedByBone(int boneIndex) { - return isAnimatedByJoint(boneIndex); + default IntEnum getMode() { + return getTopology(); } - /** - * Test whether the specified bone animates this mesh. - * - * @param jointIndex the bone's index in its skeleton - * @return true if the specified bone animates this mesh, otherwise false - */ - public boolean isAnimatedByJoint(int jointIndex) { - VertexBuffer biBuf = getBuffer(VertexBuffer.Type.BoneIndex); - VertexBuffer wBuf = getBuffer(VertexBuffer.Type.BoneWeight); - if (biBuf == null || wBuf == null) { - return false; // no bone animation data - } - - IndexBuffer boneIndexBuffer = IndexBuffer.wrapIndexBuffer(biBuf.getData()); - boneIndexBuffer.rewind(); - int numBoneIndices = boneIndexBuffer.remaining(); - assert numBoneIndices % 4 == 0 : numBoneIndices; - int numVertices = boneIndexBuffer.remaining() / 4; - - FloatBuffer weightBuffer = (FloatBuffer) wBuf.getData(); - weightBuffer.rewind(); - int numWeights = weightBuffer.remaining(); - assert numWeights == numVertices * 4 : numWeights; - /* - * Test each vertex to determine whether the bone affects it. - */ - int biByte = jointIndex; - for (int vIndex = 0; vIndex < numVertices; vIndex++) { - for (int wIndex = 0; wIndex < 4; wIndex++) { - int bIndex = boneIndexBuffer.get(); - float weight = weightBuffer.get(); - if (wIndex < maxNumWeights && bIndex == biByte && weight != 0f) { - return true; - } - } - } + // todo: determine what to do with this method + // It's used only seriously by Joint, and only then for attachment nodes. + // I hope it can be replaced by something else or attachment nodes could + // be refactored to remove that dependency, because the GlMesh implementation + // for this is concerning. + @Deprecated + default boolean isAnimatedByJoint(int i) { return false; } - /** - * Sets the count of vertices used for each tessellation patch - * - * @param patchVertexCount the desired count - */ - public void setPatchVertexCount(int patchVertexCount) { - this.patchVertexCount = patchVertexCount; - } - - /** - * Gets the amount of vertices used for each patch; - * - * @return the count (≥0) - */ - public int getPatchVertexCount() { - return patchVertexCount; - } - - public void addMorphTarget(MorphTarget target) { - if (morphTargets == null) { - morphTargets = new SafeArrayList<>(MorphTarget.class); - } - morphTargets.add(target); - } - - /** - * Remove the given MorphTarget from the Mesh - * @param target The MorphTarget to remove - * @return If the MorphTarget was removed - */ - public boolean removeMorphTarget(MorphTarget target) { - return morphTargets != null ? morphTargets.remove(target) : false; - } - - /** - * Remove the MorphTarget from the Mesh at the given index - * @throws IndexOutOfBoundsException if the index outside the number of morph targets - * @param index Index of the MorphTarget to remove - * @return The MorphTarget that was removed - */ - public MorphTarget removeMorphTarget(int index) { - if (morphTargets == null) { - throw new IndexOutOfBoundsException("Index:" + index + ", Size:0"); - } - return morphTargets.remove(index); - } - - /** - * Get the MorphTarget at the given index - * @throws IndexOutOfBoundsException if the index outside the number of morph targets - * @param index The index of the morph target to get - * @return The MorphTarget at the index - */ - public MorphTarget getMorphTarget(int index) { - if (morphTargets == null) { - throw new IndexOutOfBoundsException("Index:" + index + ", Size:0"); - } - return morphTargets.get(index); - } - - public MorphTarget[] getMorphTargets() { - if (morphTargets == null) { - return new MorphTarget[0]; - } else { - return morphTargets.getArray(); - } - } - - /** - * Get the name of all morphs in order. - * Morphs without names will be null - * @return an array - */ - public String[] getMorphTargetNames() { - MorphTarget[] nbMorphTargets = getMorphTargets(); - if (nbMorphTargets.length == 0) { - return new String[0]; - } - String[] targets = new String[nbMorphTargets.length]; - - for (int index = 0; index < nbMorphTargets.length; index++) { - targets[index] = nbMorphTargets[index].getName(); - } - return targets; - } - - public boolean hasMorphTargets() { - return morphTargets != null && !morphTargets.isEmpty(); - } - - /** - * Get the index of the morph that has the given name. - * - * @param morphName The name of the morph to search for - * @return The index of the morph, or -1 if not found. - */ - public int getMorphIndex(String morphName) { - int index = -1; - MorphTarget[] nbMorphTargets = getMorphTargets(); - for (int i = 0; i < nbMorphTargets.length; i++) { - if (nbMorphTargets[i].getName().equals(morphName)) { - index = i; - break; - } - } - return index; - } - - @Override - @SuppressWarnings("unchecked") - public void write(JmeExporter ex) throws IOException { - OutputCapsule out = ex.getCapsule(this); - - out.write(meshBound, "modelBound", null); - out.write(vertCount, "vertCount", DEFAULT_VERT_COUNT); - out.write(elementCount, "elementCount", DEFAULT_ELEMENT_COUNT); - out.write(instanceCount, "instanceCount", DEFAULT_INSTANCE_COUNT); - out.write(maxNumWeights, "max_num_weights", DEFAULT_MAX_NUM_WEIGHTS); - out.write(mode, "mode", Mode.Triangles); - out.write(collisionTree, "collisionTree", DEFAULT_COLLISION_TREE); - out.write(elementLengths, "elementLengths", null); - out.write(modeStart, "modeStart", null); - out.write(pointSize, "pointSize", DEFAULT_POINT_SIZE); - - //Removing HW skinning buffers to not save them - VertexBuffer hwBoneIndex = null; - VertexBuffer hwBoneWeight = null; - hwBoneIndex = getBuffer(Type.HWBoneIndex); - if (hwBoneIndex != null) { - buffers.remove(Type.HWBoneIndex.ordinal()); - } - hwBoneWeight = getBuffer(Type.HWBoneWeight); - if (hwBoneWeight != null) { - buffers.remove(Type.HWBoneWeight.ordinal()); - } - - out.writeIntSavableMap(buffers, "buffers", null); - - //restoring Hw skinning buffers. - if (hwBoneIndex != null) { - buffers.put(hwBoneIndex.getBufferType().ordinal(), hwBoneIndex); - } - if (hwBoneWeight != null) { - buffers.put(hwBoneWeight.getBufferType().ordinal(), hwBoneWeight); - } - - out.write(lodLevels, "lodLevels", null); - if (morphTargets != null) { - out.writeSavableArrayList(new ArrayList(morphTargets), "morphTargets", null); - } - } - - @Override - @SuppressWarnings("unchecked") - public void read(JmeImporter im) throws IOException { - InputCapsule in = im.getCapsule(this); - meshBound = (BoundingVolume) in.readSavable("modelBound", null); - vertCount = in.readInt("vertCount", DEFAULT_VERT_COUNT); - elementCount = in.readInt("elementCount", DEFAULT_ELEMENT_COUNT); - instanceCount = in.readInt("instanceCount", DEFAULT_INSTANCE_COUNT); - maxNumWeights = in.readInt("max_num_weights", DEFAULT_MAX_NUM_WEIGHTS); - mode = in.readEnum("mode", Mode.class, Mode.Triangles); - elementLengths = in.readIntArray("elementLengths", null); - modeStart = in.readIntArray("modeStart", null); - collisionTree = (BIHTree) in.readSavable("collisionTree", DEFAULT_COLLISION_TREE); - elementLengths = in.readIntArray("elementLengths", null); - modeStart = in.readIntArray("modeStart", null); - pointSize = in.readFloat("pointSize", DEFAULT_POINT_SIZE); - -// in.readStringSavableMap("buffers", null); - buffers = (IntMap) in.readIntSavableMap("buffers", null); - for (Entry entry : buffers) { - buffersList.add(entry.getValue()); - } - - //creating hw animation buffers empty so that they are put in the cache - if (isAnimated()) { - VertexBuffer hwBoneIndex = new VertexBuffer(Type.HWBoneIndex); - hwBoneIndex.setUsage(Usage.CpuOnly); - setBuffer(hwBoneIndex); - VertexBuffer hwBoneWeight = new VertexBuffer(Type.HWBoneWeight); - hwBoneWeight.setUsage(Usage.CpuOnly); - setBuffer(hwBoneWeight); - } - - Savable[] lodLevelsSavable = in.readSavableArray("lodLevels", null); - if (lodLevelsSavable != null) { - lodLevels = new VertexBuffer[lodLevelsSavable.length]; - System.arraycopy(lodLevelsSavable, 0, lodLevels, 0, lodLevels.length); - } - - ArrayList l = in.readSavableArrayList("morphTargets", null); - if (l != null) { - morphTargets = new SafeArrayList(MorphTarget.class, l); - } - } } diff --git a/jme3-core/src/main/java/com/jme3/scene/NewGlMesh.java b/jme3-core/src/main/java/com/jme3/scene/NewGlMesh.java new file mode 100644 index 0000000000..2070df852d --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/scene/NewGlMesh.java @@ -0,0 +1,21 @@ +package com.jme3.scene; + +import com.jme3.vulkan.mesh.attribute.Attribute; + +import java.util.HashMap; +import java.util.Map; + +public class NewGlMesh { + + private final Map buffers = new HashMap<>(); + private int vertices = 0; + + public T getAttribute(Class key) { + GlVertexBuffer buf = buffers.get(key); + if (buf == null) { + // create buffer??? How??? + } + return (T)buf.getAttribute().map(); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/scene/Node.java b/jme3-core/src/main/java/com/jme3/scene/Node.java index 0424cea053..12d79e80e2 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Node.java +++ b/jme3-core/src/main/java/com/jme3/scene/Node.java @@ -40,6 +40,8 @@ import com.jme3.material.Material; import com.jme3.util.SafeArrayList; import com.jme3.util.clone.Cloner; +import com.jme3.vulkan.material.NewMaterial; + import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -813,4 +815,15 @@ public void depthFirstTraversal(SceneGraphVisitor visitor, DFSMode mode) { protected void breadthFirstTraversal(SceneGraphVisitor visitor, Queue queue) { queue.addAll(children); } + + @Override + protected void findNextIteration(GraphIterator iterator) { + int i = iterator.advanceIndex(); + if (i >= children.size()) { + iterator.moveUp(); + } else { + iterator.moveDown(children.get(i)); + } + } + } diff --git a/jme3-core/src/main/java/com/jme3/scene/Spatial.java b/jme3-core/src/main/java/com/jme3/scene/Spatial.java index 2fe1775d3e..85ad18890c 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Spatial.java +++ b/jme3-core/src/main/java/com/jme3/scene/Spatial.java @@ -54,6 +54,8 @@ import com.jme3.util.clone.Cloner; import com.jme3.util.clone.IdentityCloneFunction; import com.jme3.util.clone.JmeCloneable; +import com.jme3.vulkan.material.NewMaterial; + import java.io.IOException; import java.util.*; import java.util.logging.Logger; @@ -68,7 +70,7 @@ * @author Joshua Slack * @version $Revision: 4075 $, $Data$ */ -public abstract class Spatial implements Savable, Cloneable, Collidable, +public abstract class Spatial implements Iterable, Savable, Cloneable, Collidable, CloneableSmartAsset, JmeCloneable, HasLocalTransform { private static final Logger logger = Logger.getLogger(Spatial.class.getName()); @@ -1874,4 +1876,88 @@ public void breadthFirstTraversal(SceneGraphVisitor visitor) { } protected abstract void breadthFirstTraversal(SceneGraphVisitor visitor, Queue queue); + + protected abstract void findNextIteration(GraphIterator iterator); + + @Override + public GraphIterator iterator() { + return new GraphIterator(this); + } + + public static class GraphIterator implements Iterator { + + private final Stack childIndices = new Stack<>(); + private Spatial current; + private int currentIndex = 0; + private int iteration = -1; + + public GraphIterator(Spatial root) { + current = Objects.requireNonNull(root); + } + + @Override + public boolean hasNext() { + return current != null; + } + + @Override + public Spatial next() { + if (++iteration > 0) { + current.findNextIteration(this); + } + return current; + } + + @Override + public void remove() { + if (current.getParent() != null) { + current.removeFromParent(); + moveUp(); + currentIndex--; + } + } + + protected void moveUp() { + if (!childIndices.isEmpty()) { + current = current.getParent(); + currentIndex = childIndices.pop(); + if (current != null) { + current.findNextIteration(this); + } + } else { + current = null; + } + } + + protected void moveDown(Spatial node) { + if (node.getParent() != current) { + throw new IllegalArgumentException("Next spatial must be a child of the current spatial."); + } + current = node; + childIndices.push(currentIndex); + currentIndex = 0; + } + + protected int advanceIndex() { + return currentIndex++; + } + + protected int getCurrentIndex() { + return currentIndex; + } + + public void skipChildren() { + moveUp(); + } + + public int getDepth() { + return childIndices.size(); + } + + public int getIteration() { + return iteration; + } + + } + } diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/Arrow.java b/jme3-core/src/main/java/com/jme3/scene/debug/Arrow.java index 8f46298693..1c236abe29 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/Arrow.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/Arrow.java @@ -33,9 +33,9 @@ import com.jme3.math.Quaternion; import com.jme3.math.Vector3f; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Type; import java.nio.FloatBuffer; /** @@ -45,7 +45,7 @@ * * @author Kirill Vainer */ -public class Arrow extends Mesh { +public class Arrow extends GlMesh { private final Quaternion tempQuat = new Quaternion(); private final Vector3f tempVec = new Vector3f(); @@ -119,7 +119,7 @@ public void setArrowExtent(Vector3f extent) { tempQuat.lookAt(extent, Vector3f.UNIT_Y); tempQuat.normalizeLocal(); - VertexBuffer pvb = getBuffer(Type.Position); + GlVertexBuffer pvb = getBuffer(Type.Position); FloatBuffer buffer = (FloatBuffer)pvb.getData(); buffer.rewind(); for (int i = 0; i < positions.length; i += 3) { diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/Grid.java b/jme3-core/src/main/java/com/jme3/scene/debug/Grid.java index d2947e52a9..3c6d4c5caa 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/Grid.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/Grid.java @@ -31,8 +31,8 @@ */ package com.jme3.scene.debug; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.util.BufferUtils; import java.nio.FloatBuffer; @@ -43,7 +43,7 @@ * * @author Kirill Vainer */ -public class Grid extends Mesh { +public class Grid extends GlMesh { public Grid() { } diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/SkeletonDebugger.java b/jme3-core/src/main/java/com/jme3/scene/debug/SkeletonDebugger.java deleted file mode 100644 index f8f450ab47..0000000000 --- a/jme3-core/src/main/java/com/jme3/scene/debug/SkeletonDebugger.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.scene.debug; - -import com.jme3.animation.Skeleton; -import com.jme3.export.JmeImporter; -import com.jme3.renderer.queue.RenderQueue.Bucket; -import com.jme3.scene.Geometry; -import com.jme3.scene.Mesh; -import com.jme3.scene.Node; -import com.jme3.util.clone.Cloner; - -import java.io.IOException; -import java.util.Map; - -/** - * The class that creates a mesh to display how bones behave. - * If it is supplied with the bones' lengths it will show exactly how the bones look like on the scene. - * If not then only connections between each bone heads will be shown. - */ -public class SkeletonDebugger extends Node { - /** The lines of the bones or the wires between their heads. */ - private SkeletonWire wires; - /** The heads and tails points of the bones or only heads if no length data is available. */ - private SkeletonPoints points; - /** The dotted lines between a bone's tail and the had of its children. Not available if the length data was not provided. */ - private SkeletonInterBoneWire interBoneWires; - - public SkeletonDebugger() { - } - - /** - * Creates a debugger with no length data. The wires will be a connection between the bones' heads only. - * The points will show the bones' heads only and no dotted line of inter bones connection will be visible. - * @param name - * the name of the debugger's node - * @param skeleton - * the skeleton that will be shown - */ - public SkeletonDebugger(String name, Skeleton skeleton) { - this(name, skeleton, null); - } - - /** - * Creates a debugger with bone lengths data. If the data is supplied then the wires will show each full bone (from head to tail), - * the points will display both heads and tails of the bones and dotted lines between bones will be seen. - * @param name - * the name of the debugger's node - * @param skeleton - * the skeleton that will be shown - * @param boneLengths - * a map between the bone's index and the bone's length - */ - public SkeletonDebugger(String name, Skeleton skeleton, Map boneLengths) { - super(name); - - wires = new SkeletonWire(skeleton, boneLengths); - points = new SkeletonPoints(skeleton, boneLengths); - - this.attachChild(new Geometry(getGeometryName("_wires"), wires)); - this.attachChild(new Geometry(getGeometryName("_points"), points)); - if (boneLengths != null) { - interBoneWires = new SkeletonInterBoneWire(skeleton, boneLengths); - this.attachChild(new Geometry(getGeometryName("_interwires"), interBoneWires)); - } - - this.setQueueBucket(Bucket.Transparent); - } - - private String getGeometryName(String suffix) { - return name + suffix; - } - - @Override - public void updateLogicalState(float tpf) { - super.updateLogicalState(tpf); - wires.updateGeometry(); - points.updateGeometry(); - if(interBoneWires != null) { - interBoneWires.updateGeometry(); - } - } - - /** - * @return the skeleton points - */ - public SkeletonPoints getPoints() { - return points; - } - - /** - * @return the skeleton wires - */ - public SkeletonWire getWires() { - return wires; - } - - /** - * @return the dotted line between bones (can be null) - */ - public SkeletonInterBoneWire getInterBoneWires() { - return interBoneWires; - } - - @Override - public void cloneFields(Cloner cloner, Object original) { - super.cloneFields(cloner, original); - - this.wires = cloner.clone(wires); - this.points = cloner.clone(points); - this.interBoneWires = cloner.clone(interBoneWires); - } - - @Override - public void read(JmeImporter importer) throws IOException { - super.read(importer); - - // Find our stuff - wires = getMesh("_wires"); - points = getMesh("_points"); - interBoneWires = getMesh("_interwires"); - } - - @SuppressWarnings("unchecked") - private T getMesh(String suffix) { - Geometry child = (Geometry)getChild(getGeometryName(suffix)); - if(child != null) { - return (T) child.getMesh(); - } - - return null; - } -} \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/SkeletonInterBoneWire.java b/jme3-core/src/main/java/com/jme3/scene/debug/SkeletonInterBoneWire.java deleted file mode 100644 index 6db686bed8..0000000000 --- a/jme3-core/src/main/java/com/jme3/scene/debug/SkeletonInterBoneWire.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.scene.debug; - -import java.nio.FloatBuffer; -import java.util.Map; - -import com.jme3.animation.Bone; -import com.jme3.animation.Skeleton; -import com.jme3.export.InputCapsule; -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.OutputCapsule; -import com.jme3.math.Vector3f; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Format; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.VertexBuffer.Usage; -import com.jme3.util.BufferUtils; -import java.io.IOException; -import java.util.HashMap; - -/** - * A class that displays a dotted line between a bone tail and its children's heads. - * - * @author Marcin Roguski (Kaelthas) - */ -public class SkeletonInterBoneWire extends Mesh { - private static final int POINT_AMOUNT = 10; - /** The amount of connections between bones. */ - private int connectionsAmount; - /** The skeleton that will be showed. */ - private Skeleton skeleton; - /** The map between the bone index and its length. */ - private Map boneLengths; - - /** - * Creates buffers for points. Each line has POINT_AMOUNT of points. - * @param skeleton - * the skeleton that will be showed - * @param boneLengths - * the lengths of the bones - */ - public SkeletonInterBoneWire(Skeleton skeleton, Map boneLengths) { - this.skeleton = skeleton; - - for (Bone bone : skeleton.getRoots()) { - this.countConnections(bone); - } - - this.setMode(Mode.Points); - this.boneLengths = boneLengths; - - VertexBuffer pb = new VertexBuffer(Type.Position); - FloatBuffer fpb = BufferUtils.createFloatBuffer(POINT_AMOUNT * connectionsAmount * 3); - pb.setupData(Usage.Stream, 3, Format.Float, fpb); - this.setBuffer(pb); - - this.updateCounts(); - } - - /** - * For serialization only. Do not use. - */ - protected SkeletonInterBoneWire() { - } - - /** - * This method updates the geometry according to the positions of the bones. - */ - public void updateGeometry() { - VertexBuffer vb = this.getBuffer(Type.Position); - FloatBuffer posBuf = this.getFloatBuffer(Type.Position); - posBuf.clear(); - for (int i = 0; i < skeleton.getBoneCount(); ++i) { - Bone bone = skeleton.getBone(i); - Vector3f parentTail = bone.getModelSpacePosition().add(bone.getModelSpaceRotation().mult(Vector3f.UNIT_Y.mult(boneLengths.get(i)))); - - for (Bone child : bone.getChildren()) { - Vector3f childHead = child.getModelSpacePosition(); - Vector3f v = childHead.subtract(parentTail); - float pointDelta = v.length() / POINT_AMOUNT; - v.normalizeLocal().multLocal(pointDelta); - Vector3f pointPosition = parentTail.clone(); - for (int j = 0; j < POINT_AMOUNT; ++j) { - posBuf.put(pointPosition.getX()).put(pointPosition.getY()).put(pointPosition.getZ()); - pointPosition.addLocal(v); - } - } - } - posBuf.flip(); - vb.updateData(posBuf); - - this.updateBound(); - } - - /** - * De-serializes from the specified importer, for example when loading from - * a J3O file. - * - * @param importer the importer to use (not null) - * @throws IOException from the importer - */ - @Override - public void read(JmeImporter importer) throws IOException { - super.read(importer); - InputCapsule capsule = importer.getCapsule(this); - - connectionsAmount = capsule.readInt("connectionsAmount", 1); - skeleton = (Skeleton) capsule.readSavable("skeleton", null); - - int[] blKeys = capsule.readIntArray("blKeys", null); - float[] blValues = capsule.readFloatArray("blValues", null); - if (blKeys == null) { - boneLengths = null; - } else { - assert blValues.length == blKeys.length; - int numLengths = blKeys.length; - boneLengths = new HashMap<>(numLengths); - for (int i = 0; i < numLengths; ++i) { - boneLengths.put(blKeys[i], blValues[i]); - } - } - } - - /** - * Serializes to the specified exporter, for example when saving to a J3O - * file. The current instance is unaffected. - * - * @param exporter the exporter to use (not null) - * @throws IOException from the exporter - */ - @Override - public void write(JmeExporter exporter) throws IOException { - super.write(exporter); - OutputCapsule capsule = exporter.getCapsule(this); - - capsule.write(connectionsAmount, "connectionsAmount", 1); - capsule.write(skeleton, "skeleton", null); - - if (boneLengths != null) { - int numLengths = boneLengths.size(); - int[] blKeys = new int[numLengths]; - float[] blValues = new float[numLengths]; - int i = 0; - for (Map.Entry entry : boneLengths.entrySet()) { - blKeys[i] = entry.getKey(); - blValues[i] = entry.getValue(); - ++i; - } - capsule.write(blKeys, "blKeys", null); - capsule.write(blValues, "blValues", null); - } - } - - /** - * This method counts the connections between bones. - * @param bone - * the bone where counting starts - */ - private void countConnections(Bone bone) { - for (Bone child : bone.getChildren()) { - ++connectionsAmount; - this.countConnections(child); - } - } -} diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/SkeletonPoints.java b/jme3-core/src/main/java/com/jme3/scene/debug/SkeletonPoints.java deleted file mode 100644 index 878b611673..0000000000 --- a/jme3-core/src/main/java/com/jme3/scene/debug/SkeletonPoints.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.scene.debug; - -import com.jme3.animation.Bone; -import com.jme3.animation.Skeleton; -import com.jme3.export.InputCapsule; -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.OutputCapsule; -import com.jme3.math.Vector3f; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Format; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.VertexBuffer.Usage; -import com.jme3.util.BufferUtils; -import java.io.IOException; - -import java.nio.FloatBuffer; -import java.util.HashMap; -import java.util.Map; - -/** - * The class that displays either heads of the bones if no length data is supplied or both heads and tails otherwise. - */ -public class SkeletonPoints extends Mesh { - /** The skeleton to be displayed. */ - private Skeleton skeleton; - /** The map between the bone index and its length. */ - private Map boneLengths; - - /** - * Creates a points with no length data. The points will only show the bone's heads. - * @param skeleton - * the skeleton that will be shown - */ - public SkeletonPoints(Skeleton skeleton) { - this(skeleton, null); - } - - /** - * Creates a points with bone lengths data. If the data is supplied then the points will show both head and tail of each bone. - * @param skeleton - * the skeleton that will be shown - * @param boneLengths - * a map between the bone's index and the bone's length - */ - public SkeletonPoints(Skeleton skeleton, Map boneLengths) { - this.skeleton = skeleton; - this.setMode(Mode.Points); - int pointsCount = skeleton.getBoneCount(); - - if (boneLengths != null) { - this.boneLengths = boneLengths; - pointsCount *= 2; - } - - VertexBuffer pb = new VertexBuffer(Type.Position); - FloatBuffer fpb = BufferUtils.createFloatBuffer(pointsCount * 3); - pb.setupData(Usage.Stream, 3, Format.Float, fpb); - this.setBuffer(pb); - - this.updateCounts(); - - } - - /** - * For serialization only. Do not use. - */ - protected SkeletonPoints() { - } - - /** - * The method updates the geometry according to the positions of the bones. - */ - public void updateGeometry() { - VertexBuffer vb = this.getBuffer(Type.Position); - FloatBuffer posBuf = this.getFloatBuffer(Type.Position); - posBuf.clear(); - for (int i = 0; i < skeleton.getBoneCount(); ++i) { - Bone bone = skeleton.getBone(i); - Vector3f head = bone.getModelSpacePosition(); - - posBuf.put(head.getX()).put(head.getY()).put(head.getZ()); - if (boneLengths != null) { - Vector3f tail = head.add(bone.getModelSpaceRotation().mult(Vector3f.UNIT_Y.mult(boneLengths.get(i)))); - posBuf.put(tail.getX()).put(tail.getY()).put(tail.getZ()); - } - } - posBuf.flip(); - vb.updateData(posBuf); - - this.updateBound(); - } - - /** - * De-serializes from the specified importer, for example when loading from - * a J3O file. - * - * @param importer the importer to use (not null) - * @throws IOException from the importer - */ - @Override - public void read(JmeImporter importer) throws IOException { - super.read(importer); - InputCapsule capsule = importer.getCapsule(this); - - skeleton = (Skeleton) capsule.readSavable("skeleton", null); - - int[] blKeys = capsule.readIntArray("blKeys", null); - float[] blValues = capsule.readFloatArray("blValues", null); - if (blKeys == null) { - boneLengths = null; - } else { - assert blValues.length == blKeys.length; - int numLengths = blKeys.length; - boneLengths = new HashMap<>(numLengths); - for (int i = 0; i < numLengths; ++i) { - boneLengths.put(blKeys[i], blValues[i]); - } - } - } - - /** - * Serializes to the specified exporter, for example when saving to a J3O - * file. The current instance is unaffected. - * - * @param exporter the exporter to use (not null) - * @throws IOException from the exporter - */ - @Override - public void write(JmeExporter exporter) throws IOException { - super.write(exporter); - OutputCapsule capsule = exporter.getCapsule(this); - - capsule.write(skeleton, "skeleton", null); - - if (boneLengths != null) { - int numLengths = boneLengths.size(); - int[] blKeys = new int[numLengths]; - float[] blValues = new float[numLengths]; - int i = 0; - for (Map.Entry entry : boneLengths.entrySet()) { - blKeys[i] = entry.getKey(); - blValues[i] = entry.getValue(); - ++i; - } - capsule.write(blKeys, "blKeys", null); - capsule.write(blValues, "blValues", null); - } - } -} diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/SkeletonWire.java b/jme3-core/src/main/java/com/jme3/scene/debug/SkeletonWire.java deleted file mode 100644 index afcecce778..0000000000 --- a/jme3-core/src/main/java/com/jme3/scene/debug/SkeletonWire.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (c) 2009-2012 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.scene.debug; - -import java.nio.FloatBuffer; -import java.nio.ShortBuffer; -import java.util.Map; - -import com.jme3.animation.Bone; -import com.jme3.animation.Skeleton; -import com.jme3.export.InputCapsule; -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.OutputCapsule; -import com.jme3.math.Vector3f; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Format; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.VertexBuffer.Usage; -import com.jme3.util.BufferUtils; -import java.io.IOException; -import java.util.HashMap; - -/** - * The class that displays either wires between the bones' heads if no length data is supplied and - * full bones' shapes otherwise. - */ -public class SkeletonWire extends Mesh { - /** The number of bones' connections. Used in non-length mode. */ - private int numConnections; - /** The skeleton to be displayed. */ - private Skeleton skeleton; - /** The map between the bone index and its length. */ - private Map boneLengths; - - /** - * Creates a wire with no length data. The wires will be a connection between the bones' heads only. - * @param skeleton - * the skeleton that will be shown - */ - public SkeletonWire(Skeleton skeleton) { - this(skeleton, null); - } - - /** - * Creates a wire with bone lengths data. If the data is supplied then the wires will show each full bone (from head to tail). - * @param skeleton - * the skeleton that will be shown - * @param boneLengths - * a map between the bone's index and the bone's length - */ - public SkeletonWire(Skeleton skeleton, Map boneLengths) { - this.skeleton = skeleton; - - for (Bone bone : skeleton.getRoots()) { - this.countConnections(bone); - } - - this.setMode(Mode.Lines); - int lineVerticesCount = skeleton.getBoneCount(); - if (boneLengths != null) { - this.boneLengths = boneLengths; - lineVerticesCount *= 2; - } - - VertexBuffer pb = new VertexBuffer(Type.Position); - FloatBuffer fpb = BufferUtils.createFloatBuffer(lineVerticesCount * 3); - pb.setupData(Usage.Stream, 3, Format.Float, fpb); - this.setBuffer(pb); - - VertexBuffer ib = new VertexBuffer(Type.Index); - ShortBuffer sib = BufferUtils.createShortBuffer(boneLengths != null ? lineVerticesCount : numConnections * 2); - ib.setupData(Usage.Static, 2, Format.UnsignedShort, sib); - this.setBuffer(ib); - - if (boneLengths != null) { - for (int i = 0; i < lineVerticesCount; ++i) { - sib.put((short) i); - } - } else { - for (Bone bone : skeleton.getRoots()) { - this.writeConnections(sib, bone); - } - } - sib.flip(); - - this.updateCounts(); - } - - /** - * For serialization only. Do not use. - */ - protected SkeletonWire() { - } - - /** - * This method updates the geometry according to the positions of the bones. - */ - public void updateGeometry() { - VertexBuffer vb = this.getBuffer(Type.Position); - FloatBuffer posBuf = this.getFloatBuffer(Type.Position); - posBuf.clear(); - for (int i = 0; i < skeleton.getBoneCount(); ++i) { - Bone bone = skeleton.getBone(i); - Vector3f head = bone.getModelSpacePosition(); - - posBuf.put(head.getX()).put(head.getY()).put(head.getZ()); - if (boneLengths != null) { - Vector3f tail = head.add(bone.getModelSpaceRotation().mult(Vector3f.UNIT_Y.mult(boneLengths.get(i)))); - posBuf.put(tail.getX()).put(tail.getY()).put(tail.getZ()); - } - } - posBuf.flip(); - vb.updateData(posBuf); - - this.updateBound(); - } - - /** - * De-serializes from the specified importer, for example when loading from - * a J3O file. - * - * @param importer the importer to use (not null) - * @throws IOException from the importer - */ - @Override - public void read(JmeImporter importer) throws IOException { - super.read(importer); - InputCapsule capsule = importer.getCapsule(this); - - numConnections = capsule.readInt("numConnections", 1); - skeleton = (Skeleton) capsule.readSavable("skeleton", null); - - int[] blKeys = capsule.readIntArray("blKeys", null); - float[] blValues = capsule.readFloatArray("blValues", null); - if (blKeys == null) { - boneLengths = null; - } else { - assert blValues.length == blKeys.length; - int numLengths = blKeys.length; - boneLengths = new HashMap<>(numLengths); - for (int i = 0; i < numLengths; ++i) { - boneLengths.put(blKeys[i], blValues[i]); - } - } - } - - /** - * Serializes to the specified exporter, for example when saving to a J3O - * file. The current instance is unaffected. - * - * @param exporter the exporter to use (not null) - * @throws IOException from the exporter - */ - @Override - public void write(JmeExporter exporter) throws IOException { - super.write(exporter); - OutputCapsule capsule = exporter.getCapsule(this); - - capsule.write(numConnections, "numConnections", 1); - capsule.write(skeleton, "skeleton", null); - - if (boneLengths != null) { - int numLengths = boneLengths.size(); - int[] blKeys = new int[numLengths]; - float[] blValues = new float[numLengths]; - int i = 0; - for (Map.Entry entry : boneLengths.entrySet()) { - blKeys[i] = entry.getKey(); - blValues[i] = entry.getValue(); - ++i; - } - capsule.write(blKeys, "blKeys", null); - capsule.write(blValues, "blValues", null); - } - } - - /** - * This method counts the connections between bones. - * @param bone - * the bone where counting starts - */ - private void countConnections(Bone bone) { - for (Bone child : bone.getChildren()) { - numConnections++; - this.countConnections(child); - } - } - - /** - * The method writes the indexes for the connection vertices. Used in non-length mode. - * @param indexBuf - * the index buffer - * @param bone - * the bone - */ - private void writeConnections(ShortBuffer indexBuf, Bone bone) { - for (Bone child : bone.getChildren()) { - // write myself - indexBuf.put((short) skeleton.getBoneIndex(bone)); - // write the child - indexBuf.put((short) skeleton.getBoneIndex(child)); - - this.writeConnections(indexBuf, child); - } - } -} diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/WireBox.java b/jme3-core/src/main/java/com/jme3/scene/debug/WireBox.java index 38593898c1..fbe3ce4545 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/WireBox.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/WireBox.java @@ -34,15 +34,15 @@ import com.jme3.bounding.BoundingBox; import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Format; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.VertexBuffer.Usage; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Format; +import com.jme3.scene.GlVertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Usage; import com.jme3.util.BufferUtils; import java.nio.FloatBuffer; -public class WireBox extends Mesh { +public class WireBox extends GlMesh { public WireBox() { this(1,1,1); @@ -74,10 +74,10 @@ public WireBox(float xExt, float yExt, float zExt) { } public void updatePositions(float xExt, float yExt, float zExt) { - VertexBuffer pvb = getBuffer(Type.Position); + GlVertexBuffer pvb = getBuffer(Type.Position); FloatBuffer pb; if (pvb == null) { - pvb = new VertexBuffer(Type.Position); + pvb = new GlVertexBuffer(Type.Position); pb = BufferUtils.createVector3Buffer(8); pvb.setupData(Usage.Dynamic, 3, Format.Float, pb); setBuffer(pvb); diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/WireFrustum.java b/jme3-core/src/main/java/com/jme3/scene/debug/WireFrustum.java index 9a2de74927..e8fb633e07 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/WireFrustum.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/WireFrustum.java @@ -35,9 +35,10 @@ import com.jme3.renderer.Camera; import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.VertexBuffer.Usage; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Usage; import com.jme3.shadow.ShadowUtil; import com.jme3.util.BufferUtils; import java.nio.FloatBuffer; @@ -52,7 +53,7 @@ * and four for the far plane. These points are connected by lines * to form a wireframe cube-like structure. */ -public class WireFrustum extends Mesh { +public class WireFrustum extends GlMesh { /** * For Serialization only. Do not use. @@ -130,7 +131,7 @@ public void update(Vector3f[] points) { throw new IllegalArgumentException("Frustum points array must not be null and must contain 8 points."); } - VertexBuffer vb = getBuffer(Type.Position); + GlVertexBuffer vb = getBuffer(Type.Position); if (vb == null) { // If for some reason the position buffer is missing, re-create it. // This case should ideally not happen if the object is constructed properly. diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/WireSphere.java b/jme3-core/src/main/java/com/jme3/scene/debug/WireSphere.java index 72ee624bb0..80066fd94b 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/WireSphere.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/WireSphere.java @@ -35,17 +35,17 @@ import com.jme3.math.FastMath; import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Format; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.VertexBuffer.Usage; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Format; +import com.jme3.scene.GlVertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Usage; import com.jme3.util.BufferUtils; import java.nio.FloatBuffer; import java.nio.ShortBuffer; -public class WireSphere extends Mesh { +public class WireSphere extends GlMesh { private static final int samples = 30; private static final int zSamples = 10; @@ -82,11 +82,11 @@ public WireSphere(float radius) { } public void updatePositions(float radius) { - VertexBuffer pvb = getBuffer(Type.Position); + GlVertexBuffer pvb = getBuffer(Type.Position); FloatBuffer pb; if (pvb == null) { - pvb = new VertexBuffer(Type.Position); + pvb = new GlVertexBuffer(Type.Position); pb = BufferUtils.createVector3Buffer(samples * 2 + samples * zSamples /*+ 6 * 3*/); pvb.setupData(Usage.Dynamic, 3, Format.Float, pb); setBuffer(pvb); diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugger.java b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugger.java index d7973eabc1..2886c5bc46 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugger.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureDebugger.java @@ -37,12 +37,14 @@ import com.jme3.asset.AssetManager; import com.jme3.collision.Collidable; import com.jme3.collision.CollisionResults; +import com.jme3.material.GlMaterial; import com.jme3.material.Material; import com.jme3.material.RenderState; import com.jme3.renderer.Camera; import com.jme3.renderer.queue.RenderQueue; import com.jme3.scene.Geometry; import com.jme3.scene.Node; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture; import java.util.List; @@ -125,7 +127,7 @@ public void initialize(AssetManager assetManager, Camera camera) { armatureNode.setCamera(camera); - Material matJoints = new Material(assetManager, "Common/MatDefs/Misc/Billboard.j3md"); + GlMaterial matJoints = new GlMaterial(assetManager, "Common/MatDefs/Misc/Billboard.j3md"); Texture t = assetManager.loadTexture("Common/Textures/dot.png"); matJoints.setTexture("Texture", t); matJoints.getAdditionalRenderState().setDepthTest(false); @@ -133,21 +135,21 @@ public void initialize(AssetManager assetManager, Camera camera) { joints.setQueueBucket(RenderQueue.Bucket.Translucent); joints.setMaterial(matJoints); - Material matWires = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + GlMaterial matWires = new GlMaterial(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); matWires.setBoolean("VertexColor", true); matWires.getAdditionalRenderState().setLineWidth(1f); wires.setMaterial(matWires); - Material matOutline = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + GlMaterial matOutline = new GlMaterial(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); matOutline.setBoolean("VertexColor", true); matOutline.getAdditionalRenderState().setLineWidth(1f); outlines.setMaterial(matOutline); - Material matOutline2 = new Material(assetManager, "Common/MatDefs/Misc/DashedLine.j3md"); + GlMaterial matOutline2 = new GlMaterial(assetManager, "Common/MatDefs/Misc/DashedLine.j3md"); matOutline2.getAdditionalRenderState().setLineWidth(1); outlines.getChild(1).setMaterial(matOutline2); - Material matWires2 = new Material(assetManager, "Common/MatDefs/Misc/DashedLine.j3md"); + GlMaterial matWires2 = new GlMaterial(assetManager, "Common/MatDefs/Misc/DashedLine.j3md"); matWires2.getAdditionalRenderState().setLineWidth(1); wires.getChild(1).setMaterial(matWires2); diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureInterJointsWire.java b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureInterJointsWire.java index 1fe8bdcdc6..4ea2adf8c0 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureInterJointsWire.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureInterJointsWire.java @@ -34,9 +34,9 @@ import com.jme3.math.Vector3f; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Type; import java.nio.FloatBuffer; @@ -45,7 +45,7 @@ * * @author Marcin Roguski (Kaelthas) */ -public class ArmatureInterJointsWire extends Mesh { +public class ArmatureInterJointsWire extends GlMesh { private final Vector3f tmp = new Vector3f(); @@ -108,7 +108,7 @@ protected void updateGeometry(Vector3f start, Vector3f[] ends) { * @param ends array of location vectors (not null, unaffected) */ public void updatePoints(Vector3f start, Vector3f[] ends) { - VertexBuffer posBuf = getBuffer(Type.Position); + GlVertexBuffer posBuf = getBuffer(Type.Position); FloatBuffer fb = (FloatBuffer) posBuf.getData(); fb.rewind(); fb.put(start.x).put(start.y).put(start.z); @@ -119,7 +119,7 @@ public void updatePoints(Vector3f start, Vector3f[] ends) { } posBuf.updateData(fb); - VertexBuffer normBuf = getBuffer(Type.Normal); + GlVertexBuffer normBuf = getBuffer(Type.Normal); fb = (FloatBuffer) normBuf.getData(); fb.rewind(); for (int i = 0; i < ends.length * 3 + 3; i += 3) { diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureNode.java b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureNode.java index c46466f181..bd39fa108b 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureNode.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/custom/ArmatureNode.java @@ -40,8 +40,8 @@ import com.jme3.renderer.queue.RenderQueue; import com.jme3.scene.*; import com.jme3.scene.shape.Line; +import com.jme3.vulkan.mesh.AttributeModifier; -import java.nio.FloatBuffer; import java.util.*; /** @@ -306,22 +306,28 @@ private void updateBoneMesh(Geometry geom, Vector3f start, Vector3f[] ends) { } private void setColor(Geometry g, ColorRGBA color) { - float[] colors = new float[g.getMesh().getVertexCount() * 4]; - for (int i = 0; i < g.getMesh().getVertexCount() * 4; i += 4) { - colors[i] = color.r; - colors[i + 1] = color.g; - colors[i + 2] = color.b; - colors[i + 3] = color.a; - } - VertexBuffer colorBuff = g.getMesh().getBuffer(VertexBuffer.Type.Color); - if (colorBuff == null) { - g.getMesh().setBuffer(VertexBuffer.Type.Color, 4, colors); - } else { - FloatBuffer cBuff = (FloatBuffer) colorBuff.getData(); - cBuff.rewind(); - cBuff.put(colors); - colorBuff.updateData(cBuff); + try (AttributeModifier colorMod = g.getMesh().modify(GlVertexBuffer.Type.Color)) { + for (int i = 0; i < g.getMesh().getVertexCount(); i++) { + colorMod.putColor(i, 0, color); + } } + // todo: ensure this code is properly replaced +// float[] colors = new float[g.getMesh().getVertexCount() * 4]; +// for (int i = 0; i < g.getMesh().getVertexCount() * 4; i += 4) { +// colors[i] = color.r; +// colors[i + 1] = color.g; +// colors[i + 2] = color.b; +// colors[i + 3] = color.a; +// } +// GlVertexBuffer colorBuff = g.getMesh().getBuffer(GlVertexBuffer.Type.Color); +// if (colorBuff == null) { +// g.getMesh().setBuffer(GlVertexBuffer.Type.Color, 4, colors); +// } else { +// FloatBuffer cBuff = (FloatBuffer) colorBuff.getData(); +// cBuff.rewind(); +// cBuff.put(colors); +// colorBuff.updateData(cBuff); +// } } /** diff --git a/jme3-core/src/main/java/com/jme3/scene/debug/custom/JointShape.java b/jme3-core/src/main/java/com/jme3/scene/debug/custom/JointShape.java index 989667eb23..608c8d0b70 100644 --- a/jme3-core/src/main/java/com/jme3/scene/debug/custom/JointShape.java +++ b/jme3-core/src/main/java/com/jme3/scene/debug/custom/JointShape.java @@ -32,10 +32,10 @@ package com.jme3.scene.debug.custom; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer.Type; -public class JointShape extends Mesh { +public class JointShape extends GlMesh { /** * Serialization only. Do not use. diff --git a/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedGeometry.java b/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedGeometry.java index e0a673838f..a17b51623e 100644 --- a/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedGeometry.java +++ b/jme3-core/src/main/java/com/jme3/scene/instancing/InstancedGeometry.java @@ -45,15 +45,17 @@ import com.jme3.math.Quaternion; import com.jme3.renderer.Camera; import com.jme3.renderer.Camera.FrustumIntersect; +import com.jme3.renderer.Renderer; import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Format; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.VertexBuffer.Usage; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Format; +import com.jme3.scene.GlVertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Usage; import com.jme3.util.BufferUtils; import com.jme3.util.TempVars; import com.jme3.util.clone.Cloner; + import java.io.IOException; import java.nio.FloatBuffer; import java.util.ArrayList; @@ -66,12 +68,12 @@ public class InstancedGeometry extends Geometry { private static BiFunction instanceCullingFunction = new DefaultInstanceCullingFunction(); - private VertexBuffer[] globalInstanceData; - private VertexBuffer transformInstanceData; + private GlVertexBuffer[] globalInstanceData; + private GlVertexBuffer transformInstanceData; private Geometry[] geometries = new Geometry[1]; // Keep track of both transformInstanceData and globalInstanceData // that is used by renderer. - private VertexBuffer[] allInstanceData; + private GlVertexBuffer[] allInstanceData; private int firstUnusedIndex = 0; private int numVisibleInstances = 0; @@ -118,12 +120,12 @@ public static BiFunction getInstanceCullingFunction() * Global user specified per-instance data. * * By default set to null, specify an array of VertexBuffers - * via {@link #setGlobalUserInstanceData(com.jme3.scene.VertexBuffer[]) }. + * via {@link #setGlobalUserInstanceData(GlVertexBuffer[]) }. * * @return global user specified per-instance data. - * @see #setGlobalUserInstanceData(com.jme3.scene.VertexBuffer[]) + * @see #setGlobalUserInstanceData(GlVertexBuffer[]) */ - public VertexBuffer[] getGlobalUserInstanceData() { + public GlVertexBuffer[] getGlobalUserInstanceData() { return globalInstanceData; } @@ -136,9 +138,9 @@ public VertexBuffer[] getGlobalUserInstanceData() { * @param globalInstanceData global user per-instance data. * * @throws IllegalArgumentException If one of the VertexBuffers is not - * {@link VertexBuffer#setInstanced(boolean) instanced}. + * {@link GlVertexBuffer#setInstanced(boolean) instanced}. */ - public void setGlobalUserInstanceData(VertexBuffer[] globalInstanceData) { + public void setGlobalUserInstanceData(GlVertexBuffer[] globalInstanceData) { this.globalInstanceData = globalInstanceData; updateAllInstanceData(); } @@ -148,7 +150,7 @@ public void setGlobalUserInstanceData(VertexBuffer[] globalInstanceData) { * * @param transformInstanceData The transforms for each instance. */ - public void setTransformUserInstanceData(VertexBuffer transformInstanceData) { + public void setTransformUserInstanceData(GlVertexBuffer transformInstanceData) { this.transformInstanceData = transformInstanceData; updateAllInstanceData(); } @@ -158,9 +160,9 @@ public void setTransformUserInstanceData(VertexBuffer transformInstanceData) { * * @return The per-instance transform data. * - * @see #setTransformUserInstanceData(com.jme3.scene.VertexBuffer) + * @see #setTransformUserInstanceData(GlVertexBuffer) */ - public VertexBuffer getTransformUserInstanceData() { + public GlVertexBuffer getTransformUserInstanceData() { return transformInstanceData; } @@ -225,7 +227,7 @@ public final void setMaxNumInstances(int maxNumInstances) { BufferUtils.destroyDirectBuffer(transformInstanceData.getData()); transformInstanceData.updateData(BufferUtils.createFloatBuffer(geometries.length * INSTANCE_SIZE)); } else if (transformInstanceData == null) { - transformInstanceData = new VertexBuffer(Type.InstanceData); + transformInstanceData = new GlVertexBuffer(Type.InstanceData); transformInstanceData.setInstanced(true); transformInstanceData.setupData(Usage.Stream, INSTANCE_SIZE, @@ -408,19 +410,19 @@ public Geometry[] getGeometries() { return geometries; } - public VertexBuffer[] getAllInstanceData() { + public GlVertexBuffer[] getAllInstanceData() { return allInstanceData; } private void updateAllInstanceData() { - ArrayList allData = new ArrayList<>(); + ArrayList allData = new ArrayList<>(); if (transformInstanceData != null) { allData.add(transformInstanceData); } if (globalInstanceData != null) { allData.addAll(Arrays.asList(globalInstanceData)); } - allInstanceData = allData.toArray(new VertexBuffer[allData.size()]); + allInstanceData = allData.toArray(new GlVertexBuffer[allData.size()]); } @Override diff --git a/jme3-core/src/main/java/com/jme3/scene/mesh/GlMeshDescription.java b/jme3-core/src/main/java/com/jme3/scene/mesh/GlMeshDescription.java new file mode 100644 index 0000000000..585512dcdb --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/scene/mesh/GlMeshDescription.java @@ -0,0 +1,23 @@ +package com.jme3.scene.mesh; + +import com.jme3.scene.GlVertexBuffer; + +import java.util.HashMap; +import java.util.Map; + +public class GlMeshDescription { + + private final Map buffers = new HashMap<>(); + + + private static class BufferDescription { + + public final int components; + + public BufferDescription(int components) { + this.components = components; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/scene/mesh/GlMeshModifier.java b/jme3-core/src/main/java/com/jme3/scene/mesh/GlMeshModifier.java new file mode 100644 index 0000000000..4d26099c32 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/scene/mesh/GlMeshModifier.java @@ -0,0 +1,147 @@ +package com.jme3.scene.mesh; + +import com.jme3.scene.GlVertexBuffer; +import com.jme3.util.BufferUtils; +import com.jme3.vulkan.mesh.AttributeModifier; +import com.jme3.vulkan.mesh.VertexWriter; + +import java.nio.ByteBuffer; + +public class GlMeshModifier implements AttributeModifier { + + private final GlVertexBuffer vertices; + private final ByteBuffer buffer; + private final int bytesPerComponent; + + public GlMeshModifier(GlVertexBuffer vertices) { + this.vertices = vertices; + this.buffer = BufferUtils.interfaceByteBuffer(vertices.getData()); + this.bytesPerComponent = vertices.getStride() / vertices.getNumComponents(); + } + + @Override + public void close() {} + + @Override + public void setUpdateNeeded() { + vertices.setUpdateNeeded(); + } + + public int vertexToPosition(int vertex) { + return vertex * vertices.getStride(); + } + + public int vertexToPosition(int vertex, int component) { + return vertex * vertices.getStride() + component * bytesPerComponent; + } + + public int positionToVertex(int position) { + return position / vertices.getStride(); + } + + @Override + public VertexWriter limit(int vertex) { + buffer.limit(vertexToPosition(vertex)); + return this; + } + + @Override + public VertexWriter putByte(int vertex, int component, byte value) { + buffer.put(vertexToPosition(vertex, component), value); + return this; + } + + @Override + public VertexWriter putShort(int vertex, int component, short value) { + buffer.putShort(vertexToPosition(vertex, component), value); + return this; + } + + @Override + public VertexWriter putInt(int vertex, int component, int value) { + buffer.putInt(vertexToPosition(vertex, component), value); + return this; + } + + @Override + public VertexWriter putFloat(int vertex, int component, float value) { + buffer.putFloat(vertexToPosition(vertex, component), value); + return this; + } + + @Override + public VertexWriter putDouble(int vertex, int component, double value) { + buffer.putDouble(vertexToPosition(vertex, component), value); + return this; + } + + @Override + public VertexWriter putLong(int vertex, int component, long value) { + buffer.putLong(vertexToPosition(vertex, component), value); + return this; + } + + @Override + public VertexWriter putNumber(int vertex, int component, Number value) { + if (value instanceof Byte) { + putByte(vertex, component, value.byteValue()); + } else if (value instanceof Short) { + putShort(vertex, component, value.shortValue()); + } else if (value instanceof Integer) { + putInt(vertex, component, value.intValue()); + } else if (value instanceof Float) { + putFloat(vertex, component, value.floatValue()); + } else if (value instanceof Double) { + putDouble(vertex, component, value.doubleValue()); + } else if (value instanceof Long) { + putLong(vertex, component, value.longValue()); + } + return this; + } + + @Override + public int capacity() { + return positionToVertex(buffer.position()); + } + + @Override + public int limit() { + return positionToVertex(buffer.limit()); + } + + @Override + public int components() { + return vertices.getNumComponents(); + } + + @Override + public byte getByte(int vertex, int component) { + return buffer.get(vertexToPosition(vertex, component)); + } + + @Override + public short getShort(int vertex, int component) { + return buffer.getShort(vertexToPosition(vertex, component)); + } + + @Override + public int getInt(int vertex, int component) { + return buffer.getInt(vertexToPosition(vertex, component)); + } + + @Override + public float getFloat(int vertex, int component) { + return buffer.getFloat(vertexToPosition(vertex, component)); + } + + @Override + public double getDouble(int vertex, int component) { + return buffer.getDouble(vertexToPosition(vertex, component)); + } + + @Override + public long getLong(int vertex, int component) { + return buffer.getLong(vertexToPosition(vertex, component)); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/scene/mesh/IndexBuffer.java b/jme3-core/src/main/java/com/jme3/scene/mesh/IndexBuffer.java index 29ff8050c7..92d4c6843c 100644 --- a/jme3-core/src/main/java/com/jme3/scene/mesh/IndexBuffer.java +++ b/jme3-core/src/main/java/com/jme3/scene/mesh/IndexBuffer.java @@ -36,7 +36,8 @@ import java.nio.IntBuffer; import java.nio.ShortBuffer; -import com.jme3.scene.VertexBuffer.Format; +import com.jme3.scene.Mesh; +import com.jme3.scene.GlVertexBuffer.Format; import com.jme3.util.BufferUtils; /** @@ -170,7 +171,7 @@ public int remaining() { * Returns the format of the data stored in this buffer. * *

This method can be used to set an {@link IndexBuffer} to a - * {@link com.jme3.scene.Mesh Mesh}:

+ * {@link Mesh Mesh}:

*
      * mesh.setBuffer(Type.Index, 3, 
      *     indexBuffer.getFormat(), indexBuffer);
diff --git a/jme3-core/src/main/java/com/jme3/scene/mesh/IndexByteBuffer.java b/jme3-core/src/main/java/com/jme3/scene/mesh/IndexByteBuffer.java
index f7f66a2b04..2d5098fef3 100644
--- a/jme3-core/src/main/java/com/jme3/scene/mesh/IndexByteBuffer.java
+++ b/jme3-core/src/main/java/com/jme3/scene/mesh/IndexByteBuffer.java
@@ -34,7 +34,7 @@
 import java.nio.Buffer;
 import java.nio.ByteBuffer;
 
-import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.GlVertexBuffer.Format;
 
 /**
  * IndexBuffer implementation for {@link ByteBuffer}s.
diff --git a/jme3-core/src/main/java/com/jme3/scene/mesh/IndexIntBuffer.java b/jme3-core/src/main/java/com/jme3/scene/mesh/IndexIntBuffer.java
index c8d691589f..db1dddaa8c 100644
--- a/jme3-core/src/main/java/com/jme3/scene/mesh/IndexIntBuffer.java
+++ b/jme3-core/src/main/java/com/jme3/scene/mesh/IndexIntBuffer.java
@@ -34,7 +34,7 @@
 import java.nio.Buffer;
 import java.nio.IntBuffer;
 
-import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.GlVertexBuffer.Format;
 
 /**
  * IndexBuffer implementation for {@link IntBuffer}s.
diff --git a/jme3-core/src/main/java/com/jme3/scene/mesh/IndexShortBuffer.java b/jme3-core/src/main/java/com/jme3/scene/mesh/IndexShortBuffer.java
index c505947eb9..fc78662b3a 100644
--- a/jme3-core/src/main/java/com/jme3/scene/mesh/IndexShortBuffer.java
+++ b/jme3-core/src/main/java/com/jme3/scene/mesh/IndexShortBuffer.java
@@ -34,7 +34,7 @@
 import java.nio.Buffer;
 import java.nio.ShortBuffer;
 
-import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.GlVertexBuffer.Format;
 
 /**
  * IndexBuffer implementation for {@link ShortBuffer}s.
diff --git a/jme3-core/src/main/java/com/jme3/scene/mesh/MorphTarget.java b/jme3-core/src/main/java/com/jme3/scene/mesh/MorphTarget.java
index 9d57b00588..a9c0b05ec3 100644
--- a/jme3-core/src/main/java/com/jme3/scene/mesh/MorphTarget.java
+++ b/jme3-core/src/main/java/com/jme3/scene/mesh/MorphTarget.java
@@ -36,7 +36,7 @@
 import com.jme3.export.JmeImporter;
 import com.jme3.export.OutputCapsule;
 import com.jme3.export.Savable;
-import com.jme3.scene.VertexBuffer;
+import com.jme3.scene.GlVertexBuffer;
 
 import java.io.IOException;
 import java.nio.FloatBuffer;
@@ -60,7 +60,7 @@ public class MorphTarget implements Savable {
      * Stores the `FloatBuffer` instances for each `VertexBuffer.Type` that
      * this morph target affects.
      */
-    private final EnumMap buffers = new EnumMap<>(VertexBuffer.Type.class);
+    private final EnumMap buffers = new EnumMap<>(GlVertexBuffer.Type.class);
     /**
      * An optional name for this morph target, useful for identification
      * and targeting in animations.
@@ -107,7 +107,7 @@ public String getName() {
      * @param type The type of vertex buffer (e.g., `POSITION`, `NORMAL`).
      * @param buffer The `FloatBuffer` containing the delta data for the given type.
      */
-    public void setBuffer(VertexBuffer.Type type, FloatBuffer buffer) {
+    public void setBuffer(GlVertexBuffer.Type type, FloatBuffer buffer) {
         buffers.put(type, buffer);
     }
 
@@ -117,7 +117,7 @@ public void setBuffer(VertexBuffer.Type type, FloatBuffer buffer) {
      * @param type The type of vertex buffer.
      * @return The `FloatBuffer` for the given type, or null if not set.
      */
-    public FloatBuffer getBuffer(VertexBuffer.Type type) {
+    public FloatBuffer getBuffer(GlVertexBuffer.Type type) {
         return buffers.get(type);
     }
 
@@ -127,7 +127,7 @@ public FloatBuffer getBuffer(VertexBuffer.Type type) {
      *
      * @return An `EnumMap` of vertex buffer types to their corresponding `FloatBuffer`s.
      */
-    public EnumMap getBuffers() {
+    public EnumMap getBuffers() {
         return buffers;
     }
 
@@ -144,8 +144,8 @@ public int getNumBuffers() {
     @Override
     public void write(JmeExporter ex) throws IOException {
         OutputCapsule oc = ex.getCapsule(this);
-        for (Map.Entry entry : buffers.entrySet()) {
-            VertexBuffer.Type type = entry.getKey();
+        for (Map.Entry entry : buffers.entrySet()) {
+            GlVertexBuffer.Type type = entry.getKey();
             FloatBuffer roData = entry.getValue().asReadOnlyBuffer();
             oc.write(roData, type.name(), null);
         }
@@ -155,7 +155,7 @@ public void write(JmeExporter ex) throws IOException {
     @Override
     public void read(JmeImporter im) throws IOException {
         InputCapsule ic = im.getCapsule(this);
-        for (VertexBuffer.Type type : VertexBuffer.Type.values()) {
+        for (GlVertexBuffer.Type type : GlVertexBuffer.Type.values()) {
             FloatBuffer fb = ic.readFloatBuffer(type.name(), null);
             if (fb != null) {
                 setBuffer(type, fb);
diff --git a/jme3-core/src/main/java/com/jme3/scene/mesh/VirtualIndexBuffer.java b/jme3-core/src/main/java/com/jme3/scene/mesh/VirtualIndexBuffer.java
index c3144e6b97..b9402ae857 100644
--- a/jme3-core/src/main/java/com/jme3/scene/mesh/VirtualIndexBuffer.java
+++ b/jme3-core/src/main/java/com/jme3/scene/mesh/VirtualIndexBuffer.java
@@ -31,23 +31,23 @@
  */
 package com.jme3.scene.mesh;
 
-import com.jme3.scene.Mesh.Mode;
-import com.jme3.scene.VertexBuffer.Format;
+import com.jme3.scene.GlMesh;
+import com.jme3.scene.GlVertexBuffer.Format;
 
 import java.nio.Buffer;
 
 /**
  * IndexBuffer implementation that generates vertex indices sequentially
- * based on a specific Mesh {@link Mode}.
+ * based on a specific Mesh {@link GlMesh.Mode}.
  * The generated indices are as if the mesh is in the given mode
  * but contains no index buffer, thus this implementation will
  * return the indices if the index buffer was there and contained sequential
  * triangles.
  * Example:
  * 
    - *
  • {@link Mode#Triangles}: 0, 1, 2 | 3, 4, 5 | 6, 7, 8 | ...
  • - *
  • {@link Mode#TriangleStrip}: 0, 1, 2 | 2, 1, 3 | 2, 3, 4 | ...
  • - *
  • {@link Mode#TriangleFan}: 0, 1, 2 | 0, 2, 3 | 0, 3, 4 | ...
  • + *
  • {@link GlMesh.Mode#Triangles}: 0, 1, 2 | 3, 4, 5 | 6, 7, 8 | ...
  • + *
  • {@link GlMesh.Mode#TriangleStrip}: 0, 1, 2 | 2, 1, 3 | 2, 3, 4 | ...
  • + *
  • {@link GlMesh.Mode#TriangleFan}: 0, 1, 2 | 0, 2, 3 | 0, 3, 4 | ...
  • *
* * @author Kirill Vainer @@ -56,10 +56,10 @@ public class VirtualIndexBuffer extends IndexBuffer { protected int numVerts = 0; protected int numIndices = 0; - protected Mode meshMode; + protected GlMesh.Mode meshMode; protected int position = 0; - public VirtualIndexBuffer(int numVerts, Mode meshMode) { + public VirtualIndexBuffer(int numVerts, GlMesh.Mode meshMode) { this.numVerts = numVerts; this.meshMode = meshMode; switch (meshMode) { @@ -108,13 +108,13 @@ public int remaining() { @Override public int get(int i) { - if (meshMode == Mode.Triangles || meshMode == Mode.Lines || meshMode == Mode.Points) { + if (meshMode == GlMesh.Mode.Triangles || meshMode == GlMesh.Mode.Lines || meshMode == GlMesh.Mode.Points) { return i; - } else if (meshMode == Mode.LineStrip) { + } else if (meshMode == GlMesh.Mode.LineStrip) { return (i + 1) / 2; - } else if (meshMode == Mode.LineLoop) { + } else if (meshMode == GlMesh.Mode.LineLoop) { return (i == (numIndices - 1)) ? 0 : ((i + 1) / 2); - } else if (meshMode == Mode.TriangleStrip) { + } else if (meshMode == GlMesh.Mode.TriangleStrip) { int triIndex = i / 3; int vertIndex = i % 3; boolean isBack = (i / 3) % 2 == 1; @@ -132,7 +132,7 @@ public int get(int i) { throw new AssertionError(); } } - } else if (meshMode == Mode.TriangleFan) { + } else if (meshMode == GlMesh.Mode.TriangleFan) { int vertIndex = i % 3; if (vertIndex == 0) { return 0; diff --git a/jme3-core/src/main/java/com/jme3/scene/mesh/WrappedIndexBuffer.java b/jme3-core/src/main/java/com/jme3/scene/mesh/WrappedIndexBuffer.java index f6c9d4bfea..e0981925c4 100644 --- a/jme3-core/src/main/java/com/jme3/scene/mesh/WrappedIndexBuffer.java +++ b/jme3-core/src/main/java/com/jme3/scene/mesh/WrappedIndexBuffer.java @@ -32,8 +32,8 @@ package com.jme3.scene.mesh; import com.jme3.scene.Mesh; -import com.jme3.scene.Mesh.Mode; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlMesh.Mode; +import com.jme3.scene.GlVertexBuffer.Type; import java.nio.Buffer; /** @@ -82,30 +82,32 @@ public int get(int i) { public Buffer getBuffer() { return ib.getBuffer(); } - - public static void convertToList(Mesh mesh){ - IndexBuffer inBuf = mesh.getIndicesAsList(); - IndexBuffer outBuf = IndexBuffer.createIndexBuffer(mesh.getVertexCount(), - inBuf.size()); - - for (int i = 0; i < inBuf.size(); i++){ - outBuf.put(i, inBuf.get(i)); - } - mesh.clearBuffer(Type.Index); - switch (mesh.getMode()){ - case LineLoop: - case LineStrip: - mesh.setMode(Mode.Lines); - break; - case TriangleStrip: - case TriangleFan: - mesh.setMode(Mode.Triangles); - break; - default: - break; - } - mesh.setBuffer(Type.Index, 3, outBuf.getFormat(), outBuf.getBuffer()); + @Deprecated + public static void convertToList(Mesh mesh) { +// IndexBuffer inBuf = mesh.getIndicesAsList(); +// IndexBuffer outBuf = IndexBuffer.createIndexBuffer(mesh.getVertexCount(), +// inBuf.size()); +// +// for (int i = 0; i < inBuf.size(); i++){ +// outBuf.put(i, inBuf.get(i)); +// } +// +// // fixme: is this setting the mesh's mode with the mesh's mode? +// mesh.clearBuffer(Type.Index); +// switch (mesh.getMode()){ +// case LineLoop: +// case LineStrip: +// mesh.setMode(Mode.Lines); +// break; +// case TriangleStrip: +// case TriangleFan: +// mesh.setMode(Mode.Triangles); +// break; +// default: +// break; +// } +// mesh.setBuffer(Type.Index, 3, outBuf.getFormat(), outBuf.getBuffer()); } } diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/AbstractBox.java b/jme3-core/src/main/java/com/jme3/scene/shape/AbstractBox.java index e096a91a4c..1092512b67 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/AbstractBox.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/AbstractBox.java @@ -33,7 +33,7 @@ import com.jme3.export.*; import com.jme3.math.Vector3f; -import com.jme3.scene.Mesh; +import com.jme3.scene.GlMesh; import java.io.IOException; @@ -50,7 +50,7 @@ * @author Ian Phillips * @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $ */ -public abstract class AbstractBox extends Mesh { +public abstract class AbstractBox extends GlMesh { public final Vector3f center = new Vector3f(0f, 0f, 0f); diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/Box.java b/jme3-core/src/main/java/com/jme3/scene/shape/Box.java index 40be05df6a..bcb18edf67 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/Box.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/Box.java @@ -33,7 +33,7 @@ package com.jme3.scene.shape; import com.jme3.math.Vector3f; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.util.BufferUtils; import java.nio.FloatBuffer; diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/CenterQuad.java b/jme3-core/src/main/java/com/jme3/scene/shape/CenterQuad.java index cfe51dbaff..141511f441 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/CenterQuad.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/CenterQuad.java @@ -35,8 +35,8 @@ import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer.Type; import java.io.IOException; /** @@ -51,7 +51,7 @@ * * @author Kirill Vainer */ -public class CenterQuad extends Mesh { +public class CenterQuad extends GlMesh { private float width; private float height; diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/Curve.java b/jme3-core/src/main/java/com/jme3/scene/shape/Curve.java index e1b7dd994f..510569d4a4 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/Curve.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/Curve.java @@ -34,7 +34,8 @@ import com.jme3.math.Spline; import com.jme3.math.Vector3f; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer; import java.util.Iterator; import java.util.List; @@ -47,7 +48,7 @@ * * @author Nehon */ -public class Curve extends Mesh { +public class Curve extends GlMesh { private Spline spline; private Vector3f temp = new Vector3f(); @@ -132,9 +133,9 @@ private void createCatmullRomMesh(int nbSubSegments) { i++; } - this.setMode(Mesh.Mode.Lines); - this.setBuffer(VertexBuffer.Type.Position, 3, array); - this.setBuffer(VertexBuffer.Type.Index, 2, indices);//(spline.getControlPoints().size() - 1) * nbSubSegments * 2 + this.setMode(GlMesh.Mode.Lines); + this.setBuffer(GlVertexBuffer.Type.Position, 3, array); + this.setBuffer(GlVertexBuffer.Type.Index, 2, indices);//(spline.getControlPoints().size() - 1) * nbSubSegments * 2 this.updateBound(); this.updateCounts(); } @@ -184,9 +185,9 @@ private void createBezierMesh(int nbSubSegments) { indices[i++] = (short) k; } - this.setMode(Mesh.Mode.Lines); - this.setBuffer(VertexBuffer.Type.Position, 3, array); - this.setBuffer(VertexBuffer.Type.Index, 2, indices); + this.setMode(GlMesh.Mode.Lines); + this.setBuffer(GlVertexBuffer.Type.Position, 3, array); + this.setBuffer(GlVertexBuffer.Type.Index, 2, indices); this.updateBound(); this.updateCounts(); } @@ -228,9 +229,9 @@ private void createNurbMesh(int nbSubSegments) { indices[i++] = (short) (j + 1); } - this.setMode(Mesh.Mode.Lines); - this.setBuffer(VertexBuffer.Type.Position, 3, array); - this.setBuffer(VertexBuffer.Type.Index, 2, indices); + this.setMode(GlMesh.Mode.Lines); + this.setBuffer(GlVertexBuffer.Type.Position, 3, array); + this.setBuffer(GlVertexBuffer.Type.Index, 2, indices); this.updateBound(); this.updateCounts(); } @@ -262,9 +263,9 @@ private void createLinearMesh() { } } - this.setMode(Mesh.Mode.Lines); - this.setBuffer(VertexBuffer.Type.Position, 3, array); - this.setBuffer(VertexBuffer.Type.Index, 2, indices); + this.setMode(GlMesh.Mode.Lines); + this.setBuffer(GlVertexBuffer.Type.Position, 3, array); + this.setBuffer(GlVertexBuffer.Type.Index, 2, indices); this.updateBound(); this.updateCounts(); } diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java b/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java index 36df9d9892..3e28d38269 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/Cylinder.java @@ -38,8 +38,8 @@ import com.jme3.export.OutputCapsule; import com.jme3.math.FastMath; import com.jme3.math.Vector3f; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.util.BufferUtils; import java.io.IOException; @@ -50,7 +50,7 @@ * @author Mark Powell * @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $ */ -public class Cylinder extends Mesh { +public class Cylinder extends GlMesh { private int axisSamples; diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/Dome.java b/jme3-core/src/main/java/com/jme3/scene/shape/Dome.java index 5cfd312f82..1413957aba 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/Dome.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/Dome.java @@ -38,8 +38,8 @@ import com.jme3.export.OutputCapsule; import com.jme3.math.FastMath; import com.jme3.math.Vector3f; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.util.BufferUtils; import com.jme3.util.TempVars; import java.io.IOException; @@ -53,7 +53,7 @@ * @author Joshua Slack (Original sphere code that was adapted) * @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $ */ -public class Dome extends Mesh { +public class Dome extends GlMesh { private int planes; private int radialSamples; diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/Line.java b/jme3-core/src/main/java/com/jme3/scene/shape/Line.java index c9c5f3c197..78807d6827 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/Line.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/Line.java @@ -36,9 +36,9 @@ import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; import com.jme3.math.Vector3f; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Type; import java.io.IOException; import java.nio.FloatBuffer; @@ -47,7 +47,7 @@ * * @author Brent Owens */ -public class Line extends Mesh { +public class Line extends GlMesh { private Vector3f start = new Vector3f(); private Vector3f end = new Vector3f(); @@ -91,7 +91,7 @@ public void updatePoints(Vector3f start, Vector3f end) { this.start.set(start); this.end.set(end); - VertexBuffer posBuf = getBuffer(Type.Position); + GlVertexBuffer posBuf = getBuffer(Type.Position); FloatBuffer fb = (FloatBuffer) posBuf.getData(); fb.rewind(); diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/PQTorus.java b/jme3-core/src/main/java/com/jme3/scene/shape/PQTorus.java index d301a98675..ab67e5ff4a 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/PQTorus.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/PQTorus.java @@ -38,8 +38,8 @@ import com.jme3.export.OutputCapsule; import com.jme3.math.FastMath; import com.jme3.math.Vector3f; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer.Type; import static com.jme3.util.BufferUtils.*; import java.io.IOException; import java.nio.FloatBuffer; @@ -51,7 +51,7 @@ * @author Joshua Slack, Eric Woroshow * @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $ */ -public class PQTorus extends Mesh { +public class PQTorus extends GlMesh { private float p, q; diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/Quad.java b/jme3-core/src/main/java/com/jme3/scene/shape/Quad.java index 07c161c301..debcda1834 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/Quad.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/Quad.java @@ -36,8 +36,8 @@ import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer.Type; import java.io.IOException; /** @@ -48,7 +48,7 @@ * * @author Kirill Vainer */ -public class Quad extends Mesh { +public class Quad extends GlMesh { private float width; private float height; diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/RectangleMesh.java b/jme3-core/src/main/java/com/jme3/scene/shape/RectangleMesh.java index 6f16847390..9c8de31f6c 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/RectangleMesh.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/RectangleMesh.java @@ -40,8 +40,8 @@ import com.jme3.math.Rectangle; import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.util.BufferUtils; import com.jme3.util.clone.Cloner; @@ -71,7 +71,7 @@ * * @author Francivan Bezerra */ -public class RectangleMesh extends Mesh { +public class RectangleMesh extends GlMesh { /** * Used to locate the vertices and calculate a default normal. diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/Sphere.java b/jme3-core/src/main/java/com/jme3/scene/shape/Sphere.java index f4b2ba34c6..b91781783b 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/Sphere.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/Sphere.java @@ -38,8 +38,8 @@ import com.jme3.export.OutputCapsule; import com.jme3.math.FastMath; import com.jme3.math.Vector3f; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.util.BufferUtils; import com.jme3.util.TempVars; import java.io.IOException; @@ -53,7 +53,7 @@ * @author Joshua Slack * @version $Revision: 4163 $, $Date: 2009-03-24 21:14:55 -0400 (Tue, 24 Mar 2009) $ */ -public class Sphere extends Mesh { +public class Sphere extends GlMesh { public enum TextureMode { diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/StripBox.java b/jme3-core/src/main/java/com/jme3/scene/shape/StripBox.java index 16d1eb31d6..ada378571a 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/StripBox.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/StripBox.java @@ -33,7 +33,7 @@ package com.jme3.scene.shape; import com.jme3.math.Vector3f; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.util.BufferUtils; import java.nio.FloatBuffer; diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/Surface.java b/jme3-core/src/main/java/com/jme3/scene/shape/Surface.java index 9beef259e4..b235960e46 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/Surface.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/Surface.java @@ -40,8 +40,8 @@ import com.jme3.math.Spline.SplineType; import com.jme3.math.Vector3f; import com.jme3.math.Vector4f; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer; import com.jme3.util.BufferUtils; import java.io.IOException; @@ -56,7 +56,7 @@ * a) NURBS * @author Marcin Roguski (Kealthas) */ -public class Surface extends Mesh { +public class Surface extends GlMesh { private SplineType type; // the type of the surface private List> controlPoints; // space control points and their weights private List[] knots; // knots of the surface @@ -248,9 +248,9 @@ private void buildSurface(boolean smooth) { normals[arrayIndex++] = n.z; } - this.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(verticesArray)); - this.setBuffer(VertexBuffer.Type.Index, 3, indices); - this.setBuffer(VertexBuffer.Type.Normal, 3, normals); + this.setBuffer(GlVertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(verticesArray)); + this.setBuffer(GlVertexBuffer.Type.Index, 3, indices); + this.setBuffer(GlVertexBuffer.Type.Normal, 3, normals); this.updateBound(); this.updateCounts(); } diff --git a/jme3-core/src/main/java/com/jme3/scene/shape/Torus.java b/jme3-core/src/main/java/com/jme3/scene/shape/Torus.java index d3e75d6faf..46b2372ced 100644 --- a/jme3-core/src/main/java/com/jme3/scene/shape/Torus.java +++ b/jme3-core/src/main/java/com/jme3/scene/shape/Torus.java @@ -37,8 +37,8 @@ import com.jme3.export.OutputCapsule; import com.jme3.math.FastMath; import com.jme3.math.Vector3f; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.util.BufferUtils; import java.io.IOException; import java.nio.FloatBuffer; @@ -52,7 +52,7 @@ * @author Mark Powell * @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $ */ -public class Torus extends Mesh { +public class Torus extends GlMesh { private int circleSamples; diff --git a/jme3-core/src/main/java/com/jme3/shader/Shader.java b/jme3-core/src/main/java/com/jme3/shader/Shader.java index eb2f778d39..cbefbc2abd 100644 --- a/jme3-core/src/main/java/com/jme3/shader/Shader.java +++ b/jme3-core/src/main/java/com/jme3/shader/Shader.java @@ -32,7 +32,7 @@ package com.jme3.shader; import com.jme3.renderer.Renderer; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import com.jme3.util.IntMap; import com.jme3.util.IntMap.Entry; import com.jme3.util.ListMap; @@ -335,7 +335,7 @@ public void removeBufferBlock(final String name){ bufferBlocks.remove(name); } - public Attribute getAttribute(VertexBuffer.Type attribType){ + public Attribute getAttribute(GlVertexBuffer.Type attribType){ int ordinal = attribType.ordinal(); Attribute attrib = attribs.get(ordinal); if (attrib == null){ diff --git a/jme3-core/src/main/java/com/jme3/shader/Uniform.java b/jme3-core/src/main/java/com/jme3/shader/Uniform.java index ebd42f9605..ff9ef0351c 100644 --- a/jme3-core/src/main/java/com/jme3/shader/Uniform.java +++ b/jme3-core/src/main/java/com/jme3/shader/Uniform.java @@ -31,7 +31,7 @@ */ package com.jme3.shader; -import com.jme3.material.Material.BindUnits; +import com.jme3.material.GlMaterial.BindUnits; import com.jme3.math.*; import com.jme3.util.BufferUtils; import com.jme3.util.TempVars; diff --git a/jme3-core/src/main/java/com/jme3/shader/VarType.java b/jme3-core/src/main/java/com/jme3/shader/VarType.java index ebf7edc64c..4d847358ab 100644 --- a/jme3-core/src/main/java/com/jme3/shader/VarType.java +++ b/jme3-core/src/main/java/com/jme3/shader/VarType.java @@ -38,7 +38,7 @@ import com.jme3.math.Vector3f; import com.jme3.math.Vector4f; import com.jme3.shader.bufferobject.BufferObject; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; import com.jme3.texture.Texture3D; import com.jme3.texture.TextureArray; @@ -68,10 +68,10 @@ public enum VarType { Matrix4Array(true, false, "mat4", Matrix4f[].class), TextureBuffer(false, true, "sampler1D|sampler1DShadow"), - Texture2D(false, true, "sampler2D|sampler2DShadow", Texture2D.class, Texture.class), - Texture3D(false, true, "sampler3D", Texture3D.class, Texture.class), - TextureArray(false, true, "sampler2DArray|sampler2DArrayShadow", TextureArray.class, Texture.class), - TextureCubeMap(false, true, "samplerCube", TextureCubeMap.class, Texture.class), + Texture2D(false, true, "sampler2D|sampler2DShadow", Texture2D.class, GlTexture.class), + Texture3D(false, true, "sampler3D", Texture3D.class, GlTexture.class), + TextureArray(false, true, "sampler2DArray|sampler2DArrayShadow", TextureArray.class, GlTexture.class), + TextureCubeMap(false, true, "samplerCube", TextureCubeMap.class, GlTexture.class), Image2D(false, false, true, "image2D", TextureImage.class), Image3D(false, false, true, "image3D", TextureImage.class), diff --git a/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowFilter.java b/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowFilter.java index b8e8d4a22b..851d2d1316 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowFilter.java +++ b/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowFilter.java @@ -36,6 +36,7 @@ import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; +import com.jme3.material.GlMaterial; import com.jme3.material.Material; import com.jme3.material.RenderState; import com.jme3.math.Matrix4f; @@ -44,7 +45,7 @@ import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; -import com.jme3.texture.FrameBuffer; +import com.jme3.texture.GlFrameBuffer; import com.jme3.util.clone.Cloner; import com.jme3.util.clone.JmeCloneable; @@ -117,7 +118,7 @@ protected void postQueue(RenderQueue queue) { } @Override - protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) { + protected void postFrame(RenderManager renderManager, ViewPort viewPort, GlFrameBuffer prevFilterBuffer, GlFrameBuffer sceneBuffer) { if (!shadowRenderer.skipPostPass) { shadowRenderer.setPostShadowParams(); } @@ -126,7 +127,7 @@ protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBu @Override protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) { shadowRenderer.needsfallBackMaterial = true; - material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md"); + material = new GlMaterial(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md"); shadowRenderer.setPostShadowMaterial(material); shadowRenderer.initialize(renderManager, vp); this.viewPort = vp; diff --git a/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java index 50db5a5a64..ac6ba78475 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java @@ -35,6 +35,7 @@ import com.jme3.export.*; import com.jme3.light.LightFilter; import com.jme3.light.NullLightFilter; +import com.jme3.material.GlMaterial; import com.jme3.material.Material; import com.jme3.material.RenderState; import com.jme3.math.ColorRGBA; @@ -54,16 +55,18 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; import com.jme3.scene.debug.WireFrustum; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture.MagFilter; -import com.jme3.texture.Texture.MinFilter; -import com.jme3.texture.Texture.ShadowCompareMode; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture.MagFilter; +import com.jme3.texture.GlTexture.MinFilter; +import com.jme3.texture.GlTexture.ShadowCompareMode; import com.jme3.texture.Texture2D; -import com.jme3.texture.FrameBuffer.FrameBufferTarget; +import com.jme3.texture.GlFrameBuffer.FrameBufferTarget; import com.jme3.ui.Picture; import com.jme3.util.clone.Cloner; import com.jme3.util.clone.JmeCloneable; +import com.jme3.vulkan.render.GeometryBatch; +import com.jme3.vulkan.render.GlGeometryBatch; import java.io.IOException; import java.util.ArrayList; @@ -85,7 +88,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable, protected float shadowIntensity = 0.7f; protected RenderManager renderManager; protected ViewPort viewPort; - protected FrameBuffer[] shadowFB; + protected GlFrameBuffer[] shadowFB; protected Texture2D[] shadowMaps; protected Texture2D dummyTex; protected Material preshadowMat; @@ -152,8 +155,8 @@ protected AbstractShadowRenderer(AssetManager assetManager, int shadowMapSize, i } private void init(AssetManager assetManager, int nbShadowMaps, int shadowMapSize) { - this.postshadowMat = new Material(assetManager, "Common/MatDefs/Shadow/PostShadow.j3md"); - shadowFB = new FrameBuffer[nbShadowMaps]; + this.postshadowMat = new GlMaterial(assetManager, "Common/MatDefs/Shadow/PostShadow.j3md"); + shadowFB = new GlFrameBuffer[nbShadowMaps]; shadowMaps = new Texture2D[nbShadowMaps]; dispPic = new Picture[nbShadowMaps]; lightViewProjectionsMatrices = new Matrix4f[nbShadowMaps]; @@ -163,12 +166,12 @@ private void init(AssetManager assetManager, int nbShadowMaps, int shadowMapSize //DO NOT COMMENT THIS (it prevents the OSX incomplete read-buffer crash) dummyTex = new Texture2D(shadowMapSize, shadowMapSize, Format.RGBA8); - preshadowMat = new Material(assetManager, "Common/MatDefs/Shadow/PreShadow.j3md"); + preshadowMat = new GlMaterial(assetManager, "Common/MatDefs/Shadow/PreShadow.j3md"); postshadowMat.setFloat("ShadowMapSize", shadowMapSize); for (int i = 0; i < nbShadowMaps; i++) { lightViewProjectionsMatrices[i] = new Matrix4f(); - shadowFB[i] = new FrameBuffer(shadowMapSize, shadowMapSize, 1); + shadowFB[i] = new GlFrameBuffer(shadowMapSize, shadowMapSize, 1); shadowMaps[i] = new Texture2D(shadowMapSize, shadowMapSize, Format.Depth); shadowFB[i].setDepthTarget(FrameBufferTarget.newTarget(shadowMaps[i])); @@ -301,11 +304,13 @@ public CompareMode getShadowCompareMode() { */ protected Geometry createFrustum(Vector3f[] pts, int i) { WireFrustum frustum = new WireFrustum(pts); - Geometry frustumMdl = new Geometry("f", frustum); + // fixme + Geometry frustumMdl = new Geometry("f"/*, frustum*/); frustumMdl.setCullHint(Spatial.CullHint.Never); frustumMdl.setShadowMode(ShadowMode.Off); - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + Material mat = new GlMaterial(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat.getAdditionalRenderState().setWireframe(true); + // fixme frustumMdl.setMaterial(mat); switch (i) { case 0: @@ -408,6 +413,11 @@ public void postQueue(RenderQueue rq) { updateShadowCams(viewPort.getCamera()); + GeometryBatch batch = new GlGeometryBatch(); + batch.setForcedMaterial(preshadowMat); + batch.setForcedTechnique("PreShadow"); + + Renderer r = renderManager.getRenderer(); renderManager.setForcedMaterial(preshadowMat); renderManager.setForcedTechnique("PreShadow"); @@ -485,7 +495,7 @@ public void displayDebug() { protected abstract void getReceivers(GeometryList lightReceivers); @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { if (skipPostPass) { return; } diff --git a/jme3-core/src/main/java/com/jme3/shadow/BasicShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/BasicShadowRenderer.java index 3f41721b3c..6f5a77f806 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/BasicShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/BasicShadowRenderer.java @@ -34,6 +34,7 @@ import com.jme3.asset.AssetManager; import com.jme3.light.LightFilter; import com.jme3.light.NullLightFilter; +import com.jme3.material.GlMaterial; import com.jme3.material.Material; import com.jme3.math.Vector3f; import com.jme3.post.SceneProcessor; @@ -47,8 +48,8 @@ import com.jme3.renderer.queue.RenderQueue; import com.jme3.renderer.queue.RenderQueue.ShadowMode; import com.jme3.scene.Spatial; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage.Format; import com.jme3.texture.Texture2D; import com.jme3.ui.Picture; @@ -64,7 +65,7 @@ public class BasicShadowRenderer implements SceneProcessor { private static final LightFilter NULL_LIGHT_FILTER = new NullLightFilter(); private RenderManager renderManager; private ViewPort viewPort; - private final FrameBuffer shadowFB; + private final GlFrameBuffer shadowFB; private final Texture2D shadowMap; private final Camera shadowCam; private final Material preshadowMat; @@ -85,17 +86,17 @@ public class BasicShadowRenderer implements SceneProcessor { * @param size the size of the shadow map (the map is square) */ public BasicShadowRenderer(AssetManager manager, int size) { - shadowFB = new FrameBuffer(size, size, 1); + shadowFB = new GlFrameBuffer(size, size, 1); shadowMap = new Texture2D(size, size, Format.Depth); - shadowFB.setDepthTexture(shadowMap); + shadowFB.setDepthTarget(GlFrameBuffer.newTarget(shadowMap)); shadowCam = new Camera(size, size); //DO NOT COMMENT THIS (It prevents the OSX incomplete read buffer crash.) - dummyTex = new Texture2D(size, size, Format.RGBA8); - shadowFB.setColorTexture(dummyTex); + dummyTex = new Texture2D(size, size, Format.RGBA8); + shadowFB.addColorTarget(GlFrameBuffer.newTarget(dummyTex)); shadowMapSize = size; - preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md"); - postshadowMat = new Material(manager, "Common/MatDefs/Shadow/BasicPostShadow.j3md"); + preshadowMat = new GlMaterial(manager, "Common/MatDefs/Shadow/PreShadow.j3md"); + postshadowMat = new GlMaterial(manager, "Common/MatDefs/Shadow/BasicPostShadow.j3md"); postshadowMat.setTexture("ShadowMap", shadowMap); dispPic.setTexture(manager, shadowMap, false); @@ -218,7 +219,7 @@ public Picture getDisplayPicture() { } @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { if (!noOccluders) { postshadowMat.setMatrix4("LightViewProjectionMatrix", shadowCam.getViewProjectionMatrix()); renderManager.setForcedMaterial(postshadowMat); diff --git a/jme3-core/src/main/java/com/jme3/shadow/PssmShadowFilter.java b/jme3-core/src/main/java/com/jme3/shadow/PssmShadowFilter.java index 5c4e6186e4..c31f5ec1f6 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/PssmShadowFilter.java +++ b/jme3-core/src/main/java/com/jme3/shadow/PssmShadowFilter.java @@ -46,7 +46,7 @@ import com.jme3.renderer.queue.RenderQueue; import com.jme3.shadow.PssmShadowRenderer.CompareMode; import com.jme3.shadow.PssmShadowRenderer.FilterMode; -import com.jme3.texture.FrameBuffer; +import com.jme3.texture.GlFrameBuffer; import java.io.IOException; /** @@ -90,7 +90,7 @@ protected PssmShadowFilter() { */ public PssmShadowFilter(AssetManager manager, int size, int nbSplits) { super("Post Shadow"); - material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md"); + material = Backend.material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md"); pssmRenderer = new PssmShadowRenderer(manager, size, nbSplits, material); pssmRenderer.needsfallBackMaterial = true; } @@ -125,7 +125,7 @@ protected void postQueue(RenderQueue queue) { } @Override - protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) { + protected void postFrame(RenderManager renderManager, ViewPort viewPort, GlFrameBuffer prevFilterBuffer, GlFrameBuffer sceneBuffer) { pssmRenderer.setPostShadowParams(); } diff --git a/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java index a0d405000b..864ced6464 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java @@ -53,11 +53,11 @@ import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.scene.debug.WireFrustum; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture.MagFilter; -import com.jme3.texture.Texture.MinFilter; -import com.jme3.texture.Texture.ShadowCompareMode; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture.MagFilter; +import com.jme3.texture.GlTexture.MinFilter; +import com.jme3.texture.GlTexture.ShadowCompareMode; import com.jme3.texture.Texture2D; import com.jme3.ui.Picture; import java.util.ArrayList; @@ -141,7 +141,7 @@ public enum CompareMode { protected float zFarOverride = 0; protected RenderManager renderManager; protected ViewPort viewPort; - protected FrameBuffer[] shadowFB; + protected GlFrameBuffer[] shadowFB; protected Texture2D[] shadowMaps; protected Texture2D dummyTex; protected Camera shadowCam; @@ -189,7 +189,7 @@ public enum CompareMode { * the more quality, the less fps) */ public PssmShadowRenderer(AssetManager manager, int size, int nbSplits) { - this(manager, size, nbSplits, new Material(manager, "Common/MatDefs/Shadow/PostShadow.j3md")); + this(manager, size, nbSplits, Backend.material(manager, "Common/MatDefs/Shadow/PostShadow.j3md")); } /** @@ -211,7 +211,7 @@ protected PssmShadowRenderer(AssetManager manager, int size, int nbSplits, Mater this.nbSplits = nbSplits; shadowMapSize = size; - shadowFB = new FrameBuffer[nbSplits]; + shadowFB = new GlFrameBuffer[nbSplits]; shadowMaps = new Texture2D[nbSplits]; dispPic = new Picture[nbSplits]; lightViewProjectionsMatrices = new Matrix4f[nbSplits]; @@ -221,12 +221,12 @@ protected PssmShadowRenderer(AssetManager manager, int size, int nbSplits, Mater //DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash) dummyTex = new Texture2D(size, size, Format.RGBA8); - preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md"); + preshadowMat = Backend.material(manager, "Common/MatDefs/Shadow/PreShadow.j3md"); postshadowMat.setFloat("ShadowMapSize", size); for (int i = 0; i < nbSplits; i++) { lightViewProjectionsMatrices[i] = new Matrix4f(); - shadowFB[i] = new FrameBuffer(size, size, 1); + shadowFB[i] = new GlFrameBuffer(size, size, 1); shadowMaps[i] = new Texture2D(size, size, Format.Depth); shadowFB[i].setDepthTexture(shadowMaps[i]); @@ -327,7 +327,7 @@ private Geometry createFrustum(Vector3f[] pts, int i) { Geometry frustumMdl = new Geometry("f", frustum); frustumMdl.setCullHint(Spatial.CullHint.Never); frustumMdl.setShadowMode(ShadowMode.Off); - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + Material mat = Backend.material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat.getAdditionalRenderState().setWireframe(true); frustumMdl.setMaterial(mat); switch (i) { @@ -495,7 +495,7 @@ public void displayDebug() { } @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { if (debug) { displayShadowMap(renderManager.getRenderer()); diff --git a/jme3-core/src/main/java/com/jme3/system/JmeContext.java b/jme3-core/src/main/java/com/jme3/system/JmeContext.java index c2ffe912ef..e162fc399a 100644 --- a/jme3-core/src/main/java/com/jme3/system/JmeContext.java +++ b/jme3-core/src/main/java/com/jme3/system/JmeContext.java @@ -44,7 +44,7 @@ public interface JmeContext { /** * The type of context. */ - public enum Type { + enum Type { /** * A display can represent a windowed or a fullscreen-exclusive display. * If windowed, the graphics are rendered to a new on-screen surface @@ -79,6 +79,23 @@ public enum Type { Headless, } + /** + * Enum specifying the backend to use. + */ + enum Backend { + + /** + * Specifies the OpenGL backend. + */ + OpenGL, + + /** + * Specifies the Vulkan backend. + */ + Vulkan; + + } + /** * @return The type of the context. */ diff --git a/jme3-core/src/main/java/com/jme3/system/JmeSystem.java b/jme3-core/src/main/java/com/jme3/system/JmeSystem.java index 4a832916c8..e257e9d091 100644 --- a/jme3-core/src/main/java/com/jme3/system/JmeSystem.java +++ b/jme3-core/src/main/java/com/jme3/system/JmeSystem.java @@ -34,6 +34,7 @@ import com.jme3.asset.AssetManager; import com.jme3.audio.AudioRenderer; import com.jme3.input.SoftTextDialogInput; +import com.jme3.texture.GlImage; import java.io.File; import java.io.IOException; @@ -137,7 +138,7 @@ public static SoftTextDialogInput getSoftTextDialogInput() { * * @param outStream The stream where to write the image data. * @param format The format to use, either "png" or "jpg". - * @param imageData The image data in {@link com.jme3.texture.Image.Format#RGBA8} format. + * @param imageData The image data in {@link GlImage.Format#RGBA8} format. * @param width The width of the image. * @param height The height of the image. * @throws IOException If outStream throws an exception while writing. diff --git a/jme3-core/src/main/java/com/jme3/system/NullRenderer.java b/jme3-core/src/main/java/com/jme3/system/NullRenderer.java index 28eb6b5231..f02ff3dd0a 100644 --- a/jme3-core/src/main/java/com/jme3/system/NullRenderer.java +++ b/jme3-core/src/main/java/com/jme3/system/NullRenderer.java @@ -40,14 +40,14 @@ import com.jme3.renderer.Renderer; import com.jme3.renderer.Statistics; import com.jme3.renderer.TextureUnitException; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlMesh; +import com.jme3.scene.GlVertexBuffer; import com.jme3.shader.Shader; import com.jme3.shader.Shader.ShaderSource; import com.jme3.shader.bufferobject.BufferObject; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; import com.jme3.texture.TextureImage; import java.nio.ByteBuffer; @@ -139,36 +139,36 @@ public void deleteShader(Shader shader) { public void deleteShaderSource(ShaderSource source) { } - public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) { + public void copyFrameBuffer(GlFrameBuffer src, GlFrameBuffer dst) { } @Override - public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) { + public void copyFrameBuffer(GlFrameBuffer src, GlFrameBuffer dst, boolean copyDepth) { } @Override - public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyColor, boolean copyDepth) { + public void copyFrameBuffer(GlFrameBuffer src, GlFrameBuffer dst, boolean copyColor, boolean copyDepth) { } @Override - public void setMainFrameBufferOverride(FrameBuffer fb) { + public void setMainFrameBufferOverride(GlFrameBuffer fb) { } @Override - public void setFrameBuffer(FrameBuffer fb) { + public void setFrameBuffer(GlFrameBuffer fb) { } @Override - public void readFrameBuffer(FrameBuffer fb, ByteBuffer byteBuf) { + public void readFrameBuffer(GlFrameBuffer fb, ByteBuffer byteBuf) { } @Override - public void deleteFrameBuffer(FrameBuffer fb) { + public void deleteFrameBuffer(GlFrameBuffer fb) { } @Override - public void setTexture(int unit, Texture tex) throws TextureUnitException { + public void setTexture(int unit, GlTexture tex) throws TextureUnitException { // do nothing } @@ -178,15 +178,15 @@ public void setTextureImage(int unit, TextureImage tex) throws TextureUnitExcept } @Override - public void modifyTexture(Texture tex, Image pixels, int x, int y) { + public void modifyTexture(GlTexture tex, GlImage pixels, int x, int y) { } @Override - public void updateBufferData(VertexBuffer vb) { + public void updateBufferData(GlVertexBuffer vb) { } @Override - public void deleteBuffer(VertexBuffer vb) { + public void deleteBuffer(GlVertexBuffer vb) { } @Override @@ -195,7 +195,7 @@ public void deleteBuffer(BufferObject bo) { } @Override - public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceData) { + public void renderMesh(GlMesh mesh, GlMesh.Mode mode, int lod, int count, GlVertexBuffer[] instanceData) { } @Override @@ -207,7 +207,7 @@ public void cleanup() { } @Override - public void deleteImage(Image image) { + public void deleteImage(GlImage image) { } @Override @@ -248,7 +248,7 @@ public boolean isTaskResultAvailable(int taskId) { } @Override - public void readFrameBufferWithFormat(FrameBuffer fb, ByteBuffer byteBuf, Image.Format format) { + public void readFrameBufferWithFormat(GlFrameBuffer fb, ByteBuffer byteBuf, GlImage.Format format) { } @Override @@ -298,10 +298,11 @@ public boolean isMainFrameBufferSrgb() { } @Override - public FrameBuffer getCurrentFrameBuffer() { + public GlFrameBuffer getCurrentFrameBuffer() { return null; } + @Override public void updateShaderStorageBufferObjectData(BufferObject bo) { } diff --git a/jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java b/jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java deleted file mode 100644 index f3cc721df9..0000000000 --- a/jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java +++ /dev/null @@ -1,901 +0,0 @@ -/* - * Copyright (c) 2009-2023 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.texture; - -import com.jme3.renderer.Caps; -import com.jme3.renderer.Renderer; -import com.jme3.texture.Image.Format; -import com.jme3.util.NativeObject; -import java.util.ArrayList; - -/** - *

- * FrameBuffers are rendering surfaces allowing - * off-screen rendering and render-to-texture functionality. - * Instead of the scene rendering to the screen, it is rendered into the - * FrameBuffer, the result can be either a texture or a buffer. - *

- * A FrameBuffer supports two methods of rendering, - * using a {@link Texture} or using a buffer. - * When using a texture, the result of the rendering will be rendered - * onto the texture, after which the texture can be placed on an object - * and rendered as if the texture was uploaded from disk. - * When using a buffer, the result is rendered onto - * a buffer located on the GPU, the data of this buffer is not accessible - * to the user. buffers are useful if one - * wishes to retrieve only the color content of the scene, but still desires - * depth testing (which requires a depth buffer). - * Buffers can be copied to other framebuffers - * including the main screen, by using - * {@link Renderer#copyFrameBuffer(com.jme3.texture.FrameBuffer, com.jme3.texture.FrameBuffer, boolean, boolean)}. - * The content of a {@link RenderBuffer} can be retrieved by using - * {@link Renderer#readFrameBuffer(com.jme3.texture.FrameBuffer, java.nio.ByteBuffer) }. - *

- * FrameBuffers have several attachment points, there are - * several color attachment points and a single depth - * attachment point. - * The color attachment points support image formats such as - * {@link Format#RGBA8}, allowing rendering the color content of the scene. - * The depth attachment point requires a depth image format. - * - * @see Renderer#setFrameBuffer(com.jme3.texture.FrameBuffer) - * - * @author Kirill Vainer - */ -public class FrameBuffer extends NativeObject { - - public static final int SLOT_UNDEF = -1; - public static final int SLOT_DEPTH = -100; - public static final int SLOT_DEPTH_STENCIL = -101; - - private int width = 0; - private int height = 0; - private int samples = 1; - private final ArrayList colorBufs = new ArrayList<>(); - private RenderBuffer depthBuf = null; - private int colorBufIndex = 0; - private boolean srgb; - private String name; - private Boolean mipMapsGenerationHint = null; - - /** - * RenderBuffer represents either a texture or a - * buffer that will be rendered to. RenderBuffers - * are attached to an attachment slot on a FrameBuffer. - */ - public static class RenderBuffer { - - Texture tex; - Image.Format format; - int id = -1; - int slot = SLOT_UNDEF; - int face = -1; - int layer = -1; - int level = 0; - - - public int getLevel() { - return this.level; - } - - /** - * @return The image format of the render buffer. - */ - public Format getFormat() { - return format; - } - - /** - * @return The texture to render to for this RenderBuffer - * or null if content should be rendered into a buffer. - */ - public Texture getTexture() { - return tex; - } - - /** - * Do not use. - * @return the buffer's ID - */ - public int getId() { - return id; - } - - /** - * Do not use. - * - * @param id the desired ID - */ - public void setId(int id) { - this.id = id; - } - - /** - * Do not use. - * - * @return the slot code, such as SLOT_DEPTH_STENCIL - */ - public int getSlot() { - return slot; - } - - public int getFace() { - return face; - } - - public void resetObject() { - id = -1; - } - - public RenderBuffer createDestructableClone() { - if (tex != null) { - return null; - } else { - RenderBuffer destructClone = new RenderBuffer(); - destructClone.id = id; - return destructClone; - } - } - - @Override - public String toString() { - if (tex != null) { - return "TextureTarget[format=" + format + "]"; - } else { - return "BufferTarget[format=" + format + "]"; - } - } - - public int getLayer() { - return this.layer; - } - } - - public static class FrameBufferTextureTarget extends RenderBuffer { - private FrameBufferTextureTarget(){} - void setTexture(Texture tx){ - this.tex=tx; - this.format=tx.getImage().getFormat(); - } - - void setFormat(Format f){ - this.format=f; - } - - public FrameBufferTextureTarget layer(int i){ - this.layer=i; - return this; - } - - public FrameBufferTextureTarget level(int i){ - this.level=i; - return this; - } - - public FrameBufferTextureTarget face(TextureCubeMap.Face f){ - return face(f.ordinal()); - } - - public FrameBufferTextureTarget face(int f){ - this.face=f; - return this; - } - - } - - public static class FrameBufferBufferTarget extends RenderBuffer { - private FrameBufferBufferTarget(){} - void setFormat(Format f){ - this.format=f; - } - } - - public static class FrameBufferTarget { - private FrameBufferTarget(){} - public static FrameBufferTextureTarget newTarget(Texture tx){ - FrameBufferTextureTarget t=new FrameBufferTextureTarget(); - t.setTexture(tx); - return t; - } - - public static FrameBufferBufferTarget newTarget(Format format){ - FrameBufferBufferTarget t=new FrameBufferBufferTarget(); - t.setFormat(format); - return t; - } - - /** - * Creates a frame buffer texture and sets the face position by using the face parameter. It uses - * {@link TextureCubeMap} ordinal number for the face position. - * - * @param tx texture to add to the frame buffer - * @param face face to add to the color buffer to - * @return FrameBufferTexture Target - */ - public static FrameBufferTextureTarget newTarget(Texture tx, TextureCubeMap.Face face) { - FrameBufferTextureTarget t = new FrameBufferTextureTarget(); - t.face = face.ordinal(); - t.setTexture(tx); - return t; - } - } - - /** - * A private constructor to inhibit instantiation of this class. - */ - private FrameBuffer() { - } - - public void addColorTarget(FrameBufferBufferTarget colorBuf){ - colorBuf.slot=colorBufs.size(); - colorBufs.add(colorBuf); - } - - public void addColorTarget(FrameBufferTextureTarget colorBuf){ - // checkSetTexture(colorBuf.getTexture(), false); // TODO: this won't work for levels. - colorBuf.slot=colorBufs.size(); - colorBufs.add(colorBuf); - } - - /** - * Replaces the color target at the index. - *

- * A color target must already exist at the index, otherwise - * an exception will be thrown. - * - * @param i index of color target to replace - * @param colorBuf color target to replace with - */ - public void replaceColorTarget(int i, FrameBufferTextureTarget colorBuf) { - if (i < 0 || i >= colorBufs.size()) { - throw new IndexOutOfBoundsException("No color target exists to replace at index=" + i); - } - colorBuf.slot = i; - colorBufs.set(i, colorBuf); - } - - /** - * Removes the color target at the index. - *

- * Color targets above the removed target will have their - * slot indices shifted accordingly. - * - * @param i - */ - public void removeColorTarget(int i) { - colorBufs.remove(i); - for (; i < colorBufs.size(); i++) { - colorBufs.get(i).slot = i; - } - } - - /** - * Adds a texture to one of the color Buffers Array. It uses {@link TextureCubeMap} ordinal number for the - * position in the color buffer ArrayList. - * - * @param colorBuf texture to add to the color Buffer - * @param face position to add to the color buffer - */ - public void addColorTarget(FrameBufferTextureTarget colorBuf, TextureCubeMap.Face face) { - // checkSetTexture(colorBuf.getTexture(), false); // TODO: this won't work for levels. - colorBuf.slot = colorBufs.size(); - colorBuf.face = face.ordinal(); - colorBufs.add(colorBuf); - } - - public void setDepthTarget(FrameBufferBufferTarget depthBuf){ - if (!depthBuf.getFormat().isDepthFormat()) - throw new IllegalArgumentException("Depth buffer format must be depth."); - this.depthBuf = depthBuf; - this.depthBuf.slot = this.depthBuf.getFormat().isDepthStencilFormat() ? SLOT_DEPTH_STENCIL : SLOT_DEPTH; - } - - public void setDepthTarget(FrameBufferTextureTarget depthBuf){ - checkSetTexture(depthBuf.getTexture(), true); - this.depthBuf = depthBuf; - this.depthBuf.slot = depthBuf.getTexture().getImage().getFormat().isDepthStencilFormat() ? SLOT_DEPTH_STENCIL : SLOT_DEPTH; - } - - public int getNumColorTargets(){ - return colorBufs.size(); - } - - public RenderBuffer getColorTarget(int index){ - return colorBufs.get(index); - } - - public RenderBuffer getColorTarget() { - if (colorBufs.isEmpty()) - return null; - if (colorBufIndex<0 || colorBufIndex>=colorBufs.size()) { - return colorBufs.get(0); - } - return colorBufs.get(colorBufIndex); - } - - public RenderBuffer getDepthTarget() { - return depthBuf; - } - - - - /** - *

- * Creates a new FrameBuffer with the given width, height, and number - * of samples. If any textures are attached to this FrameBuffer, then - * they must have the same number of samples as given in this constructor. - *

- * Note that if the {@link Renderer} does not expose the - * {@link Caps#NonPowerOfTwoTextures}, then an exception will be thrown - * if the width and height arguments are not power of two. - * - * @param width The width to use - * @param height The height to use - * @param samples The number of samples to use for a multisampled - * framebuffer, or 1 if the framebuffer should be single-sampled. - * - * @throws IllegalArgumentException If width or height are not positive. - */ - public FrameBuffer(int width, int height, int samples) { - super(); - if (width <= 0 || height <= 0) { - throw new IllegalArgumentException("FrameBuffer must have valid size."); - } - - this.width = width; - this.height = height; - this.samples = samples == 0 ? 1 : samples; - } - - protected FrameBuffer(FrameBuffer src) { - super(src.id); - /* - for (RenderBuffer renderBuf : src.colorBufs){ - RenderBuffer clone = renderBuf.createDestructableClone(); - if (clone != null) - this.colorBufs.add(clone); - } - - this.depthBuf = src.depthBuf.createDestructableClone(); - */ - } - - /** - * Enables the use of a depth buffer for this FrameBuffer. - * - * @param format The format to use for the depth buffer. - * @throws IllegalArgumentException If format is not a depth format. - * @deprecated Use - * {@link #setDepthTarget(com.jme3.texture.FrameBuffer.FrameBufferBufferTarget)} - */ - @Deprecated - public void setDepthBuffer(Image.Format format) { - if (id != -1) { - throw new UnsupportedOperationException("FrameBuffer already initialized."); - } - - if (!format.isDepthFormat()) { - throw new IllegalArgumentException("Depth buffer format must be depth."); - } - - depthBuf = new RenderBuffer(); - depthBuf.slot = format.isDepthStencilFormat() ? SLOT_DEPTH_STENCIL : SLOT_DEPTH; - depthBuf.format = format; - } - - /** - * Enables the use of a color buffer for this FrameBuffer. - * - * @param format The format to use for the color buffer. - * @throws IllegalArgumentException If format is not a color format. - * @deprecated Use addColorTarget - */ - @Deprecated - public void setColorBuffer(Image.Format format) { - if (id != -1) { - throw new UnsupportedOperationException("FrameBuffer already initialized."); - } - - if (format.isDepthFormat()) { - throw new IllegalArgumentException("Color buffer format must be color/luminance."); - } - - RenderBuffer colorBuf = new RenderBuffer(); - colorBuf.slot = 0; - colorBuf.format = format; - - colorBufs.clear(); - colorBufs.add(colorBuf); - } - - private void checkSetTexture(Texture tex, boolean depth) { - Image img = tex.getImage(); - if (img == null) { - throw new IllegalArgumentException("Texture not initialized with RTT."); - } - - if (depth && !img.getFormat().isDepthFormat()) { - throw new IllegalArgumentException("Texture image format must be depth."); - } else if (!depth && img.getFormat().isDepthFormat()) { - throw new IllegalArgumentException("Texture image format must be color/luminance."); - } - - // check that resolution matches texture resolution - if (width != img.getWidth() || height != img.getHeight()) { - throw new IllegalArgumentException("Texture image resolution " - + "must match FB resolution"); - } - - if (samples != tex.getImage().getMultiSamples()) { - throw new IllegalStateException("Texture samples must match framebuffer samples"); - } - } - - /** - * If enabled, any shaders rendering into this FrameBuffer - * will be able to write several results into the renderbuffers - * by using the gl_FragData array. Every slot in that - * array maps into a color buffer attached to this framebuffer. - * - * @param enabled True to enable MRT (multiple rendering targets). - */ - public void setMultiTarget(boolean enabled) { - if (enabled) { - colorBufIndex = -1; - } else { - colorBufIndex = 0; - } - } - - /** - * @return True if MRT (multiple rendering targets) is enabled. - * @see FrameBuffer#setMultiTarget(boolean) - */ - public boolean isMultiTarget() { - return colorBufIndex == -1; - } - - /** - * If MRT is not enabled ({@link FrameBuffer#setMultiTarget(boolean) } is false) - * then this specifies the color target to which the scene should be rendered. - *

- * By default the value is 0. - * - * @param index The color attachment index. - * @throws IllegalArgumentException If index is negative or doesn't map - * to any attachment on this framebuffer. - */ - public void setTargetIndex(int index) { - if (index < 0 || index >= 16) { - throw new IllegalArgumentException("Target index must be between 0 and 16"); - } - - if (colorBufs.size() < index) { - throw new IllegalArgumentException("The target at " + index + " is not set!"); - } - - colorBufIndex = index; - setUpdateNeeded(); - } - - /** - * @return The color target to which the scene should be rendered. - * - * @see FrameBuffer#setTargetIndex(int) - */ - public int getTargetIndex() { - return colorBufIndex; - } - - /** - * Set the color texture to use for this framebuffer. - * This automatically clears all existing textures added previously - * with {@link FrameBuffer#addColorTexture } and adds this texture as the - * only target. - * - * @param tex The color texture to set. - * @deprecated Use addColorTarget - */ - @Deprecated - public void setColorTexture(Texture2D tex) { - clearColorTargets(); - addColorTexture(tex); - } - - /** - * Set the color texture array to use for this framebuffer. - * This automatically clears all existing textures added previously - * with {@link FrameBuffer#addColorTexture } and adds this texture as the - * only target. - * - * @param tex The color texture array to set. - * @param layer (default=-1) - * @deprecated Use addColorTarget - */ - @Deprecated - public void setColorTexture(TextureArray tex, int layer) { - clearColorTargets(); - addColorTexture(tex, layer); - } - - /** - * Set the color texture to use for this framebuffer. - * This automatically clears all existing textures added previously - * with {@link FrameBuffer#addColorTexture } and adds this texture as the - * only target. - * - * @param tex The cube-map texture to set. - * @param face The face of the cube-map to render to. - * @deprecated Use addColorTarget - */ - @Deprecated - public void setColorTexture(TextureCubeMap tex, TextureCubeMap.Face face) { - clearColorTargets(); - addColorTexture(tex, face); - } - - /** - * Clears all color targets that were set or added previously. - */ - public void clearColorTargets() { - colorBufs.clear(); - } - - /** - * Add a color buffer without a texture bound to it. - * If MRT is enabled, then each subsequently added texture or buffer can be - * rendered to through a shader that writes to the array gl_FragData. - * If MRT is not enabled, then the index set with {@link FrameBuffer#setTargetIndex(int) } - * is rendered to by the shader. - * - * @param format the format of the color buffer - * @see #addColorTexture(com.jme3.texture.Texture2D) - * @deprecated Use addColorTarget - */ - @Deprecated - public void addColorBuffer(Image.Format format) { - if (id != -1) { - throw new UnsupportedOperationException("FrameBuffer already initialized."); - } - - if (format.isDepthFormat()) { - throw new IllegalArgumentException("Color buffer format must be color/luminance."); - } - - RenderBuffer colorBuf = new RenderBuffer(); - colorBuf.slot = colorBufs.size(); - colorBuf.format = format; - - colorBufs.add(colorBuf); - } - - /** - * Add a color texture to use for this framebuffer. - * If MRT is enabled, then each subsequently added texture can be - * rendered to through a shader that writes to the array gl_FragData. - * If MRT is not enabled, then the index set with {@link FrameBuffer#setTargetIndex(int) } - * is rendered to by the shader. - * - * @param tex The texture to add. - * @see #addColorBuffer(com.jme3.texture.Image.Format) - * @deprecated Use addColorTarget - */ - @Deprecated - public void addColorTexture(Texture2D tex) { - if (id != -1) { - throw new UnsupportedOperationException("FrameBuffer already initialized."); - } - - Image img = tex.getImage(); - checkSetTexture(tex, false); - - RenderBuffer colorBuf = new RenderBuffer(); - colorBuf.slot = colorBufs.size(); - colorBuf.tex = tex; - colorBuf.format = img.getFormat(); - - colorBufs.add(colorBuf); - } - - /** - * Add a color texture array to use for this framebuffer. - * If MRT is enabled, then each subsequently added texture can be - * rendered to through a shader that writes to the array gl_FragData. - * If MRT is not enabled, then the index set with {@link FrameBuffer#setTargetIndex(int) } - * is rendered to by the shader. - * - * @param tex The texture array to add. - * @param layer (default=-1) - * @deprecated Use addColorTarget - */ - @Deprecated - public void addColorTexture(TextureArray tex, int layer) { - if (id != -1) { - throw new UnsupportedOperationException("FrameBuffer already initialized."); - } - - Image img = tex.getImage(); - checkSetTexture(tex, false); - - RenderBuffer colorBuf = new RenderBuffer(); - colorBuf.slot = colorBufs.size(); - colorBuf.tex = tex; - colorBuf.format = img.getFormat(); - colorBuf.layer = layer; - - colorBufs.add(colorBuf); - } - - /** - * Add a color texture to use for this framebuffer. - * If MRT is enabled, then each subsequently added texture can be - * rendered to through a shader that writes to the array gl_FragData. - * If MRT is not enabled, then the index set with {@link FrameBuffer#setTargetIndex(int) } - * is rendered to by the shader. - * - * @param tex The cube-map texture to add. - * @param face The face of the cube-map to render to. - * @deprecated Use addColorTarget - */ - @Deprecated - public void addColorTexture(TextureCubeMap tex, TextureCubeMap.Face face) { - if (id != -1) { - throw new UnsupportedOperationException("FrameBuffer already initialized."); - } - - Image img = tex.getImage(); - checkSetTexture(tex, false); - - RenderBuffer colorBuf = new RenderBuffer(); - colorBuf.slot = colorBufs.size(); - colorBuf.tex = tex; - colorBuf.format = img.getFormat(); - colorBuf.face = face.ordinal(); - - colorBufs.add(colorBuf); - } - - /** - * Set the depth texture to use for this framebuffer. - * - * @param tex The color texture to set. - * @deprecated Use - * {@link #setDepthTarget(com.jme3.texture.FrameBuffer.FrameBufferTextureTarget)} - */ - @Deprecated - public void setDepthTexture(Texture2D tex) { - if (id != -1) { - throw new UnsupportedOperationException("FrameBuffer already initialized."); - } - - Image img = tex.getImage(); - checkSetTexture(tex, true); - - depthBuf = new RenderBuffer(); - depthBuf.slot = img.getFormat().isDepthStencilFormat() ? SLOT_DEPTH_STENCIL : SLOT_DEPTH; - depthBuf.tex = tex; - depthBuf.format = img.getFormat(); - } - - /** - * - * @param tex the TextureArray to apply - * @param layer (default=-1) - * @deprecated Use - * {@link #setDepthTarget(com.jme3.texture.FrameBuffer.FrameBufferTextureTarget)} - */ - @Deprecated - public void setDepthTexture(TextureArray tex, int layer) { - if (id != -1) { - throw new UnsupportedOperationException("FrameBuffer already initialized."); - } - - Image img = tex.getImage(); - checkSetTexture(tex, true); - - depthBuf = new RenderBuffer(); - depthBuf.slot = img.getFormat().isDepthStencilFormat() ? SLOT_DEPTH_STENCIL : SLOT_DEPTH; - depthBuf.tex = tex; - depthBuf.format = img.getFormat(); - depthBuf.layer = layer; - } - - /** - * @return The number of color buffers attached to this texture. - * @deprecated Use getNumColorTargets - */ - @Deprecated - public int getNumColorBuffers() { - return colorBufs.size(); - } - - /** - * @param index the zero-base index (≥0) - * @return The color buffer at the given index. - * @deprecated Use getColorTarget(int) - */ - @Deprecated - public RenderBuffer getColorBuffer(int index) { - return colorBufs.get(index); - } - - /** - * @return The color buffer with the index set by {@link #setTargetIndex(int)}, or null - * if no color buffers are attached. - * If MRT is disabled, the first color buffer is returned. - * @deprecated Use getColorTarget() - */ - @Deprecated - public RenderBuffer getColorBuffer() { - if (colorBufs.isEmpty()) { - return null; - } - if (colorBufIndex < 0 || colorBufIndex >= colorBufs.size()) { - return colorBufs.get(0); - } - return colorBufs.get(colorBufIndex); - } - - /** - * @return The depth buffer attached to this FrameBuffer, or null - * if no depth buffer is attached - * @deprecated Use getDepthTarget() - */ - @Deprecated - public RenderBuffer getDepthBuffer() { - return depthBuf; - } - - /** - * @return The height in pixels of this framebuffer. - */ - public int getHeight() { - return height; - } - - /** - * @return The width in pixels of this framebuffer. - */ - public int getWidth() { - return width; - } - - /** - * @return The number of samples when using a multisample framebuffer, or - * 1 if this is a single-sampled framebuffer. - */ - public int getSamples() { - return samples; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - String mrtStr = colorBufIndex >= 0 ? "" + colorBufIndex : "mrt"; - sb.append("FrameBuffer[format=").append(width).append("x").append(height) - .append("x").append(samples).append(", drawBuf=").append(mrtStr).append("]\n"); - if (depthBuf != null) { - sb.append("Depth => ").append(depthBuf).append("\n"); - } - for (RenderBuffer colorBuf : colorBufs) { - sb.append("Color(").append(colorBuf.slot) - .append(") => ").append(colorBuf).append("\n"); - } - return sb.toString(); - } - - @Override - public void resetObject() { - this.id = -1; - - for (int i = 0; i < colorBufs.size(); i++) { - colorBufs.get(i).resetObject(); - } - - if (depthBuf != null) { - depthBuf.resetObject(); - } - - setUpdateNeeded(); - } - - @Override - public void deleteObject(Object rendererObject) { - ((Renderer) rendererObject).deleteFrameBuffer(this); - } - - @Override - public NativeObject createDestructableClone() { - return new FrameBuffer(this); - } - - @Override - public long getUniqueId() { - return ((long) OBJTYPE_FRAMEBUFFER << 32) | (0xffffffffL & (long) id); - } - - /** - * Specifies that the color values stored in this framebuffer are in SRGB - * format. - * - * The FrameBuffer must have an SRGB texture attached. - * - * The Renderer must expose the {@link Caps#Srgb sRGB pipeline} capability - * for this option to take any effect. - * - * Rendering operations performed on this framebuffer shall undergo a linear - * -> sRGB color space conversion when this flag is enabled. If - * {@link com.jme3.material.RenderState#getBlendMode() blending} is enabled, it will be - * performed in linear space by first decoding the stored sRGB pixel values - * into linear, combining with the shader result, and then converted back to - * sRGB upon being written into the framebuffer. - * - * @param srgb If the framebuffer color values should be stored in sRGB - * color space. - * - * @throws IllegalStateException If the texture attached to this framebuffer - * is not sRGB. - */ - public void setSrgb(boolean srgb) { - this.srgb = srgb; - } - - /** - * Determines if this framebuffer contains SRGB data. - * - * @return True if the framebuffer color values are in SRGB space, false if - * in linear space. - */ - public boolean isSrgb() { - return srgb; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - /** - * Hints the renderer to generate mipmaps for this framebuffer if necessary - * @param v true to enable, null to use the default value for the renderer (default to null) - */ - public void setMipMapsGenerationHint(Boolean v) { - mipMapsGenerationHint = v; - } - - public Boolean getMipMapsGenerationHint() { - return mipMapsGenerationHint; - } -} diff --git a/jme3-core/src/main/java/com/jme3/texture/GlFrameBuffer.java b/jme3-core/src/main/java/com/jme3/texture/GlFrameBuffer.java new file mode 100644 index 0000000000..dfe2135c08 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/texture/GlFrameBuffer.java @@ -0,0 +1,650 @@ +/* + * Copyright (c) 2009-2023 jMonkeyEngine + * 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 'jMonkeyEngine' 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 com.jme3.texture; + +import com.jme3.renderer.Caps; +import com.jme3.renderer.Renderer; +import com.jme3.texture.GlImage.Format; +import com.jme3.util.NativeObject; +import com.jme3.util.natives.GlNative; +import com.jme3.vulkan.pipeline.framebuffer.FrameBuffer; +import com.jme3.vulkan.util.IntEnum; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + *

+ * FrameBuffers are rendering surfaces allowing + * off-screen rendering and render-to-texture functionality. + * Instead of the scene rendering to the screen, it is rendered into the + * FrameBuffer, the result can be either a texture or a buffer. + *

+ * A FrameBuffer supports two methods of rendering, + * using a {@link GlTexture} or using a buffer. + * When using a texture, the result of the rendering will be rendered + * onto the texture, after which the texture can be placed on an object + * and rendered as if the texture was uploaded from disk. + * When using a buffer, the result is rendered onto + * a buffer located on the GPU, the data of this buffer is not accessible + * to the user. buffers are useful if one + * wishes to retrieve only the color content of the scene, but still desires + * depth testing (which requires a depth buffer). + * Buffers can be copied to other framebuffers + * including the main screen, by using + * {@link Renderer#copyFrameBuffer(GlFrameBuffer, GlFrameBuffer, boolean, boolean)}. + * The content of a {@link RenderBuffer} can be retrieved by using + * {@link Renderer#readFrameBuffer(GlFrameBuffer, java.nio.ByteBuffer) }. + *

+ * FrameBuffers have several attachment points, there are + * several color attachment points and a single depth + * attachment point. + * The color attachment points support image formats such as + * {@link Format#RGBA8}, allowing rendering the color content of the scene. + * The depth attachment point requires a depth image format. + * + * @see Renderer#setFrameBuffer(GlFrameBuffer) + * + * @author Kirill Vainer + */ +public class GlFrameBuffer extends GlNative implements FrameBuffer { + + public static final int SLOT_UNDEF = -1; + public static final int SLOT_DEPTH = -100; + public static final int SLOT_DEPTH_STENCIL = -101; + + private int width = 0; + private int height = 0; + private int samples = 1; + private final ArrayList colorBufs = new ArrayList<>(); + private RenderBuffer depthBuf = null; + private int colorBufIndex = 0; + private boolean srgb; + private String name; + private Boolean mipMapsGenerationHint = null; + + /** + * RenderBuffer represents either a texture or a + * buffer that will be rendered to. RenderBuffers + * are attached to an attachment slot on a FrameBuffer. + */ + public static abstract class RenderBuffer implements ImageView { + + GlTexture tex; + GlImage.Format format; + int id = -1; + int slot = SLOT_UNDEF; + int face = -1; + int layer = -1; + int level = 0; + + + public int getLevel() { + return this.level; + } + + /** + * @return The image format of the render buffer. + */ + public Format getFormat() { + return format; + } + + /** + * @return The texture to render to for this RenderBuffer + * or null if content should be rendered into a buffer. + */ + public GlTexture getTexture() { + return tex; + } + + /** + * Do not use. + * @return the render buffer's ID + */ + public int getRenderBufferId() { + return id; + } + + /** + * Do not use. + * + * @param id the desired render buffer ID + */ + public void setRenderBufferId(int id) { + this.id = id; + } + + /** + * Do not use. + * + * @return the slot code, such as SLOT_DEPTH_STENCIL + */ + public int getSlot() { + return slot; + } + + public int getFace() { + return face; + } + + public void resetObject() { + id = -1; + } + + protected abstract void verifyTextureProperties(int width, int height, int samples, boolean depth); + + @Override + public String toString() { + if (tex != null) { + return "TextureTarget[format=" + format + "]"; + } else { + return "BufferTarget[format=" + format + "]"; + } + } + + public int getLayer() { + return this.layer; + } + + @Override + public long getId() { + return tex.getId(); + } + + @Override + public GlImage getImage() { + return tex.getImage(); + } + + @Override + public IntEnum getViewType() { + return tex.getViewType(); + } + + @Override + public int getBaseMipmap() { + return tex.getBaseMipmap(); + } + + @Override + public int getMipmapCount() { + return tex.getMipmapCount(); + } + + @Override + public int getBaseLayer() { + return tex.getBaseLayer(); + } + + @Override + public int getLayerCount() { + return tex.getLayerCount(); + } + + } + + public static class FrameBufferTextureTarget extends RenderBuffer { + + private FrameBufferTextureTarget() {} + + void setTexture(GlTexture tx){ + this.tex=tx; + this.format=tx.getImage().getGlFormat(); + } + + void setFormat(Format f) { + this.format=f; + } + + public FrameBufferTextureTarget layer(int i){ + this.layer=i; + return this; + } + + public FrameBufferTextureTarget level(int i){ + this.level=i; + return this; + } + + public FrameBufferTextureTarget face(TextureCubeMap.Face f){ + return face(f.ordinal()); + } + + public FrameBufferTextureTarget face(int f){ + this.face=f; + return this; + } + + @Override + protected void verifyTextureProperties(int width, int height, int samples, boolean depth) { + GlImage img = tex.getImage(); + if (img == null) { + throw new IllegalArgumentException("Texture not initialized with RTT."); + } + if (depth && !img.getGlFormat().isDepthFormat()) { + throw new IllegalArgumentException("Texture image format must be depth."); + } else if (!depth && img.getGlFormat().isDepthFormat()) { + throw new IllegalArgumentException("Texture image format must be color/luminance."); + } + if (width != img.getWidth() || height != img.getHeight()) { + throw new IllegalArgumentException("Texture image resolution " + + "must match FB resolution"); + } + if (samples != tex.getImage().getMultiSamples()) { + throw new IllegalStateException("Texture samples must match framebuffer samples"); + } + } + + } + + public static class FrameBufferBufferTarget extends RenderBuffer { + private FrameBufferBufferTarget(){} + void setFormat(Format f){ + this.format=f; + } + + @Override + protected void verifyTextureProperties(int width, int height, int samples, boolean depth) { + if (!getFormat().isDepthFormat()) { + throw new IllegalArgumentException("Depth buffer format must be depth."); + } + } + + } + + /** + * Creates FrameBuffer render targets. + * + * @deprecated use static methods under {@link GlFrameBuffer} directly + */ + @Deprecated + public static class FrameBufferTarget { + private FrameBufferTarget(){} + public static FrameBufferTextureTarget newTarget(GlTexture tx){ + FrameBufferTextureTarget t=new FrameBufferTextureTarget(); + t.setTexture(tx); + return t; + } + + public static FrameBufferBufferTarget newTarget(Format format){ + FrameBufferBufferTarget t=new FrameBufferBufferTarget(); + t.setFormat(format); + return t; + } + + /** + * Creates a frame buffer texture and sets the face position by using the face parameter. It uses + * {@link TextureCubeMap} ordinal number for the face position. + * + * @param tx texture to add to the frame buffer + * @param face face to add to the color buffer to + * @return FrameBufferTexture Target + */ + public static FrameBufferTextureTarget newTarget(GlTexture tx, TextureCubeMap.Face face) { + FrameBufferTextureTarget t = new FrameBufferTextureTarget(); + t.face = face.ordinal(); + t.setTexture(tx); + return t; + } + } + + public static FrameBufferTextureTarget newTarget(GlTexture tx) { + FrameBufferTextureTarget t = new FrameBufferTextureTarget(); + t.setTexture(tx); + return t; + } + + public static FrameBufferTextureTarget newTarget(GlTexture tx, TextureCubeMap.Face face) { + FrameBufferTextureTarget t = new FrameBufferTextureTarget(); + t.face = face.ordinal(); + t.setTexture(tx); + return t; + } + + public static FrameBufferBufferTarget newTarget(Format format) { + FrameBufferBufferTarget t = new FrameBufferBufferTarget(); + t.setFormat(format); + return t; + } + + /** + * A private constructor to inhibit instantiation of this class. + */ + private GlFrameBuffer() { + } + + @Override + public void addColorTarget(RenderBuffer image) { + image.verifyTextureProperties(width, height, samples, false); + image.slot = colorBufs.size(); + colorBufs.add(image); + setUpdateNeeded(); + } + + @Override + public void setColorTarget(int i, RenderBuffer image) { + image.verifyTextureProperties(width, height, samples, false); + image.slot = i; + if (colorBufs.set(i, image) != image) { + setUpdateNeeded(); + } + } + + /** + * Removes the color target at the index. + *

+ * Color targets above the removed target will have their + * slot indices shifted accordingly. + * + * @param i index of the target to remove + */ + @Override + public void removeColorTarget(int i) { + if (i < colorBufs.size()) { + colorBufs.remove(i); + for (; i < colorBufs.size(); i++) { + colorBufs.get(i).slot = i; + } + setUpdateNeeded(); + } + } + + @Override + public void removeColorTarget(RenderBuffer image) { + if (colorBufs.remove(image)) { + for (int i = 0; i < colorBufs.size(); i++) { + colorBufs.get(i).slot = i; + } + setUpdateNeeded(); + } + } + + @Override + public void setDepthTarget(RenderBuffer image) { + image.verifyTextureProperties(width, height, samples, true); + this.depthBuf = image; + this.depthBuf.slot = this.depthBuf.getFormat().isDepthStencilFormat() ? SLOT_DEPTH_STENCIL : SLOT_DEPTH; + } + + @Override + public List getColorTargets() { + return Collections.unmodifiableList(colorBufs); + } + + /** + * Adds a texture to one of the color Buffers Array. It uses {@link TextureCubeMap} ordinal number for the + * position in the color buffer ArrayList. + * + * @param colorBuf texture to add to the color Buffer + * @param face position to add to the color buffer + * @deprecated use {@link FrameBufferTextureTarget#face(TextureCubeMap.Face)} instead + */ + @Deprecated + public void addColorTarget(RenderBuffer colorBuf, TextureCubeMap.Face face) { + colorBuf.face = face.ordinal(); + addColorTarget(colorBuf); + } + + public int getNumColorTargets(){ + return colorBufs.size(); + } + + @Override + public RenderBuffer getColorTarget(int index){ + return colorBufs.get(index); + } + + public RenderBuffer getColorTarget() { + if (colorBufs.isEmpty()) + return null; + if (colorBufIndex<0 || colorBufIndex>=colorBufs.size()) { + return colorBufs.get(0); + } + return colorBufs.get(colorBufIndex); + } + + @Override + public RenderBuffer getDepthTarget() { + return depthBuf; + } + + + + /** + *

+ * Creates a new FrameBuffer with the given width, height, and number + * of samples. If any textures are attached to this FrameBuffer, then + * they must have the same number of samples as given in this constructor. + *

+ * Note that if the {@link Renderer} does not expose the + * {@link Caps#NonPowerOfTwoTextures}, then an exception will be thrown + * if the width and height arguments are not power of two. + * + * @param width The width to use + * @param height The height to use + * @param samples The number of samples to use for a multisampled + * framebuffer, or 1 if the framebuffer should be single-sampled. + * + * @throws IllegalArgumentException If width or height are not positive. + */ + public GlFrameBuffer(int width, int height, int samples) { + super(); + if (width <= 0 || height <= 0) { + throw new IllegalArgumentException("FrameBuffer must have valid size."); + } + + this.width = width; + this.height = height; + this.samples = samples == 0 ? 1 : samples; + } + + protected GlFrameBuffer(GlFrameBuffer src) { + super(src.object); + } + + /** + * If enabled, any shaders rendering into this FrameBuffer + * will be able to write several results into the renderbuffers + * by using the gl_FragData array. Every slot in that + * array maps into a color buffer attached to this framebuffer. + * + * @param enabled True to enable MRT (multiple rendering targets). + */ + public void setMultiTarget(boolean enabled) { + if (enabled) { + colorBufIndex = -1; + } else { + colorBufIndex = 0; + } + } + + /** + * @return True if MRT (multiple rendering targets) is enabled. + * @see GlFrameBuffer#setMultiTarget(boolean) + */ + public boolean isMultiTarget() { + return colorBufIndex == -1; + } + + /** + * If MRT is not enabled ({@link GlFrameBuffer#setMultiTarget(boolean) } is false) + * then this specifies the color target to which the scene should be rendered. + *

+ * By default the value is 0. + * + * @param index The color attachment index. + * @throws IllegalArgumentException If index is negative or doesn't map + * to any attachment on this framebuffer. + */ + public void setTargetIndex(int index) { + if (index < 0 || index >= 16) { + throw new IllegalArgumentException("Target index must be between 0 and 16"); + } + + if (colorBufs.size() < index) { + throw new IllegalArgumentException("The target at " + index + " is not set!"); + } + + colorBufIndex = index; + setUpdateNeeded(); + } + + /** + * @return The color target to which the scene should be rendered. + * + * @see GlFrameBuffer#setTargetIndex(int) + */ + public int getTargetIndex() { + return colorBufIndex; + } + + /** + * Clears all color targets that were set or added previously. + */ + @Override + public void clearColorTargets() { + colorBufs.clear(); + } + + /** + * @return The height in pixels of this framebuffer. + */ + public int getHeight() { + return height; + } + + /** + * @return The width in pixels of this framebuffer. + */ + public int getWidth() { + return width; + } + + /** + * @return The number of samples when using a multisample framebuffer, or + * 1 if this is a single-sampled framebuffer. + */ + public int getSamples() { + return samples; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + String mrtStr = colorBufIndex >= 0 ? "" + colorBufIndex : "mrt"; + sb.append("FrameBuffer[format=").append(width).append("x").append(height) + .append("x").append(samples).append(", drawBuf=").append(mrtStr).append("]\n"); + if (depthBuf != null) { + sb.append("Depth => ").append(depthBuf).append("\n"); + } + for (RenderBuffer colorBuf : colorBufs) { + sb.append("Color(").append(colorBuf.slot) + .append(") => ").append(colorBuf).append("\n"); + } + return sb.toString(); + } + + @Override + public Runnable createNativeDestroyer() { + return () -> renderer.deleteFrameBuffer(new GlFrameBuffer(this)); + } + + @Override + public void resetObject() { + this.object = -1; + for (int i = 0; i < colorBufs.size(); i++) { + colorBufs.get(i).resetObject(); + } + if (depthBuf != null) { + depthBuf.resetObject(); + } + setUpdateNeeded(); + } + + /** + * Specifies that the color values stored in this framebuffer are in SRGB + * format. + * + * The FrameBuffer must have an SRGB texture attached. + * + * The Renderer must expose the {@link Caps#Srgb sRGB pipeline} capability + * for this option to take any effect. + * + * Rendering operations performed on this framebuffer shall undergo a linear + * -> sRGB color space conversion when this flag is enabled. If + * {@link com.jme3.material.RenderState#getBlendMode() blending} is enabled, it will be + * performed in linear space by first decoding the stored sRGB pixel values + * into linear, combining with the shader result, and then converted back to + * sRGB upon being written into the framebuffer. + * + * @param srgb If the framebuffer color values should be stored in sRGB + * color space. + * + * @throws IllegalStateException If the texture attached to this framebuffer + * is not sRGB. + */ + public void setSrgb(boolean srgb) { + this.srgb = srgb; + } + + /** + * Determines if this framebuffer contains SRGB data. + * + * @return True if the framebuffer color values are in SRGB space, false if + * in linear space. + */ + public boolean isSrgb() { + return srgb; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + /** + * Hints the renderer to generate mipmaps for this framebuffer if necessary + * @param v true to enable, null to use the default value for the renderer (default to null) + */ + public void setMipMapsGenerationHint(Boolean v) { + mipMapsGenerationHint = v; + } + + public Boolean getMipMapsGenerationHint() { + return mipMapsGenerationHint; + } +} diff --git a/jme3-core/src/main/java/com/jme3/texture/Image.java b/jme3-core/src/main/java/com/jme3/texture/GlImage.java similarity index 88% rename from jme3-core/src/main/java/com/jme3/texture/Image.java rename to jme3-core/src/main/java/com/jme3/texture/GlImage.java index 3159f0856a..d4327bd925 100644 --- a/jme3-core/src/main/java/com/jme3/texture/Image.java +++ b/jme3-core/src/main/java/com/jme3/texture/GlImage.java @@ -1,1308 +1,1311 @@ -/* - * Copyright (c) 2009-2024 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.texture; - -import com.jme3.export.InputCapsule; -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.OutputCapsule; -import com.jme3.export.Savable; -import com.jme3.math.FastMath; -import com.jme3.renderer.Caps; -import com.jme3.renderer.Renderer; -import com.jme3.texture.image.ColorSpace; -import com.jme3.texture.image.LastTextureState; -import com.jme3.util.BufferUtils; -import com.jme3.util.NativeObject; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Image defines a data format for a graphical image. The image - * is defined by a format, a height and width, and the image data. The width and - * height must be greater than 0. The data is contained in a byte buffer, and - * should be packed before creation of the image object. - * - * @author Mark Powell - * @author Joshua Slack - * @version $Id: Image.java 4131 2009-03-19 20:15:28Z blaine.dev $ - */ -public class Image extends NativeObject implements Savable /*, Cloneable*/ { - - public enum Format { - /** - * 8-bit alpha - */ - Alpha8(8), - - @Deprecated - Reserved1(0), - - /** - * 8-bit grayscale/luminance. - */ - Luminance8(8), - - @Deprecated - Reserved2(0), - - /** - * half-precision floating-point grayscale/luminance. - * - * Requires {@link Caps#FloatTexture}. - */ - Luminance16F(16,true), - - /** - * single-precision floating-point grayscale/luminance. - * - * Requires {@link Caps#FloatTexture}. - */ - Luminance32F(32,true), - - /** - * 8-bit luminance/grayscale and 8-bit alpha. - */ - Luminance8Alpha8(16), - - @Deprecated - Reserved3(0), - - /** - * half-precision floating-point grayscale/luminance and alpha. - * - * Requires {@link Caps#FloatTexture}. - */ - Luminance16FAlpha16F(32,true), - - @Deprecated - Reserved4(0), - - @Deprecated - Reserved5(0), - - /** - * 8-bit blue, green, and red. - */ - BGR8(24), // BGR and ABGR formats are often used on Windows systems - - /** - * 8-bit red, green, and blue. - */ - RGB8(24), - - @Deprecated - Reserved6(0), - - @Deprecated - Reserved7(0), - - /** - * 5-bit red, 6-bit green, and 5-bit blue. - */ - RGB565(16), - - @Deprecated - Reserved8(0), - - /** - * 5-bit red, green, and blue with 1-bit alpha. - */ - RGB5A1(16), - - /** - * 8-bit red, green, blue, and alpha. - */ - RGBA8(32), - - /** - * 8-bit alpha, blue, green, and red. - */ - ABGR8(32), - - /** - * 8-bit alpha, red, blue and green - */ - ARGB8(32), - - /** - * 8-bit blue, green, red and alpha. - */ - BGRA8(32), - - @Deprecated - Reserved9(0), - - /** - * S3TC compression DXT1. - */ - DXT1(4,false,true, false), - - /** - * S3TC compression DXT1 with 1-bit alpha. - */ - DXT1A(4,false,true, false), - - /** - * S3TC compression DXT3 with 4-bit alpha. - */ - DXT3(8,false,true, false), - - /** - * S3TC compression DXT5 with interpolated 8-bit alpha. - * - */ - DXT5(8,false,true, false), - - RGTC2(8,false,true, false), - - SIGNED_RGTC2(8,false,true, false), - - RGTC1(4,false,true, false), - - SIGNED_RGTC1(4,false,true, false), - - /** - * BPTC compression BC6 signed float RGB - */ - BC6H_SF16(8, false, true, true), - - /** - * BPTC compression BC6 unsigned float RGB - */ - BC6H_UF16(8, false, true, true), - - /** - * BPTC compression BC7 RGBA - */ - BC7_UNORM(8, false, true, false), - - /** - * BPTC compression BC7 SRGB Alpha - */ - BC7_UNORM_SRGB(8, false, true, false), - - /** - * Luminance-Alpha Texture Compression. - * - * @deprecated Not supported by OpenGL 3.0. - */ - @Deprecated - Reserved10(0), - - /** - * Arbitrary depth format. The precision is chosen by the video - * hardware. - */ - Depth(0,true,false,false), - - /** - * 16-bit depth. - */ - Depth16(16,true,false,false), - - /** - * 24-bit depth. - */ - Depth24(24,true,false,false), - - /** - * 32-bit depth. - */ - Depth32(32,true,false,false), - - /** - * single-precision floating point depth. - * - * Requires {@link Caps#FloatDepthBuffer}. - */ - Depth32F(32,true,false,true), - - /** - * Texture data is stored as {@link Format#RGB16F} in system memory, - * but will be converted to {@link Format#RGB111110F} when sent - * to the video hardware. - * - * Requires {@link Caps#FloatTexture} and {@link Caps#PackedFloatTexture}. - */ - RGB16F_to_RGB111110F(48,true), - - /** - * unsigned floating-point red, green and blue that uses 32 bits. - * - * Requires {@link Caps#PackedFloatTexture}. - */ - RGB111110F(32,true), - - /** - * Texture data is stored as {@link Format#RGB16F} in system memory, - * but will be converted to {@link Format#RGB9E5} when sent - * to the video hardware. - * - * Requires {@link Caps#FloatTexture} and {@link Caps#SharedExponentTexture}. - */ - RGB16F_to_RGB9E5(48,true), - - /** - * 9-bit red, green and blue with 5-bit exponent. - * - * Requires {@link Caps#SharedExponentTexture}. - */ - RGB9E5(32,true), - - /** - * half-precision floating point red, green, and blue. - * - * Requires {@link Caps#FloatTexture}. - * May be supported for renderbuffers, but the OpenGL specification does not require it. - */ - RGB16F(48,true), - - /** - * half-precision floating point red, green, blue, and alpha. - * - * Requires {@link Caps#FloatTexture}. - */ - RGBA16F(64,true), - - /** - * single-precision floating point red, green, and blue. - * - * Requires {@link Caps#FloatTexture}. - * May be supported for renderbuffers, but the OpenGL specification does not require it. - */ - RGB32F(96,true), - - /** - * single-precision floating point red, green, blue and alpha. - * - * Requires {@link Caps#FloatTexture}. - */ - RGBA32F(128,true), - - @Deprecated - Reserved11(0), - - /** - * 24-bit depth with 8-bit stencil. - * Check the cap {@link Caps#PackedDepthStencilBuffer}. - */ - Depth24Stencil8(32, true, false, false), - - @Deprecated - Reserved12(0), - - /** - * Ericsson Texture Compression. Typically used on Android. - * - * Requires {@link Caps#TextureCompressionETC1}. - */ - ETC1(4, false, true, false), - - /** - * Ericsson Texture Compression 2. Typically used on Android. - * Same as {@link #ETC1} but with alpha. - * - * Requires {@link Caps#TextureCompressionETC2}. - */ - ETC2(8, false, true, false), - - /** - * Ericsson Texture Compression 2. Typically used on Android. - * Same as {@link #ETC2} but alpha can be either 1 or 0. - * - * Requires {@link Caps#TextureCompressionETC2}. - */ - ETC2_ALPHA1(4, false, true, false), - - - /** - * 8-bit signed int red. - * - * Requires {@link Caps#IntegerTexture}. - */ - R8I(8), - /** - * 8-bit unsigned int red. - * - * Requires {@link Caps#IntegerTexture}. - */ - R8UI(8), - /** - * 16-bit signed int red. - * - * Requires {@link Caps#IntegerTexture}. - */ - R16I(16), - /** - * 16-bit unsigned int red. - * - * Requires {@link Caps#IntegerTexture}. - */ - R16UI(16), - /** - * 32-bit signed int red. - * - * Requires {@link Caps#IntegerTexture}. - */ - R32I(32), - /** - * 32-bit unsigned int red. - * - * Requires {@link Caps#IntegerTexture}. - */ - R32UI(32), - - - /** - * 8-bit signed int red and green. - * - * Requires {@link Caps#IntegerTexture}. - */ - RG8I(16), - /** - * 8-bit unsigned int red and green. - * - * Requires {@link Caps#IntegerTexture}. - */ - RG8UI(16), - /** - * 16-bit signed int red and green. - * - * Requires {@link Caps#IntegerTexture}. - */ - RG16I(32), - /** - * 16-bit unsigned int red and green. - * - * Requires {@link Caps#IntegerTexture}. - */ - RG16UI(32), - /** - * 32-bit signed int red and green. - * - * Requires {@link Caps#IntegerTexture}. - */ - RG32I(64), - /** - * 32-bit unsigned int red and green. - * - * Requires {@link Caps#IntegerTexture}. - */ - RG32UI(64), - - /** - * 8-bit signed int red, green and blue. - * - * Requires {@link Caps#IntegerTexture} to be supported for textures. - * May be supported for renderbuffers, but the OpenGL specification does not require it. - */ - RGB8I(24), - /** - * 8 bit unsigned int red, green and blue. - * - * Requires {@link Caps#IntegerTexture} to be supported for textures. - * May be supported for renderbuffers, but the OpenGL specification does not require it. - */ - RGB8UI(24), - /** - * 16-bit signed int red, green and blue. - * - * Requires {@link Caps#IntegerTexture} to be supported for textures. - * May be supported for renderbuffers, but the OpenGL specification does not require it. - */ - RGB16I(48), - /** - * 16-bit unsigned int red, green and blue. - * - * Requires {@link Caps#IntegerTexture} to be supported for textures. - * May be supported for renderbuffers, but the OpenGL specification does not require it. - */ - RGB16UI(48), - /** - * 32-bit signed int red, green and blue. - * - * Requires {@link Caps#IntegerTexture} to be supported for textures. - * May be supported for renderbuffers, but the OpenGL specification does not require it. - */ - RGB32I(96), - /** - * 32-bit unsigned int red, green and blue. - * - * Requires {@link Caps#IntegerTexture} to be supported for textures. - * May be supported for renderbuffers, but the OpenGL specification does not require it. - */ - RGB32UI(96), - - - /** - * 8-bit signed int red, green, blue and alpha. - * - * Requires {@link Caps#IntegerTexture}. - */ - RGBA8I(32), - /** - * 8-bit unsigned int red, green, blue and alpha. - * - * Requires {@link Caps#IntegerTexture}. - */ - RGBA8UI(32), - /** - * 16-bit signed int red, green, blue and alpha. - * - * Requires {@link Caps#IntegerTexture}. - */ - RGBA16I(64), - /** - * 16-bit unsigned int red, green, blue and alpha. - * - * Requires {@link Caps#IntegerTexture}. - */ - RGBA16UI(64), - /** - * 32-bit signed int red, green, blue and alpha. - * - * Requires {@link Caps#IntegerTexture}. - */ - RGBA32I(128), - /** - * 32-bit unsigned int red, green, blue and alpha. - * - * Requires {@link Caps#IntegerTexture}. - */ - RGBA32UI(128), - - /** - * half-precision floating point red. - * - * Requires {@link Caps#FloatTexture}. - */ - R16F(16,true), - - /** - * single-precision floating point red. - * - * Requires {@link Caps#FloatTexture}. - */ - R32F(32,true), - - /** - * half-precision floating point red and green. - * - * Requires {@link Caps#FloatTexture}. - */ - RG16F(32,true), - - /** - * single-precision floating point red and green. - * - * Requires {@link Caps#FloatTexture}. - */ - RG32F(64,true), - - /** - * 10-bit red, green, and blue with 2-bit alpha. - */ - RGB10A2(32), - ; - - private int bpp; - private boolean isDepth; - private boolean isCompressed; - private boolean isFloatingPoint; - - private Format(int bpp){ - this.bpp = bpp; - } - - private Format(int bpp, boolean isFP){ - this(bpp); - this.isFloatingPoint = isFP; - } - - private Format(int bpp, boolean isDepth, boolean isCompressed, boolean isFP){ - this(bpp, isFP); - this.isDepth = isDepth; - this.isCompressed = isCompressed; - } - - /** - * @return bits per pixel. - */ - public int getBitsPerPixel(){ - return bpp; - } - - /** - * @return True if this format is a depth format, false otherwise. - */ - public boolean isDepthFormat(){ - return isDepth; - } - - /** - * @return True if this format is a depth + stencil (packed) format, false otherwise. - */ - boolean isDepthStencilFormat() { - return this == Depth24Stencil8; - } - - /** - * @return True if this is a compressed image format, false if - * uncompressed. - */ - public boolean isCompressed() { - return isCompressed; - } - - /** - * @return True if this image format is in floating point, - * false if it is an integer format. - */ - public boolean isFloatingPont(){ - return isFloatingPoint; - } - - - - } - - // image attributes - protected Format format; - protected int width, height, depth; - protected int[] mipMapSizes; - protected ArrayList data; - protected int multiSamples = 1; - protected ColorSpace colorSpace = null; -// protected int mipOffset = 0; - - // attributes relating to GL object - protected boolean mipsWereGenerated = false; - protected boolean needGeneratedMips = false; - protected LastTextureState lastTextureState = new LastTextureState(); - - /** - * Internal use only. - * The renderer stores the texture state set from the last texture, - * so it doesn't have to change it unless necessary. - * - * @return The image parameter state. - */ - public LastTextureState getLastTextureState() { - return lastTextureState; - } - - /** - * Internal use only. - * The renderer marks which images have generated mipmaps in VRAM - * and which do not, so it can generate them as needed. - * - * @param generated If mipmaps were generated or not. - */ - public void setMipmapsGenerated(boolean generated) { - this.mipsWereGenerated = generated; - } - - /** - * Internal use only. - * Check if the renderer has generated mipmaps for this image in VRAM - * or not. - * - * @return If mipmaps were generated already. - */ - public boolean isMipmapsGenerated() { - return mipsWereGenerated; - } - - /** - * (Package private) Called by {@link Texture} when - * {@link #isMipmapsGenerated() } is false in order to generate - * mipmaps for this image. - */ - void setNeedGeneratedMipmaps() { - needGeneratedMips = true; - } - - /** - * @return True if the image needs to have mipmaps generated - * for it (as requested by the texture). This stays true even - * after mipmaps have been generated. - */ - public boolean isGeneratedMipmapsRequired() { - return needGeneratedMips; - } - - /** - * Sets the update needed flag, while also checking if mipmaps - * need to be regenerated. - */ - @Override - public void setUpdateNeeded() { - super.setUpdateNeeded(); - if (isGeneratedMipmapsRequired() && !hasMipmaps()) { - // Mipmaps are no longer valid, since the image was changed. - setMipmapsGenerated(false); - } - } - - /** - * Determine if the image is NPOT. - * - * @return if the image is a non-power-of-2 image, e.g. having dimensions - * that are not powers of 2. - */ - public boolean isNPOT() { - return width != 0 && height != 0 - && (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height)); - } - - @Override - public void resetObject() { - this.id = -1; - this.mipsWereGenerated = false; - this.lastTextureState.reset(); - setUpdateNeeded(); - } - - @Override - protected void deleteNativeBuffers() { - for (ByteBuffer buf : data) { - BufferUtils.destroyDirectBuffer(buf); - } - } - - @Override - public void deleteObject(Object rendererObject) { - ((Renderer)rendererObject).deleteImage(this); - } - - @Override - public NativeObject createDestructableClone() { - return new Image(id); - } - - @Override - public long getUniqueId() { - return ((long)OBJTYPE_TEXTURE << 32) | (0xffffffffL & (long)id); - } - - /** - * @return A shallow clone of this image. The data is not cloned. - */ - @Override - public Image clone(){ - Image clone = (Image) super.clone(); - clone.mipMapSizes = mipMapSizes != null ? mipMapSizes.clone() : null; - clone.data = data != null ? new ArrayList(data) : null; - clone.lastTextureState = new LastTextureState(); - clone.setUpdateNeeded(); - return clone; - } - - /** - * Constructor instantiates a new Image object. All values - * are undefined. - */ - public Image() { - super(); - data = new ArrayList(1); - } - - protected Image(int id){ - super(id); - } - - /** - * Constructor instantiates a new Image object. The - * attributes of the image are defined during construction. - * - * @param format - * the data format of the image. - * @param width - * the width of the image. - * @param height - * the height of the image. - * @param depth - * the desired image depth - * @param data - * the image data. - * @param mipMapSizes - * the array of mipmap sizes, or null for no mipmaps. - * @param colorSpace - * the colorSpace of the image - */ - public Image(Format format, int width, int height, int depth, ArrayList data, - int[] mipMapSizes, ColorSpace colorSpace) { - - this(); - - if (mipMapSizes != null) { - if (mipMapSizes.length <= 1) { - mipMapSizes = null; - } else { - needGeneratedMips = false; - mipsWereGenerated = true; - } - } - - setFormat(format); - this.width = width; - this.height = height; - this.data = data; - this.depth = depth; - this.mipMapSizes = mipMapSizes; - this.colorSpace = colorSpace; - } - - /** - * @see #Image(com.jme3.texture.Image.Format, int, int, int, java.util.ArrayList, int[], com.jme3.texture.image.ColorSpace) - * @param format the desired data format - * @param width the desired width (in pixels) - * @param height the desired height (in pixels) - * @param depth the desired image depth - * @param data the image data to use - * @param mipMapSizes the desired mipmap sizes, or null for no mipmaps - * @deprecated use {@link #Image(com.jme3.texture.Image.Format, int, int, int, java.util.ArrayList, int[], com.jme3.texture.image.ColorSpace)} - */ - @Deprecated - public Image(Format format, int width, int height, int depth, ArrayList data, - int[] mipMapSizes) { - this(format, width, height, depth, data, mipMapSizes, ColorSpace.Linear); - } - - /** - * Constructor instantiates a new Image object. The - * attributes of the image are defined during construction. - * - * @param format - * the data format of the image. - * @param width - * the width of the image. - * @param height - * the height of the image. - * @param data - * the image data. - * @param mipMapSizes - * the array of mipmap sizes, or null for no mipmaps. - * @param colorSpace - * the colorSpace of the image - */ - public Image(Format format, int width, int height, ByteBuffer data, - int[] mipMapSizes, ColorSpace colorSpace) { - - this(); - - if (mipMapSizes != null) { - if (mipMapSizes.length <= 1) { - mipMapSizes = null; - } else { - needGeneratedMips = false; - mipsWereGenerated = true; - } - } - - setFormat(format); - this.width = width; - this.height = height; - if (data != null){ - this.data = new ArrayList(1); - this.data.add(data); - } - this.mipMapSizes = mipMapSizes; - this.colorSpace = colorSpace; - } - - /** - * @see #Image(com.jme3.texture.Image.Format, int, int, java.nio.ByteBuffer, int[], com.jme3.texture.image.ColorSpace) - * @param format the desired data format - * @param width the desired width (in pixels) - * @param height the desired height (in pixels) - * @param data the image data to use - * @param mipMapSizes the desired mipmap sizes, or null for no mipmaps - * @deprecated use {@link #Image(com.jme3.texture.Image.Format, int, int, java.nio.ByteBuffer, int[], com.jme3.texture.image.ColorSpace)} - */ - @Deprecated - public Image(Format format, int width, int height, ByteBuffer data, - int[] mipMapSizes) { - this(format, width, height, data, mipMapSizes, ColorSpace.Linear); - } - - /** - * Constructor instantiates a new Image object. The - * attributes of the image are defined during construction. - * - * @param format - * the data format of the image. - * @param width - * the width of the image. - * @param height - * the height of the image. - * @param depth - * the desired image depth - * @param data - * the image data. - * @param colorSpace - * the colorSpace of the image - */ - public Image(Format format, int width, int height, int depth, ArrayList data, ColorSpace colorSpace) { - this(format, width, height, depth, data, null, colorSpace); - } - - /** - * @see #Image(com.jme3.texture.Image.Format, int, int, int, java.util.ArrayList, com.jme3.texture.image.ColorSpace) - * @param format the desired data format - * @param width the desired width (in pixels) - * @param height the desired height (in pixels) - * @param depth the desired image depth - * @param data the image data to use - * @deprecated use {@link #Image(com.jme3.texture.Image.Format, int, int, int, java.util.ArrayList, com.jme3.texture.image.ColorSpace)} - */ - @Deprecated - public Image(Format format, int width, int height, int depth, ArrayList data) { - this(format, width, height, depth, data, ColorSpace.Linear); - } - - /** - * Constructor instantiates a new Image object. The - * attributes of the image are defined during construction. - * - * @param format - * the data format of the image. - * @param width - * the width of the image. - * @param height - * the height of the image. - * @param data - * the image data. - * @param colorSpace - * the colorSpace of the image - */ - public Image(Format format, int width, int height, ByteBuffer data, ColorSpace colorSpace) { - this(format, width, height, data, null, colorSpace); - } - - - /** - * @see #Image(com.jme3.texture.Image.Format, int, int, java.nio.ByteBuffer, com.jme3.texture.image.ColorSpace) - * @param format the desired data format - * @param width the desired width (in pixels) - * @param height the desired height (in pixels) - * @param data the image data - * @deprecated use {@link #Image(com.jme3.texture.Image.Format, int, int, java.nio.ByteBuffer, com.jme3.texture.image.ColorSpace)} - */ - @Deprecated - public Image(Format format, int width, int height, ByteBuffer data) { - this(format, width, height, data, null, ColorSpace.Linear); - } - - - /** - * @return The number of samples (for multisampled textures). - * @see Image#setMultiSamples(int) - */ - public int getMultiSamples() { - return multiSamples; - } - - /** - * @param multiSamples Set the number of samples to use for this image, - * setting this to a value higher than 1 turns this image/texture - * into a multisample texture (on OpenGL3.1 and higher). - */ - public void setMultiSamples(int multiSamples) { - if (multiSamples <= 0) { - throw new IllegalArgumentException("multiSamples must be > 0"); - } - - if (multiSamples > 1) { - if (getData(0) != null) { - throw new IllegalArgumentException("Cannot upload data as multisample texture"); - } - - if (hasMipmaps()) { - throw new IllegalArgumentException("Multisample textures do not support mipmaps"); - } - } - - this.multiSamples = multiSamples; - } - - /** - * setData sets the data that makes up the image. This data - * is packed into an array of ByteBuffer objects. - * - * @param data - * the data that contains the image information. - */ - public void setData(ArrayList data) { - this.data = data; - setUpdateNeeded(); - } - - /** - * setData sets the data that makes up the image. This data - * is packed into a single ByteBuffer. - * - * @param data - * the data that contains the image information. - */ - public void setData(ByteBuffer data) { - this.data = new ArrayList(1); - this.data.add(data); - setUpdateNeeded(); - } - - public void addData(ByteBuffer data) { - if (this.data == null) - this.data = new ArrayList(1); - this.data.add(data); - setUpdateNeeded(); - } - - public void setData(int index, ByteBuffer data) { - if (index >= 0) { - while (this.data.size() <= index) { - this.data.add(null); - } - this.data.set(index, data); - setUpdateNeeded(); - } else { - throw new IllegalArgumentException("index must be greater than or equal to 0."); - } - } - - /** - * @return null - * @deprecated This feature is no longer used by the engine - */ - @Deprecated - public Object getEfficentData(){ - return null; - } - - /** - * Sets the mipmap sizes stored in this image's data buffer. Mipmaps are - * stored sequentially, and the first mipmap is the main image data. To - * specify no mipmaps, pass null and this will automatically be expanded - * into a single mipmap of the full - * - * @param mipMapSizes - * the mipmap sizes array, or null for a single image map. - */ - public void setMipMapSizes(int[] mipMapSizes) { - if (mipMapSizes != null && mipMapSizes.length <= 1) - mipMapSizes = null; - - this.mipMapSizes = mipMapSizes; - - if (mipMapSizes != null) { - needGeneratedMips = false; - mipsWereGenerated = false; - } else { - needGeneratedMips = true; - mipsWereGenerated = false; - } - - setUpdateNeeded(); - } - - /** - * setHeight sets the height value of the image. It is - * typically a good idea to try to keep this as a multiple of 2. - * - * @param height - * the height of the image. - */ - public void setHeight(int height) { - this.height = height; - setUpdateNeeded(); - } - - /** - * setDepth sets the depth value of the image. It is - * typically a good idea to try to keep this as a multiple of 2. This is - * used for 3d images. - * - * @param depth - * the depth of the image. - */ - public void setDepth(int depth) { - this.depth = depth; - setUpdateNeeded(); - } - - /** - * setWidth sets the width value of the image. It is - * typically a good idea to try to keep this as a multiple of 2. - * - * @param width - * the width of the image. - */ - public void setWidth(int width) { - this.width = width; - setUpdateNeeded(); - } - - /** - * setFormat sets the image format for this image. - * - * @param format - * the image format (not null) - * @throws IllegalArgumentException - * if format is null - * @see Format - */ - public void setFormat(Format format) { - if (format == null) { - throw new IllegalArgumentException("format may not be null."); - } - - this.format = format; - setUpdateNeeded(); - } - - /** - * getFormat returns the image format for this image. - * - * @return the image format. - * @see Format - */ - public Format getFormat() { - return format; - } - - /** - * getWidth returns the width of this image. - * - * @return the width of this image. - */ - public int getWidth() { - return width; - } - - /** - * getHeight returns the height of this image. - * - * @return the height of this image. - */ - public int getHeight() { - return height; - } - - /** - * getDepth returns the depth of this image (for 3d images). - * - * @return the depth of this image. - */ - public int getDepth() { - return depth; - } - - /** - * getData returns the data for this image. If the data is - * undefined, null will be returned. - * - * @return the data for this image. - */ - public List getData() { - return data; - } - - /** - * getData returns the data for this image. If the data is - * undefined, null will be returned. - * - * @param index index of the data buffer to access - * @return the data for this image. - */ - public ByteBuffer getData(int index) { - if (data.size() > index) - return data.get(index); - else - return null; - } - - /** - * Returns whether the image data contains mipmaps. - * - * @return true if the image data contains mipmaps, false if not. - */ - public boolean hasMipmaps() { - return mipMapSizes != null; - } - - /** - * Returns the mipmap sizes for this image. - * - * @return the mipmap sizes for this image. - */ - public int[] getMipMapSizes() { - return mipMapSizes; - } - - /** - * image loader is responsible for setting this attribute based on the color - * space in which the image has been encoded with. In the majority of cases, - * this flag will be set to sRGB by default since many image formats do not - * contain any color space information and the most frequently used colors - * space is sRGB - * - * The material loader may override this attribute to Linear if it determines that - * such conversion must not be performed, for example, when loading normal - * maps. - * - * @param colorSpace Set to sRGB to enable srgb -> linear - * conversion, Linear otherwise. - * - * @see Renderer#setLinearizeSrgbImages(boolean) - * - */ - public void setColorSpace(ColorSpace colorSpace) { - this.colorSpace = colorSpace; - } - - /** - * Specifies that this image is an SRGB image and therefore must undergo an - * sRGB -> linear RGB color conversion prior to being read by a shader and - * with the {@link Renderer#setLinearizeSrgbImages(boolean)} option is - * enabled. - * - * This option is only supported for the 8-bit color and grayscale image - * formats. Determines if the image is in SRGB color space or not. - * - * @return True, if the image is an SRGB image, false if it is linear RGB. - * - * @see Renderer#setLinearizeSrgbImages(boolean) - */ - public ColorSpace getColorSpace() { - return colorSpace; - } - - @Override - public String toString(){ - StringBuilder sb = new StringBuilder(); - sb.append(getClass().getSimpleName()); - sb.append("[size=").append(width).append("x").append(height); - - if (depth > 1) - sb.append("x").append(depth); - - sb.append(", format=").append(format.name()); - - if (hasMipmaps()) - sb.append(", mips"); - - if (getId() >= 0) - sb.append(", id=").append(id); - - sb.append("]"); - - return sb.toString(); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - if (!(other instanceof Image)) { - return false; - } - Image that = (Image) other; - if (this.getFormat() != that.getFormat()) - return false; - if (this.getWidth() != that.getWidth()) - return false; - if (this.getHeight() != that.getHeight()) - return false; - if (this.getData() != null && !this.getData().equals(that.getData())) - return false; - if (this.getData() == null && that.getData() != null) - return false; - if (this.getMipMapSizes() != null - && !Arrays.equals(this.getMipMapSizes(), that.getMipMapSizes())) - return false; - if (this.getMipMapSizes() == null && that.getMipMapSizes() != null) - return false; - if (this.getMultiSamples() != that.getMultiSamples()) - return false; - - return true; - } - - @Override - public int hashCode() { - int hash = 7; - hash = 97 * hash + (this.format != null ? this.format.hashCode() : 0); - hash = 97 * hash + this.width; - hash = 97 * hash + this.height; - hash = 97 * hash + this.depth; - hash = 97 * hash + Arrays.hashCode(this.mipMapSizes); - hash = 97 * hash + (this.data != null ? this.data.hashCode() : 0); - hash = 97 * hash + this.multiSamples; - return hash; - } - - @Override - public void write(JmeExporter e) throws IOException { - OutputCapsule capsule = e.getCapsule(this); - capsule.write(format, "format", Format.RGBA8); - capsule.write(width, "width", 0); - capsule.write(height, "height", 0); - capsule.write(depth, "depth", 0); - capsule.write(mipMapSizes, "mipMapSizes", null); - capsule.write(multiSamples, "multiSamples", 1); - capsule.writeByteBufferArrayList(data, "data", null); - capsule.write(colorSpace, "colorSpace", null); - } - - @Override - public void read(JmeImporter importer) throws IOException { - InputCapsule capsule = importer.getCapsule(this); - format = capsule.readEnum("format", Format.class, Format.RGBA8); - width = capsule.readInt("width", 0); - height = capsule.readInt("height", 0); - depth = capsule.readInt("depth", 0); - mipMapSizes = capsule.readIntArray("mipMapSizes", null); - multiSamples = capsule.readInt("multiSamples", 1); - data = capsule.readByteBufferArrayList("data", null); - colorSpace = capsule.readEnum("colorSpace", ColorSpace.class, null); - if (mipMapSizes != null) { - needGeneratedMips = false; - mipsWereGenerated = true; - } - } - -} +/* + * Copyright (c) 2009-2024 jMonkeyEngine + * 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 'jMonkeyEngine' 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 com.jme3.texture; + +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.export.Savable; +import com.jme3.math.FastMath; +import com.jme3.renderer.Caps; +import com.jme3.renderer.Renderer; +import com.jme3.texture.image.ColorSpace; +import com.jme3.texture.image.LastTextureState; +import com.jme3.util.natives.GlNative; +import com.jme3.vulkan.images.GpuImage; +import com.jme3.vulkan.util.IntEnum; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Image defines a data format for a graphical image. The image + * is defined by a format, a height and width, and the image data. The width and + * height must be greater than 0. The data is contained in a byte buffer, and + * should be packed before creation of the image object. + * + * @author Mark Powell + * @author Joshua Slack + * @version $Id: Image.java 4131 2009-03-19 20:15:28Z blaine.dev $ + */ +public class GlImage extends GlNative implements GpuImage, Savable /*, Cloneable*/ { + + @Override + public IntEnum getType() { + return Type.TwoDemensional; + } + + public enum Format { + /** + * 8-bit alpha + */ + Alpha8(8), + + @Deprecated + Reserved1(0), + + /** + * 8-bit grayscale/luminance. + */ + Luminance8(8), + + @Deprecated + Reserved2(0), + + /** + * half-precision floating-point grayscale/luminance. + * + * Requires {@link Caps#FloatTexture}. + */ + Luminance16F(16,true), + + /** + * single-precision floating-point grayscale/luminance. + * + * Requires {@link Caps#FloatTexture}. + */ + Luminance32F(32,true), + + /** + * 8-bit luminance/grayscale and 8-bit alpha. + */ + Luminance8Alpha8(16), + + @Deprecated + Reserved3(0), + + /** + * half-precision floating-point grayscale/luminance and alpha. + * + * Requires {@link Caps#FloatTexture}. + */ + Luminance16FAlpha16F(32,true), + + @Deprecated + Reserved4(0), + + @Deprecated + Reserved5(0), + + /** + * 8-bit blue, green, and red. + */ + BGR8(24), // BGR and ABGR formats are often used on Windows systems + + /** + * 8-bit red, green, and blue. + */ + RGB8(24), + + @Deprecated + Reserved6(0), + + @Deprecated + Reserved7(0), + + /** + * 5-bit red, 6-bit green, and 5-bit blue. + */ + RGB565(16), + + @Deprecated + Reserved8(0), + + /** + * 5-bit red, green, and blue with 1-bit alpha. + */ + RGB5A1(16), + + /** + * 8-bit red, green, blue, and alpha. + */ + RGBA8(32), + + /** + * 8-bit alpha, blue, green, and red. + */ + ABGR8(32), + + /** + * 8-bit alpha, red, blue and green + */ + ARGB8(32), + + /** + * 8-bit blue, green, red and alpha. + */ + BGRA8(32), + + @Deprecated + Reserved9(0), + + /** + * S3TC compression DXT1. + */ + DXT1(4,false,true, false), + + /** + * S3TC compression DXT1 with 1-bit alpha. + */ + DXT1A(4,false,true, false), + + /** + * S3TC compression DXT3 with 4-bit alpha. + */ + DXT3(8,false,true, false), + + /** + * S3TC compression DXT5 with interpolated 8-bit alpha. + * + */ + DXT5(8,false,true, false), + + RGTC2(8,false,true, false), + + SIGNED_RGTC2(8,false,true, false), + + RGTC1(4,false,true, false), + + SIGNED_RGTC1(4,false,true, false), + + /** + * BPTC compression BC6 signed float RGB + */ + BC6H_SF16(8, false, true, true), + + /** + * BPTC compression BC6 unsigned float RGB + */ + BC6H_UF16(8, false, true, true), + + /** + * BPTC compression BC7 RGBA + */ + BC7_UNORM(8, false, true, false), + + /** + * BPTC compression BC7 SRGB Alpha + */ + BC7_UNORM_SRGB(8, false, true, false), + + /** + * Luminance-Alpha Texture Compression. + * + * @deprecated Not supported by OpenGL 3.0. + */ + @Deprecated + Reserved10(0), + + /** + * Arbitrary depth format. The precision is chosen by the video + * hardware. + */ + Depth(0,true,false,false), + + /** + * 16-bit depth. + */ + Depth16(16,true,false,false), + + /** + * 24-bit depth. + */ + Depth24(24,true,false,false), + + /** + * 32-bit depth. + */ + Depth32(32,true,false,false), + + /** + * single-precision floating point depth. + * + * Requires {@link Caps#FloatDepthBuffer}. + */ + Depth32F(32,true,false,true), + + /** + * Texture data is stored as {@link Format#RGB16F} in system memory, + * but will be converted to {@link Format#RGB111110F} when sent + * to the video hardware. + * + * Requires {@link Caps#FloatTexture} and {@link Caps#PackedFloatTexture}. + */ + RGB16F_to_RGB111110F(48,true), + + /** + * unsigned floating-point red, green and blue that uses 32 bits. + * + * Requires {@link Caps#PackedFloatTexture}. + */ + RGB111110F(32,true), + + /** + * Texture data is stored as {@link Format#RGB16F} in system memory, + * but will be converted to {@link Format#RGB9E5} when sent + * to the video hardware. + * + * Requires {@link Caps#FloatTexture} and {@link Caps#SharedExponentTexture}. + */ + RGB16F_to_RGB9E5(48,true), + + /** + * 9-bit red, green and blue with 5-bit exponent. + * + * Requires {@link Caps#SharedExponentTexture}. + */ + RGB9E5(32,true), + + /** + * half-precision floating point red, green, and blue. + * + * Requires {@link Caps#FloatTexture}. + * May be supported for renderbuffers, but the OpenGL specification does not require it. + */ + RGB16F(48,true), + + /** + * half-precision floating point red, green, blue, and alpha. + * + * Requires {@link Caps#FloatTexture}. + */ + RGBA16F(64,true), + + /** + * single-precision floating point red, green, and blue. + * + * Requires {@link Caps#FloatTexture}. + * May be supported for renderbuffers, but the OpenGL specification does not require it. + */ + RGB32F(96,true), + + /** + * single-precision floating point red, green, blue and alpha. + * + * Requires {@link Caps#FloatTexture}. + */ + RGBA32F(128,true), + + @Deprecated + Reserved11(0), + + /** + * 24-bit depth with 8-bit stencil. + * Check the cap {@link Caps#PackedDepthStencilBuffer}. + */ + Depth24Stencil8(32, true, false, false), + + @Deprecated + Reserved12(0), + + /** + * Ericsson Texture Compression. Typically used on Android. + * + * Requires {@link Caps#TextureCompressionETC1}. + */ + ETC1(4, false, true, false), + + /** + * Ericsson Texture Compression 2. Typically used on Android. + * Same as {@link #ETC1} but with alpha. + * + * Requires {@link Caps#TextureCompressionETC2}. + */ + ETC2(8, false, true, false), + + /** + * Ericsson Texture Compression 2. Typically used on Android. + * Same as {@link #ETC2} but alpha can be either 1 or 0. + * + * Requires {@link Caps#TextureCompressionETC2}. + */ + ETC2_ALPHA1(4, false, true, false), + + + /** + * 8-bit signed int red. + * + * Requires {@link Caps#IntegerTexture}. + */ + R8I(8), + /** + * 8-bit unsigned int red. + * + * Requires {@link Caps#IntegerTexture}. + */ + R8UI(8), + /** + * 16-bit signed int red. + * + * Requires {@link Caps#IntegerTexture}. + */ + R16I(16), + /** + * 16-bit unsigned int red. + * + * Requires {@link Caps#IntegerTexture}. + */ + R16UI(16), + /** + * 32-bit signed int red. + * + * Requires {@link Caps#IntegerTexture}. + */ + R32I(32), + /** + * 32-bit unsigned int red. + * + * Requires {@link Caps#IntegerTexture}. + */ + R32UI(32), + + + /** + * 8-bit signed int red and green. + * + * Requires {@link Caps#IntegerTexture}. + */ + RG8I(16), + /** + * 8-bit unsigned int red and green. + * + * Requires {@link Caps#IntegerTexture}. + */ + RG8UI(16), + /** + * 16-bit signed int red and green. + * + * Requires {@link Caps#IntegerTexture}. + */ + RG16I(32), + /** + * 16-bit unsigned int red and green. + * + * Requires {@link Caps#IntegerTexture}. + */ + RG16UI(32), + /** + * 32-bit signed int red and green. + * + * Requires {@link Caps#IntegerTexture}. + */ + RG32I(64), + /** + * 32-bit unsigned int red and green. + * + * Requires {@link Caps#IntegerTexture}. + */ + RG32UI(64), + + /** + * 8-bit signed int red, green and blue. + * + * Requires {@link Caps#IntegerTexture} to be supported for textures. + * May be supported for renderbuffers, but the OpenGL specification does not require it. + */ + RGB8I(24), + /** + * 8 bit unsigned int red, green and blue. + * + * Requires {@link Caps#IntegerTexture} to be supported for textures. + * May be supported for renderbuffers, but the OpenGL specification does not require it. + */ + RGB8UI(24), + /** + * 16-bit signed int red, green and blue. + * + * Requires {@link Caps#IntegerTexture} to be supported for textures. + * May be supported for renderbuffers, but the OpenGL specification does not require it. + */ + RGB16I(48), + /** + * 16-bit unsigned int red, green and blue. + * + * Requires {@link Caps#IntegerTexture} to be supported for textures. + * May be supported for renderbuffers, but the OpenGL specification does not require it. + */ + RGB16UI(48), + /** + * 32-bit signed int red, green and blue. + * + * Requires {@link Caps#IntegerTexture} to be supported for textures. + * May be supported for renderbuffers, but the OpenGL specification does not require it. + */ + RGB32I(96), + /** + * 32-bit unsigned int red, green and blue. + * + * Requires {@link Caps#IntegerTexture} to be supported for textures. + * May be supported for renderbuffers, but the OpenGL specification does not require it. + */ + RGB32UI(96), + + + /** + * 8-bit signed int red, green, blue and alpha. + * + * Requires {@link Caps#IntegerTexture}. + */ + RGBA8I(32), + /** + * 8-bit unsigned int red, green, blue and alpha. + * + * Requires {@link Caps#IntegerTexture}. + */ + RGBA8UI(32), + /** + * 16-bit signed int red, green, blue and alpha. + * + * Requires {@link Caps#IntegerTexture}. + */ + RGBA16I(64), + /** + * 16-bit unsigned int red, green, blue and alpha. + * + * Requires {@link Caps#IntegerTexture}. + */ + RGBA16UI(64), + /** + * 32-bit signed int red, green, blue and alpha. + * + * Requires {@link Caps#IntegerTexture}. + */ + RGBA32I(128), + /** + * 32-bit unsigned int red, green, blue and alpha. + * + * Requires {@link Caps#IntegerTexture}. + */ + RGBA32UI(128), + + /** + * half-precision floating point red. + * + * Requires {@link Caps#FloatTexture}. + */ + R16F(16,true), + + /** + * single-precision floating point red. + * + * Requires {@link Caps#FloatTexture}. + */ + R32F(32,true), + + /** + * half-precision floating point red and green. + * + * Requires {@link Caps#FloatTexture}. + */ + RG16F(32,true), + + /** + * single-precision floating point red and green. + * + * Requires {@link Caps#FloatTexture}. + */ + RG32F(64,true), + + /** + * 10-bit red, green, and blue with 2-bit alpha. + */ + RGB10A2(32), + ; + + private int bpp; + private boolean isDepth; + private boolean isCompressed; + private boolean isFloatingPoint; + + private Format(int bpp){ + this.bpp = bpp; + } + + private Format(int bpp, boolean isFP){ + this(bpp); + this.isFloatingPoint = isFP; + } + + private Format(int bpp, boolean isDepth, boolean isCompressed, boolean isFP){ + this(bpp, isFP); + this.isDepth = isDepth; + this.isCompressed = isCompressed; + } + + /** + * @return bits per pixel. + */ + public int getBitsPerPixel(){ + return bpp; + } + + /** + * @return True if this format is a depth format, false otherwise. + */ + public boolean isDepthFormat(){ + return isDepth; + } + + /** + * @return True if this format is a depth + stencil (packed) format, false otherwise. + */ + boolean isDepthStencilFormat() { + return this == Depth24Stencil8; + } + + /** + * @return True if this is a compressed image format, false if + * uncompressed. + */ + public boolean isCompressed() { + return isCompressed; + } + + /** + * @return True if this image format is in floating point, + * false if it is an integer format. + */ + public boolean isFloatingPont(){ + return isFloatingPoint; + } + + + + } + + // image attributes + protected Format format; + protected int width, height, depth; + protected int[] mipMapSizes; + protected ArrayList data; + protected int multiSamples = 1; + protected ColorSpace colorSpace = null; +// protected int mipOffset = 0; + + // attributes relating to GL object + protected boolean mipsWereGenerated = false; + protected boolean needGeneratedMips = false; + protected LastTextureState lastTextureState = new LastTextureState(); + + /** + * Internal use only. + * The renderer stores the texture state set from the last texture, + * so it doesn't have to change it unless necessary. + * + * @return The image parameter state. + */ + public LastTextureState getLastTextureState() { + return lastTextureState; + } + + /** + * Internal use only. + * The renderer marks which images have generated mipmaps in VRAM + * and which do not, so it can generate them as needed. + * + * @param generated If mipmaps were generated or not. + */ + public void setMipmapsGenerated(boolean generated) { + this.mipsWereGenerated = generated; + } + + /** + * Internal use only. + * Check if the renderer has generated mipmaps for this image in VRAM + * or not. + * + * @return If mipmaps were generated already. + */ + public boolean isMipmapsGenerated() { + return mipsWereGenerated; + } + + /** + * (Package private) Called by {@link GlTexture} when + * {@link #isMipmapsGenerated() } is false in order to generate + * mipmaps for this image. + */ + void setNeedGeneratedMipmaps() { + needGeneratedMips = true; + } + + /** + * @return True if the image needs to have mipmaps generated + * for it (as requested by the texture). This stays true even + * after mipmaps have been generated. + */ + public boolean isGeneratedMipmapsRequired() { + return needGeneratedMips; + } + + /** + * Determine if the image is NPOT. + * + * @return if the image is a non-power-of-2 image, e.g. having dimensions + * that are not powers of 2. + */ + public boolean isNPOT() { + return width != 0 && height != 0 + && (!FastMath.isPowerOfTwo(width) || !FastMath.isPowerOfTwo(height)); + } + + @Override + public void resetObject() { + this.object = -1; + this.mipsWereGenerated = false; + this.lastTextureState.reset(); + setUpdateNeeded(); + } + + @Override + public Runnable createNativeDestroyer() { + return () -> renderer.deleteImage(new GlImage(object)); + } + + /** + * @return A shallow clone of this image. The data is not cloned. + */ + @Override + public GlImage clone(){ + GlImage clone = (GlImage) super.clone(); + clone.mipMapSizes = mipMapSizes != null ? mipMapSizes.clone() : null; + clone.data = data != null ? new ArrayList<>(data) : null; + clone.lastTextureState = new LastTextureState(); + clone.setUpdateNeeded(); + return clone; + } + + /** + * Constructor instantiates a new Image object. All values + * are undefined. + */ + public GlImage() { + super(); + data = new ArrayList(1); + } + + protected GlImage(int id) { + super(id); + } + + /** + * Constructor instantiates a new Image object. The + * attributes of the image are defined during construction. + * + * @param format + * the data format of the image. + * @param width + * the width of the image. + * @param height + * the height of the image. + * @param depth + * the desired image depth + * @param data + * the image data. + * @param mipMapSizes + * the array of mipmap sizes, or null for no mipmaps. + * @param colorSpace + * the colorSpace of the image + */ + public GlImage(Format format, int width, int height, int depth, ArrayList data, + int[] mipMapSizes, ColorSpace colorSpace) { + + this(); + + if (mipMapSizes != null) { + if (mipMapSizes.length <= 1) { + mipMapSizes = null; + } else { + needGeneratedMips = false; + mipsWereGenerated = true; + } + } + + setFormat(format); + this.width = width; + this.height = height; + this.data = data; + this.depth = depth; + this.mipMapSizes = mipMapSizes; + this.colorSpace = colorSpace; + } + + /** + * @see #GlImage(GlImage.Format, int, int, int, java.util.ArrayList, int[], com.jme3.texture.image.ColorSpace) + * @param format the desired data format + * @param width the desired width (in pixels) + * @param height the desired height (in pixels) + * @param depth the desired image depth + * @param data the image data to use + * @param mipMapSizes the desired mipmap sizes, or null for no mipmaps + * @deprecated use {@link #GlImage(GlImage.Format, int, int, int, java.util.ArrayList, int[], com.jme3.texture.image.ColorSpace)} + */ + @Deprecated + public GlImage(Format format, int width, int height, int depth, ArrayList data, + int[] mipMapSizes) { + this(format, width, height, depth, data, mipMapSizes, ColorSpace.Linear); + } + + /** + * Constructor instantiates a new Image object. The + * attributes of the image are defined during construction. + * + * @param format + * the data format of the image. + * @param width + * the width of the image. + * @param height + * the height of the image. + * @param data + * the image data. + * @param mipMapSizes + * the array of mipmap sizes, or null for no mipmaps. + * @param colorSpace + * the colorSpace of the image + */ + public GlImage(Format format, int width, int height, ByteBuffer data, + int[] mipMapSizes, ColorSpace colorSpace) { + + this(); + + if (mipMapSizes != null) { + if (mipMapSizes.length <= 1) { + mipMapSizes = null; + } else { + needGeneratedMips = false; + mipsWereGenerated = true; + } + } + + setFormat(format); + this.width = width; + this.height = height; + if (data != null){ + this.data = new ArrayList(1); + this.data.add(data); + } + this.mipMapSizes = mipMapSizes; + this.colorSpace = colorSpace; + } + + /** + * @see #GlImage(GlImage.Format, int, int, java.nio.ByteBuffer, int[], com.jme3.texture.image.ColorSpace) + * @param format the desired data format + * @param width the desired width (in pixels) + * @param height the desired height (in pixels) + * @param data the image data to use + * @param mipMapSizes the desired mipmap sizes, or null for no mipmaps + * @deprecated use {@link #GlImage(GlImage.Format, int, int, java.nio.ByteBuffer, int[], com.jme3.texture.image.ColorSpace)} + */ + @Deprecated + public GlImage(Format format, int width, int height, ByteBuffer data, + int[] mipMapSizes) { + this(format, width, height, data, mipMapSizes, ColorSpace.Linear); + } + + /** + * Constructor instantiates a new Image object. The + * attributes of the image are defined during construction. + * + * @param format + * the data format of the image. + * @param width + * the width of the image. + * @param height + * the height of the image. + * @param depth + * the desired image depth + * @param data + * the image data. + * @param colorSpace + * the colorSpace of the image + */ + public GlImage(Format format, int width, int height, int depth, ArrayList data, ColorSpace colorSpace) { + this(format, width, height, depth, data, null, colorSpace); + } + + /** + * @see #GlImage(GlImage.Format, int, int, int, java.util.ArrayList, com.jme3.texture.image.ColorSpace) + * @param format the desired data format + * @param width the desired width (in pixels) + * @param height the desired height (in pixels) + * @param depth the desired image depth + * @param data the image data to use + * @deprecated use {@link #GlImage(GlImage.Format, int, int, int, java.util.ArrayList, com.jme3.texture.image.ColorSpace)} + */ + @Deprecated + public GlImage(Format format, int width, int height, int depth, ArrayList data) { + this(format, width, height, depth, data, ColorSpace.Linear); + } + + /** + * Constructor instantiates a new Image object. The + * attributes of the image are defined during construction. + * + * @param format + * the data format of the image. + * @param width + * the width of the image. + * @param height + * the height of the image. + * @param data + * the image data. + * @param colorSpace + * the colorSpace of the image + */ + public GlImage(Format format, int width, int height, ByteBuffer data, ColorSpace colorSpace) { + this(format, width, height, data, null, colorSpace); + } + + + /** + * @see #GlImage(GlImage.Format, int, int, java.nio.ByteBuffer, com.jme3.texture.image.ColorSpace) + * @param format the desired data format + * @param width the desired width (in pixels) + * @param height the desired height (in pixels) + * @param data the image data + * @deprecated use {@link #GlImage(GlImage.Format, int, int, java.nio.ByteBuffer, com.jme3.texture.image.ColorSpace)} + */ + @Deprecated + public GlImage(Format format, int width, int height, ByteBuffer data) { + this(format, width, height, data, null, ColorSpace.Linear); + } + + + /** + * @return The number of samples (for multisampled textures). + * @see GlImage#setMultiSamples(int) + */ + public int getMultiSamples() { + return multiSamples; + } + + /** + * @param multiSamples Set the number of samples to use for this image, + * setting this to a value higher than 1 turns this image/texture + * into a multisample texture (on OpenGL3.1 and higher). + */ + public void setMultiSamples(int multiSamples) { + if (multiSamples <= 0) { + throw new IllegalArgumentException("multiSamples must be > 0"); + } + + if (multiSamples > 1) { + if (getData(0) != null) { + throw new IllegalArgumentException("Cannot upload data as multisample texture"); + } + + if (hasMipmaps()) { + throw new IllegalArgumentException("Multisample textures do not support mipmaps"); + } + } + + this.multiSamples = multiSamples; + } + + /** + * setData sets the data that makes up the image. This data + * is packed into an array of ByteBuffer objects. + * + * @param data + * the data that contains the image information. + */ + public void setData(ArrayList data) { + this.data = data; + setUpdateNeeded(); + } + + /** + * setData sets the data that makes up the image. This data + * is packed into a single ByteBuffer. + * + * @param data + * the data that contains the image information. + */ + public void setData(ByteBuffer data) { + this.data = new ArrayList(1); + this.data.add(data); + setUpdateNeeded(); + } + + public void addData(ByteBuffer data) { + if (this.data == null) + this.data = new ArrayList(1); + this.data.add(data); + setUpdateNeeded(); + } + + public void setData(int index, ByteBuffer data) { + if (index >= 0) { + while (this.data.size() <= index) { + this.data.add(null); + } + this.data.set(index, data); + setUpdateNeeded(); + } else { + throw new IllegalArgumentException("index must be greater than or equal to 0."); + } + } + + /** + * @return null + * @deprecated This feature is no longer used by the engine + */ + @Deprecated + public Object getEfficentData(){ + return null; + } + + /** + * Sets the mipmap sizes stored in this image's data buffer. Mipmaps are + * stored sequentially, and the first mipmap is the main image data. To + * specify no mipmaps, pass null and this will automatically be expanded + * into a single mipmap of the full + * + * @param mipMapSizes + * the mipmap sizes array, or null for a single image map. + */ + public void setMipMapSizes(int[] mipMapSizes) { + if (mipMapSizes != null && mipMapSizes.length <= 1) + mipMapSizes = null; + + this.mipMapSizes = mipMapSizes; + + if (mipMapSizes != null) { + needGeneratedMips = false; + mipsWereGenerated = false; + } else { + needGeneratedMips = true; + mipsWereGenerated = false; + } + + setUpdateNeeded(); + } + + /** + * setHeight sets the height value of the image. It is + * typically a good idea to try to keep this as a multiple of 2. + * + * @param height + * the height of the image. + */ + public void setHeight(int height) { + this.height = height; + setUpdateNeeded(); + } + + /** + * setDepth sets the depth value of the image. It is + * typically a good idea to try to keep this as a multiple of 2. This is + * used for 3d images. + * + * @param depth + * the depth of the image. + */ + public void setDepth(int depth) { + this.depth = depth; + setUpdateNeeded(); + } + + /** + * setWidth sets the width value of the image. It is + * typically a good idea to try to keep this as a multiple of 2. + * + * @param width + * the width of the image. + */ + public void setWidth(int width) { + this.width = width; + setUpdateNeeded(); + } + + /** + * setFormat sets the image format for this image. + * + * @param format + * the image format (not null) + * @throws IllegalArgumentException + * if format is null + * @see Format + */ + public void setFormat(Format format) { + if (format == null) { + throw new IllegalArgumentException("format may not be null."); + } + + this.format = format; + setUpdateNeeded(); + } + + /** + * getFormat returns the image format for this image. + * + * @return the image format. + * @see Format + */ + public Format getGlFormat() { + return format; + } + + @Override + public com.jme3.vulkan.Format getFormat() { + // todo: merge com.jme3.vulkan.Format and GlImage.Format + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public long getId() { + return object; + } + + /** + * getWidth returns the width of this image. + * + * @return the width of this image. + */ + @Override + public int getWidth() { + return width; + } + + /** + * getHeight returns the height of this image. + * + * @return the height of this image. + */ + @Override + public int getHeight() { + return height; + } + + /** + * getDepth returns the depth of this image (for 3d images). + * + * @return the depth of this image. + */ + @Override + public int getDepth() { + return depth; + } + + @Override + public int getMipmaps() { + // todo: check for accuracy (maybe refactor gl mipmaps?) + return mipMapSizes.length; + } + + @Override + public int getLayers() { + // todo: implement properly + return 1; + } + + /** + * getData returns the data for this image. If the data is + * undefined, null will be returned. + * + * @return the data for this image. + */ + public List getData() { + return data; + } + + /** + * getData returns the data for this image. If the data is + * undefined, null will be returned. + * + * @param index index of the data buffer to access + * @return the data for this image. + */ + public ByteBuffer getData(int index) { + if (data.size() > index) + return data.get(index); + else + return null; + } + + /** + * Returns whether the image data contains mipmaps. + * + * @return true if the image data contains mipmaps, false if not. + */ + public boolean hasMipmaps() { + return mipMapSizes != null; + } + + /** + * Returns the mipmap sizes for this image. + * + * @return the mipmap sizes for this image. + */ + public int[] getMipMapSizes() { + return mipMapSizes; + } + + /** + * image loader is responsible for setting this attribute based on the color + * space in which the image has been encoded with. In the majority of cases, + * this flag will be set to sRGB by default since many image formats do not + * contain any color space information and the most frequently used colors + * space is sRGB + * + * The material loader may override this attribute to Linear if it determines that + * such conversion must not be performed, for example, when loading normal + * maps. + * + * @param colorSpace Set to sRGB to enable srgb -> linear + * conversion, Linear otherwise. + * + * @see Renderer#setLinearizeSrgbImages(boolean) + * + */ + public void setColorSpace(ColorSpace colorSpace) { + this.colorSpace = colorSpace; + } + + /** + * Specifies that this image is an SRGB image and therefore must undergo an + * sRGB -> linear RGB color conversion prior to being read by a shader and + * with the {@link Renderer#setLinearizeSrgbImages(boolean)} option is + * enabled. + * + * This option is only supported for the 8-bit color and grayscale image + * formats. Determines if the image is in SRGB color space or not. + * + * @return True, if the image is an SRGB image, false if it is linear RGB. + * + * @see Renderer#setLinearizeSrgbImages(boolean) + */ + public ColorSpace getColorSpace() { + return colorSpace; + } + + @Override + public String toString(){ + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()); + sb.append("[size=").append(width).append("x").append(height); + + if (depth > 1) + sb.append("x").append(depth); + + sb.append(", format=").append(format.name()); + + if (hasMipmaps()) + sb.append(", mips"); + + if (getNativeObject() >= 0) + sb.append(", id=").append(object); + + sb.append("]"); + + return sb.toString(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (!(other instanceof GlImage)) { + return false; + } + GlImage that = (GlImage) other; + if (this.getGlFormat() != that.getGlFormat()) + return false; + if (this.getWidth() != that.getWidth()) + return false; + if (this.getHeight() != that.getHeight()) + return false; + if (this.getData() != null && !this.getData().equals(that.getData())) + return false; + if (this.getData() == null && that.getData() != null) + return false; + if (this.getMipMapSizes() != null + && !Arrays.equals(this.getMipMapSizes(), that.getMipMapSizes())) + return false; + if (this.getMipMapSizes() == null && that.getMipMapSizes() != null) + return false; + if (this.getMultiSamples() != that.getMultiSamples()) + return false; + + return true; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 97 * hash + (this.format != null ? this.format.hashCode() : 0); + hash = 97 * hash + this.width; + hash = 97 * hash + this.height; + hash = 97 * hash + this.depth; + hash = 97 * hash + Arrays.hashCode(this.mipMapSizes); + hash = 97 * hash + (this.data != null ? this.data.hashCode() : 0); + hash = 97 * hash + this.multiSamples; + return hash; + } + + @Override + public void write(JmeExporter e) throws IOException { + OutputCapsule capsule = e.getCapsule(this); + capsule.write(format, "format", Format.RGBA8); + capsule.write(width, "width", 0); + capsule.write(height, "height", 0); + capsule.write(depth, "depth", 0); + capsule.write(mipMapSizes, "mipMapSizes", null); + capsule.write(multiSamples, "multiSamples", 1); + capsule.writeByteBufferArrayList(data, "data", null); + capsule.write(colorSpace, "colorSpace", null); + } + + @Override + public void read(JmeImporter importer) throws IOException { + InputCapsule capsule = importer.getCapsule(this); + format = capsule.readEnum("format", Format.class, Format.RGBA8); + width = capsule.readInt("width", 0); + height = capsule.readInt("height", 0); + depth = capsule.readInt("depth", 0); + mipMapSizes = capsule.readIntArray("mipMapSizes", null); + multiSamples = capsule.readInt("multiSamples", 1); + data = capsule.readByteBufferArrayList("data", null); + colorSpace = capsule.readEnum("colorSpace", ColorSpace.class, null); + if (mipMapSizes != null) { + needGeneratedMips = false; + mipsWereGenerated = true; + } + } + +} diff --git a/jme3-core/src/main/java/com/jme3/texture/GlTexture.java b/jme3-core/src/main/java/com/jme3/texture/GlTexture.java new file mode 100644 index 0000000000..309631cf3e --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/texture/GlTexture.java @@ -0,0 +1,709 @@ +/* + * Copyright (c) 2009-2021 jMonkeyEngine + * 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 'jMonkeyEngine' 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 com.jme3.texture; + +import com.jme3.asset.AssetKey; +import com.jme3.asset.AssetNotFoundException; +import com.jme3.asset.CloneableSmartAsset; +import com.jme3.asset.TextureKey; +import com.jme3.export.*; +import com.jme3.util.PlaceholderAssets; +import com.jme3.vulkan.images.AddressMode; +import com.jme3.vulkan.images.Filter; +import com.jme3.vulkan.images.GpuImage; +import com.jme3.vulkan.images.MipmapMode; +import com.jme3.vulkan.util.IntEnum; +import org.lwjgl.vulkan.VK10; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Texture defines a texture object to be used to display an + * image on a piece of geometry. The image to be displayed is defined by the + * Image class. All attributes required for texture mapping are + * contained within this class. This includes mipmapping if desired, + * magnificationFilter options, apply options and correction options. Default + * values are as follows: minificationFilter - NearestNeighborNoMipMaps, + * magnificationFilter - NearestNeighbor, wrap - EdgeClamp on S,T and R, apply - + * Modulate, environment - None. + * + * @see GlImage + * @author Mark Powell + * @author Joshua Slack + * @version $Id: Texture.java 4131 2009-03-19 20:15:28Z blaine.dev $ + */ +public abstract class GlTexture implements Texture, ImageView, CloneableSmartAsset, Savable, Cloneable { + + public enum Type { + + /** + * Two dimensional texture (default). A rectangle. + */ + TwoDimensional, + + /** + * An array of two-dimensional textures. + */ + TwoDimensionalArray, + + /** + * Three-dimensional texture. (A cube) + */ + ThreeDimensional, + + /** + * A set of 6 TwoDimensional textures arranged as faces of a cube facing + * inwards. + */ + CubeMap; + } + + public enum MinFilter { + + /** + * Nearest neighbor interpolation is the fastest and crudest filtering + * method - it simply uses the color of the texel closest to the pixel + * center for the pixel color. While fast, this results in aliasing and + * shimmering during minification. (GL equivalent: GL_NEAREST) + */ + NearestNoMipMaps(false), + + /** + * In this method the four nearest texels to the pixel center are + * sampled (at texture level 0), and their colors are combined by + * weighted averages. Though smoother, without mipmaps it suffers the + * same aliasing and shimmering problems as nearest + * NearestNeighborNoMipMaps. (GL equivalent: GL_LINEAR) + */ + BilinearNoMipMaps(false), + + /** + * Same as NearestNeighborNoMipMaps except that instead of using samples + * from texture level 0, the closest mipmap level is chosen based on + * distance. This reduces the aliasing and shimmering significantly, but + * does not help with blockiness. (GL equivalent: + * GL_NEAREST_MIPMAP_NEAREST) + */ + NearestNearestMipMap(true), + + /** + * Same as BilinearNoMipMaps except that instead of using samples from + * texture level 0, the closest mipmap level is chosen based on + * distance. By using mipmapping we avoid the aliasing and shimmering + * problems of BilinearNoMipMaps. (GL equivalent: + * GL_LINEAR_MIPMAP_NEAREST) + */ + BilinearNearestMipMap(true), + + /** + * Similar to NearestNeighborNoMipMaps except that instead of using + * samples from texture level 0, a sample is chosen from each of the + * closest (by distance) two mipmap levels. A weighted average of these + * two samples is returned. (GL equivalent: GL_NEAREST_MIPMAP_LINEAR) + */ + NearestLinearMipMap(true), + + /** + * Trilinear filtering is a remedy to a common artifact seen in + * mipmapped bilinearly filtered images: an abrupt and very noticeable + * change in quality at boundaries where the renderer switches from one + * mipmap level to the next. Trilinear filtering solves this by doing a + * texture lookup and bilinear filtering on the two closest mipmap + * levels (one higher and one lower quality), and then linearly + * interpolating the results. This results in a smooth degradation of + * texture quality as distance from the viewer increases, rather than a + * series of sudden drops. Of course, closer than Level 0 there is only + * one mipmap level available, and the algorithm reverts to bilinear + * filtering (GL equivalent: GL_LINEAR_MIPMAP_LINEAR) + */ + Trilinear(true); + + private final boolean usesMipMapLevels; + + MinFilter(boolean usesMipMapLevels) { + this.usesMipMapLevels = usesMipMapLevels; + } + + public boolean usesMipMapLevels() { + return usesMipMapLevels; + } + + public static MinFilter of(IntEnum filter, IntEnum mipmap) { + if (filter.is(Filter.Linear)) { + if (mipmap.is(MipmapMode.Linear)) return Trilinear; + else return BilinearNearestMipMap; + } else if (filter.is(Filter.Nearest)) { + if (mipmap.is(MipmapMode.Linear)) return Trilinear; + else return NearestNearestMipMap; + } else if (mipmap.is(MipmapMode.Linear)) return NearestLinearMipMap; + else return NearestNearestMipMap; + } + + } + + public enum MagFilter { + + /** + * Nearest neighbor interpolation is the fastest and crudest filtering + * mode - it simply uses the color of the texel closest to the pixel + * center for the pixel color. While fast, this results in texture + * 'blockiness' during magnification. (GL equivalent: GL_NEAREST) + */ + Nearest, + + /** + * In this mode the four nearest texels to the pixel center are sampled + * (at the closest mipmap level), and their colors are combined by + * weighted average according to distance. This removes the 'blockiness' + * seen during magnification, as there is now a smooth gradient of color + * change from one texel to the next, instead of an abrupt jump as the + * pixel center crosses the texel boundary. (GL equivalent: GL_LINEAR) + */ + Bilinear; + + public static MagFilter of(IntEnum filter) { + if (filter.is(Filter.Linear)) return Bilinear; + else return Nearest; + } + + } + + public enum WrapMode { + /** + * Only the fractional portion of the coordinate is considered. + */ + Repeat, + + /** + * Only the fractional portion of the coordinate is considered, but if + * the integer portion is odd, we'll use 1 - the fractional portion. + * (Introduced around OpenGL1.4) Falls back on Repeat if not supported. + */ + MirroredRepeat, + + /** + * coordinate will be clamped to [0,1] + * + * @deprecated Not supported by OpenGL 3 + */ + @Deprecated + Clamp, + /** + * mirrors and clamps the texture coordinate, where mirroring and + * clamping a value f computes: + * mirrorClamp(f) = min(1, max(1/(2*N), + * abs(f))) where N + * is the size of the one-, two-, or three-dimensional texture image in + * the direction of wrapping. (Introduced after OpenGL1.4) Falls back on + * Clamp if not supported. + * + * @deprecated Not supported by OpenGL 3 + */ + @Deprecated + MirrorClamp, + + /** + * coordinate will be clamped to the range [-1/(2N), 1 + 1/(2N)] where N + * is the size of the texture in the direction of clamping. Falls back + * on Clamp if not supported. + * + * @deprecated Not supported by OpenGL 3 or OpenGL ES 2 + */ + @Deprecated + BorderClamp, + /** + * Wrap mode MIRROR_CLAMP_TO_BORDER_EXT mirrors and clamps to border the + * texture coordinate, where mirroring and clamping to border a value f + * computes: + * mirrorClampToBorder(f) = min(1+1/(2*N), max(1/(2*N), abs(f))) + * where N is the size of the one-, two-, or three-dimensional texture + * image in the direction of wrapping. (Introduced after OpenGL1.4) + * Falls back on BorderClamp if not supported. + * + * @deprecated Not supported by OpenGL 3 + */ + @Deprecated + MirrorBorderClamp, + /** + * coordinate will be clamped to the range [1/(2N), 1 - 1/(2N)] where N + * is the size of the texture in the direction of clamping. Falls back + * on Clamp if not supported. + */ + EdgeClamp, + + /** + * mirrors and clamps to edge the texture coordinate, where mirroring + * and clamping to edge a value f computes: + * mirrorClampToEdge(f) = min(1-1/(2*N), max(1/(2*N), abs(f))) + * where N is the size of the one-, two-, or three-dimensional texture + * image in the direction of wrapping. (Introduced after OpenGL1.4) + * Falls back on EdgeClamp if not supported. + * + * @deprecated Not supported by OpenGL 3 + */ + @Deprecated + MirrorEdgeClamp; + + public static WrapMode of(IntEnum address) { + if (address.is(AddressMode.Repeat)) return Repeat; + else if (address.is(AddressMode.MirroredRepeat)) return MirroredRepeat; + else if (address.is(AddressMode.ClampToBorder)) return BorderClamp; + else return EdgeClamp; + } + + } + + public enum WrapAxis { + /** + * S wrapping (u or "horizontal" wrap) + */ + S, + /** + * T wrapping (v or "vertical" wrap) + */ + T, + /** + * R wrapping (w or "depth" wrap) + */ + R; + } + + /** + * If this texture is a depth texture (the format is Depth*) then + * this value may be used to compare the texture depth to the R texture + * coordinate. + */ + public enum ShadowCompareMode { + /** + * Shadow comparison mode is disabled. + * Texturing is done normally. + */ + Off, + + /** + * {@code + * Compares the 3rd texture coordinate R to the value + * in this depth texture. If R <= texture value then result is 1.0, + * otherwise, result is 0.0. If filtering is set to bilinear or trilinear + * the implementation may sample the texture multiple times to provide + * smoother results in the range [0, 1].} + */ + LessOrEqual, + + /** + * {@code + * Compares the 3rd texture coordinate R to the value + * in this depth texture. If R >= texture value then result is 1.0, + * otherwise, result is 0.0. If filtering is set to bilinear or trilinear + * the implementation may sample the texture multiple times to provide + * smoother results in the range [0, 1].} + */ + GreaterOrEqual + } + + /** + * The name of the texture (if loaded as a resource). + */ + private String name = null; + + /** + * The image stored in the texture + */ + private GlImage image = null; + + /** + * The texture key allows to reload a texture from a file + * if needed. + */ + private TextureKey key = null; + + private MinFilter minificationFilter = MinFilter.BilinearNoMipMaps; + private MagFilter magnificationFilter = MagFilter.Bilinear; + private ShadowCompareMode shadowCompareMode = ShadowCompareMode.Off; + private int anisotropicFilter; + + /** + * @return A cloned Texture object. + */ + @Override + public GlTexture clone(){ + try { + return (GlTexture) super.clone(); + } catch (CloneNotSupportedException ex) { + throw new AssertionError(); + } + } + + @Override + public long getId() { + return VK10.VK_NULL_HANDLE; + } + + /** + * Constructor instantiates a new Texture object with default + * attributes. + */ + public GlTexture() { + } + + /** + * @return the MinificationFilterMode of this texture. + */ + public MinFilter getMinFilter() { + return minificationFilter; + } + + /** + * @param minificationFilter + * the new MinificationFilterMode for this texture. + * @throws IllegalArgumentException + * if minificationFilter is null + */ + public void setMinFilter(MinFilter minificationFilter) { + if (minificationFilter == null) { + throw new IllegalArgumentException( + "minificationFilter can not be null."); + } + this.minificationFilter = minificationFilter; + if (minificationFilter.usesMipMapLevels() && image != null && !image.isGeneratedMipmapsRequired() && !image.hasMipmaps()) { + image.setNeedGeneratedMipmaps(); + } + } + + /** + * @return the MagnificationFilterMode of this texture. + */ + public MagFilter getMagFilter() { + return magnificationFilter; + } + + /** + * @param magnificationFilter + * the new MagnificationFilter for this texture. + * @throws IllegalArgumentException + * if magnificationFilter is null + */ + public void setMagFilter(MagFilter magnificationFilter) { + if (magnificationFilter == null) { + throw new IllegalArgumentException( + "magnificationFilter can not be null."); + } + this.magnificationFilter = magnificationFilter; + } + + /** + * @return The ShadowCompareMode of this texture. + * @see ShadowCompareMode + */ + public ShadowCompareMode getShadowCompareMode(){ + return shadowCompareMode; + } + + /** + * @param compareMode + * the new ShadowCompareMode for this texture. + * @throws IllegalArgumentException + * if compareMode is null + * @see ShadowCompareMode + */ + public void setShadowCompareMode(ShadowCompareMode compareMode){ + if (compareMode == null){ + throw new IllegalArgumentException( + "compareMode can not be null."); + } + this.shadowCompareMode = compareMode; + } + + /** + * setImage sets the image object that defines the texture. + * + * @param image + * the image that defines the texture. + */ + public void setImage(GlImage image) { + this.image = image; + + // Test if mipmap generation required. + setMinFilter(getMinFilter()); + } + + /** + * @param key The texture key that was used to load this texture + */ + @Override + public void setKey(AssetKey key){ + this.key = (TextureKey) key; + } + + @Override + public AssetKey getKey(){ + return this.key; + } + + @Override + public GlTexture getView() { + return this; + } + + @Override + public GlImage getImage() { + return image; + } + + /** + * setWrap sets the wrap mode of this texture for a + * particular axis. + * + * @param axis + * the texture axis to apply the wrap mode to. + * @param mode + * the wrap mode for the given axis of the texture. + * @throws IllegalArgumentException + * if axis or mode are null or invalid for this type of texture + */ + public abstract void setWrap(WrapAxis axis, WrapMode mode); + + /** + * setWrap sets the wrap mode of this texture for all axis. + * + * @param mode + * the wrap mode for the given axis of the texture. + * @throws IllegalArgumentException + * if mode is null or invalid for this type of texture + */ + public abstract void setWrap(WrapMode mode); + + /** + * getWrap returns the wrap mode for a given coordinate axis + * on this texture. + * + * @param axis + * the axis to return for + * @return the wrap mode of the texture. + * @throws IllegalArgumentException + * if axis is null or invalid for this type of texture + */ + public abstract WrapMode getWrap(WrapAxis axis); + + public abstract Type getType(); + + @Override + public IntEnum getViewType() { + return ImageView.Type.of(getType()); + } + + @Override + public int getBaseMipmap() { + return 0; + } + + @Override + public int getMipmapCount() { + return 0; + } + + @Override + public int getBaseLayer() { + return 0; + } + + @Override + public int getLayerCount() { + return 0; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + /** + * @return the anisotropic filtering level for this texture. Default value + * is 0 (use value from config), + * 1 means 1x (no anisotropy), 2 means x2, 4 is x4, etc. + */ + public int getAnisotropicFilter() { + return anisotropicFilter; + } + + /** + * @param level + * the anisotropic filtering level for this texture. + */ + public void setAnisotropicFilter(int level) { + anisotropicFilter = Math.max(0, level); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()); + sb.append("[name=").append(name); + if (image != null) { + sb.append(", image=").append(image.toString()); + } + + sb.append("]"); + return sb.toString(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final GlTexture other = (GlTexture) obj; + + // NOTE: Since images are generally considered unique assets in jME3, + // using the image's equals() implementation is not necessary here + // (would be too slow) + if (this.image != other.image) { + return false; + } + if (this.minificationFilter != other.minificationFilter) { + return false; + } + if (this.magnificationFilter != other.magnificationFilter) { + return false; + } + if (this.shadowCompareMode != other.shadowCompareMode) { + return false; + } + if (this.anisotropicFilter != other.anisotropicFilter) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int hash = 5; + // NOTE: Since images are generally considered unique assets in jME3, + // using the image's hashCode() implementation is not necessary here + // (would be too slow) + hash = 67 * hash + (this.image != null ? System.identityHashCode(this.image) : 0); + hash = 67 * hash + (this.minificationFilter != null ? this.minificationFilter.hashCode() : 0); + hash = 67 * hash + (this.magnificationFilter != null ? this.magnificationFilter.hashCode() : 0); + hash = 67 * hash + (this.shadowCompareMode != null ? this.shadowCompareMode.hashCode() : 0); + hash = 67 * hash + this.anisotropicFilter; + return hash; + } + + /** Retrieve a basic clone of this Texture (ie, clone everything but the + * image data, which is shared) + * + * @param rVal storage for the clone (not null, modified) + * @return Texture + * + * @deprecated Use {@link GlTexture#clone()} instead. + */ + @Deprecated + public GlTexture createSimpleClone(GlTexture rVal) { + rVal.setMinFilter(minificationFilter); + rVal.setMagFilter(magnificationFilter); + rVal.setShadowCompareMode(shadowCompareMode); + rVal.setAnisotropicFilter(anisotropicFilter); + rVal.setImage(image); // NOT CLONED. + rVal.setKey(key); + rVal.setName(name); + return rVal; + } + + /** + * @return a new Texture + * @deprecated Use {@link GlTexture#clone()} instead. + */ + @Deprecated + public abstract GlTexture createSimpleClone(); + + @Override + public void write(JmeExporter e) throws IOException { + OutputCapsule capsule = e.getCapsule(this); + capsule.write(name, "name", null); + + if (key == null){ + // no texture key is set, try to save image instead then + capsule.write(image, "image", null); + }else{ + capsule.write(key, "key", null); + } + + capsule.write(anisotropicFilter, "anisotropicFilter", 1); + capsule.write(minificationFilter, "minificationFilter", + MinFilter.BilinearNoMipMaps); + capsule.write(magnificationFilter, "magnificationFilter", + MagFilter.Bilinear); + } + + @Override + public void read(JmeImporter importer) throws IOException { + InputCapsule capsule = importer.getCapsule(this); + name = capsule.readString("name", null); + key = (TextureKey) capsule.readSavable("key", null); + + // load texture from key, if available + if (key != null) { + // key is available, so try the texture from there. + try { + GlTexture loadedTex = (GlTexture)importer.getAssetManager().loadTexture(key); + image = loadedTex.getImage(); + } catch (AssetNotFoundException ex){ + Logger.getLogger(GlTexture.class.getName()).log(Level.SEVERE, "Cannot locate texture {0}", key); + image = PlaceholderAssets.getPlaceholderImage(importer.getAssetManager()); + } + }else{ + // no key is set on the texture. Attempt to load an embedded image + image = (GlImage) capsule.readSavable("image", null); + if (image == null){ + // TODO: what to print out here? the texture has no key or data, there's no useful information .. + // assume texture.name is set even though the key is null + Logger.getLogger(GlTexture.class.getName()) + .log(Level.SEVERE, "Cannot load embedded image {0}", toString()); + } + } + + setAnisotropicFilter(capsule.readInt("anisotropicFilter", 1)); + setMinFilter(capsule.readEnum("minificationFilter", + MinFilter.class, + MinFilter.BilinearNoMipMaps)); + setMagFilter(capsule.readEnum("magnificationFilter", + MagFilter.class, MagFilter.Bilinear)); + } +} diff --git a/jme3-core/src/main/java/com/jme3/texture/ImageView.java b/jme3-core/src/main/java/com/jme3/texture/ImageView.java new file mode 100644 index 0000000000..311db00d82 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/texture/ImageView.java @@ -0,0 +1,57 @@ +package com.jme3.texture; + +import com.jme3.vulkan.images.GpuImage; +import com.jme3.vulkan.util.IntEnum; + +import static org.lwjgl.vulkan.VK10.*; + +public interface ImageView { + + enum Type implements IntEnum { + + OneDemensional(VK_IMAGE_VIEW_TYPE_1D), + TwoDemensional(VK_IMAGE_VIEW_TYPE_2D), + ThreeDemensional(VK_IMAGE_VIEW_TYPE_3D), + OneDemensionalArray(VK_IMAGE_VIEW_TYPE_1D_ARRAY), + TwoDemensionalArray(VK_IMAGE_VIEW_TYPE_2D_ARRAY), + Cube(VK_IMAGE_VIEW_TYPE_CUBE), + CubeArray(VK_IMAGE_VIEW_TYPE_CUBE_ARRAY); + + private final int vkEnum; + + Type(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + + public static Type of(GlTexture.Type type) { + switch (type) { + case TwoDimensional: return TwoDemensional; + case TwoDimensionalArray: return TwoDemensionalArray; + case ThreeDimensional: return ThreeDemensional; + case CubeMap: return Cube; + default: throw new UnsupportedOperationException("No conversion implemented for " + type); + } + } + + } + + long getId(); + + T getImage(); + + IntEnum getViewType(); + + int getBaseMipmap(); + + int getMipmapCount(); + + int getBaseLayer(); + + int getLayerCount(); + +} diff --git a/jme3-core/src/main/java/com/jme3/texture/Texture.java b/jme3-core/src/main/java/com/jme3/texture/Texture.java index 3ba9b78128..fabca33f88 100644 --- a/jme3-core/src/main/java/com/jme3/texture/Texture.java +++ b/jme3-core/src/main/java/com/jme3/texture/Texture.java @@ -1,647 +1,15 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.texture; -import com.jme3.asset.AssetKey; -import com.jme3.asset.AssetNotFoundException; -import com.jme3.asset.CloneableSmartAsset; -import com.jme3.asset.TextureKey; -import com.jme3.export.*; -import com.jme3.util.PlaceholderAssets; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; +import com.jme3.vulkan.images.GpuImage; -/** - * Texture defines a texture object to be used to display an - * image on a piece of geometry. The image to be displayed is defined by the - * Image class. All attributes required for texture mapping are - * contained within this class. This includes mipmapping if desired, - * magnificationFilter options, apply options and correction options. Default - * values are as follows: minificationFilter - NearestNeighborNoMipMaps, - * magnificationFilter - NearestNeighbor, wrap - EdgeClamp on S,T and R, apply - - * Modulate, environment - None. - * - * @see com.jme3.texture.Image - * @author Mark Powell - * @author Joshua Slack - * @version $Id: Texture.java 4131 2009-03-19 20:15:28Z blaine.dev $ - */ -public abstract class Texture implements CloneableSmartAsset, Savable, Cloneable { +public interface Texture , I extends GpuImage> { - public enum Type { + long getId(); - /** - * Two dimensional texture (default). A rectangle. - */ - TwoDimensional, + V getView(); - /** - * An array of two-dimensional textures. - */ - TwoDimensionalArray, - - /** - * Three-dimensional texture. (A cube) - */ - ThreeDimensional, - - /** - * A set of 6 TwoDimensional textures arranged as faces of a cube facing - * inwards. - */ - CubeMap; + default I getImage() { + return getView().getImage(); } - public enum MinFilter { - - /** - * Nearest neighbor interpolation is the fastest and crudest filtering - * method - it simply uses the color of the texel closest to the pixel - * center for the pixel color. While fast, this results in aliasing and - * shimmering during minification. (GL equivalent: GL_NEAREST) - */ - NearestNoMipMaps(false), - - /** - * In this method the four nearest texels to the pixel center are - * sampled (at texture level 0), and their colors are combined by - * weighted averages. Though smoother, without mipmaps it suffers the - * same aliasing and shimmering problems as nearest - * NearestNeighborNoMipMaps. (GL equivalent: GL_LINEAR) - */ - BilinearNoMipMaps(false), - - /** - * Same as NearestNeighborNoMipMaps except that instead of using samples - * from texture level 0, the closest mipmap level is chosen based on - * distance. This reduces the aliasing and shimmering significantly, but - * does not help with blockiness. (GL equivalent: - * GL_NEAREST_MIPMAP_NEAREST) - */ - NearestNearestMipMap(true), - - /** - * Same as BilinearNoMipMaps except that instead of using samples from - * texture level 0, the closest mipmap level is chosen based on - * distance. By using mipmapping we avoid the aliasing and shimmering - * problems of BilinearNoMipMaps. (GL equivalent: - * GL_LINEAR_MIPMAP_NEAREST) - */ - BilinearNearestMipMap(true), - - /** - * Similar to NearestNeighborNoMipMaps except that instead of using - * samples from texture level 0, a sample is chosen from each of the - * closest (by distance) two mipmap levels. A weighted average of these - * two samples is returned. (GL equivalent: GL_NEAREST_MIPMAP_LINEAR) - */ - NearestLinearMipMap(true), - - /** - * Trilinear filtering is a remedy to a common artifact seen in - * mipmapped bilinearly filtered images: an abrupt and very noticeable - * change in quality at boundaries where the renderer switches from one - * mipmap level to the next. Trilinear filtering solves this by doing a - * texture lookup and bilinear filtering on the two closest mipmap - * levels (one higher and one lower quality), and then linearly - * interpolating the results. This results in a smooth degradation of - * texture quality as distance from the viewer increases, rather than a - * series of sudden drops. Of course, closer than Level 0 there is only - * one mipmap level available, and the algorithm reverts to bilinear - * filtering (GL equivalent: GL_LINEAR_MIPMAP_LINEAR) - */ - Trilinear(true); - - private final boolean usesMipMapLevels; - - private MinFilter(boolean usesMipMapLevels) { - this.usesMipMapLevels = usesMipMapLevels; - } - - public boolean usesMipMapLevels() { - return usesMipMapLevels; - } - } - - public enum MagFilter { - - /** - * Nearest neighbor interpolation is the fastest and crudest filtering - * mode - it simply uses the color of the texel closest to the pixel - * center for the pixel color. While fast, this results in texture - * 'blockiness' during magnification. (GL equivalent: GL_NEAREST) - */ - Nearest, - - /** - * In this mode the four nearest texels to the pixel center are sampled - * (at the closest mipmap level), and their colors are combined by - * weighted average according to distance. This removes the 'blockiness' - * seen during magnification, as there is now a smooth gradient of color - * change from one texel to the next, instead of an abrupt jump as the - * pixel center crosses the texel boundary. (GL equivalent: GL_LINEAR) - */ - Bilinear; - - } - - public enum WrapMode { - /** - * Only the fractional portion of the coordinate is considered. - */ - Repeat, - - /** - * Only the fractional portion of the coordinate is considered, but if - * the integer portion is odd, we'll use 1 - the fractional portion. - * (Introduced around OpenGL1.4) Falls back on Repeat if not supported. - */ - MirroredRepeat, - - /** - * coordinate will be clamped to [0,1] - * - * @deprecated Not supported by OpenGL 3 - */ - @Deprecated - Clamp, - /** - * mirrors and clamps the texture coordinate, where mirroring and - * clamping a value f computes: - * mirrorClamp(f) = min(1, max(1/(2*N), - * abs(f))) where N - * is the size of the one-, two-, or three-dimensional texture image in - * the direction of wrapping. (Introduced after OpenGL1.4) Falls back on - * Clamp if not supported. - * - * @deprecated Not supported by OpenGL 3 - */ - @Deprecated - MirrorClamp, - - /** - * coordinate will be clamped to the range [-1/(2N), 1 + 1/(2N)] where N - * is the size of the texture in the direction of clamping. Falls back - * on Clamp if not supported. - * - * @deprecated Not supported by OpenGL 3 or OpenGL ES 2 - */ - @Deprecated - BorderClamp, - /** - * Wrap mode MIRROR_CLAMP_TO_BORDER_EXT mirrors and clamps to border the - * texture coordinate, where mirroring and clamping to border a value f - * computes: - * mirrorClampToBorder(f) = min(1+1/(2*N), max(1/(2*N), abs(f))) - * where N is the size of the one-, two-, or three-dimensional texture - * image in the direction of wrapping. (Introduced after OpenGL1.4) - * Falls back on BorderClamp if not supported. - * - * @deprecated Not supported by OpenGL 3 - */ - @Deprecated - MirrorBorderClamp, - /** - * coordinate will be clamped to the range [1/(2N), 1 - 1/(2N)] where N - * is the size of the texture in the direction of clamping. Falls back - * on Clamp if not supported. - */ - EdgeClamp, - - /** - * mirrors and clamps to edge the texture coordinate, where mirroring - * and clamping to edge a value f computes: - * mirrorClampToEdge(f) = min(1-1/(2*N), max(1/(2*N), abs(f))) - * where N is the size of the one-, two-, or three-dimensional texture - * image in the direction of wrapping. (Introduced after OpenGL1.4) - * Falls back on EdgeClamp if not supported. - * - * @deprecated Not supported by OpenGL 3 - */ - @Deprecated - MirrorEdgeClamp; - } - - public enum WrapAxis { - /** - * S wrapping (u or "horizontal" wrap) - */ - S, - /** - * T wrapping (v or "vertical" wrap) - */ - T, - /** - * R wrapping (w or "depth" wrap) - */ - R; - } - - /** - * If this texture is a depth texture (the format is Depth*) then - * this value may be used to compare the texture depth to the R texture - * coordinate. - */ - public enum ShadowCompareMode { - /** - * Shadow comparison mode is disabled. - * Texturing is done normally. - */ - Off, - - /** - * {@code - * Compares the 3rd texture coordinate R to the value - * in this depth texture. If R <= texture value then result is 1.0, - * otherwise, result is 0.0. If filtering is set to bilinear or trilinear - * the implementation may sample the texture multiple times to provide - * smoother results in the range [0, 1].} - */ - LessOrEqual, - - /** - * {@code - * Compares the 3rd texture coordinate R to the value - * in this depth texture. If R >= texture value then result is 1.0, - * otherwise, result is 0.0. If filtering is set to bilinear or trilinear - * the implementation may sample the texture multiple times to provide - * smoother results in the range [0, 1].} - */ - GreaterOrEqual - } - - /** - * The name of the texture (if loaded as a resource). - */ - private String name = null; - - /** - * The image stored in the texture - */ - private Image image = null; - - /** - * The texture key allows to reload a texture from a file - * if needed. - */ - private TextureKey key = null; - - private MinFilter minificationFilter = MinFilter.BilinearNoMipMaps; - private MagFilter magnificationFilter = MagFilter.Bilinear; - private ShadowCompareMode shadowCompareMode = ShadowCompareMode.Off; - private int anisotropicFilter; - - /** - * @return A cloned Texture object. - */ - @Override - public Texture clone(){ - try { - return (Texture) super.clone(); - } catch (CloneNotSupportedException ex) { - throw new AssertionError(); - } - } - - /** - * Constructor instantiates a new Texture object with default - * attributes. - */ - public Texture() { - } - - /** - * @return the MinificationFilterMode of this texture. - */ - public MinFilter getMinFilter() { - return minificationFilter; - } - - /** - * @param minificationFilter - * the new MinificationFilterMode for this texture. - * @throws IllegalArgumentException - * if minificationFilter is null - */ - public void setMinFilter(MinFilter minificationFilter) { - if (minificationFilter == null) { - throw new IllegalArgumentException( - "minificationFilter can not be null."); - } - this.minificationFilter = minificationFilter; - if (minificationFilter.usesMipMapLevels() && image != null && !image.isGeneratedMipmapsRequired() && !image.hasMipmaps()) { - image.setNeedGeneratedMipmaps(); - } - } - - /** - * @return the MagnificationFilterMode of this texture. - */ - public MagFilter getMagFilter() { - return magnificationFilter; - } - - /** - * @param magnificationFilter - * the new MagnificationFilter for this texture. - * @throws IllegalArgumentException - * if magnificationFilter is null - */ - public void setMagFilter(MagFilter magnificationFilter) { - if (magnificationFilter == null) { - throw new IllegalArgumentException( - "magnificationFilter can not be null."); - } - this.magnificationFilter = magnificationFilter; - } - - /** - * @return The ShadowCompareMode of this texture. - * @see ShadowCompareMode - */ - public ShadowCompareMode getShadowCompareMode(){ - return shadowCompareMode; - } - - /** - * @param compareMode - * the new ShadowCompareMode for this texture. - * @throws IllegalArgumentException - * if compareMode is null - * @see ShadowCompareMode - */ - public void setShadowCompareMode(ShadowCompareMode compareMode){ - if (compareMode == null){ - throw new IllegalArgumentException( - "compareMode can not be null."); - } - this.shadowCompareMode = compareMode; - } - - /** - * setImage sets the image object that defines the texture. - * - * @param image - * the image that defines the texture. - */ - public void setImage(Image image) { - this.image = image; - - // Test if mipmap generation required. - setMinFilter(getMinFilter()); - } - - /** - * @param key The texture key that was used to load this texture - */ - @Override - public void setKey(AssetKey key){ - this.key = (TextureKey) key; - } - - @Override - public AssetKey getKey(){ - return this.key; - } - - /** - * getImage returns the image data that makes up this - * texture. If no image data has been set, this will return null. - * - * @return the image data that makes up the texture. - */ - public Image getImage() { - return image; - } - - /** - * setWrap sets the wrap mode of this texture for a - * particular axis. - * - * @param axis - * the texture axis to apply the wrap mode to. - * @param mode - * the wrap mode for the given axis of the texture. - * @throws IllegalArgumentException - * if axis or mode are null or invalid for this type of texture - */ - public abstract void setWrap(WrapAxis axis, WrapMode mode); - - /** - * setWrap sets the wrap mode of this texture for all axis. - * - * @param mode - * the wrap mode for the given axis of the texture. - * @throws IllegalArgumentException - * if mode is null or invalid for this type of texture - */ - public abstract void setWrap(WrapMode mode); - - /** - * getWrap returns the wrap mode for a given coordinate axis - * on this texture. - * - * @param axis - * the axis to return for - * @return the wrap mode of the texture. - * @throws IllegalArgumentException - * if axis is null or invalid for this type of texture - */ - public abstract WrapMode getWrap(WrapAxis axis); - - public abstract Type getType(); - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - /** - * @return the anisotropic filtering level for this texture. Default value - * is 0 (use value from config), - * 1 means 1x (no anisotropy), 2 means x2, 4 is x4, etc. - */ - public int getAnisotropicFilter() { - return anisotropicFilter; - } - - /** - * @param level - * the anisotropic filtering level for this texture. - */ - public void setAnisotropicFilter(int level) { - anisotropicFilter = Math.max(0, level); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(getClass().getSimpleName()); - sb.append("[name=").append(name); - if (image != null) { - sb.append(", image=").append(image.toString()); - } - - sb.append("]"); - return sb.toString(); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final Texture other = (Texture) obj; - - // NOTE: Since images are generally considered unique assets in jME3, - // using the image's equals() implementation is not necessary here - // (would be too slow) - if (this.image != other.image) { - return false; - } - if (this.minificationFilter != other.minificationFilter) { - return false; - } - if (this.magnificationFilter != other.magnificationFilter) { - return false; - } - if (this.shadowCompareMode != other.shadowCompareMode) { - return false; - } - if (this.anisotropicFilter != other.anisotropicFilter) { - return false; - } - return true; - } - - @Override - public int hashCode() { - int hash = 5; - // NOTE: Since images are generally considered unique assets in jME3, - // using the image's hashCode() implementation is not necessary here - // (would be too slow) - hash = 67 * hash + (this.image != null ? System.identityHashCode(this.image) : 0); - hash = 67 * hash + (this.minificationFilter != null ? this.minificationFilter.hashCode() : 0); - hash = 67 * hash + (this.magnificationFilter != null ? this.magnificationFilter.hashCode() : 0); - hash = 67 * hash + (this.shadowCompareMode != null ? this.shadowCompareMode.hashCode() : 0); - hash = 67 * hash + this.anisotropicFilter; - return hash; - } - - /** Retrieve a basic clone of this Texture (ie, clone everything but the - * image data, which is shared) - * - * @param rVal storage for the clone (not null, modified) - * @return Texture - * - * @deprecated Use {@link Texture#clone()} instead. - */ - @Deprecated - public Texture createSimpleClone(Texture rVal) { - rVal.setMinFilter(minificationFilter); - rVal.setMagFilter(magnificationFilter); - rVal.setShadowCompareMode(shadowCompareMode); - rVal.setAnisotropicFilter(anisotropicFilter); - rVal.setImage(image); // NOT CLONED. - rVal.setKey(key); - rVal.setName(name); - return rVal; - } - - /** - * @return a new Texture - * @deprecated Use {@link Texture#clone()} instead. - */ - @Deprecated - public abstract Texture createSimpleClone(); - - @Override - public void write(JmeExporter e) throws IOException { - OutputCapsule capsule = e.getCapsule(this); - capsule.write(name, "name", null); - - if (key == null){ - // no texture key is set, try to save image instead then - capsule.write(image, "image", null); - }else{ - capsule.write(key, "key", null); - } - - capsule.write(anisotropicFilter, "anisotropicFilter", 1); - capsule.write(minificationFilter, "minificationFilter", - MinFilter.BilinearNoMipMaps); - capsule.write(magnificationFilter, "magnificationFilter", - MagFilter.Bilinear); - } - - @Override - public void read(JmeImporter importer) throws IOException { - InputCapsule capsule = importer.getCapsule(this); - name = capsule.readString("name", null); - key = (TextureKey) capsule.readSavable("key", null); - - // load texture from key, if available - if (key != null) { - // key is available, so try the texture from there. - try { - Texture loadedTex = importer.getAssetManager().loadTexture(key); - image = loadedTex.getImage(); - } catch (AssetNotFoundException ex){ - Logger.getLogger(Texture.class.getName()).log(Level.SEVERE, "Cannot locate texture {0}", key); - image = PlaceholderAssets.getPlaceholderImage(importer.getAssetManager()); - } - }else{ - // no key is set on the texture. Attempt to load an embedded image - image = (Image) capsule.readSavable("image", null); - if (image == null){ - // TODO: what to print out here? the texture has no key or data, there's no useful information .. - // assume texture.name is set even though the key is null - Logger.getLogger(Texture.class.getName()) - .log(Level.SEVERE, "Cannot load embedded image {0}", toString()); - } - } - - setAnisotropicFilter(capsule.readInt("anisotropicFilter", 1)); - setMinFilter(capsule.readEnum("minificationFilter", - MinFilter.class, - MinFilter.BilinearNoMipMaps)); - setMagFilter(capsule.readEnum("magnificationFilter", - MagFilter.class, MagFilter.Bilinear)); - } } diff --git a/jme3-core/src/main/java/com/jme3/texture/Texture2D.java b/jme3-core/src/main/java/com/jme3/texture/Texture2D.java index ab633aed49..605ccac438 100644 --- a/jme3-core/src/main/java/com/jme3/texture/Texture2D.java +++ b/jme3-core/src/main/java/com/jme3/texture/Texture2D.java @@ -36,12 +36,13 @@ import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; import com.jme3.texture.image.ColorSpace; + import java.io.IOException; /** * @author Joshua Slack */ -public class Texture2D extends Texture { +public class Texture2D extends GlTexture { private WrapMode wrapS = WrapMode.EdgeClamp; private WrapMode wrapT = WrapMode.EdgeClamp; @@ -57,7 +58,7 @@ public Texture2D(){ * Creates a new two-dimensional texture using the given image. * @param img The image to use. */ - public Texture2D(Image img){ + public Texture2D(GlImage img){ super(); setImage(img); if (img.getData(0) == null) { @@ -70,41 +71,41 @@ public Texture2D(Image img){ * Creates a new two-dimensional texture for the purpose of offscreen * rendering. * - * @see com.jme3.texture.FrameBuffer + * @see GlFrameBuffer * * @param width the desired width (in pixels) * @param height the desired height (in pixels) * @param format the desired format */ - public Texture2D(int width, int height, Image.Format format){ - this(new Image(format, width, height, null, ColorSpace.Linear)); + public Texture2D(int width, int height, GlImage.Format format){ + this(new GlImage(format, width, height, null, ColorSpace.Linear)); } /** * Creates a new two-dimensional texture for the purpose of offscreen * rendering. * - * @see com.jme3.texture.FrameBuffer + * @see GlFrameBuffer * * @param width the desired width (in pixels) * @param height the desired height (in pixels) * @param numSamples the desired degree of multi-sampling (≥1) * @param format the desired format */ - public Texture2D(int width, int height, int numSamples, Image.Format format){ - this(new Image(format, width, height, null, ColorSpace.Linear)); + public Texture2D(int width, int height, int numSamples, GlImage.Format format){ + this(new GlImage(format, width, height, null, ColorSpace.Linear)); getImage().setMultiSamples(numSamples); } @Override - public Texture createSimpleClone() { + public GlTexture createSimpleClone() { Texture2D clone = new Texture2D(); createSimpleClone(clone); return clone; } @Override - public Texture createSimpleClone(Texture rVal) { + public GlTexture createSimpleClone(GlTexture rVal) { rVal.setWrap(WrapAxis.S, wrapS); rVal.setWrap(WrapAxis.T, wrapT); return super.createSimpleClone(rVal); diff --git a/jme3-core/src/main/java/com/jme3/texture/Texture3D.java b/jme3-core/src/main/java/com/jme3/texture/Texture3D.java index bb12a858dd..6b087a30c5 100644 --- a/jme3-core/src/main/java/com/jme3/texture/Texture3D.java +++ b/jme3-core/src/main/java/com/jme3/texture/Texture3D.java @@ -41,7 +41,7 @@ /** * @author Maarten Steur */ -public class Texture3D extends Texture { +public class Texture3D extends GlTexture { private WrapMode wrapS = WrapMode.EdgeClamp; private WrapMode wrapT = WrapMode.EdgeClamp; @@ -58,10 +58,10 @@ public Texture3D() { * Creates a new three-dimensional texture using the given image. * @param img The image to use. */ - public Texture3D(Image img) { + public Texture3D(GlImage img) { super(); setImage(img); - if (img.getFormat().isDepthFormat()) { + if (img.getGlFormat().isDepthFormat()) { setMagFilter(MagFilter.Nearest); setMinFilter(MinFilter.NearestNoMipMaps); } @@ -71,22 +71,22 @@ public Texture3D(Image img) { * Creates a new three-dimensional texture for the purpose of offscreen * rendering. * - * @see com.jme3.texture.FrameBuffer + * @see GlFrameBuffer * * @param width the desired width (in pixels) * @param height the desired height (in pixels) * @param depth the desired depth * @param format the desired format */ - public Texture3D(int width, int height, int depth, Image.Format format) { - this(new Image(format, width, height, depth, null, ColorSpace.Linear)); + public Texture3D(int width, int height, int depth, GlImage.Format format) { + this(new GlImage(format, width, height, depth, null, ColorSpace.Linear)); } /** * Creates a new three-dimensional texture for the purpose of offscreen * rendering. * - * @see com.jme3.texture.FrameBuffer + * @see GlFrameBuffer * * @param width the desired width (in pixels) * @param height the desired height (in pixels) @@ -94,20 +94,20 @@ public Texture3D(int width, int height, int depth, Image.Format format) { * @param numSamples the desired degree of multi-sampling (≥1) * @param format the desired format */ - public Texture3D(int width, int height, int depth, int numSamples, Image.Format format) { - this(new Image(format, width, height, depth, null, ColorSpace.Linear)); + public Texture3D(int width, int height, int depth, int numSamples, GlImage.Format format) { + this(new GlImage(format, width, height, depth, null, ColorSpace.Linear)); getImage().setMultiSamples(numSamples); } @Override - public Texture createSimpleClone() { + public GlTexture createSimpleClone() { Texture3D clone = new Texture3D(); createSimpleClone(clone); return clone; } @Override - public Texture createSimpleClone(Texture rVal) { + public GlTexture createSimpleClone(GlTexture rVal) { rVal.setWrap(WrapAxis.S, wrapS); rVal.setWrap(WrapAxis.T, wrapT); rVal.setWrap(WrapAxis.R, wrapR); diff --git a/jme3-core/src/main/java/com/jme3/texture/TextureArray.java b/jme3-core/src/main/java/com/jme3/texture/TextureArray.java index e839399c33..32416099ac 100644 --- a/jme3-core/src/main/java/com/jme3/texture/TextureArray.java +++ b/jme3-core/src/main/java/com/jme3/texture/TextureArray.java @@ -35,7 +35,7 @@ import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage.Format; import com.jme3.texture.image.ColorSpace; import java.io.IOException; import java.util.Arrays; @@ -48,7 +48,7 @@ * renderManager.getRenderer().getCaps().contains(Caps.TextureArray) * @author phate666 */ -public class TextureArray extends Texture { +public class TextureArray extends GlTexture { private WrapMode wrapS = WrapMode.EdgeClamp; private WrapMode wrapT = WrapMode.EdgeClamp; @@ -70,22 +70,22 @@ public TextureArray() { * * @param images the images to use (not null) */ - public TextureArray(List images) { + public TextureArray(List images) { super(); int width = images.get(0).getWidth(); int height = images.get(0).getHeight(); - Format format = images.get(0).getFormat(); + Format format = images.get(0).getGlFormat(); ColorSpace colorSpace = images.get(0).getColorSpace(); int[] mipMapSizes = images.get(0).getMipMapSizes(); - Image arrayImage = new Image(format, width, height, null, colorSpace); + GlImage arrayImage = new GlImage(format, width, height, null, colorSpace); arrayImage.setMipMapSizes(mipMapSizes); - for (Image img : images) { + for (GlImage img : images) { if (img.getHeight() != height || img.getWidth() != width) { throw new IllegalArgumentException("Images in texture array must have same dimensions"); } - if (img.getFormat() != format) { + if (img.getGlFormat() != format) { throw new IllegalArgumentException("Images in texture array must have same format"); } if (!Arrays.equals(mipMapSizes, img.getMipMapSizes())) { @@ -99,14 +99,14 @@ public TextureArray(List images) { } @Override - public Texture createSimpleClone() { + public GlTexture createSimpleClone() { TextureArray clone = new TextureArray(); createSimpleClone(clone); return clone; } @Override - public Texture createSimpleClone(Texture rVal) { + public GlTexture createSimpleClone(GlTexture rVal) { rVal.setWrap(WrapAxis.S, wrapS); rVal.setWrap(WrapAxis.T, wrapT); return super.createSimpleClone(rVal); diff --git a/jme3-core/src/main/java/com/jme3/texture/TextureCubeMap.java b/jme3-core/src/main/java/com/jme3/texture/TextureCubeMap.java index 40e6c037c3..01d3165d0c 100644 --- a/jme3-core/src/main/java/com/jme3/texture/TextureCubeMap.java +++ b/jme3-core/src/main/java/com/jme3/texture/TextureCubeMap.java @@ -55,7 +55,7 @@ * * @author Joshua Slack */ -public class TextureCubeMap extends Texture { +public class TextureCubeMap extends GlTexture { private WrapMode wrapS = WrapMode.EdgeClamp; private WrapMode wrapT = WrapMode.EdgeClamp; @@ -74,32 +74,32 @@ public TextureCubeMap() { super(); } - public TextureCubeMap(Image img) { + public TextureCubeMap(GlImage img) { super(); setImage(img); } - public TextureCubeMap(int width, int height, Image.Format format) { + public TextureCubeMap(int width, int height, GlImage.Format format) { this(createEmptyLayeredImage(width, height, 6, format)); } - private static Image createEmptyLayeredImage(int width, int height, - int layerCount, Image.Format format) { + private static GlImage createEmptyLayeredImage(int width, int height, + int layerCount, GlImage.Format format) { ArrayList layers = new ArrayList<>(); for (int i = 0; i < layerCount; i++) { layers.add(null); } - Image image = new Image(format, width, height, 0, layers, ColorSpace.Linear); + GlImage image = new GlImage(format, width, height, 0, layers, ColorSpace.Linear); return image; } @Override - public Texture createSimpleClone() { + public GlTexture createSimpleClone() { return createSimpleClone(new TextureCubeMap()); } @Override - public Texture createSimpleClone(Texture rVal) { + public GlTexture createSimpleClone(GlTexture rVal) { rVal.setWrap(WrapAxis.S, wrapS); rVal.setWrap(WrapAxis.T, wrapT); rVal.setWrap(WrapAxis.R, wrapR); diff --git a/jme3-core/src/main/java/com/jme3/texture/TextureImage.java b/jme3-core/src/main/java/com/jme3/texture/TextureImage.java index 31b705fa5d..9a86aae851 100644 --- a/jme3-core/src/main/java/com/jme3/texture/TextureImage.java +++ b/jme3-core/src/main/java/com/jme3/texture/TextureImage.java @@ -104,24 +104,24 @@ public int getGlEnum() { } - private Texture texture; + private GlTexture texture; private int level, layer; private Access access; private boolean updateFlag = true; - public TextureImage(Texture texture) { + public TextureImage(GlTexture texture) { this(texture, 0, -1, Access.ReadWrite); } - public TextureImage(Texture texture, Access access) { + public TextureImage(GlTexture texture, Access access) { this(texture, 0, -1, access); } - public TextureImage(Texture texture, int level, int layer) { + public TextureImage(GlTexture texture, int level, int layer) { this(texture, level, layer, Access.ReadWrite); } - public TextureImage(Texture texture, int level, int layer, Access access) { + public TextureImage(GlTexture texture, int level, int layer, Access access) { this.texture = Objects.requireNonNull(texture, "Underlying texture cannot be null"); this.level = level; this.layer = layer; @@ -143,9 +143,9 @@ public TextureImage(Texture texture, int level, int layer, Access access) { * @param unit texture unit to bind to */ public void bindImage(GL4 gl4, TextureUtil texUtil, int unit) { - Image img = texture.getImage(); - gl4.glBindImageTexture(unit, img.getId(), level, isLayered(), Math.max(layer, 0), - access.getGlEnum(), texUtil.getImageFormat(img.getFormat(), false).internalFormat); + GlImage img = texture.getImage(); + gl4.glBindImageTexture(unit, img.getNativeObject(), level, isLayered(), Math.max(layer, 0), + access.getGlEnum(), texUtil.getImageFormat(img.getGlFormat(), false).internalFormat); } /** @@ -172,7 +172,7 @@ public boolean clearUpdateNeeded() { * * @param texture wrapped texture (not null) */ - public void setTexture(Texture texture) { + public void setTexture(GlTexture texture) { Objects.requireNonNull(texture, "Wrapped texture cannot be null."); if (this.texture != texture) { this.texture = texture; @@ -235,7 +235,7 @@ public void setAccess(Access access) { * * @return underlying texture */ - public Texture getTexture() { + public GlTexture getTexture() { return texture; } @@ -244,7 +244,7 @@ public Texture getTexture() { * * @return */ - public Image getImage() { + public GlImage getImage() { return texture.getImage(); } @@ -253,8 +253,8 @@ public Image getImage() { * * @return */ - public Image.Format getFormat() { - return texture.getImage().getFormat(); + public GlImage.Format getFormat() { + return texture.getImage().getGlFormat(); } /** @@ -263,7 +263,7 @@ public Image.Format getFormat() { * @return */ public int getImageId() { - return texture.getImage().getId(); + return texture.getImage().getNativeObject(); } /** diff --git a/jme3-core/src/main/java/com/jme3/texture/TextureProcessor.java b/jme3-core/src/main/java/com/jme3/texture/TextureProcessor.java index c0dc0ad359..6d309d9cbc 100644 --- a/jme3-core/src/main/java/com/jme3/texture/TextureProcessor.java +++ b/jme3-core/src/main/java/com/jme3/texture/TextureProcessor.java @@ -41,13 +41,13 @@ public class TextureProcessor implements AssetProcessor { @Override public Object postProcess(AssetKey key, Object obj) { TextureKey texKey = (TextureKey) key; - Image img = (Image) obj; + GlImage img = (GlImage) obj; if (img == null) { return null; } - Texture tex; - if (texKey.getTextureTypeHint() == Texture.Type.CubeMap) { + GlTexture tex; + if (texKey.getTextureTypeHint() == GlTexture.Type.CubeMap) { if (texKey.isFlipY()) { // also flip -y and +y image in cubemap ByteBuffer pos_y = img.getData(2); @@ -55,7 +55,7 @@ public Object postProcess(AssetKey key, Object obj) { img.setData(3, pos_y); } tex = new TextureCubeMap(); - } else if (texKey.getTextureTypeHint() == Texture.Type.ThreeDimensional) { + } else if (texKey.getTextureTypeHint() == GlTexture.Type.ThreeDimensional) { tex = new Texture3D(); } else { tex = new Texture2D(); @@ -64,7 +64,7 @@ public Object postProcess(AssetKey key, Object obj) { // enable mipmaps if image has them // or generate them if requested by user if (img.hasMipmaps() || texKey.isGenerateMips()) { - tex.setMinFilter(Texture.MinFilter.Trilinear); + tex.setMinFilter(GlTexture.MinFilter.Trilinear); } tex.setAnisotropicFilter(texKey.getAnisotropy()); @@ -75,7 +75,7 @@ public Object postProcess(AssetKey key, Object obj) { @Override public Object createClone(Object obj) { - Texture tex = (Texture) obj; + GlTexture tex = (GlTexture) obj; return tex.clone(); } diff --git a/jme3-core/src/main/java/com/jme3/texture/image/DefaultImageRaster.java b/jme3-core/src/main/java/com/jme3/texture/image/DefaultImageRaster.java index 4de14566b4..f955739673 100644 --- a/jme3-core/src/main/java/com/jme3/texture/image/DefaultImageRaster.java +++ b/jme3-core/src/main/java/com/jme3/texture/image/DefaultImageRaster.java @@ -33,14 +33,14 @@ import com.jme3.math.ColorRGBA; import com.jme3.math.FastMath; -import com.jme3.texture.Image; +import com.jme3.texture.GlImage; import java.nio.ByteBuffer; public class DefaultImageRaster extends ImageRaster { private final int[] components = new int[4]; private ByteBuffer buffer; - private final Image image; + private final GlImage image; private final ImageCodec codec; private final int width; private final int height; @@ -56,7 +56,7 @@ private void rangeCheck(int x, int y) { } } - public DefaultImageRaster(Image image, int slice, int mipMapLevel, boolean convertToLinear) { + public DefaultImageRaster(GlImage image, int slice, int mipMapLevel, boolean convertToLinear) { int[] mipMapSizes = image.getMipMapSizes(); int availableMips = mipMapSizes != null ? mipMapSizes.length : 1; @@ -88,7 +88,7 @@ public DefaultImageRaster(Image image, int slice, int mipMapLevel, boolean conve this.convertToLinear = convertToLinear && image.getColorSpace() == ColorSpace.sRGB; this.buffer = image.getData(slice); - this.codec = ImageCodec.lookup(image.getFormat()); + this.codec = ImageCodec.lookup(image.getGlFormat()); if (codec instanceof ByteAlignedImageCodec || codec instanceof ByteOffsetImageCodec) { this.temp = new byte[codec.bpp]; diff --git a/jme3-core/src/main/java/com/jme3/texture/image/ImageCodec.java b/jme3-core/src/main/java/com/jme3/texture/image/ImageCodec.java index 993ce508b4..5598b6fc83 100644 --- a/jme3-core/src/main/java/com/jme3/texture/image/ImageCodec.java +++ b/jme3-core/src/main/java/com/jme3/texture/image/ImageCodec.java @@ -31,15 +31,15 @@ */ package com.jme3.texture.image; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; import java.nio.ByteBuffer; import java.util.EnumMap; abstract class ImageCodec { public static final int FLAG_F16 = 1, FLAG_F32 = 2, FLAG_GRAY = 4; //, FLAG_ALPHAONLY = 8, FLAG_SHAREDEXP = 16; - private static final EnumMap params = new EnumMap(Image.Format.class); + private static final EnumMap params = new EnumMap(GlImage.Format.class); protected final int bpp, type, maxAlpha, maxRed, maxGreen, maxBlue; protected final boolean isGray; diff --git a/jme3-core/src/main/java/com/jme3/texture/image/ImageRaster.java b/jme3-core/src/main/java/com/jme3/texture/image/ImageRaster.java index 1a75203225..758e77abcf 100644 --- a/jme3-core/src/main/java/com/jme3/texture/image/ImageRaster.java +++ b/jme3-core/src/main/java/com/jme3/texture/image/ImageRaster.java @@ -32,14 +32,14 @@ package com.jme3.texture.image; import com.jme3.math.ColorRGBA; -import com.jme3.texture.Image; +import com.jme3.texture.GlImage; /** - * Utility class for reading and writing from jME3 {@link Image images}. + * Utility class for reading and writing from jME3 {@link GlImage images}. *
* Allows directly manipulating pixels of the image by writing and * reading {@link ColorRGBA colors} at any coordinate, without - * regard to the underlying {@link com.jme3.texture.Image.Format format} of the image. + * regard to the underlying {@link GlImage.Format format} of the image. * NOTE: compressed and depth formats are not supported. * Special RGB formats like RGB111110F and RGB9E5 are not supported * at the moment, but may be added later on. For now @@ -72,14 +72,14 @@ public abstract class ImageRaster { * arrays or cubemaps. * @param mipMapLevel The mipmap level to read / write to. To access levels * other than 0, the image must have - * {@link Image#setMipMapSizes(int[]) mipmap sizes} set. + * {@link GlImage#setMipMapSizes(int[]) mipmap sizes} set. * @param convertToLinear If true, the application expects read or written * colors to be in linear color space (ImageRaster will * automatically perform a conversion as needed). If false, the application expects - * colors to be in the image's native {@link Image#getColorSpace() color space}. + * colors to be in the image's native {@link GlImage#getColorSpace() color space}. * @return An ImageRaster to read / write to the image. */ - public static ImageRaster create(Image image, int slice, int mipMapLevel, boolean convertToLinear) { + public static ImageRaster create(GlImage image, int slice, int mipMapLevel, boolean convertToLinear) { return new DefaultImageRaster(image, slice, mipMapLevel, convertToLinear); } @@ -91,7 +91,7 @@ public static ImageRaster create(Image image, int slice, int mipMapLevel, boolea * arrays or cubemaps. * @return An ImageRaster to read / write to the image. */ - public static ImageRaster create(Image image, int slice) { + public static ImageRaster create(GlImage image, int slice) { return create(image, slice, 0, false); } @@ -101,7 +101,7 @@ public static ImageRaster create(Image image, int slice) { * @param image The image to read / write to. * @return An ImageRaster to read / write to the image. */ - public static ImageRaster create(Image image) { + public static ImageRaster create(GlImage image) { if (image.getData().size() > 1) { throw new IllegalStateException("Use constructor that takes slices argument to read from multislice image"); } @@ -135,7 +135,7 @@ public ImageRaster() { * lower than 0.0 are still not allowed (as all formats are unsigned). *

* If the underlying format is grayscale (e.g. one of the luminance formats, - * such as {@link com.jme3.texture.Image.Format#Luminance8}) then a color to grayscale + * such as {@link GlImage.Format#Luminance8}) then a color to grayscale * conversion is done first, before writing the result into the image. *

* If the image lacks some components (such @@ -159,7 +159,7 @@ public ImageRaster() { *

* Any components that are not defined in the image format * will be set to 1.0 in the returned color. For example, - * reading from an {@link com.jme3.texture.Image.Format#Alpha8} format will + * reading from an {@link GlImage.Format#Alpha8} format will * return a ColorRGBA with the R, G, and B components set to 1.0, and * the A component set to the alpha in the image. *

@@ -169,7 +169,7 @@ public ImageRaster() { * Integer formats are converted to the range 0.0 - 1.0, based * on the maximum possible integer value that can be represented * by the number of bits the component has. - * For example, the {@link com.jme3.texture.Image.Format#RGB5A1} format can + * For example, the {@link GlImage.Format#RGB5A1} format can * contain the integer values 0 - 31, a conversion to floating point * is done by diving the integer value by 31 (done with floating point * precision). diff --git a/jme3-core/src/main/java/com/jme3/texture/image/LastTextureState.java b/jme3-core/src/main/java/com/jme3/texture/image/LastTextureState.java index 7a08e1cdde..63e7feab1c 100644 --- a/jme3-core/src/main/java/com/jme3/texture/image/LastTextureState.java +++ b/jme3-core/src/main/java/com/jme3/texture/image/LastTextureState.java @@ -32,7 +32,7 @@ package com.jme3.texture.image; import com.jme3.renderer.Renderer; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; /** * Stores/caches texture-state parameters so the {@link Renderer} doesn't have to @@ -42,11 +42,11 @@ */ public final class LastTextureState { - public Texture.WrapMode sWrap, tWrap, rWrap; - public Texture.MagFilter magFilter; - public Texture.MinFilter minFilter; + public GlTexture.WrapMode sWrap, tWrap, rWrap; + public GlTexture.MagFilter magFilter; + public GlTexture.MinFilter minFilter; public int anisoFilter; - public Texture.ShadowCompareMode shadowCompareMode; + public GlTexture.ShadowCompareMode shadowCompareMode; public LastTextureState() { reset(); @@ -62,6 +62,6 @@ public void reset() { // The default in OpenGL is OFF, so we avoid setting this per texture // if it's not used. - shadowCompareMode = Texture.ShadowCompareMode.Off; + shadowCompareMode = GlTexture.ShadowCompareMode.Off; } } diff --git a/jme3-core/src/main/java/com/jme3/texture/image/MipMapImageRaster.java b/jme3-core/src/main/java/com/jme3/texture/image/MipMapImageRaster.java index d37db00b67..d024b1c956 100644 --- a/jme3-core/src/main/java/com/jme3/texture/image/MipMapImageRaster.java +++ b/jme3-core/src/main/java/com/jme3/texture/image/MipMapImageRaster.java @@ -33,14 +33,14 @@ import com.jme3.math.ColorRGBA; import com.jme3.math.FastMath; -import com.jme3.texture.Image; +import com.jme3.texture.GlImage; import java.nio.ByteBuffer; public class MipMapImageRaster extends ImageRaster { private final int[] components = new int[4]; private ByteBuffer buffer; - private final Image image; + private final GlImage image; private final ImageCodec codec; private int width[]; private int height[]; @@ -55,11 +55,11 @@ private void rangeCheck(int x, int y) { } } - public MipMapImageRaster(Image image, int slice) { + public MipMapImageRaster(GlImage image, int slice) { this.image = image; this.slice = slice; this.buffer = image.getData(slice); - this.codec = ImageCodec.lookup(image.getFormat()); + this.codec = ImageCodec.lookup(image.getGlFormat()); if (image.hasMipmaps()) { int nbMipMap = image.getMipMapSizes().length; this.width = new int[nbMipMap]; diff --git a/jme3-core/src/main/java/com/jme3/ui/Picture.java b/jme3-core/src/main/java/com/jme3/ui/Picture.java index 5da04461bb..eaa60c85a9 100644 --- a/jme3-core/src/main/java/com/jme3/ui/Picture.java +++ b/jme3-core/src/main/java/com/jme3/ui/Picture.java @@ -36,7 +36,6 @@ import com.jme3.material.Material; import com.jme3.material.RenderState.BlendMode; import com.jme3.math.ColorRGBA; -import com.jme3.math.Vector3f; import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Quad; @@ -166,7 +165,7 @@ public void setImage(AssetManager assetManager, String imgName, boolean useAlpha */ public void setTexture(AssetManager assetManager, Texture2D tex, boolean useAlpha){ if (getMaterial() == null){ - Material mat = new Material(assetManager, "Common/MatDefs/Gui/Gui.j3md"); + Material mat = Backend.material(assetManager, "Common/MatDefs/Gui/Gui.j3md"); mat.setColor("Color", ColorRGBA.White); setMaterial(mat); } diff --git a/jme3-core/src/main/java/com/jme3/util/AbstractBuilder.java b/jme3-core/src/main/java/com/jme3/util/AbstractBuilder.java new file mode 100644 index 0000000000..cd78d2a8df --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/util/AbstractBuilder.java @@ -0,0 +1,17 @@ +package com.jme3.util; + +import org.lwjgl.system.MemoryStack; + +public abstract class AbstractBuilder implements AutoCloseable { + + protected final MemoryStack stack = MemoryStack.stackPush(); + + @Override + public void close() { + build(); + stack.pop(); + } + + protected abstract void build(); + +} diff --git a/jme3-core/src/main/java/com/jme3/util/ArrayIterator.java b/jme3-core/src/main/java/com/jme3/util/ArrayIterator.java new file mode 100644 index 0000000000..d189be0f35 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/util/ArrayIterator.java @@ -0,0 +1,30 @@ +package com.jme3.util; + +import java.util.Iterator; + +public class ArrayIterator implements Iterable, Iterator { + + private final T[] array; + private int index = 0; + + @SafeVarargs + public ArrayIterator(T... array) { + this.array = array; + } + + @Override + public Iterator iterator() { + return this; + } + + @Override + public boolean hasNext() { + return index < array.length; + } + + @Override + public T next() { + return array[index++]; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/util/BufferUtils.java b/jme3-core/src/main/java/com/jme3/util/BufferUtils.java index 535f91d4ed..fefbbf9f7d 100644 --- a/jme3-core/src/main/java/com/jme3/util/BufferUtils.java +++ b/jme3-core/src/main/java/com/jme3/util/BufferUtils.java @@ -36,18 +36,13 @@ import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; import com.jme3.math.Vector4f; +import org.lwjgl.system.MemoryUtil; import java.io.UnsupportedEncodingException; import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.DoubleBuffer; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -import java.nio.ShortBuffer; +import java.nio.*; import java.util.concurrent.ConcurrentHashMap; /** @@ -1038,6 +1033,54 @@ public static ByteBuffer createByteBuffer(String data) { } } + public static ByteBuffer interfaceByteBuffer(Buffer buffer, int bytesPerElement) { + return MemoryUtil.memByteBuffer(MemoryUtil.memAddress(buffer), buffer.limit() * bytesPerElement); + } + + public static ByteBuffer interfaceByteBuffer(ByteBuffer buffer) { + return interfaceByteBuffer(buffer, Byte.BYTES); + } + + public static ByteBuffer interfaceByteBuffer(ShortBuffer buffer) { + return interfaceByteBuffer(buffer, Short.BYTES); + } + + public static ByteBuffer interfaceByteBuffer(IntBuffer buffer) { + return interfaceByteBuffer(buffer, Integer.BYTES); + } + + public static ByteBuffer interfaceByteBuffer(FloatBuffer buffer) { + return interfaceByteBuffer(buffer, Float.BYTES); + } + + public static ByteBuffer interfaceByteBuffer(DoubleBuffer buffer) { + return interfaceByteBuffer(buffer, Double.BYTES); + } + + public static ByteBuffer interfaceByteBuffer(LongBuffer buffer) { + return interfaceByteBuffer(buffer, Long.BYTES); + } + + public static ByteBuffer interfaceByteBuffer(Buffer buffer) { + if (buffer instanceof ByteBuffer) return interfaceByteBuffer((ByteBuffer)buffer); + if (buffer instanceof ShortBuffer) return interfaceByteBuffer((ShortBuffer)buffer); + if (buffer instanceof IntBuffer) return interfaceByteBuffer((IntBuffer)buffer); + if (buffer instanceof FloatBuffer) return interfaceByteBuffer((FloatBuffer)buffer); + if (buffer instanceof DoubleBuffer) return interfaceByteBuffer((DoubleBuffer)buffer); + if (buffer instanceof LongBuffer) return interfaceByteBuffer((LongBuffer)buffer); + throw new UnsupportedOperationException("Buffer cannot be interfaced: " + buffer); + } + + public static int getBytesPerElement(Buffer buffer) { + if (buffer instanceof ByteBuffer) return Byte.BYTES; + if (buffer instanceof ShortBuffer) return Short.BYTES; + if (buffer instanceof IntBuffer) return Integer.BYTES; + if (buffer instanceof FloatBuffer) return Float.BYTES; + if (buffer instanceof DoubleBuffer) return Double.BYTES; + if (buffer instanceof LongBuffer) return Long.BYTES; + throw new UnsupportedOperationException("Cannot determine bytes per element of " + buffer); + } + /** * Creates a new ByteBuffer with the same contents as the given ByteBuffer. * The new ByteBuffer is separate from the old one and changes are not diff --git a/jme3-core/src/main/java/com/jme3/util/MipMapGenerator.java b/jme3-core/src/main/java/com/jme3/util/MipMapGenerator.java index 7073dfab52..23ccb420b6 100644 --- a/jme3-core/src/main/java/com/jme3/util/MipMapGenerator.java +++ b/jme3-core/src/main/java/com/jme3/util/MipMapGenerator.java @@ -33,7 +33,7 @@ import com.jme3.math.ColorRGBA; import com.jme3.math.FastMath; -import com.jme3.texture.Image; +import com.jme3.texture.GlImage; import com.jme3.texture.image.ImageRaster; import java.nio.ByteBuffer; @@ -44,10 +44,10 @@ public class MipMapGenerator { private MipMapGenerator() { } - public static Image scaleImage(Image inputImage, int outputWidth, int outputHeight) { - int size = outputWidth * outputHeight * inputImage.getFormat().getBitsPerPixel() / 8; + public static GlImage scaleImage(GlImage inputImage, int outputWidth, int outputHeight) { + int size = outputWidth * outputHeight * inputImage.getGlFormat().getBitsPerPixel() / 8; ByteBuffer buffer = BufferUtils.createByteBuffer(size); - Image outputImage = new Image(inputImage.getFormat(), + GlImage outputImage = new GlImage(inputImage.getGlFormat(), outputWidth, outputHeight, buffer, @@ -87,17 +87,17 @@ public static Image scaleImage(Image inputImage, int outputWidth, int outputHeig return outputImage; } - public static Image resizeToPowerOf2(Image original){ + public static GlImage resizeToPowerOf2(GlImage original){ int potWidth = FastMath.nearestPowerOfTwo(original.getWidth()); int potHeight = FastMath.nearestPowerOfTwo(original.getHeight()); return scaleImage(original, potWidth, potHeight); } - public static void generateMipMaps(Image image){ + public static void generateMipMaps(GlImage image){ int width = image.getWidth(); int height = image.getHeight(); - Image current = image; + GlImage current = image; ArrayList output = new ArrayList<>(); int totalSize = 0; diff --git a/jme3-core/src/main/java/com/jme3/util/NativeObject.java b/jme3-core/src/main/java/com/jme3/util/NativeObject.java index c7ff725294..e54820925a 100644 --- a/jme3-core/src/main/java/com/jme3/util/NativeObject.java +++ b/jme3-core/src/main/java/com/jme3/util/NativeObject.java @@ -41,7 +41,10 @@ * This class is used to track when OpenGL and OpenAL native objects are * collected by the garbage collector, and then invoke the proper destructor * on the OpenGL library to delete it from memory. + * + * @deprecated use {@link com.jme3.util.natives.Native} instead */ +@Deprecated public abstract class NativeObject implements Cloneable { public static final int INVALID_ID = -1; diff --git a/jme3-core/src/main/java/com/jme3/util/PlaceholderAssets.java b/jme3-core/src/main/java/com/jme3/util/PlaceholderAssets.java index 63edfecaa7..19adc6652e 100644 --- a/jme3-core/src/main/java/com/jme3/util/PlaceholderAssets.java +++ b/jme3-core/src/main/java/com/jme3/util/PlaceholderAssets.java @@ -38,9 +38,9 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; import com.jme3.scene.shape.Box; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture; import com.jme3.texture.image.ColorSpace; import java.nio.ByteBuffer; @@ -78,20 +78,20 @@ private PlaceholderAssets() { } @Deprecated - public static Image getPlaceholderImage(){ + public static GlImage getPlaceholderImage(){ ByteBuffer tempData = BufferUtils.createByteBuffer(3 * 4 * 4); tempData.put(imageData).flip(); - return new Image(Format.RGB8, 4, 4, tempData, null, ColorSpace.Linear); + return new GlImage(Format.RGB8, 4, 4, tempData, null, ColorSpace.Linear); } - public static Image getPlaceholderImage(AssetManager assetManager){ + public static GlImage getPlaceholderImage(AssetManager assetManager){ return assetManager.loadTexture("Common/Textures/MissingTexture.png").getImage(); } public static Material getPlaceholderMaterial(AssetManager assetManager){ Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - Texture tex = assetManager.loadTexture("Common/Textures/MissingMaterial.png"); - tex.setWrap(Texture.WrapMode.Repeat); + GlTexture tex = assetManager.loadTexture("Common/Textures/MissingMaterial.png"); + tex.setWrap(GlTexture.WrapMode.Repeat); mat.setTexture("ColorMap", tex); return mat; } @@ -102,8 +102,8 @@ public static Spatial getPlaceholderModel(AssetManager assetManager){ Box box = new Box(1, 1, 1); Geometry geom = new Geometry("placeholder", box); Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - Texture tex = assetManager.loadTexture("Common/Textures/MissingModel.png"); - tex.setWrap(Texture.WrapMode.Repeat); + GlTexture tex = assetManager.loadTexture("Common/Textures/MissingModel.png"); + tex.setWrap(GlTexture.WrapMode.Repeat); mat.setTexture("ColorMap", tex); geom.setMaterial(mat); return geom; diff --git a/jme3-core/src/main/java/com/jme3/util/SkyFactory.java b/jme3-core/src/main/java/com/jme3/util/SkyFactory.java index 48794ad991..724f8e12e0 100644 --- a/jme3-core/src/main/java/com/jme3/util/SkyFactory.java +++ b/jme3-core/src/main/java/com/jme3/util/SkyFactory.java @@ -41,9 +41,9 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; import com.jme3.scene.shape.Sphere; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture; import com.jme3.texture.TextureCubeMap; import java.nio.ByteBuffer; @@ -108,11 +108,11 @@ private SkyFactory() { * * @return a new spatial representing the sky, ready to be attached to the * scene graph - * @deprecated use {@link SkyFactory#createSky(com.jme3.asset.AssetManager, com.jme3.texture.Texture, + * @deprecated use {@link SkyFactory#createSky(com.jme3.asset.AssetManager, GlTexture, * com.jme3.math.Vector3f, com.jme3.util.SkyFactory.EnvMapType)} */ @Deprecated - public static Spatial createSky(AssetManager assetManager, Texture texture, + public static Spatial createSky(AssetManager assetManager, GlTexture texture, Vector3f normalScale, boolean sphereMap) { return createSky(assetManager, texture, normalScale, sphereMap, 10); } @@ -132,7 +132,7 @@ public static Spatial createSky(AssetManager assetManager, Texture texture, * @return a new spatial representing the sky, ready to be attached to the * scene graph */ - public static Spatial createSky(AssetManager assetManager, Texture texture, + public static Spatial createSky(AssetManager assetManager, GlTexture texture, Vector3f normalScale, EnvMapType envMapType) { return createSky(assetManager, texture, normalScale, envMapType, 10); } @@ -159,11 +159,11 @@ public static Spatial createSky(AssetManager assetManager, Texture texture, * frustum * @return a new spatial representing the sky, ready to be attached to the * scene graph - * @deprecated use {@link #createSky(com.jme3.asset.AssetManager, com.jme3.texture.Texture, + * @deprecated use {@link #createSky(com.jme3.asset.AssetManager, GlTexture, * com.jme3.math.Vector3f, com.jme3.util.SkyFactory.EnvMapType, float)} */ @Deprecated - public static Spatial createSky(AssetManager assetManager, Texture texture, + public static Spatial createSky(AssetManager assetManager, GlTexture texture, Vector3f normalScale, boolean sphereMap, int sphereRadius) { return createSky(assetManager, texture, normalScale, sphereMap ? EnvMapType.SphereMap : EnvMapType.CubeMap, sphereRadius); @@ -184,7 +184,7 @@ public static Spatial createSky(AssetManager assetManager, Texture texture, * @return a new spatial representing the sky, ready to be attached to the * scene graph */ - public static Spatial createSky(AssetManager assetManager, Texture texture, + public static Spatial createSky(AssetManager assetManager, GlTexture texture, Vector3f normalScale, EnvMapType envMapType, float sphereRadius) { if (texture == null) { throw new IllegalArgumentException("texture cannot be null"); @@ -202,7 +202,7 @@ public static Spatial createSky(AssetManager assetManager, Texture texture, case CubeMap: // make sure it's a cubemap if (!(texture instanceof TextureCubeMap)) { - Image img = texture.getImage(); + GlImage img = texture.getImage(); texture = new TextureCubeMap(); texture.setImage(img); } @@ -223,10 +223,10 @@ public static Spatial createSky(AssetManager assetManager, Texture texture, throw new IllegalArgumentException("envMapType=" + envMapType); } skyMat.setVector3("NormalScale", normalScale); - texture.setMagFilter(Texture.MagFilter.Bilinear); - texture.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); + texture.setMagFilter(GlTexture.MagFilter.Bilinear); + texture.setMinFilter(GlTexture.MinFilter.BilinearNoMipMaps); texture.setAnisotropicFilter(0); - texture.setWrap(Texture.WrapMode.EdgeClamp); + texture.setWrap(GlTexture.WrapMode.EdgeClamp); skyMat.setTexture("Texture", texture); sky.setMaterial(skyMat); @@ -249,11 +249,11 @@ public static Spatial createSky(AssetManager assetManager, Texture texture, * * @return a new spatial representing the sky, ready to be attached to the * scene graph - * @deprecated use {@link SkyFactory#createSky(com.jme3.asset.AssetManager, com.jme3.texture.Texture, + * @deprecated use {@link SkyFactory#createSky(com.jme3.asset.AssetManager, GlTexture, * com.jme3.math.Vector3f, com.jme3.util.SkyFactory.EnvMapType)} */ @Deprecated - public static Spatial createSky(AssetManager assetManager, Texture texture, boolean sphereMap) { + public static Spatial createSky(AssetManager assetManager, GlTexture texture, boolean sphereMap) { return createSky(assetManager, texture, Vector3f.UNIT_XYZ, sphereMap ? EnvMapType.SphereMap : EnvMapType.CubeMap); } @@ -291,7 +291,7 @@ public static Spatial createSky(AssetManager assetManager, String textureName, b * @return a new spatial representing the sky, ready to be attached to the * scene graph */ - public static Spatial createSky(AssetManager assetManager, Texture texture, EnvMapType envMapType) { + public static Spatial createSky(AssetManager assetManager, GlTexture texture, EnvMapType envMapType) { return createSky(assetManager, texture, Vector3f.UNIT_XYZ, envMapType); } @@ -308,13 +308,13 @@ public static Spatial createSky(AssetManager assetManager, String textureName, E TextureKey key = new TextureKey(textureName, true); key.setGenerateMips(false); if (envMapType == EnvMapType.CubeMap) { - key.setTextureTypeHint(Texture.Type.CubeMap); + key.setTextureTypeHint(GlTexture.Type.CubeMap); } - Texture tex = assetManager.loadTexture(key); + GlTexture tex = assetManager.loadTexture(key); return createSky(assetManager, tex, envMapType); } - private static void checkImage(Image image) { + private static void checkImage(GlImage image) { // if (image.getDepth() != 1) // throw new IllegalArgumentException("3D/Array images not allowed"); @@ -327,12 +327,12 @@ private static void checkImage(Image image) { } } - private static void checkImagesForCubeMap(Image... images) { + private static void checkImagesForCubeMap(GlImage... images) { if (images.length == 1) { return; } - Format fmt = images[0].getFormat(); + Format fmt = images[0].getGlFormat(); int width = images[0].getWidth(); int height = images[0].getHeight(); @@ -342,9 +342,9 @@ private static void checkImagesForCubeMap(Image... images) { checkImage(images[0]); for (int i = 1; i < images.length; i++) { - Image image = images[i]; + GlImage image = images[i]; checkImage(images[i]); - if (image.getFormat() != fmt) { + if (image.getGlFormat() != fmt) { throw new IllegalArgumentException("Images must have same format"); } if (image.getWidth() != width || image.getHeight() != height) { @@ -378,9 +378,9 @@ private static void checkImagesForCubeMap(Image... images) { * @return a new spatial representing the sky, ready to be attached to the * scene graph */ - public static Spatial createSky(AssetManager assetManager, Texture west, - Texture east, Texture north, Texture south, Texture up, - Texture down, Vector3f normalScale) { + public static Spatial createSky(AssetManager assetManager, GlTexture west, + GlTexture east, GlTexture north, GlTexture south, GlTexture up, + GlTexture down, Vector3f normalScale) { return createSky(assetManager, west, east, north, south, up, down, normalScale, 10); } @@ -404,20 +404,20 @@ public static Spatial createSky(AssetManager assetManager, Texture west, * @return a new spatial representing the sky, ready to be attached to the * scene graph */ - public static Spatial createSky(AssetManager assetManager, Texture west, - Texture east, Texture north, Texture south, Texture up, - Texture down, Vector3f normalScale, float sphereRadius) { + public static Spatial createSky(AssetManager assetManager, GlTexture west, + GlTexture east, GlTexture north, GlTexture south, GlTexture up, + GlTexture down, Vector3f normalScale, float sphereRadius) { - Image westImg = west.getImage(); - Image eastImg = east.getImage(); - Image northImg = north.getImage(); - Image southImg = south.getImage(); - Image upImg = up.getImage(); - Image downImg = down.getImage(); + GlImage westImg = west.getImage(); + GlImage eastImg = east.getImage(); + GlImage northImg = north.getImage(); + GlImage southImg = south.getImage(); + GlImage upImg = up.getImage(); + GlImage downImg = down.getImage(); checkImagesForCubeMap(westImg, eastImg, northImg, southImg, upImg, downImg); - Image cubeImage = new Image(westImg.getFormat(), westImg.getWidth(), westImg.getHeight(), + GlImage cubeImage = new GlImage(westImg.getGlFormat(), westImg.getWidth(), westImg.getHeight(), null, westImg.getColorSpace()); cubeImage.addData(westImg.getData(0)); @@ -445,7 +445,7 @@ public static Spatial createSky(AssetManager assetManager, Texture west, * scene graph */ public static Spatial createSky(AssetManager assetManager, - Texture west, Texture east, Texture north, Texture south, Texture up, Texture down) { + GlTexture west, GlTexture east, GlTexture north, GlTexture south, GlTexture up, GlTexture down) { return createSky(assetManager, west, east, north, south, up, down, Vector3f.UNIT_XYZ); } } diff --git a/jme3-core/src/main/java/com/jme3/util/TangentBinormalGenerator.java b/jme3-core/src/main/java/com/jme3/util/TangentBinormalGenerator.java index f19aacd914..8ed82ff568 100644 --- a/jme3-core/src/main/java/com/jme3/util/TangentBinormalGenerator.java +++ b/jme3-core/src/main/java/com/jme3/util/TangentBinormalGenerator.java @@ -36,7 +36,7 @@ import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; import com.jme3.scene.*; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.scene.mesh.IndexBuffer; import static com.jme3.util.BufferUtils.*; @@ -238,7 +238,7 @@ public static void generate(Mesh mesh, boolean approxTangents) { } private static List processTriangles(Mesh mesh, - int[] index, Vector3f[] v, Vector2f[] t, boolean splitMirrored) { + int[] index, Vector3f[] v, Vector2f[] t, boolean splitMirrored) { IndexBuffer indexBuffer = mesh.getIndexBuffer(); FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData(); if (mesh.getBuffer(Type.TexCoord) == null) { @@ -325,12 +325,12 @@ private static List splitVertices(Mesh mesh, List vertex if (!newVertices.isEmpty()) { //we have new vertices, we need to update the mesh's buffers. - for (Type type : VertexBuffer.Type.values()) { + for (Type type : GlVertexBuffer.Type.values()) { //skip tangent buffer as we're gonna overwrite it later if (type == Type.Tangent || type == Type.BindPoseTangent) { continue; } - VertexBuffer vb = mesh.getBuffer(type); + GlVertexBuffer vb = mesh.getBuffer(type); //Some buffer (hardware skinning ones) can be there but not //initialized, they must be skipped. //They'll be initialized when Hardware Skinning is engaged @@ -341,7 +341,7 @@ private static List splitVertices(Mesh mesh, List vertex Buffer buffer = vb.getData(); //IndexBuffer has special treatment, only swapping the vertex indices is needed if (type == Type.Index) { - boolean isShortBuffer = vb.getFormat() == VertexBuffer.Format.UnsignedShort; + boolean isShortBuffer = vb.getFormat() == GlVertexBuffer.Format.UnsignedShort; for (VertexData vertex : newVertices) { for (TriangleData tri : vertex.triangles) { for (int i = 0; i < tri.index.length; i++) { @@ -356,7 +356,7 @@ private static List splitVertices(Mesh mesh, List vertex vb.setUpdateNeeded(); } else { //copy the buffer in a bigger one and append nex vertices to the end - Buffer newVerts = VertexBuffer.createBuffer(vb.getFormat(), vb.getNumComponents(), nbVertices); + Buffer newVerts = GlVertexBuffer.createBuffer(vb.getFormat(), vb.getNumComponents(), nbVertices); if (buffer != null) { buffer.rewind(); bulkPut(vb.getFormat(), newVerts, buffer); @@ -384,7 +384,7 @@ private static List splitVertices(Mesh mesh, List vertex return vertexData; } - private static void bulkPut(VertexBuffer.Format format, Buffer buf1, Buffer buf2) { + private static void bulkPut(GlVertexBuffer.Format format, Buffer buf1, Buffer buf2) { switch (format) { case Byte: case Half: @@ -414,7 +414,7 @@ private static void bulkPut(VertexBuffer.Format format, Buffer buf1, Buffer buf2 } } - private static void putValue(VertexBuffer.Format format, Buffer buf1, Buffer buf2, int index) { + private static void putValue(GlVertexBuffer.Format format, Buffer buf1, Buffer buf2, int index) { switch (format) { case Byte: case Half: @@ -447,7 +447,7 @@ private static void putValue(VertexBuffer.Format format, Buffer buf1, Buffer buf } private static List processTriangleStrip(Mesh mesh, - int[] index, Vector3f[] v, Vector2f[] t) { + int[] index, Vector3f[] v, Vector2f[] t) { IndexBuffer indexBuffer = mesh.getIndexBuffer(); FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData(); @@ -496,7 +496,7 @@ private static List processTriangleStrip(Mesh mesh, } private static List processTriangleFan(Mesh mesh, - int[] index, Vector3f[] v, Vector2f[] t) { + int[] index, Vector3f[] v, Vector2f[] t) { IndexBuffer indexBuffer = mesh.getIndexBuffer(); FloatBuffer vertexBuffer = (FloatBuffer) mesh.getBuffer(Type.Position).getData(); @@ -673,7 +673,7 @@ && approxEqual(vertexInfo.texCoord, texCoord)) { } private static void processTriangleData(Mesh mesh, List vertices, - boolean approxTangent, boolean splitMirrored) { + boolean approxTangent, boolean splitMirrored) { ArrayList vertexMap = linkVertices(mesh, splitMirrored); FloatBuffer tangents = BufferUtils.createFloatBuffer(vertices.size() * 4); @@ -863,7 +863,7 @@ private static int parity(Vector3f n1, Vector3f n) { } /** - * @deprecated Use {@link TangentUtils#genTbnLines(com.jme3.scene.Mesh, float) } instead. + * @deprecated Use {@link TangentUtils#genTbnLines(Mesh, float) } instead. */ @Deprecated public static Mesh genTbnLines(Mesh mesh, float scale) { @@ -871,7 +871,7 @@ public static Mesh genTbnLines(Mesh mesh, float scale) { } /** - * @deprecated Use {@link TangentUtils#genNormalLines(com.jme3.scene.Mesh, float) } instead. + * @deprecated Use {@link TangentUtils#genNormalLines(Mesh, float) } instead. */ @Deprecated public static Mesh genNormalLines(Mesh mesh, float scale) { diff --git a/jme3-core/src/main/java/com/jme3/util/TangentUtils.java b/jme3-core/src/main/java/com/jme3/util/TangentUtils.java index 2014eaf003..22d6f5c2a1 100644 --- a/jme3-core/src/main/java/com/jme3/util/TangentUtils.java +++ b/jme3-core/src/main/java/com/jme3/util/TangentUtils.java @@ -40,7 +40,7 @@ import com.jme3.math.ColorRGBA; import com.jme3.math.Vector3f; import com.jme3.scene.*; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; /** * Created by Nehon on 03/10/2016. @@ -54,21 +54,21 @@ private TangentUtils() { } public static void generateBindPoseTangentsIfNecessary(Mesh mesh){ - if (mesh.getBuffer(VertexBuffer.Type.BindPosePosition) != null) { + if (mesh.getBuffer(GlVertexBuffer.Type.BindPosePosition) != null) { - VertexBuffer tangents = mesh.getBuffer(VertexBuffer.Type.Tangent); + GlVertexBuffer tangents = mesh.getBuffer(GlVertexBuffer.Type.Tangent); if (tangents != null) { - VertexBuffer bindTangents = new VertexBuffer(VertexBuffer.Type.BindPoseTangent); - bindTangents.setupData(VertexBuffer.Usage.CpuOnly, + GlVertexBuffer bindTangents = new GlVertexBuffer(GlVertexBuffer.Type.BindPoseTangent); + bindTangents.setupData(GlVertexBuffer.Usage.CpuOnly, 4, - VertexBuffer.Format.Float, + GlVertexBuffer.Format.Float, BufferUtils.clone(tangents.getData())); - if (mesh.getBuffer(VertexBuffer.Type.BindPoseTangent) != null) { - mesh.clearBuffer(VertexBuffer.Type.BindPoseTangent); + if (mesh.getBuffer(GlVertexBuffer.Type.BindPoseTangent) != null) { + mesh.clearBuffer(GlVertexBuffer.Type.BindPoseTangent); } mesh.setBuffer(bindTangents); - tangents.setUsage(VertexBuffer.Usage.Stream); + tangents.setUsage(GlVertexBuffer.Usage.Stream); } } } diff --git a/jme3-core/src/main/java/com/jme3/util/Version.java b/jme3-core/src/main/java/com/jme3/util/Version.java new file mode 100644 index 0000000000..560b1b2b00 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/util/Version.java @@ -0,0 +1,49 @@ +package com.jme3.util; + +import java.util.Objects; + +public class Version implements Versionable { + + private final T object; + private long version = -1L; + + public Version(T object) { + this.object = object; + } + + public boolean updateNeeded() { + return version < object.getVersionNumber(); + } + + public boolean update() { + long prev = version; + version = object.getVersionNumber(); + return prev < version; + } + + public T get() { + return object; + } + + @Override + public long getVersionNumber() { + return object.getVersionNumber(); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + Version version = (Version) o; + return Objects.equals(object, version.object); + } + + @Override + public int hashCode() { + return Objects.hashCode(object); + } + + public static T get(Version v) { + return v != null ? v.get() : null; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/util/Versionable.java b/jme3-core/src/main/java/com/jme3/util/Versionable.java new file mode 100644 index 0000000000..929a33d2f5 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/util/Versionable.java @@ -0,0 +1,14 @@ +package com.jme3.util; + +public interface Versionable { + + /** + * Returns the version number reflecting the current state + * of this object. If the state of this object is changed, + * the version number must be incremented by at least one. + * + * @return version number for the current state + */ + long getVersionNumber(); + +} diff --git a/jme3-core/src/main/java/com/jme3/util/mikktspace/MikkTSpaceImpl.java b/jme3-core/src/main/java/com/jme3/util/mikktspace/MikkTSpaceImpl.java index 18417b6b4c..bb54ffffdb 100644 --- a/jme3-core/src/main/java/com/jme3/util/mikktspace/MikkTSpaceImpl.java +++ b/jme3-core/src/main/java/com/jme3/util/mikktspace/MikkTSpaceImpl.java @@ -32,7 +32,7 @@ package com.jme3.util.mikktspace; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import com.jme3.scene.mesh.IndexBuffer; import com.jme3.util.BufferUtils; import java.nio.FloatBuffer; @@ -53,9 +53,9 @@ public MikkTSpaceImpl(Mesh mesh) { this.index = mesh.getIndicesAsList(); //replacing any existing tangent buffer, if you came here you want them new. - mesh.clearBuffer(VertexBuffer.Type.Tangent); + mesh.clearBuffer(GlVertexBuffer.Type.Tangent); FloatBuffer fb = BufferUtils.createFloatBuffer(mesh.getVertexCount() * 4); - mesh.setBuffer(VertexBuffer.Type.Tangent, 4, fb); + mesh.setBuffer(GlVertexBuffer.Type.Tangent, 4, fb); } @Override @@ -71,7 +71,7 @@ public int getNumVerticesOfFace(int face) { @Override public void getPosition(float[] posOut, int face, int vert) { int vertIndex = getIndex(face, vert); - VertexBuffer position = mesh.getBuffer(VertexBuffer.Type.Position); + GlVertexBuffer position = mesh.getBuffer(GlVertexBuffer.Type.Position); FloatBuffer pos = (FloatBuffer) position.getData(); pos.position(vertIndex * 3); posOut[0] = pos.get(); @@ -82,7 +82,7 @@ public void getPosition(float[] posOut, int face, int vert) { @Override public void getNormal(float[] normOut, int face, int vert) { int vertIndex = getIndex(face, vert); - VertexBuffer normal = mesh.getBuffer(VertexBuffer.Type.Normal); + GlVertexBuffer normal = mesh.getBuffer(GlVertexBuffer.Type.Normal); FloatBuffer norm = (FloatBuffer) normal.getData(); norm.position(vertIndex * 3); normOut[0] = norm.get(); @@ -93,7 +93,7 @@ public void getNormal(float[] normOut, int face, int vert) { @Override public void getTexCoord(float[] texOut, int face, int vert) { int vertIndex = getIndex(face, vert); - VertexBuffer texCoord = mesh.getBuffer(VertexBuffer.Type.TexCoord); + GlVertexBuffer texCoord = mesh.getBuffer(GlVertexBuffer.Type.TexCoord); FloatBuffer tex = (FloatBuffer) texCoord.getData(); tex.position(vertIndex * 2); texOut[0] = tex.get(); @@ -103,7 +103,7 @@ public void getTexCoord(float[] texOut, int face, int vert) { @Override public void setTSpaceBasic(float[] tangent, float sign, int face, int vert) { int vertIndex = getIndex(face, vert); - VertexBuffer tangentBuffer = mesh.getBuffer(VertexBuffer.Type.Tangent); + GlVertexBuffer tangentBuffer = mesh.getBuffer(GlVertexBuffer.Type.Tangent); FloatBuffer tan = (FloatBuffer) tangentBuffer.getData(); tan.position(vertIndex * 4); diff --git a/jme3-core/src/main/java/com/jme3/util/mikktspace/MikktspaceTangentGenerator.java b/jme3-core/src/main/java/com/jme3/util/mikktspace/MikktspaceTangentGenerator.java index 0f272bd080..bfaa52931b 100644 --- a/jme3-core/src/main/java/com/jme3/util/mikktspace/MikktspaceTangentGenerator.java +++ b/jme3-core/src/main/java/com/jme3/util/mikktspace/MikktspaceTangentGenerator.java @@ -34,7 +34,7 @@ import com.jme3.math.FastMath; import com.jme3.math.Vector3f; import com.jme3.scene.*; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.util.*; import java.util.ArrayList; diff --git a/jme3-core/src/main/java/com/jme3/util/natives/AbstractNative.java b/jme3-core/src/main/java/com/jme3/util/natives/AbstractNative.java new file mode 100644 index 0000000000..29a06d2152 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/util/natives/AbstractNative.java @@ -0,0 +1,23 @@ +package com.jme3.util.natives; + +import org.lwjgl.system.MemoryStack; + +public abstract class AbstractNative implements Native { + + protected T object; + protected NativeReference ref; + + @Override + public T getNativeObject() { + return object; + } + + @Override + public void prematureNativeDestruction() {} + + @Override + public NativeReference getNativeReference() { + return ref; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/util/natives/BasicNativeManager.java b/jme3-core/src/main/java/com/jme3/util/natives/BasicNativeManager.java new file mode 100644 index 0000000000..2ef3076b84 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/util/natives/BasicNativeManager.java @@ -0,0 +1,115 @@ +package com.jme3.util.natives; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; + +public class BasicNativeManager implements NativeManager { + + private static NativeManager globalInstance = new BasicNativeManager(); + + public static void setGlobalInstance(NativeManager instance) { + globalInstance = Objects.requireNonNull(instance, "NativeManager global instance cannot be null."); + } + + public static NativeManager getGlobalInstance() { + return globalInstance; + } + + private final ReferenceQueue unreachable = new ReferenceQueue<>(); + private final Map refMap = new ConcurrentHashMap<>(); + private final AtomicLong nextId = new AtomicLong(0L); + + @Override + public NativeRef register(Native object) { + NativeRef ref = new NativeRef(nextId.getAndIncrement(), object, unreachable); + refMap.put(ref.getId(), ref); + return ref; + } + + @Override + public int flush() { + int flushed = 0; + for (NativeRef ref; (ref = (NativeRef)unreachable.poll()) != null;) { + ref.destroy(); + refMap.remove(ref.id); + flushed++; + } + if (flushed > 0) { + refMap.values().removeIf(NativeRef::isDestroyed); + } + return flushed; + } + + @Override + public int clear() { + int size = refMap.size(); + for (NativeRef ref : refMap.values()) { + ref.destroy(); + } + refMap.clear(); + return size; + } + + public class NativeRef extends WeakReference implements NativeReference { + + private final long id; + private final WeakReference weakRef; + private final AtomicBoolean active = new AtomicBoolean(true); + private final Collection dependents = new ArrayList<>(); + private Runnable destroyer; + + private NativeRef(long id, Native referent, ReferenceQueue q) { + super(referent, q); + this.id = id; + destroyer = referent.createNativeDestroyer(); + weakRef = new WeakReference<>(referent); + } + + @Override + public void destroy() { + if (active.getAndSet(false)) { + for (NativeReference ref : dependents) { + ref.destroy(); + } + dependents.clear(); + destroyer.run(); + Native referent = weakRef.get(); + if (referent != null) { + referent.prematureNativeDestruction(); + } + } + } + + @Override + public void addDependent(NativeReference reference) { + if (isDestroyed()) { + throw new IllegalStateException("Cannot add dependent to destroyed resource."); + } + dependents.add(reference); + } + + @Override + public boolean isDestroyed() { + return !active.get(); + } + + @Override + public void refresh() { + Native obj = weakRef.get(); + if (obj != null) { + destroyer = obj.createNativeDestroyer(); + } + } + + private long getId() { + return id; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/util/natives/GlNative.java b/jme3-core/src/main/java/com/jme3/util/natives/GlNative.java new file mode 100644 index 0000000000..a3d20a2e06 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/util/natives/GlNative.java @@ -0,0 +1,80 @@ +package com.jme3.util.natives; + +import com.jme3.renderer.opengl.GLRenderer; + +import java.lang.ref.WeakReference; + +public abstract class GlNative extends AbstractNative implements Cloneable { + + protected GLRenderer renderer; + protected boolean updateNeeded = true; + private WeakReference weakRef; + + public GlNative() { + this(-1); + } + + public GlNative(int object) { + this.object = object; + } + + public abstract void resetObject(); + + protected void setId(int id) { + setId(null, id); + } + + public void setId(GLRenderer renderer, int id) { + object = id; + if (ref != null) { + ref.destroy(); + } + if (renderer != null) { + this.renderer = renderer; + } + ref = Native.get().register(this); + } + + public void setUpdateNeeded() { + updateNeeded = true; + } + + public void clearUpdateNeeded() { + updateNeeded = false; + } + + public boolean isUpdateNeeded() { + return updateNeeded; + } + + public void dispose() { + if (ref != null) { + ref.destroy(); + ref = null; + } + } + + @SuppressWarnings("unchecked") + public WeakReference getWeakRef() { + if (weakRef == null) { + weakRef = new WeakReference<>(this); + } + return (WeakReference) weakRef; + } + + @Override + @SuppressWarnings("unchecked") + public GlNative clone() { + try { + GlNative clone = (GlNative)super.clone(); + // TODO: copy mutable state here, so the clone can't change the internals of the original + clone.updateNeeded = true; + clone.renderer = renderer; + clone.object = object; + return clone; + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } + +} diff --git a/jme3-core/src/main/java/com/jme3/util/natives/Native.java b/jme3-core/src/main/java/com/jme3/util/natives/Native.java new file mode 100644 index 0000000000..1e6fd2b035 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/util/natives/Native.java @@ -0,0 +1,31 @@ +package com.jme3.util.natives; + +import org.lwjgl.system.MemoryUtil; + +public interface Native { + + T getNativeObject(); + + Runnable createNativeDestroyer(); + + void prematureNativeDestruction(); + + NativeReference getNativeReference(); + + static void set(NativeManager manager) { + BasicNativeManager.setGlobalInstance(manager); + } + + static NativeManager get() { + return BasicNativeManager.getGlobalInstance(); + } + + static T getObject(Native n) { + return n != null ? n.getNativeObject() : null; + } + + static long getId(Native n) { + return n != null ? n.getNativeObject() : MemoryUtil.NULL; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/util/natives/NativeManager.java b/jme3-core/src/main/java/com/jme3/util/natives/NativeManager.java new file mode 100644 index 0000000000..95e2dce698 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/util/natives/NativeManager.java @@ -0,0 +1,13 @@ +package com.jme3.util.natives; + +import org.lwjgl.system.NativeResource; + +public interface NativeManager { + + NativeReference register(Native object); + + int flush(); + + int clear(); + +} diff --git a/jme3-core/src/main/java/com/jme3/util/natives/NativeReference.java b/jme3-core/src/main/java/com/jme3/util/natives/NativeReference.java new file mode 100644 index 0000000000..f699c81b39 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/util/natives/NativeReference.java @@ -0,0 +1,13 @@ +package com.jme3.util.natives; + +public interface NativeReference { + + void destroy(); + + void refresh(); + + void addDependent(NativeReference reference); + + boolean isDestroyed(); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/ColorSpace.java b/jme3-core/src/main/java/com/jme3/vulkan/ColorSpace.java new file mode 100644 index 0000000000..3a2cd14563 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/ColorSpace.java @@ -0,0 +1,21 @@ +package com.jme3.vulkan; + +import com.jme3.vulkan.util.IntEnum; +import org.lwjgl.vulkan.KHRSurface; + +public enum ColorSpace implements IntEnum { + + KhrSrgbNonlinear(KHRSurface.VK_COLOR_SPACE_SRGB_NONLINEAR_KHR); + + private final int vkEnum; + + ColorSpace(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/Format.java b/jme3-core/src/main/java/com/jme3/vulkan/Format.java new file mode 100644 index 0000000000..542b78be41 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/Format.java @@ -0,0 +1,391 @@ +package com.jme3.vulkan; + +import com.jme3.vulkan.images.VulkanImage; +import com.jme3.vulkan.util.Flag; + +import java.nio.ByteBuffer; +import java.util.Iterator; + +import static org.lwjgl.vulkan.VK10.*; + +public enum Format implements Iterable { + + RGBA32SFloat(VK_FORMAT_R32G32B32A32_SFLOAT, array(cf(4), cf(4), cf(4), cf(4)), true, false, false), + RGB32SFloat(VK_FORMAT_R32G32B32_SFLOAT, array(cf(4), cf(4), cf(4)), true, false, false), + RG32SFloat(VK_FORMAT_R32G32_SFLOAT, array(cf(4), cf(4)), true, false, false), + + RGBA8_SRGB(VK_FORMAT_R8G8B8A8_SRGB, array(cf(1), cf(1), cf(1), cf(1)), true, false, false), + R8_SRGB(VK_FORMAT_R8_SRGB, array(cf(1)), true, false, false), + BGR8_SRGB(VK_FORMAT_B8G8R8_SRGB, array(cf(1), cf(1), cf(1)), true, false, false), + ABGR8_SRGB(VK_FORMAT_A8B8G8R8_SRGB_PACK32, array(cf(1), cf(1), cf(1), cf(1)), true, false, false), + B8G8R8A8_SRGB(VK_FORMAT_B8G8R8A8_SRGB, array(cf(1), cf(1), cf(1), cf(1)), true, false, false), + + Depth32SFloat(VK_FORMAT_D32_SFLOAT, array(cf(4)), false, true, false), + Depth32SFloat_Stencil8UInt(VK_FORMAT_D32_SFLOAT_S8_UINT, array(cf(4), c(1)), false, true, true), + Depth24UNorm_Stencil8UInt(VK_FORMAT_D24_UNORM_S8_UINT, array(cf(3), c(1)), false, true, true), + Depth16UNorm(VK_FORMAT_D16_UNORM, array(cf(2)), false, true, false), + Depth16UNorm_Stencil8UInt(VK_FORMAT_D16_UNORM_S8_UINT, array(cf(2), c(1)), false, true, true); + + public enum Feature implements Flag { + + DepthStencilAttachment(VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT), + BlitDst(VK_FORMAT_FEATURE_BLIT_DST_BIT), + BlitSrc(VK_FORMAT_FEATURE_BLIT_SRC_BIT), + ColorAttachment(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT), + SampledImage(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT), + ColorAttachmentBlend(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT), + SampledImageFilterLinear(VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT), + StorageImageAtomic(VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT), + StorageImage(VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT), + StorageTexelBufferAtomic(VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT), + StorageTexelBuffer(VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT), + UniformTexelBuffer(VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT), + VertexBuffer(VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + + private final int bits; + + Feature(int bits) { + this.bits = bits; + } + + @Override + public int bits() { + return bits; + } + + } + + private final int vkEnum, totalBytes; + private final Component[] components; + private final boolean color, depth, stencil; + + Format(int vkEnum, Component[] components, boolean color, boolean depth, boolean stencil) { + this.vkEnum = vkEnum; + this.components = components; + this.color = color; + this.depth = depth; + this.stencil = stencil; + int total = 0; + for (Component c : components) { + c.setOffset(total); + total += c.getBytes(); + } + this.totalBytes = total; + } + + @Override + public Iterator iterator() { + return new ComponentIterator(components); + } + + public int getVkEnum() { + return vkEnum; + } + + public Flag getAspects() { + int bits = 0; + if (color) bits |= VulkanImage.Aspect.Color.bits(); + if (depth) bits |= VulkanImage.Aspect.Depth.bits(); + if (stencil) bits |= VulkanImage.Aspect.Stencil.bits(); + return Flag.of(bits); + } + + public int getTotalBits() { + return totalBytes * Byte.SIZE; + } + + public int getTotalBytes() { + return totalBytes; + } + + public int getNumComponents() { + return components.length; + } + + public Component getComponent(int component) { + return components[component]; + } + + public boolean isColor() { + return color; + } + + public boolean isDepth() { + return depth; + } + + public boolean isStencil() { + return stencil; + } + + public static Format byVkEnum(int vkEnum) { + for (Format f : Format.values()) { + if (f.getVkEnum() == vkEnum) { + return f; + } + } + return null; + } + + private static Component[] array(Component... components) { + return components; + } + + private static Component c(int bytes) { + return new Component(bytes, false); + } + + private static Component cf(int bytes) { + return new Component(bytes, true); + } + + public static class Component { + + private final int bytes; + private final boolean floatingPoint; + private int offset; + + private Component(int bytes, boolean floatingPoint) { + assert bytes > 0 : "Component size in bytes must be positive"; + this.bytes = bytes; + this.floatingPoint = floatingPoint && bytes >= 4; + } + + private void setOffset(int offset) { + this.offset = offset; + } + + public int getBytes() { + return bytes; + } + + public int getOffset() { + return offset; + } + + public boolean isFloatingPoint() { + return floatingPoint; + } + + public Component putByte(ByteBuffer buffer, int position, byte value) { + position += offset; + switch (bytes) { + case 1: buffer.put(position, value); break; + case 2: case 3: buffer.putShort(position, value); break; + case 4: case 5: case 6: case 7: { + if (floatingPoint) buffer.putFloat(position, value); + else buffer.putInt(position, value); + } break; + default: { + if (floatingPoint) buffer.putDouble(position, value); + else buffer.putLong(position, value); + } + } + return this; + } + + public Component putShort(ByteBuffer buffer, int position, short value) { + position += offset; + switch (bytes) { + case 1: buffer.put(position, (byte)value); break; + case 2: case 3: buffer.putShort(position, value); break; + case 4: case 5: case 6: case 7: { + if (floatingPoint) buffer.putFloat(position, value); + else buffer.putInt(position, value); + } break; + default: { + if (floatingPoint) buffer.putDouble(position, value); + else buffer.putLong(position, value); + } + } + return this; + } + + public Component putInt(ByteBuffer buffer, int position, int value) { + position += offset; + switch (bytes) { + case 1: buffer.put(position, (byte)value); break; + case 2: case 3: buffer.putShort(position, (short)value); break; + case 4: case 5: case 6: case 7: { + if (floatingPoint) buffer.putFloat(position, value); + else buffer.putInt(position, value); + } break; + default: { + if (floatingPoint) buffer.putDouble(position, value); + else buffer.putLong(position, value); + } + } + return this; + } + + public Component putFloat(ByteBuffer buffer, int position, float value) { + position += offset; + switch (bytes) { + case 1: buffer.put(position, (byte)value); break; + case 2: case 3: buffer.putShort(position, (short)value); break; + case 4: case 5: case 6: case 7: { + if (floatingPoint) buffer.putFloat(position, value); + else buffer.putInt(position, (int)value); + } break; + default: { + if (floatingPoint) buffer.putDouble(position, value); + else buffer.putLong(position, (long)value); + } + } + return this; + } + + public Component putDouble(ByteBuffer buffer, int position, double value) { + position += offset; + switch (bytes) { + case 1: buffer.put(position, (byte)value); break; + case 2: case 3: buffer.putShort(position, (short)value); break; + case 4: case 5: case 6: case 7: { + if (floatingPoint) buffer.putFloat(position, (float)value); + else buffer.putInt(position, (int)value); + } break; + default: { + if (floatingPoint) buffer.putDouble(position, value); + else buffer.putLong(position, (long)value); + } + } + return this; + } + + public Component putLong(ByteBuffer buffer, int position, long value) { + position += offset; + switch (bytes) { + case 1: buffer.put(position, (byte)value); break; + case 2: case 3: buffer.putShort(position, (short)value); break; + case 4: case 5: case 6: case 7: { + if (floatingPoint) buffer.putFloat(position, value); + else buffer.putInt(position, (int)value); + } break; + default: { + if (floatingPoint) buffer.putDouble(position, value); + else buffer.putLong(position, value); + } + } + return this; + } + + public byte getByte(ByteBuffer buffer, int position) { + position += offset; + switch (bytes) { + case 1: return buffer.get(position); + case 2: case 3: return (byte)buffer.getShort(position); + case 4: case 5: case 6: case 7: { + if (floatingPoint) return (byte)buffer.getFloat(position); + else return (byte)buffer.getInt(position); + } + default: { + if (floatingPoint) return (byte)buffer.getDouble(position); + else return (byte)buffer.getLong(position); + } + } + } + + public short getShort(ByteBuffer buffer, int position) { + position += offset; + switch (bytes) { + case 1: return buffer.get(position); + case 2: case 3: return buffer.getShort(position); + case 4: case 5: case 6: case 7: { + if (floatingPoint) return (short)buffer.getFloat(position); + else return (short)buffer.getInt(position); + } + default: { + if (floatingPoint) return (short)buffer.getDouble(position); + else return (short)buffer.getLong(position); + } + } + } + + public int getInt(ByteBuffer buffer, int position) { + position += offset; + switch (bytes) { + case 1: return buffer.get(position); + case 2: case 3: return buffer.getShort(position); + case 4: case 5: case 6: case 7: { + if (floatingPoint) return (int)buffer.getFloat(position); + else return buffer.getInt(position); + } + default: { + if (floatingPoint) return (int)buffer.getDouble(position); + else return (int)buffer.getLong(position); + } + } + } + + public float getFloat(ByteBuffer buffer, int position) { + position += offset; + switch (bytes) { + case 1: return buffer.get(position); + case 2: case 3: return buffer.getShort(position); + case 4: case 5: case 6: case 7: { + if (floatingPoint) return buffer.getFloat(position); + else return buffer.getInt(position); + } + default: { + if (floatingPoint) return (float)buffer.getDouble(position); + else return buffer.getLong(position); + } + } + } + + public double getDouble(ByteBuffer buffer, int position) { + position += offset; + switch (bytes) { + case 1: return buffer.get(position); + case 2: case 3: return buffer.getShort(position); + case 4: case 5: case 6: case 7: { + if (floatingPoint) return buffer.getFloat(position); + else return buffer.getInt(position); + } + default: { + if (floatingPoint) return buffer.getDouble(position); + else return buffer.getLong(position); + } + } + } + + public long getLong(ByteBuffer buffer, int position) { + position += offset; + switch (bytes) { + case 1: return buffer.get(position); + case 2: case 3: return buffer.getShort(position); + case 4: case 5: case 6: case 7: { + if (floatingPoint) return (long)buffer.getFloat(position); + else return buffer.getInt(position); + } + default: { + if (floatingPoint) return (long)buffer.getDouble(position); + else return buffer.getLong(position); + } + } + } + + } + + private static class ComponentIterator implements Iterator { + + private final Component[] components; + private int index = 0; + + private ComponentIterator(Component[] components) { + this.components = components; + } + + @Override + public boolean hasNext() { + return index < components.length; + } + + @Override + public Component next() { + return components[index++]; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/FormatFeature.java b/jme3-core/src/main/java/com/jme3/vulkan/FormatFeature.java new file mode 100644 index 0000000000..c5c8b11ff0 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/FormatFeature.java @@ -0,0 +1,34 @@ +package com.jme3.vulkan; + +import com.jme3.vulkan.util.Flag; + +import static org.lwjgl.vulkan.VK10.*; + +public enum FormatFeature implements Flag { + + DepthStencilAttachment(VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT), + BlitDst(VK_FORMAT_FEATURE_BLIT_DST_BIT), + BlitSrc(VK_FORMAT_FEATURE_BLIT_SRC_BIT), + ColorAttachment(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT), + ColorAttachmentBlend(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT), + SampledImage(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT), + SampledImageFilterLinear(VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT), + StorageImageAtomic(VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT), + StorageImage(VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT), + StorageTexelBufferAtomic(VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT), + StorageTexelBuffer(VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT), + UniformTexelBuffer(VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT), + VertexBuffer(VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT); + + private final int bits; + + FormatFeature(int bits) { + this.bits = bits; + } + + @Override + public int bits() { + return 0; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/SharingMode.java b/jme3-core/src/main/java/com/jme3/vulkan/SharingMode.java new file mode 100644 index 0000000000..1368b80b0d --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/SharingMode.java @@ -0,0 +1,27 @@ +package com.jme3.vulkan; + +import com.jme3.vulkan.util.IntEnum; + +import static org.lwjgl.vulkan.VK10.*; + +public enum SharingMode implements IntEnum { + + Exclusive(VK_SHARING_MODE_EXCLUSIVE), + Concurrent(VK_SHARING_MODE_CONCURRENT); + + private final int vkEnum; + + SharingMode(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + + public static SharingMode concurrent(boolean concurrent) { + return concurrent ? Concurrent : Exclusive; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/Swizzle.java b/jme3-core/src/main/java/com/jme3/vulkan/Swizzle.java new file mode 100644 index 0000000000..8717c51d3c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/Swizzle.java @@ -0,0 +1,28 @@ +package com.jme3.vulkan; + +import com.jme3.vulkan.util.IntEnum; + +import static org.lwjgl.vulkan.VK10.*; + +public enum Swizzle implements IntEnum { + + Identity(VK_COMPONENT_SWIZZLE_IDENTITY), + R(VK_COMPONENT_SWIZZLE_R), + G(VK_COMPONENT_SWIZZLE_G), + B(VK_COMPONENT_SWIZZLE_B), + A(VK_COMPONENT_SWIZZLE_A), + One(VK_COMPONENT_SWIZZLE_ONE), + Zero(VK_COMPONENT_SWIZZLE_ZERO); + + private final int vkEnum; + + Swizzle(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/VulkanInstance.java b/jme3-core/src/main/java/com/jme3/vulkan/VulkanInstance.java new file mode 100644 index 0000000000..042e04fdd2 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/VulkanInstance.java @@ -0,0 +1,141 @@ +package com.jme3.vulkan; + +import com.jme3.system.JmeVersion; +import com.jme3.util.AbstractBuilder; +import com.jme3.util.natives.AbstractNative; +import com.jme3.util.natives.Native; +import org.lwjgl.PointerBuffer; +import org.lwjgl.glfw.GLFWVulkan; +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.vulkan.EXTDebugUtils; +import org.lwjgl.vulkan.VkApplicationInfo; +import org.lwjgl.vulkan.VkInstance; +import org.lwjgl.vulkan.VkInstanceCreateInfo; + +import java.util.*; +import java.util.logging.Level; + +import static com.jme3.renderer.vulkan.VulkanUtils.*; +import static org.lwjgl.vulkan.VK10.*; + +public class VulkanInstance extends AbstractNative { + + public static final String ENGINE_NAME = "jMonkeyEngine"; + public static final String LUNARG_LAYER = "VK_LAYER_KHRONOS_validation"; + + private final Set extensions = new HashSet<>(); + private final Set layers = new HashSet<>(); + private String appName = "Unnamed App"; + private int appVersion = VK_MAKE_VERSION(0, 0, 0); + private int apiVersion; + private VulkanLogger logger; + + public VulkanInstance() { + this(VK_API_VERSION_1_0); + } + + public VulkanInstance(int apiVersion) { + this.apiVersion = apiVersion; + } + + @Override + public Runnable createNativeDestroyer() { + return () -> vkDestroyInstance(object, null); + } + + public VulkanLogger createLogger(Level exceptionThreshold) { + return logger = new VulkanLogger(this, exceptionThreshold); + } + + public VulkanLogger getLogger() { + return logger; + } + + public Set getExtensions() { + return extensions; + } + + public Set getLayers() { + return layers; + } + + public Builder build() { + return new Builder(); + } + + public class Builder extends AbstractBuilder { + + @Override + protected void build() { + String[] ver = JmeVersion.VERSION_NUMBER.split("\\.", 3); + VkApplicationInfo info = VkApplicationInfo.calloc(stack) + .apiVersion(apiVersion) + .pEngineName(stack.UTF8(ENGINE_NAME)) + .engineVersion(VK_MAKE_VERSION( + Integer.parseInt(ver[0]), + Integer.parseInt(ver[1]), + Integer.parseInt(ver[2]))) + .pApplicationName(stack.UTF8(appName)) + .applicationVersion(appVersion); + VkInstanceCreateInfo create = VkInstanceCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO) + .pApplicationInfo(info); + if (!extensions.isEmpty()) { + PointerBuffer exts = stack.mallocPointer(extensions.size()); + for (String e : extensions) { + exts.put(stack.UTF8(e)); + } + create.ppEnabledExtensionNames(exts.flip()); + } + if (!layers.isEmpty()) { + PointerBuffer lyrs = stack.mallocPointer(layers.size()); + for (String l : layers) { + lyrs.put(stack.UTF8(l)); + } + create.ppEnabledLayerNames(lyrs.flip()); + } + PointerBuffer ptr = stack.mallocPointer(1); + check(vkCreateInstance(create, null, ptr), "Failed to create instance."); + object = new VkInstance(ptr.get(0), create); + ref = Native.get().register(VulkanInstance.this); + } + + public void setApplicationName(String name) { + VulkanInstance.this.appName = name; + } + + public void setApplicationVersion(int major, int minor, int patch) { + VulkanInstance.this.appVersion = VK_MAKE_VERSION(major, minor, patch); + } + + public void setApiVersion(int version) { + VulkanInstance.this.apiVersion = version; + } + + public void addGlfwExtensions() { + PointerBuffer exts = Objects.requireNonNull(GLFWVulkan.glfwGetRequiredInstanceExtensions(), + "Vulkan extensions for GLFW are not available."); + for (int i = 0; i < exts.limit(); i++) { + extensions.add(MemoryUtil.memUTF8(exts.get(i))); + } + } + + public void addDebugExtension() { + extensions.add(EXTDebugUtils.VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } + + public void addLunarGLayer() { + layers.add(LUNARG_LAYER); + } + + public void addExtension(String ext) { + extensions.add(ext); + } + + public void addLayer(String layer) { + layers.add(layer); + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/VulkanLogger.java b/jme3-core/src/main/java/com/jme3/vulkan/VulkanLogger.java new file mode 100644 index 0000000000..a11fed7644 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/VulkanLogger.java @@ -0,0 +1,101 @@ +package com.jme3.vulkan; + +import com.jme3.util.natives.Native; +import com.jme3.util.natives.NativeReference; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.EXTDebugUtils; +import org.lwjgl.vulkan.VkDebugUtilsMessengerCallbackDataEXT; +import org.lwjgl.vulkan.VkDebugUtilsMessengerCallbackEXT; +import org.lwjgl.vulkan.VkDebugUtilsMessengerCreateInfoEXT; + +import java.util.logging.Level; + +import static com.jme3.renderer.vulkan.VulkanUtils.getLong; +import static com.jme3.renderer.vulkan.VulkanUtils.verifyExtensionMethod; +import static org.lwjgl.vulkan.EXTDebugUtils.*; +import static org.lwjgl.vulkan.VK10.VK_FALSE; + +public class VulkanLogger implements Native { + + private final VulkanInstance instance; + private final Level exceptionThreshold; + private final NativeReference ref; + private final long id; + private final VkDebugUtilsMessengerCallbackEXT callback = new VkDebugUtilsMessengerCallbackEXT() { + @Override + public int invoke(int messageSeverity, int messageTypes, long pCallbackData, long pUserData) { + return message(messageSeverity, messageTypes, pCallbackData, pUserData); + } + }; + + public VulkanLogger(VulkanInstance instance, Level exceptionThreshold) { + this.instance = instance; + this.exceptionThreshold = exceptionThreshold; + try (MemoryStack stack = MemoryStack.stackPush()) { + VkDebugUtilsMessengerCreateInfoEXT create = VkDebugUtilsMessengerCreateInfoEXT.calloc(stack) + .sType(VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT) + .messageSeverity( + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT + | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT + | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT + | VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT + ).messageType( + VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT + | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT + | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT + ).pfnUserCallback(callback); + verifyExtensionMethod(instance.getNativeObject(), "vkCreateDebugUtilsMessengerEXT"); + id = getLong(stack, ptr -> vkCreateDebugUtilsMessengerEXT(instance.getNativeObject(), create, null, ptr)); + } + ref = Native.get().register(this); + instance.getNativeReference().addDependent(ref); + } + + public int message(int messageSeverity, int messageTypes, long pCallbackData, long pUserData) { + VkDebugUtilsMessengerCallbackDataEXT data = VkDebugUtilsMessengerCallbackDataEXT.create(pCallbackData); + Level lvl = getLoggingLevel(messageSeverity); + System.err.println(lvl.getName() + " " + data.pMessageString()); + if (exceptionThreshold != null && lvl.intValue() >= exceptionThreshold.intValue()) { + throw new RuntimeException(lvl.getName() + ": " + data.pMessageString()); + } + return VK_FALSE; // always return false, true is only really used for testing validation layers + } + + @Override + public Long getNativeObject() { + return id; + } + + @Override + public Runnable createNativeDestroyer() { + return () -> { + verifyExtensionMethod(instance.getNativeObject(), "vkDestroyDebugUtilsMessengerEXT"); + vkDestroyDebugUtilsMessengerEXT(instance.getNativeObject(), id, null); + callback.close(); + }; + } + + @Override + public void prematureNativeDestruction() {} + + @Override + public NativeReference getNativeReference() { + return ref; + } + + private Level getLoggingLevel(int messageSeverity) { + switch (messageSeverity) { + case EXTDebugUtils.VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + return Level.SEVERE; + case EXTDebugUtils.VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + return Level.WARNING; + case EXTDebugUtils.VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + return Level.INFO; + case EXTDebugUtils.VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + return Level.FINE; + default: throw new UnsupportedOperationException("Unsupported severity bit: " + + Integer.numberOfTrailingZeros(Integer.highestOneBit(messageSeverity))); + } + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/allocate/ResourceTicket.java b/jme3-core/src/main/java/com/jme3/vulkan/allocate/ResourceTicket.java new file mode 100644 index 0000000000..b258252469 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/allocate/ResourceTicket.java @@ -0,0 +1,9 @@ +package com.jme3.vulkan.allocate; + +public interface ResourceTicket { + + Float selectResource(T resource); + + T createResource(); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/allocate/ResourceWrapper.java b/jme3-core/src/main/java/com/jme3/vulkan/allocate/ResourceWrapper.java new file mode 100644 index 0000000000..1b1d537625 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/allocate/ResourceWrapper.java @@ -0,0 +1,9 @@ +package com.jme3.vulkan.allocate; + +public interface ResourceWrapper { + + T get(); + + void release(); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/allocate/StaticAllocator.java b/jme3-core/src/main/java/com/jme3/vulkan/allocate/StaticAllocator.java new file mode 100644 index 0000000000..d751cac40c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/allocate/StaticAllocator.java @@ -0,0 +1,87 @@ +package com.jme3.vulkan.allocate; + +import java.util.Collection; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; + +public class StaticAllocator { + + private final Collection> cache = new ConcurrentLinkedQueue<>(); + + public ResourceWrapper allocate(ResourceTicket ticket) { + ResourceWrapper selected = null; + float eval = Float.POSITIVE_INFINITY; + for (CacheEntry e : cache) { + Float s = ticket.selectResource(e.get()); + if (s != null && s < eval && e.acquire()) { + if (selected != null) { + selected.release(); + } + if (s <= 0f) { + return e; + } + selected = e; + eval = s; + } + } + if (selected != null) { + return selected; + } + CacheEntry e = new CacheEntry(ticket.createResource()); + if (!e.acquire()) { + throw new IllegalStateException("New entry cannot reject initial acquire."); + } + cache.add(e); + return e; + } + + public ResourceWrapper submit(E resource) { + CacheEntry e = new CacheEntry<>(resource); + e.acquire(); + cache.add(e); + return e; + } + + public void flush(long idleMillis) { + long time = System.currentTimeMillis(); + cache.removeIf(e -> e.free(time, idleMillis)); + } + + public void clear() { + cache.clear(); + } + + private static class CacheEntry implements ResourceWrapper { + + private final T buffer; + private final AtomicBoolean allocated = new AtomicBoolean(false); + private long lastUsed = System.currentTimeMillis(); + + private CacheEntry(T buffer) { + this.buffer = buffer; + } + + @Override + public T get() { + return buffer; + } + + @Override + public void release() { + if (!allocated.getAndSet(false)) { + throw new IllegalStateException("Buffer has not been acquired."); + } + lastUsed = System.currentTimeMillis(); + } + + public boolean acquire() { + return !allocated.getAndSet(true); + } + + public boolean free(long time, long idle) { + return Math.abs(time - lastUsed) >= idle && acquire(); + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/app/VulkanApplication.java b/jme3-core/src/main/java/com/jme3/vulkan/app/VulkanApplication.java new file mode 100644 index 0000000000..9d32f9623c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/app/VulkanApplication.java @@ -0,0 +1,18 @@ +package com.jme3.vulkan.app; + +import com.jme3.app.SimpleApplication; +import com.jme3.vulkan.VulkanInstance; +import org.lwjgl.system.MemoryStack; + +public class VulkanApplication extends SimpleApplication { + + protected VulkanInstance instance; + + @Override + public void simpleInitApp() { + try (MemoryStack stack = MemoryStack.stackPush()) { + + } + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/AdaptiveBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/AdaptiveBuffer.java new file mode 100644 index 0000000000..7bfd8d2670 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/AdaptiveBuffer.java @@ -0,0 +1,11 @@ +package com.jme3.vulkan.buffers; + +import com.jme3.scene.GlVertexBuffer; + +public interface AdaptiveBuffer extends GpuBuffer { + + void setAccessMode(GlVertexBuffer.Usage mode); + + GlVertexBuffer.Usage getAccessMode(); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/AdaptiveVulkanBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/AdaptiveVulkanBuffer.java new file mode 100644 index 0000000000..b5df8ea035 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/AdaptiveVulkanBuffer.java @@ -0,0 +1,183 @@ +package com.jme3.vulkan.buffers; + +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Usage; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.vulkan.allocate.ResourceTicket; +import com.jme3.vulkan.allocate.ResourceWrapper; +import com.jme3.vulkan.allocate.StaticAllocator; +import com.jme3.vulkan.commands.CommandBuffer; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.frames.UpdateFrameManager; +import com.jme3.vulkan.memory.MemoryProp; +import com.jme3.vulkan.memory.MemoryRegion; +import com.jme3.vulkan.memory.MemorySize; +import com.jme3.vulkan.update.Command; +import com.jme3.vulkan.util.Flag; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryUtil; + +import java.util.ArrayList; +import java.util.List; + +public class AdaptiveVulkanBuffer implements VulkanBuffer, AdaptiveBuffer, Command { + + private final LogicalDevice device; + private final StaticAllocator allocator; + private final UpdateFrameManager frames; + private GlVertexBuffer.Usage mode; + + private VulkanBuffer gpuOnlyBuffer; + private NioBuffer cpuOnlyBuffer; + private final SharedBuffer[] sharedBuffers; + + private Flag usage = BufferUsage.Storage; + private boolean concurrent = false; + + public AdaptiveVulkanBuffer(LogicalDevice device, StaticAllocator allocator, UpdateFrameManager frames) { + this.device = device; + this.allocator = allocator; + this.frames = frames; + this.sharedBuffers = new SharedBuffer[frames.getTotalFrames()]; + } + + @Override + public void setAccessMode(Usage mode) { + + } + + @Override + public Usage getAccessMode() { + return null; + } + + @Override + public LogicalDevice getDevice() { + return device; + } + + @Override + public Flag getUsage() { + return usage; + } + + @Override + public Flag getMemoryProperties() { + switch (mode) { + case Stream: return MemoryProp.HostVisible.add(MemoryProp.HostCoherent); + case Dynamic: + case Static: return MemoryProp.DeviceLocal; + case CpuOnly: return Flag.empty(); + default: throw new UnsupportedOperationException(); + } + } + + @Override + public boolean isConcurrent() { + return concurrent; + } + + @Override + public PointerBuffer map(int offset, int size) { + return cpuOnlyBuffer.map(offset, size); + } + + @Override + public long getId() { + switch (mode) { + case Stream: return sharedBuffers[frames.getCurrentFrame()].get().getId(); + case Dynamic: + case Static: return gpuOnlyBuffer.getId(); + case CpuOnly: return MemoryUtil.NULL; + default: throw new UnsupportedOperationException(); + } + } + + @Override + public boolean resize(MemorySize size) { + switch (mode) { + case Stream: { + boolean resize = false; + for (int i = 0; i < sharedBuffers.length; i++) { + sharedBuffers[i] = new PersistentSharedBuffer(allocator.allocate()); + } + } + } + } + + @Override + public MemorySize size() { + return null; + } + + @Override + public void unmap() { + + } + + @Override + public boolean requiresCommandBuffer(int frame) { + return false; + } + + @Override + public void run(CommandBuffer cmd, int frame) { + + } + + private static class Ticket implements ResourceTicket { + + private final MemorySize size; + private final Flag usage; + + private final boolean concurrent; + + private Ticket(MemorySize size, Flag usage, boolean concurrent) { + this.size = size; + this.usage = usage; + this.concurrent = concurrent; + } + + @Override + public Float selectResource(VulkanBuffer resource) { + if () + } + + @Override + public VulkanBuffer createResource() { + return null; + } + + } + + private static class SharedBuffer { + + protected final ResourceWrapper wrapper; + + private SharedBuffer(ResourceWrapper wrapper) { + this.wrapper = wrapper; + } + + public GpuBuffer get() { + return wrapper.get(); + } + + } + + private static class PersistentSharedBuffer extends SharedBuffer { + + private final PersistentVulkanBuffer buffer; + + private PersistentSharedBuffer(ResourceWrapper wrapper) { + super(wrapper); + buffer = new PersistentVulkanBuffer<>(wrapper.get()); + } + + @Override + public VulkanBuffer get() { + return buffer; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/AsyncBufferHandler.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/AsyncBufferHandler.java new file mode 100644 index 0000000000..5377a5b0c8 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/AsyncBufferHandler.java @@ -0,0 +1,162 @@ +package com.jme3.vulkan.buffers; + +import com.jme3.vulkan.memory.MemorySize; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryUtil; + +import java.nio.*; +import java.util.function.Supplier; + +public class AsyncBufferHandler implements Mappable { + + private T buffer; + private PointerBuffer pointer; + private int mappings = 0; + + public AsyncBufferHandler() {} + + public AsyncBufferHandler(T buffer) { + this.buffer = buffer; + } + + @Override + public PointerBuffer map() { + if (mappings++ == 0) { + pointer = buffer.map(); + } + return pointer; + } + + @Override + public void unmap() { + if (mappings == 1) { + mappings--; + buffer.unmap(); + pointer = null; + } + } + + public void forceUnmap() { + if (mappings > 0) { + buffer.unmap(); + mappings = 0; + pointer = null; + } + } + + public boolean resize(int elements) { + if (buffer == null) { + throw new NullPointerException("Internal buffer is null."); + } + if (mappings > 0) { + throw new IllegalStateException("Cannot resize when mapped."); + } + return buffer.resize(elements); + } + + @Override + public ByteBuffer mapBytes() { + return map().getByteBuffer(0, buffer.size().getBytes()); + } + + @Override + public ShortBuffer mapShorts() { + return map().getShortBuffer(0, buffer.size().getShorts()); + } + + @Override + public IntBuffer mapInts() { + return map().getIntBuffer(0, buffer.size().getInts()); + } + + @Override + public FloatBuffer mapFloats() { + return map().getFloatBuffer(0, buffer.size().getFloats()); + } + + @Override + public DoubleBuffer mapDoubles() { + return map().getDoubleBuffer(0, buffer.size().getDoubles()); + } + + @Override + public LongBuffer mapLongs() { + return map().getLongBuffer(0, buffer.size().getLongs()); + } + + public void copy(GpuBuffer buffer) { + copy(buffer.mapBytes()); + } + + public void copy(AsyncBufferHandler buffer) { + copy(buffer.mapBytes()); + } + + public void copy(ByteBuffer buffer) { + MemoryUtil.memCopy(buffer, mapBytes()); + } + + public void copy(ShortBuffer buffer) { + MemoryUtil.memCopy(buffer, mapShorts()); + } + + public void copy(IntBuffer buffer) { + MemoryUtil.memCopy(buffer, mapInts()); + } + + public void copy(FloatBuffer buffer) { + MemoryUtil.memCopy(buffer, mapFloats()); + } + + public void copy(DoubleBuffer buffer) { + MemoryUtil.memCopy(buffer, mapDoubles()); + } + + public void copy(LongBuffer buffer) { + MemoryUtil.memCopy(buffer, mapLongs()); + } + + public void copy(Buffer buffer) { + if (buffer instanceof ByteBuffer) { + copy((ByteBuffer) buffer); + } else if (buffer instanceof ShortBuffer) { + copy((ShortBuffer) buffer); + } else if (buffer instanceof IntBuffer) { + copy((IntBuffer) buffer); + } else if (buffer instanceof FloatBuffer) { + copy((FloatBuffer) buffer); + } else if (buffer instanceof DoubleBuffer) { + copy((DoubleBuffer) buffer); + } else if (buffer instanceof LongBuffer) { + copy((LongBuffer) buffer); + } + } + + public void setBuffer(T buffer) { + this.buffer = buffer; + } + + public void setBufferIfAbsent(Supplier factory) { + if (buffer == null) { + buffer = factory.get(); + } + } + + @Override + public MemorySize size() { + return buffer.size(); + } + + public T getBuffer() { + return buffer; + } + + public int getMappings() { + return mappings; + } + + public boolean hasBuffer() { + return buffer != null; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/BackedStaticBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/BackedStaticBuffer.java new file mode 100644 index 0000000000..75496d6c69 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/BackedStaticBuffer.java @@ -0,0 +1,60 @@ +package com.jme3.vulkan.buffers; + +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.memory.MemorySize; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryUtil; + +import java.nio.ByteBuffer; + +public class BackedStaticBuffer extends StaticBuffer { + + private final PointerBuffer location = MemoryUtil.memAllocPointer(1); + private final ByteBuffer backing; + private boolean backed = false; + + public BackedStaticBuffer(LogicalDevice device, MemorySize size) { + super(device, size); + backing = MemoryUtil.memCalloc(size.getBytes(), Byte.BYTES); + ref.refresh(); + } + + @Override + public PointerBuffer map(int offset, int size) { + if (backed) { + location.put(0, MemoryUtil.memAddress(backing, offset)); + return location; + } + return super.map(offset, size); + } + + @Override + public ByteBuffer mapBytes() { + if (backed) { + return backing; + } + return super.mapBytes(); + } + + @Override + public void unmap() { + super.unmap(); + if (!backed) { + ByteBuffer data = super.mapBytes(); + MemoryUtil.memCopy(data, backing); + super.unmap(); + backed = true; + } + } + + @Override + public Runnable createNativeDestroyer() { + Runnable sup = super.createNativeDestroyer(); + return () -> { + sup.run(); + MemoryUtil.memFree(location); + MemoryUtil.memFree(backing); + }; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/BasicVulkanBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/BasicVulkanBuffer.java new file mode 100644 index 0000000000..d5036be50a --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/BasicVulkanBuffer.java @@ -0,0 +1,171 @@ +package com.jme3.vulkan.buffers; + +import com.jme3.renderer.vulkan.VulkanUtils; +import com.jme3.util.AbstractBuilder; +import com.jme3.util.natives.Native; +import com.jme3.util.natives.AbstractNative; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.memory.MemoryProp; +import com.jme3.vulkan.memory.MemoryRegion; +import com.jme3.vulkan.memory.MemorySize; +import com.jme3.vulkan.util.Flag; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkBufferCreateInfo; +import org.lwjgl.vulkan.VkMemoryRequirements; + +import java.nio.BufferOverflowException; +import java.nio.LongBuffer; + +import static com.jme3.renderer.vulkan.VulkanUtils.check; +import static org.lwjgl.vulkan.VK10.*; + +public class BasicVulkanBuffer extends AbstractNative implements VulkanBuffer { + + private final LogicalDevice device; + private MemorySize size; + private MemoryRegion memory; + protected Flag usage = BufferUsage.Storage; + protected boolean concurrent = false; + protected long padding; + + public BasicVulkanBuffer(LogicalDevice device, MemorySize size) { + this(device, size, 0); + } + + public BasicVulkanBuffer(LogicalDevice device, MemorySize size, long padding) { + this.device = device; + this.size = size; + this.padding = padding; + } + + @Override + public long getId() { + return object; + } + + @Override + public Runnable createNativeDestroyer() { + return () -> vkDestroyBuffer(device.getNativeObject(), object, null); + } + + @Override + public void verifyBufferSize(int elements, long bytesPerElement) { + if (elements * bytesPerElement > size.getBytes()) { + throw new BufferOverflowException(); + } + } + + @Override + public PointerBuffer map(int offset, int size) { + return memory.map(offset, size); + } + + @Override + public void unmap() { + memory.unmap(); + } + + @Override + public MemorySize size() { + return size; + } + + @Override + public LogicalDevice getDevice() { + return device; + } + + @Override + public boolean resize(MemorySize size) { + this.size = size; + if (memory != null && size.getBytes() > memory.getSize()) { + build().close(); + return true; + } + return false; + } + + @Override + public Flag getMemoryProperties() { + return memory.getFlags(); + } + + protected MemoryRegion getMemory() { + return memory; + } + + public void setPadding(long padding) { + assert padding >= 0 : "Padding cannot be negative."; + this.padding = padding; + } + + @Override + public Flag getUsage() { + return usage; + } + + @Override + public boolean isConcurrent() { + return concurrent; + } + + public long getPadding() { + return padding; + } + + public Builder build() { + return new Builder(); + } + + public class Builder extends AbstractBuilder { + + protected Flag memFlags = MemoryProp.DeviceLocal; + + @Override + protected void build() { + if (ref != null) { + ref.destroy(); + ref = null; + } + VkBufferCreateInfo create = VkBufferCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO) + .size(size.getBytes() + padding * size.getBytesPerElement()) + .usage(usage.bits()) + .sharingMode(VulkanUtils.sharingMode(concurrent)); + LongBuffer idBuf = stack.mallocLong(1); + check(vkCreateBuffer(device.getNativeObject(), create, null, idBuf), + "Failed to create buffer."); + object = idBuf.get(0); + VkMemoryRequirements bufferMem = VkMemoryRequirements.malloc(stack); + vkGetBufferMemoryRequirements(device.getNativeObject(), object, bufferMem); + memory = new MemoryRegion(device, bufferMem.size()); + try (MemoryRegion.Builder m = memory.build()) { + m.setFlags(memFlags); + m.setUsableMemoryTypes(bufferMem.memoryTypeBits()); + } + memory.bind(BasicVulkanBuffer.this, 0); + ref = Native.get().register(BasicVulkanBuffer.this); + device.getNativeReference().addDependent(ref); + memory.getNativeReference().addDependent(ref); + } + + public void setMemFlags(Flag memFlags) { + this.memFlags = memFlags; + } + + public void setUsage(Flag usage) { + BasicVulkanBuffer.this.usage = usage; + } + + public void setSize(MemorySize size) { + BasicVulkanBuffer.this.size = size; + } + + public void setConcurrent(boolean concurrent) { + BasicVulkanBuffer.this.concurrent = concurrent; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/BufferMember.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/BufferMember.java new file mode 100644 index 0000000000..f7611743a7 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/BufferMember.java @@ -0,0 +1,23 @@ +package com.jme3.vulkan.buffers; + +import java.nio.ByteBuffer; + +public interface BufferMember { + + /** + * Fills {@link #getSizeInBytes()} bytes of {@code buffer}, + * starting from the buffer's current position and incrementing + * the position by {@link #getSizeInBytes()} bytes. + * + * @param buffer buffer to fill + */ + void fillBuffer(ByteBuffer buffer); + + /** + * Gets the size of this member in bytes. + * + * @return size in bytes + */ + int getSizeInBytes(); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/BufferRegion.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/BufferRegion.java new file mode 100644 index 0000000000..02285d3661 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/BufferRegion.java @@ -0,0 +1,84 @@ +package com.jme3.vulkan.buffers; + +import com.jme3.vulkan.memory.MemorySize; + +public class BufferRegion { + + private int offset, end; + + public BufferRegion(int offset, int size) { + this.offset = verifyOffset(offset); + this.end = offset + verifySize(size); + } + + public BufferRegion set(BufferRegion region) { + this.offset = region.offset; + this.end = region.end; + return this; + } + + public BufferRegion set(int offset, int size) { + this.offset = verifyOffset(offset); + this.end = offset + verifySize(size); + return this; + } + + public boolean setEnd(int end) { + if (end <= offset) { + return false; + } + this.end = end; + return true; + } + + public BufferRegion unionLocal(BufferRegion region) { + offset = Math.min(offset, region.offset); + end = Math.max(end, region.end); + return this; + } + + public BufferRegion unionLocal(int offset, int size) { + this.offset = Math.min(this.offset, verifyOffset(offset)); + this.end = Math.max(this.end, offset + verifySize(size)); + return this; + } + + public int getOffset() { + return offset; + } + + public int getSize() { + return end - offset; + } + + public int getEnd() { + return end; + } + + public static BufferRegion all(MemorySize size) { + return new BufferRegion(0, size.getBytes()); + } + + public static BufferRegion union(BufferRegion region, int offset, int size) { + if (region == null) { + return new BufferRegion(offset, size); + } else { + return region.unionLocal(offset, size); + } + } + + private static int verifyOffset(int offset) { + if (offset < 0) { + throw new IllegalArgumentException("Offset cannot be negative."); + } + return offset; + } + + private static int verifySize(int size) { + if (size <= 0) { + throw new IllegalArgumentException("Size must be positive."); + } + return size; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/BufferUsage.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/BufferUsage.java new file mode 100644 index 0000000000..8e78019c98 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/BufferUsage.java @@ -0,0 +1,30 @@ +package com.jme3.vulkan.buffers; + +import com.jme3.vulkan.util.Flag; + +import static org.lwjgl.vulkan.VK10.*; + +public enum BufferUsage implements Flag { + + Uniform(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT), + Index(VK_BUFFER_USAGE_INDEX_BUFFER_BIT), + Storage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), + StorageTexel(VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT), + Indirect(VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT), + TransferDst(VK_BUFFER_USAGE_TRANSFER_DST_BIT), + TransferSrc(VK_BUFFER_USAGE_TRANSFER_SRC_BIT), + UniformTexel(VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT), + Vertex(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); + + private final int vkEnum; + + BufferUsage(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int bits() { + return vkEnum; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/GlBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/GlBuffer.java new file mode 100644 index 0000000000..8fdfa28dc0 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/GlBuffer.java @@ -0,0 +1,56 @@ +package com.jme3.vulkan.buffers; + +import com.jme3.vulkan.memory.MemorySize; +import org.lwjgl.PointerBuffer; + +import java.nio.Buffer; + +public class GlBuffer extends NioBuffer { + + private boolean updateNeeded = true; + + public GlBuffer(MemorySize size) { + super(size); + } + + public GlBuffer(MemorySize size, int padding) { + super(size, padding); + } + + public GlBuffer(Buffer size) { + super(size); + } + + public GlBuffer(NioBuffer size) { + super(size); + } + + public GlBuffer(GpuBuffer size) { + super(size); + } + + public GlBuffer(GpuBuffer size, int padding) { + super(size, padding); + } + + @Override + public PointerBuffer map(int offset, int size) { + updateNeeded = true; + return super.map(offset, size); + } + + public void setUpdateNeeded() { + updateNeeded = true; + } + + public boolean update() { + boolean u = updateNeeded; + updateNeeded = false; + return u; + } + + public boolean isUpdateNeeded() { + return updateNeeded; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/GpuBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/GpuBuffer.java new file mode 100644 index 0000000000..bee32d13a4 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/GpuBuffer.java @@ -0,0 +1,619 @@ +package com.jme3.vulkan.buffers; + +import com.jme3.scene.mesh.IndexBuffer; +import com.jme3.scene.mesh.IndexByteBuffer; +import com.jme3.scene.mesh.IndexIntBuffer; +import com.jme3.scene.mesh.IndexShortBuffer; +import com.jme3.vulkan.memory.MemorySize; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.Struct; +import org.lwjgl.system.StructBuffer; + +import java.nio.*; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.LongFunction; + +public interface GpuBuffer extends Mappable { + + /** + * Maps the memory of this buffer and returns a pointer to the mapped + * region. If the memory is currently mapped when this is called, an + * exception is thrown. + * + * @param offset offset, in bytes, of the mapping + * @param size size, in bytes, of the mapping + * @return Buffer containing a pointer to the mapped memory + * @throws IllegalStateException if this buffer is already mapped + */ + PointerBuffer map(int offset, int size); + + /** + * Gets the native buffer ID. + * + * @return buffer ID + */ + long getId(); + + /** + * Resizes this buffer to accomodate the memory size. + * Implementations may choose to throw an exception rather than resizing. + * + * @param size buffer size this buffer must accommodate + * @return true if the buffer capacity had to be changed + * @throws UnsupportedOperationException if this buffer is unable to resize + */ + boolean resize(MemorySize size); + + /** + * Resizes this buffer to accomodate the given number of elements. + * Implementations may choose to throw an exception rather than resizing. + * + * @param elements number of elements this buffer must accommodate + * @return true if the buffer capacity had to be changed + * @throws UnsupportedOperationException if this buffer is unable to resize + */ + default boolean resize(int elements) { + return resize(new MemorySize(elements, size().getBytesPerElement())); + } + + /** + * Verifies that there are enough bytes in the buffer to represent {@code elements} + * with {@code bytesPerElement}. + * + * @param elements number of elements + * @param bytesPerElement number of bytes needed per element + * @throws BufferOverflowException if this buffer is not large enough + */ + default void verifyBufferSize(int elements, long bytesPerElement) { + if (elements * bytesPerElement > size().getBytes()) { + throw new BufferOverflowException(); + } + } + + /** + * Maps bytes starting from the byte offset and extending to the end of the buffer. + * + * @param offset offset in bytes + * @return pointer to the mapped region + * @see #map(int, int) + */ + default PointerBuffer map(int offset) { + return map(offset, size().getBytes() - offset); + } + + /** + * Maps all bytes in the buffer. + * + * @return pointer to the mapped region + * @see #map(int, int) + */ + @Override + default PointerBuffer map() { + return map(0, size().getBytes()); + } + + /** + * Maps bytes within the {@code offset} and {@code size} (in bytes) as an object. + * + * @param offset offset in bytes + * @param size size in bytes + * @param factory creates an object from the mapped pointer + * @return mapped object + * @param mapping type + * @see #map(int, int) + */ + default T map(int offset, int size, Function factory) { + return factory.apply(map(offset, size)); + } + + /** + * Maps bytes starting from the offset and extending to the end of the buffer + * as an object. + * + * @param offset offset in bytes + * @param factory creates an object from the mapped pointer + * @return mapped object + * @param mapping type + * @see #map(int, int) + */ + default T map(int offset, Function factory) { + return factory.apply(map(offset, size().getBytes() - offset)); + } + + /** + * Maps all bytes in the buffer as an object. + * + * @param factory creates an object from the mapped pointer + * @return mapped object + * @param mapping type + * @see #map(int, int) + */ + default T map(Function factory) { + return factory.apply(map(0, size().getBytes())); + } + + /** + * Maps bytes within the {@code offset} and {@code size} (in bytes) as an object. + * + * @param offset offset in bytes + * @param size size in bytes + * @param factory creates an object from the mapped pointer + * @return mapped object + * @param mapping type + * @see #map(int, int) + */ + default T map(int offset, int size, LongFunction factory) { + return factory.apply(map(offset, size).get(0)); + } + + /** + * Maps bytes starting from the offset and extending to the end of the buffer + * as an object. + * + * @param offset offset in bytes + * @param factory creates an object from the mapped pointer + * @return mapped object + * @param mapping type + * @see #map(int, int) + */ + default T map(int offset, LongFunction factory) { + return factory.apply(map(offset, size().getBytes() - offset).get(0)); + } + + /** + * Maps all bytes in the buffer as an object. + * + * @param factory creates an object from the mapped pointer + * @return mapped object + * @param mapping type + * @see #map(int, int) + */ + default T map(LongFunction factory) { + return factory.apply(map(0, size().getBytes()).get(0)); + } + + /** + * Maps bytes within the {@code offset} and {@code size} (in elements) as an object. + * + * @param offset offset in bytes + * @param size size in bytes + * @param factory creates an object from the mapped pointer and the number of + * elements in the mapped region + * @return mapped object + * @param mapped type + * @see #map(int, int) + */ + default T map(int offset, int size, BiFunction factory) { + offset *= size().getBytesPerElement(); + return factory.apply(map(offset, size * size().getBytesPerElement()).get(0), size); + } + + /** + * Maps bytes starting from the offset and extending to the end of the buffer + * as an object. + * + * @param offset offset in elements + * @param factory creates an object from the mapped pointer and the number of + * elements in the mapped region + * @return mapped object + * @param mapping type + * @see #map(int, int) + */ + default T map(int offset, BiFunction factory) { + offset *= size().getBytesPerElement(); + int size = size().getBytes() - offset; + return factory.apply(map(offset, size).get(0), size / size().getBytesPerElement()); + } + + /** + * Maps all bytes in the buffer as an object. + * + * @param factory creates an object from the mapped pointer and the number of + * elements in the mapped region + * @return mapped object + * @param mapping type + * @see #map(int, int) + */ + default T map(BiFunction factory) { + return factory.apply(map(0, size().getBytes()).get(0), size().getElements()); + } + + /** + * Maps bytes within {@code offset} and {@code size}. + * + * @param offset offset in bytes + * @param size size in bytes + * @return mapped region as a byte buffer + * @see #map(int, int) + */ + default ByteBuffer mapBytes(int offset, int size) { + return map(offset * Byte.BYTES, size * Byte.BYTES).getByteBuffer(0, size); + } + + /** + * Maps bytes from {@code offset} to the end of the buffer. + * + * @param offset offset in bytes + * @return mapped region as a byte buffer + * @see #map(int, int) + */ + default ByteBuffer mapBytes(int offset) { + return mapBytes(offset, size().getBytes() - offset); + } + + /** + * Maps all bytes in the buffer. + * + * @return mapped region as a byte buffer + * @see #map(int, int) + */ + @Override + default ByteBuffer mapBytes() { + return mapBytes(0, size().getBytes()); + } + + /** + * Maps shorts within {@code offset} and {@code size}. + * + * @param offset offset in shorts + * @param size size in shorts + * @return mapped region as a short buffer + * @see #map(int, int) + */ + default ShortBuffer mapShorts(int offset, int size) { + return map(offset * Short.BYTES, size * Short.BYTES).getShortBuffer(0, size); + } + + /** + * Maps shorts from {@code offset} to the end of the buffer. + * + * @param offset offset in shorts + * @return mapped region as a short buffer + * @see #map(int, int) + */ + default ShortBuffer mapShorts(int offset) { + return mapShorts(offset, size().getShorts() - offset); + } + + /** + * Maps all shorts in the buffer. + * + * @return mapped region as a short buffer + * @see #map(int, int) + */ + @Override + default ShortBuffer mapShorts() { + return mapShorts(0, size().getShorts()); + } + + /** + * Maps ints within {@code offset} and {@code size}. + * + * @param offset offset in ints + * @param size size in ints + * @return mapped region as an int buffer + * @see #map(int, int) + */ + default IntBuffer mapInts(int offset, int size) { + return map(offset * Integer.BYTES, size * Integer.BYTES).getIntBuffer(0, size); + } + + /** + * Maps ints from {@code offset} to the end of the buffer. + * + * @param offset offset in ints + * @return mapped region as an int buffer + * @see #map(int, int) + */ + default IntBuffer mapInts(int offset) { + return mapInts(offset, size().getInts() - offset); + } + + /** + * Maps all ints in the buffer. + * + * @return mapped region as an int buffer + * @see #map(int, int) + */ + @Override + default IntBuffer mapInts() { + return mapInts(0, size().getInts()); + } + + /** + * Maps floats within {@code offset} and {@code size}. + * + * @param offset offset in floats + * @param size size in floats + * @return mapped region as a float buffer + * @see #map(int, int) + */ + default FloatBuffer mapFloats(int offset, int size) { + return map(offset * Float.BYTES, size * Float.BYTES).getFloatBuffer(0, size); + } + + /** + * Maps floats from {@code offset} to the end of the buffer. + * + * @param offset offset in floats + * @return mapped region as a float buffer + * @see #map(int, int) + */ + default FloatBuffer mapFloats(int offset) { + return mapFloats(offset, size().getFloats() - offset); + } + + /** + * Maps all floats in the buffer. + * + * @return mapped region as a float buffer + * @see #map(int, int) + */ + @Override + default FloatBuffer mapFloats() { + return mapFloats(0, size().getFloats()); + } + + /** + * Maps doubles within {@code offset} and {@code size}. + * + * @param offset offset in doubles + * @param size size in doubles + * @return mapped region as a double buffer + * @see #map(int, int) + */ + default DoubleBuffer mapDoubles(int offset, int size) { + return map(offset * Double.BYTES, size * Double.BYTES).getDoubleBuffer(0, size); + } + + /** + * Maps doubles from {@code offset} to the end of the buffer. + * + * @param offset offset in doubles + * @return mapped region as a double buffer + * @see #map(int, int) + */ + default DoubleBuffer mapDoubles(int offset) { + return mapDoubles(offset, size().getDoubles() - offset); + } + + /** + * Maps all doubles in the buffer. + * + * @return mapped region as a double buffer + * @see #map(int, int) + */ + @Override + default DoubleBuffer mapDoubles() { + return mapDoubles(0, size().getDoubles()); + } + + /** + * Maps longs within {@code offset} and {@code size}. + * + * @param offset offset in longs + * @param size size in longs + * @return mapped region as a long buffer + * @see #map(int, int) + */ + default LongBuffer mapLongs(int offset, int size) { + return map(offset * Long.BYTES, size * Long.BYTES).getLongBuffer(0, size); + } + + /** + * Maps longs from {@code offset} to the end of the buffer. + * + * @param offset offset in longs + * @return mapped region as a long buffer + * @see #map(int, int) + */ + default LongBuffer mapLongs(int offset) { + return mapLongs(offset, size().getLongs() - offset); + } + + /** + * Maps all longs in the buffer. + * + * @return mapped region as a long buffer + * @see #map(int, int) + */ + @Override + default LongBuffer mapLongs() { + return mapLongs(0, size().getLongs()); + } + + /** + * Maps indices as an IndexBuffer from {@code offset} through {@code size}. + * + *

This buffer must have {@link Byte#BYTES}, {@link Short#BYTES}, or {@link Integer#BYTES} number + * of bytes per element to map to either {@link IndexByteBuffer}, {@link IndexShortBuffer}, or + * {@link IndexIntBuffer}, respectively. For better results, use {@link #mapInts()} or another + * similar method that maps directly to a primitive buffer.

+ * + * @param offset offset in elements + * @param size size in elements + * @return mapped region as an index buffer + * @throws UnsupportedOperationException if the number of bytes per element of this buffer + * does not correspond to a byte, short, or integer + * @see #map(int, int) + */ + default IndexBuffer mapIndices(int offset, int size) { + switch (size().getBytesPerElement()) { + case Byte.BYTES: return new IndexByteBuffer(mapBytes(offset, size)); + case Short.BYTES: return new IndexShortBuffer(mapShorts(offset, size)); + case Integer.BYTES: return new IndexIntBuffer(mapInts(offset, size)); + default: throw new UnsupportedOperationException("Unable to map to index buffer with " + + size().getBytesPerElement() + " bytes per element."); + } + } + + /** + * Maps indices as an IndexBuffer from {@code offset} to the end of the buffer. + * + *

This buffer must have {@link Byte#BYTES}, {@link Short#BYTES}, or {@link Integer#BYTES} number + * of bytes per element to map to either {@link IndexByteBuffer}, {@link IndexShortBuffer}, or + * {@link IndexIntBuffer}, respectively. For better results, use {@link #mapInts()} or another + * similar method that maps directly to a primitive buffer.

+ * + * @param offset offset in elements + * @return mapped region as an index buffer + * @throws UnsupportedOperationException if the number of bytes per element of this buffer + * does not correspond to a byte, short, or integer + * @see #map(int, int) + */ + default IndexBuffer mapIndices(int offset) { + switch (size().getBytesPerElement()) { + case Byte.BYTES: return new IndexByteBuffer(mapBytes(offset)); + case Short.BYTES: return new IndexShortBuffer(mapShorts(offset)); + case Integer.BYTES: return new IndexIntBuffer(mapInts(offset)); + default: throw new UnsupportedOperationException("Unable to map to index buffer with " + + size().getBytesPerElement() + " bytes per element."); + } + } + + /** + * Maps all indices of this buffer as an IndexBuffer. + * + *

This buffer must have {@link Byte#BYTES}, {@link Short#BYTES}, or {@link Integer#BYTES} number + * of bytes per element to map to either {@link IndexByteBuffer}, {@link IndexShortBuffer}, or + * {@link IndexIntBuffer}, respectively. For better results, use {@link #mapInts()} or another + * similar method that maps directly to a primitive buffer.

+ * + * @return mapped region as an index buffer + * @throws UnsupportedOperationException if the number of bytes per element of this buffer + * does not correspond to a byte, short, or integer + * @see #map(int, int) + */ + default IndexBuffer mapIndices() { + switch (size().getBytesPerElement()) { + case Byte.BYTES: return new IndexByteBuffer(mapBytes()); + case Short.BYTES: return new IndexShortBuffer(mapShorts()); + case Integer.BYTES: return new IndexIntBuffer(mapInts()); + default: throw new UnsupportedOperationException("Unable to map to index buffer with " + + size().getBytesPerElement() + " bytes per element."); + } + } + + /** + * Copies the contents of {@code buffer} to this buffer. This buffer must be large + * enough to fit the contents of {@code buffer}. + * + * @param buffer buffer to copy + */ + default void copy(GpuBuffer buffer) { + verifyBufferSize(buffer.size().getBytes(), Byte.BYTES); + MemoryUtil.memCopy(buffer.mapBytes(), mapBytes(0, buffer.size().getBytes())); + buffer.unmap(); + unmap(); + } + + /** + * Copies all bytes from {@code buffer} to this buffer. This buffer must be large + * enough to fit all bytes from {@code buffer}. + * + * @param buffer buffer to copy + */ + default void copy(ByteBuffer buffer) { + verifyBufferSize(buffer.limit(), Byte.BYTES); + MemoryUtil.memCopy(buffer, mapBytes(0, buffer.limit())); + unmap(); + } + + /** + * Copies all shorts from {@code buffer} to this buffer. This buffer must be large + * enough to fit all shorts from {@code buffer}. + * + * @param buffer buffer to copy + */ + default void copy(ShortBuffer buffer) { + verifyBufferSize(buffer.limit(), Short.BYTES); + MemoryUtil.memCopy(buffer, mapShorts(0, buffer.limit())); + unmap(); + } + + /** + * Copies all ints from {@code buffer} to this buffer. This buffer must be large + * enough to fit all integers from {@code buffer}. + * + * @param buffer buffer to copy + */ + default void copy(IntBuffer buffer) { + verifyBufferSize(buffer.limit(), Integer.BYTES); + MemoryUtil.memCopy(buffer, mapInts(0, buffer.limit())); + unmap(); + } + + /** + * Copies all floats from {@code buffer} to this buffer. This buffer must be large + * enough to fit all floats from {@code buffer}. + * + * @param buffer buffer to copy + */ + default void copy(FloatBuffer buffer) { + verifyBufferSize(buffer.limit(), Float.BYTES); + MemoryUtil.memCopy(buffer, mapFloats(0, buffer.limit())); + unmap(); + } + + /** + * Copies all doubles from {@code buffer} to this buffer. This buffer must be large + * enough to fit all doubles from {@code buffer}. + * + * @param buffer buffer to copy + */ + default void copy(DoubleBuffer buffer) { + verifyBufferSize(buffer.limit(), Double.BYTES); + MemoryUtil.memCopy(buffer, mapDoubles(0, buffer.limit())); + unmap(); + } + + /** + * Copies all longs from {@code buffer} to this buffer. This buffer must be large + * enough to fit all longs from {@code buffer}. + * + * @param buffer buffer to copy + */ + default void copy(LongBuffer buffer) { + verifyBufferSize(buffer.limit(), Long.BYTES); + MemoryUtil.memCopy(buffer, mapLongs(0, buffer.limit())); + unmap(); + } + + /** + * Copies all content from {@code buffer} to this buffer. This buffer must be large + * enough to fit all content from {@code buffer}. + * + * @param buffer buffer to copy + */ + default void copy(Buffer buffer) { + if (buffer instanceof ByteBuffer) { + copy((ByteBuffer)buffer); + } else if (buffer instanceof ShortBuffer) { + copy((ShortBuffer)buffer); + } else if (buffer instanceof IntBuffer) { + copy((IntBuffer)buffer); + } else if (buffer instanceof FloatBuffer) { + copy((FloatBuffer)buffer); + } else if (buffer instanceof DoubleBuffer) { + copy((DoubleBuffer)buffer); + } else if (buffer instanceof LongBuffer) { + copy((LongBuffer)buffer); + } + } + + default void copy(Struct struct) { + verifyBufferSize(struct.sizeof(), Byte.BYTES); + MemoryUtil.memCopy(MemoryUtil.memByteBuffer(struct.address(), struct.sizeof()), mapBytes(0, struct.sizeof())); + unmap(); + } + + default void copy(StructBuffer buffer) { + verifyBufferSize(buffer.limit(), buffer.sizeof()); + int size = buffer.limit() * buffer.sizeof(); + MemoryUtil.memCopy(MemoryUtil.memByteBuffer(buffer.address(), size), mapBytes(0, size)); + unmap(); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/Mappable.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/Mappable.java new file mode 100644 index 0000000000..09b57e7205 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/Mappable.java @@ -0,0 +1,28 @@ +package com.jme3.vulkan.buffers; + +import com.jme3.vulkan.memory.MemorySize; +import org.lwjgl.PointerBuffer; + +import java.nio.*; + +public interface Mappable { + + PointerBuffer map(); + + MemorySize size(); + + void unmap(); + + ByteBuffer mapBytes(); + + ShortBuffer mapShorts(); + + IntBuffer mapInts(); + + FloatBuffer mapFloats(); + + DoubleBuffer mapDoubles(); + + LongBuffer mapLongs(); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/NioBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/NioBuffer.java new file mode 100644 index 0000000000..955ef9107d --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/NioBuffer.java @@ -0,0 +1,138 @@ +package com.jme3.vulkan.buffers; + +import com.jme3.util.BufferUtils; +import com.jme3.util.natives.Native; +import com.jme3.util.natives.NativeReference; +import com.jme3.vulkan.memory.MemorySize; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryUtil; + +import java.nio.*; + +public class NioBuffer implements GpuBuffer, Native { + + private final PointerBuffer address = MemoryUtil.memCallocPointer(1); + private final NativeReference ref; + private ByteBuffer buffer; + private MemorySize size; + private int padding; + private long baseBufferAddress; + private int lastMappedOffset = -1; + + public NioBuffer(MemorySize size) { + this(size, 0); + } + + public NioBuffer(MemorySize size, int padding) { + this(size, padding, true); + } + + public NioBuffer(MemorySize size, int padding, boolean clearMem) { + this.size = size; + this.padding = padding; + if (clearMem) { + buffer = MemoryUtil.memCalloc(size.getBytes(padding)); + } else { + buffer = MemoryUtil.memAlloc(size.getBytes(padding)); + } + buffer.limit(size.getBytes()); + baseBufferAddress = MemoryUtil.memAddress(buffer, 0); + ref = Native.get().register(this); + } + + public NioBuffer(Buffer size) { + this(new MemorySize(size.limit(), BufferUtils.getBytesPerElement(size)), size.capacity() - size.limit()); + } + + public NioBuffer(NioBuffer size) { + this(size.size(), size.getPadding()); + } + + public NioBuffer(GpuBuffer size) { + this(size.size(), 0); + } + + public NioBuffer(GpuBuffer size, int padding) { + this(size.size(), padding); + } + + @Override + public PointerBuffer map(int offset, int size) { + if (offset < 0) { + throw new IllegalArgumentException("Offset cannot be negative."); + } + if (offset != lastMappedOffset) { + if (offset == 0) { + address.put(0, baseBufferAddress); + } else { + address.put(0, MemoryUtil.memAddress(buffer, offset)); + } + lastMappedOffset = offset; + } + return address; + } + + @Override + public void unmap() {} + + @Override + public MemorySize size() { + return size; + } + + @Override + public long getId() { + return baseBufferAddress; + } + + @Override + public boolean resize(MemorySize size) { + this.size = size; + if (size.getBytes() > buffer.capacity()) { + ByteBuffer newBuffer = MemoryUtil.memRealloc(buffer, size.getBytes(padding)); + newBuffer.limit(size.getBytes()); + if (newBuffer != buffer) { + buffer = newBuffer; + baseBufferAddress = MemoryUtil.memAddress(buffer, 0); + ref.refresh(); + } + return true; + } + buffer.limit(size.getBytes()); + return false; + } + + @Override + public Long getNativeObject() { + return baseBufferAddress; + } + + @Override + public Runnable createNativeDestroyer() { + return () -> { + MemoryUtil.memFree(buffer); + MemoryUtil.memFree(address); + }; + } + + @Override + public void prematureNativeDestruction() {} + + @Override + public NativeReference getNativeReference() { + return ref; + } + + public void setPadding(int padding) { + this.padding = padding; + } + + public ByteBuffer getBuffer() { + return buffer; + } + + public int getPadding() { + return padding; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/OldPersistentBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/OldPersistentBuffer.java new file mode 100644 index 0000000000..0c829192c8 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/OldPersistentBuffer.java @@ -0,0 +1,68 @@ +package com.jme3.vulkan.buffers; + +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.memory.MemoryProp; +import com.jme3.vulkan.memory.MemorySize; +import com.jme3.vulkan.util.Flag; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryUtil; + +import java.nio.ByteBuffer; + +@Deprecated +public class OldPersistentBuffer extends BasicVulkanBuffer { + + private final PointerBuffer mappedAddress = MemoryUtil.memCallocPointer(1); + private PointerBuffer regionAddress; + private ByteBuffer regionBuffer; + + public OldPersistentBuffer(LogicalDevice device, MemorySize size) { + super(device, size); + } + + @Override + public PointerBuffer map(int offset, int size) { + if (offset > 0) { + mappedAddress.put(0, MemoryUtil.memAddress(regionBuffer, offset)); + } else { + mappedAddress.put(0, regionAddress.get(0)); + } + return mappedAddress; + } + + @Override + public void unmap() {} + + @Override + public Runnable createNativeDestroyer() { + Runnable sup = super.createNativeDestroyer(); + return () -> { + sup.run(); + MemoryUtil.memFree(mappedAddress); + }; + } + + @Override + public Builder build() { + return new Builder(); + } + + public class Builder extends BasicVulkanBuffer.Builder { + + public Builder() { + memFlags = Flag.of(MemoryProp.HostVisible, MemoryProp.HostCoherent); + } + + @Override + public void build() { + if (!memFlags.contains(MemoryProp.HostVisible)) { + throw new IllegalArgumentException("Memory must be host visible."); + } + super.build(); + regionAddress = getMemory().map(0, size().getBytes()); + regionBuffer = regionAddress.getByteBuffer(0, size().getBytes()); + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/PerFrameBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/PerFrameBuffer.java new file mode 100644 index 0000000000..3361cd9ef1 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/PerFrameBuffer.java @@ -0,0 +1,153 @@ +package com.jme3.vulkan.buffers; + +import com.jme3.vulkan.commands.CommandBuffer; +import com.jme3.vulkan.frames.UpdateFrameManager; +import com.jme3.vulkan.frames.VersionedResource; +import com.jme3.vulkan.memory.MemorySize; +import com.jme3.vulkan.update.Command; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryUtil; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.function.Function; + +/** + * Maintains an internal buffer (versions) for each possible frame. + * + *

CPU-side changes are tracked by an {@link NioBuffer}. On command, the + * local changes are copied to the current version if changes were made since + * the last copy operation to that version. For consistency, the current version + * is updated when this buffer is {@link #unmap() unmapped}.

+ * + * @param + */ +@Deprecated +public class PerFrameBuffer implements GpuBuffer, VersionedResource, Command { + + private final UpdateFrameManager frames; + private final List buffers; + private final NioBuffer cpuBuffer; + private final BufferRegion[] dirtyRegions; + private int elements; + + public PerFrameBuffer(UpdateFrameManager frames, MemorySize size, Function generator) { + this.frames = frames; + this.elements = size.getElements(); + List buffers = new ArrayList<>(frames.getTotalFrames()); + for (int i = 0; i < frames.getTotalFrames(); i++) { + buffers.add(generator.apply(size)); + } + this.buffers = Collections.unmodifiableList(buffers); + this.cpuBuffer = new NioBuffer(size, 10); + this.dirtyRegions = new BufferRegion[frames.getTotalFrames()]; + } + + @Override + public PointerBuffer map(int offset, int size) { + // mark mapped region as dirty on all versioned buffers + for (int i = 0; i < dirtyRegions.length; i++) { + if (dirtyRegions[i] == null) { + dirtyRegions[i] = new BufferRegion(offset, size); + } else { + dirtyRegions[i].unionLocal(offset, size); + } + } + //return updateBufferSize().map(offset, size); + return cpuBuffer.map(offset, size); + } + + @Override + public void unmap() { + cpuBuffer.unmap(); + updateCurrentBuffer(); + } + + @Override + public MemorySize size() { + return cpuBuffer.size(); + } + + @Override + public long getId() { + return updateCurrentBuffer().getId(); + } + + @Override + public boolean resize(MemorySize size) { + return false; + } + + @Override + public boolean resize(int elements) { + this.elements = elements; + int length = elements * size().getBytesPerElement(); + // adjust dirty regions + for (int i = 0; i < dirtyRegions.length; i++) { + BufferRegion d = dirtyRegions[i]; + // if the full dirty region is now outside the buffer, nullify the dirty region + if (d != null && d.getEnd() > length && !d.setEnd(length)) { + dirtyRegions[i] = null; + } + } + boolean resized = cpuBuffer.resize(elements); + updateCurrentBuffer(); + return resized; + } + + @Override + public T get() { + return buffers.get(frames.getCurrentFrame()); + } + + @Override + public T get(int frame) { + return buffers.get(frame); + } + + @Override + public int getNumResources() { + return buffers.size(); + } + + @Override + public Iterator iterator() { + return buffers.iterator(); + } + + @Override + public boolean requiresCommandBuffer(int frame) { + return false; + } + + @Override + public void run(CommandBuffer cmd, int frame) { + updateBuffer(frame); + } + + private T updateBuffer(int frame) { + T buf = get(frame); + if (elements != buf.size().getElements()) { + buf.resize(elements); + } + // copy staged data to the current version on the dirty region + BufferRegion d = dirtyRegions[frame]; + if (d != null) { + ByteBuffer src = cpuBuffer.mapBytes(d.getOffset(), d.getSize()); + ByteBuffer dst = buf.mapBytes(d.getOffset(), d.getSize()); + MemoryUtil.memCopy(src, dst); + cpuBuffer.unmap(); + buf.unmap(); + dirtyRegions[frame] = null; // dirty region has been cleaned + } + return buf; + } + + private T updateCurrentBuffer() { + return updateBuffer(frames.getCurrentFrame()); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/PersistentBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/PersistentBuffer.java new file mode 100644 index 0000000000..2bc689a4ec --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/PersistentBuffer.java @@ -0,0 +1,82 @@ +package com.jme3.vulkan.buffers; + +import com.jme3.util.natives.Native; +import com.jme3.util.natives.NativeReference; +import com.jme3.vulkan.memory.MemorySize; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryUtil; + +import java.nio.ByteBuffer; + +public class PersistentBuffer implements GpuBuffer, Native { + + private final T buffer; + private final PointerBuffer pointer = MemoryUtil.memCallocPointer(1); + private final NativeReference ref; + private ByteBuffer mapping; + + public PersistentBuffer(T buffer) { + this.buffer = buffer; + this.ref = Native.get().register(this); + } + + @Override + public PointerBuffer map(int offset, int size) { + if (mapping == null) { + mapping = buffer.mapBytes(); + } + return pointer.put(0, MemoryUtil.memAddress(mapping, offset)); + } + + @Override + public long getId() { + return buffer.getId(); + } + + @Override + public boolean resize(MemorySize size) { + if (buffer.resize(size)) { + mapping = null; + return true; + } + return false; + } + + @Override + public MemorySize size() { + return buffer.size(); + } + + @Override + public void unmap() {} + + @Override + public T getNativeObject() { + return buffer; + } + + @Override + public Runnable createNativeDestroyer() { + return () -> MemoryUtil.memFree(pointer); + } + + @Override + public void prematureNativeDestruction() {} + + @Override + public NativeReference getNativeReference() { + return ref; + } + + public void forceUnmap() { + if (mapping != null) { + mapping = null; + buffer.unmap(); + } + } + + public T getBuffer() { + return buffer; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/PersistentVulkanBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/PersistentVulkanBuffer.java new file mode 100644 index 0000000000..bea77037c3 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/PersistentVulkanBuffer.java @@ -0,0 +1,33 @@ +package com.jme3.vulkan.buffers; + +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.memory.MemoryProp; +import com.jme3.vulkan.util.Flag; + +public class PersistentVulkanBuffer extends PersistentBuffer implements VulkanBuffer { + + public PersistentVulkanBuffer(T buffer) { + super(buffer); + } + + @Override + public LogicalDevice getDevice() { + return getBuffer().getDevice(); + } + + @Override + public Flag getUsage() { + return getBuffer().getUsage(); + } + + @Override + public Flag getMemoryProperties() { + return getBuffer().getMemoryProperties(); + } + + @Override + public boolean isConcurrent() { + return getBuffer().isConcurrent(); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/StageableBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/StageableBuffer.java new file mode 100644 index 0000000000..8104ad56a9 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/StageableBuffer.java @@ -0,0 +1,107 @@ +package com.jme3.vulkan.buffers; + +import com.jme3.vulkan.commands.CommandBuffer; +import com.jme3.vulkan.commands.CommandPool; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.memory.MemoryProp; +import com.jme3.vulkan.memory.MemorySize; +import com.jme3.vulkan.sync.SyncGroup; +import com.jme3.vulkan.update.Command; +import com.jme3.vulkan.util.Flag; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; + +public class StageableBuffer extends BasicVulkanBuffer implements Command { + + protected VulkanBuffer stage; + protected BufferRegion dirtyRegion; + + public StageableBuffer(LogicalDevice device, MemorySize size) { + super(device, size); + usage = usage.add(BufferUsage.TransferDst); + } + + @Override + public boolean requiresCommandBuffer(int frame) { + return stage != null && dirtyRegion != null; + } + + @Override + public void run(CommandBuffer cmd, int frame) { + if (cmd != null) { + transfer(cmd); + } + } + + @Override + public PointerBuffer map(int offset, int size) { + updateDirtyRegion(offset, size); + if (stage == null) { + stage = createStagingBuffer(); + } + return stage.map(offset, size); + } + + @Override + public void unmap() { + stage.unmap(); + } + + @Override + public boolean resize(int elements) { + if (stage != null) { + stage.resize(elements); + } + return super.resize(elements); + } + + protected VulkanBuffer createStagingBuffer() { + BasicVulkanBuffer buf = new BasicVulkanBuffer(getDevice(), size(), padding); + try (BasicVulkanBuffer.Builder b = buf.build()) { + b.setUsage(BufferUsage.TransferSrc); + b.setMemFlags(Flag.of(MemoryProp.HostVisible, MemoryProp.HostCoherent)); + } + return buf; + } + + protected void updateDirtyRegion(int offset, int size) { + if (dirtyRegion != null) { + dirtyRegion.unionLocal(offset, size); + } else { + dirtyRegion = new BufferRegion(offset, size); + } + } + + public void transfer(CommandPool transferPool) { + transfer(transferPool, SyncGroup.ASYNC); + } + + public void transfer(CommandPool transferPool, SyncGroup sync) { + if (stage != null && dirtyRegion != null) { + CommandBuffer cmd = transferPool.allocateTransientCommandBuffer(); + cmd.begin(); + transfer(cmd); + cmd.endAndSubmit(sync); + } + } + + public boolean transfer(CommandBuffer cmd) { + if (stage != null && dirtyRegion != null) { + try (MemoryStack stack = MemoryStack.stackPush()) { + recordCopy(stack, cmd, stage, dirtyRegion.getOffset(), dirtyRegion.getOffset(), dirtyRegion.getSize()); + } + dirtyRegion = null; + return true; + } + return false; + } + + public void freeStagingBuffer() { + stage = null; + } + + public GpuBuffer getStagingBuffer() { + return stage; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/StaticBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/StaticBuffer.java new file mode 100644 index 0000000000..a92a3e0fc4 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/StaticBuffer.java @@ -0,0 +1,23 @@ +package com.jme3.vulkan.buffers; + +import com.jme3.vulkan.commands.CommandBuffer; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.memory.MemoryProp; +import com.jme3.vulkan.memory.MemorySize; +import com.jme3.vulkan.util.Flag; + +public class StaticBuffer extends StageableBuffer { + + public StaticBuffer(LogicalDevice device, MemorySize size) { + super(device, size); + } + + @Override + public void run(CommandBuffer cmd, int frame) { + if (dirtyRegion == null) { + freeStagingBuffer(); + } + super.run(cmd, frame); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/VulkanBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/VulkanBuffer.java new file mode 100644 index 0000000000..bd9d2e4e59 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/VulkanBuffer.java @@ -0,0 +1,32 @@ +package com.jme3.vulkan.buffers; + +import com.jme3.vulkan.commands.CommandBuffer; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.memory.MemoryProp; +import com.jme3.vulkan.memory.MemoryRegion; +import com.jme3.vulkan.util.Flag; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkBufferCopy; + +import static org.lwjgl.vulkan.VK10.*; + +public interface VulkanBuffer extends GpuBuffer { + + LogicalDevice getDevice(); + + Flag getUsage(); + + Flag getMemoryProperties(); + + boolean isConcurrent(); + + default void recordCopy(MemoryStack stack, CommandBuffer commands, GpuBuffer source, + long srcOffset, long dstOffset, long size) { + VkBufferCopy.Buffer copy = VkBufferCopy.calloc(1, stack) + .srcOffset(srcOffset) + .dstOffset(dstOffset) + .size(size); + vkCmdCopyBuffer(commands.getBuffer(), source.getId(), getId(), copy); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/generate/BufferGenerator.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/generate/BufferGenerator.java new file mode 100644 index 0000000000..7d91db645f --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/generate/BufferGenerator.java @@ -0,0 +1,13 @@ +package com.jme3.vulkan.buffers.generate; + +import com.jme3.vulkan.buffers.BufferUsage; +import com.jme3.vulkan.buffers.GpuBuffer; +import com.jme3.vulkan.memory.MemorySize; +import com.jme3.vulkan.mesh.DataAccess; +import com.jme3.vulkan.util.Flag; + +public interface BufferGenerator { + + T createBuffer(MemorySize size, Flag usage, DataAccess access); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/generate/MeshBufferGenerator.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/generate/MeshBufferGenerator.java new file mode 100644 index 0000000000..def8121d76 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/generate/MeshBufferGenerator.java @@ -0,0 +1,78 @@ +package com.jme3.vulkan.buffers.generate; + +import com.jme3.vulkan.buffers.*; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.frames.UpdateFrameManager; +import com.jme3.vulkan.memory.MemoryProp; +import com.jme3.vulkan.memory.MemorySize; +import com.jme3.vulkan.mesh.DataAccess; +import com.jme3.vulkan.update.CommandBatch; +import com.jme3.vulkan.util.Flag; + +public class MeshBufferGenerator implements BufferGenerator { + + private final LogicalDevice device; + private final UpdateFrameManager frames; + private final CommandBatch dynamicBatch, sharedBatch; + + public MeshBufferGenerator(LogicalDevice device, UpdateFrameManager frames) { + this(device, frames, null, null); + } + + public MeshBufferGenerator(LogicalDevice device, UpdateFrameManager frames, + CommandBatch dynamicBatch, CommandBatch sharedBatch) { + this.device = device; + this.frames = frames; + this.dynamicBatch = dynamicBatch; + this.sharedBatch = sharedBatch; + } + + @Override + public GpuBuffer createBuffer(MemorySize size, Flag usage, DataAccess access) { + switch (access) { + case Stream: return createStreamingBuffer(size, usage); + case Dynamic: return createDynamicBuffer(size, usage); + case Static: return createStaticBuffer(size, usage); + default: throw new UnsupportedOperationException("Unable to create \"" + access + "\" buffer."); + } + } + + public GpuBuffer createStreamingBuffer(MemorySize size, Flag usage) { + PerFrameBuffer buffer = new PerFrameBuffer<>(frames, size, + s -> new OldPersistentBuffer(device, s)); + for (OldPersistentBuffer buf : buffer) { + try (OldPersistentBuffer.Builder b = buf.build()) { + b.setUsage(usage); + } + } + return buffer; + } + + public GpuBuffer createDynamicBuffer(MemorySize size, Flag usage) { + if (dynamicBatch == null) { + throw new UnsupportedOperationException("Cannot create dynamic buffer: dynamic batch is null."); + } + PerFrameBuffer buffer = new PerFrameBuffer<>(frames, size, + s -> new StageableBuffer(device, s)); + for (StageableBuffer buf : buffer) { + try (BasicVulkanBuffer.Builder b = buf.build()) { + b.setUsage(usage); + b.setMemFlags(MemoryProp.DeviceLocal); + } + } + return dynamicBatch.addAll(buffer); + } + + public GpuBuffer createStaticBuffer(MemorySize size, Flag usage) { + if (sharedBatch == null) { + throw new UnsupportedOperationException("Cannot create static buffer: shared batch is null."); + } + BackedStaticBuffer buffer = new BackedStaticBuffer(device, size); + try (BasicVulkanBuffer.Builder b = buffer.build()) { + b.setUsage(usage); + b.setMemFlags(MemoryProp.DeviceLocal); + } + return sharedBatch.add(buffer); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/buffers/generate/NioBufferGenerator.java b/jme3-core/src/main/java/com/jme3/vulkan/buffers/generate/NioBufferGenerator.java new file mode 100644 index 0000000000..70ed451f60 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/buffers/generate/NioBufferGenerator.java @@ -0,0 +1,16 @@ +package com.jme3.vulkan.buffers.generate; + +import com.jme3.vulkan.buffers.NioBuffer; +import com.jme3.vulkan.buffers.BufferUsage; +import com.jme3.vulkan.memory.MemorySize; +import com.jme3.vulkan.mesh.DataAccess; +import com.jme3.vulkan.util.Flag; + +public class NioBufferGenerator implements BufferGenerator { + + @Override + public NioBuffer createBuffer(MemorySize size, Flag usage, DataAccess access) { + return new NioBuffer(size); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/commands/CommandBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/commands/CommandBuffer.java new file mode 100644 index 0000000000..88932f2259 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/commands/CommandBuffer.java @@ -0,0 +1,105 @@ +package com.jme3.vulkan.commands; + +import com.jme3.vulkan.sync.Semaphore; +import com.jme3.vulkan.sync.SyncGroup; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkCommandBuffer; +import org.lwjgl.vulkan.VkCommandBufferAllocateInfo; +import org.lwjgl.vulkan.VkCommandBufferBeginInfo; +import org.lwjgl.vulkan.VkSubmitInfo; + +import static com.jme3.renderer.vulkan.VulkanUtils.*; +import static org.lwjgl.vulkan.VK10.*; + +public class CommandBuffer { + + protected final CommandPool pool; + protected final VkCommandBuffer buffer; + protected boolean recording = false; + + public CommandBuffer(CommandPool pool) { + this.pool = pool; + try (MemoryStack stack = MemoryStack.stackPush()) { + VkCommandBufferAllocateInfo allocate = VkCommandBufferAllocateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO) + .commandPool(pool.getNativeObject()) + .level(VK_COMMAND_BUFFER_LEVEL_PRIMARY) + .commandBufferCount(1); + PointerBuffer ptr = stack.mallocPointer(1); + check(vkAllocateCommandBuffers(pool.getDevice().getNativeObject(), allocate, ptr), + "Failed to allocate command buffer"); + buffer = new VkCommandBuffer(ptr.get(0), pool.getDevice().getNativeObject()); + } + } + + public void begin() { + if (recording) { + throw new IllegalStateException("Command buffer already recording."); + } + try (MemoryStack stack = MemoryStack.stackPush()) { + VkCommandBufferBeginInfo begin = VkCommandBufferBeginInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO); + check(vkBeginCommandBuffer(buffer, begin), "Failed to begin command buffer"); + recording = true; + } + } + + public void resetAndBegin() { + reset(); + begin(); + } + + public void end() { + if (!recording) { + throw new IllegalStateException("Command buffer has not begun recording."); + } + check(vkEndCommandBuffer(buffer), "Failed to record command buffer"); + recording = false; + } + + public void submit(SyncGroup sync) { + if (recording) { + throw new IllegalStateException("Command buffer is still recording."); + } + try (MemoryStack stack = MemoryStack.stackPush()) { + VkSubmitInfo.Buffer submit = VkSubmitInfo.calloc(1, stack) + .sType(VK_STRUCTURE_TYPE_SUBMIT_INFO) + .pCommandBuffers(stack.pointers(buffer)); + if (sync.containsWaits()) { + submit.waitSemaphoreCount(sync.getWaits().length) + .pWaitSemaphores(sync.onRegisterWait(stack)) + .pWaitDstStageMask(sync.toDstStageBuffer(stack)); + } + if (sync.containsSignals()) { + submit.pSignalSemaphores(sync.onRegisterSignal(stack)); + } + pool.getQueue().submit(submit, sync.getFence()); + } + } + + public void endAndSubmit(SyncGroup sync) { + end(); + submit(sync); + } + + public void reset() { + if (!pool.getFlags().contains(CommandPool.Create.ResetCommandBuffer)) { + throw new UnsupportedOperationException("Command buffer resetting is not supported by the allocating pool."); + } + vkResetCommandBuffer(buffer, 0); + } + + public CommandPool getPool() { + return pool; + } + + public VkCommandBuffer getBuffer() { + return buffer; + } + + public boolean isRecording() { + return recording; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/commands/CommandPool.java b/jme3-core/src/main/java/com/jme3/vulkan/commands/CommandPool.java new file mode 100644 index 0000000000..de3d4dfeed --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/commands/CommandPool.java @@ -0,0 +1,104 @@ +package com.jme3.vulkan.commands; + +import com.jme3.util.natives.Native; +import com.jme3.util.natives.NativeReference; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.util.Flag; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkCommandPoolCreateInfo; + +import java.nio.LongBuffer; + +import static com.jme3.renderer.vulkan.VulkanUtils.*; +import static org.lwjgl.vulkan.VK11.*; + +public class CommandPool implements Native { + + public enum Create implements Flag { + + Transient(VK_COMMAND_POOL_CREATE_TRANSIENT_BIT), + ResetCommandBuffer(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT), + Protected(VK_COMMAND_POOL_CREATE_PROTECTED_BIT); + + private final int vkEnum; + + Create(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int bits() { + return vkEnum; + } + + } + + private final Queue queue; + private final NativeReference ref; + private final Flag flags; + private long id; + + public CommandPool(Queue queue) { + this(queue, Create.ResetCommandBuffer); + } + + public CommandPool(Queue queue, Flag flags) { + this.queue = queue; + this.flags = flags; + try (MemoryStack stack = MemoryStack.stackPush()) { + VkCommandPoolCreateInfo create = VkCommandPoolCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO) + .flags(this.flags.bits()) + .queueFamilyIndex(queue.getFamilyIndex()); + LongBuffer idBuf = stack.mallocLong(1); + check(vkCreateCommandPool(queue.getDevice().getNativeObject(), create, null, idBuf), + "Failed to create command pool."); + id = idBuf.get(0); + } + ref = Native.get().register(this); + queue.getDevice().getNativeReference().addDependent(ref); + } + + @Override + public Long getNativeObject() { + return id; + } + + @Override + public Runnable createNativeDestroyer() { + return () -> { + vkDestroyCommandPool(queue.getDevice().getNativeObject(), id, null); + }; + } + + @Override + public void prematureNativeDestruction() { + id = VK_NULL_HANDLE; + } + + @Override + public NativeReference getNativeReference() { + return null; + } + + public CommandBuffer allocateCommandBuffer() { + return new CommandBuffer(this); + } + + public TransientCommandBuffer allocateTransientCommandBuffer() { + return new TransientCommandBuffer(this); + } + + public LogicalDevice getDevice() { + return queue.getDevice(); + } + + public Queue getQueue() { + return queue; + } + + public Flag getFlags() { + return flags; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/commands/Queue.java b/jme3-core/src/main/java/com/jme3/vulkan/commands/Queue.java new file mode 100644 index 0000000000..faaff260f4 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/commands/Queue.java @@ -0,0 +1,59 @@ +package com.jme3.vulkan.commands; + +import com.jme3.vulkan.sync.Fence; +import com.jme3.vulkan.devices.LogicalDevice; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkQueue; +import org.lwjgl.vulkan.VkSubmitInfo; + +import static com.jme3.renderer.vulkan.VulkanUtils.*; +import static org.lwjgl.vulkan.VK10.*; + +public class Queue { + + private final LogicalDevice device; + private final VkQueue queue; + private final int familyIndex, queueIndex; + + public Queue(LogicalDevice device, int familyIndex, int queueIndex) { + this.device = device; + this.familyIndex = familyIndex; + this.queueIndex = queueIndex; + try (MemoryStack stack = MemoryStack.stackPush()) { + PointerBuffer ptr = stack.mallocPointer(1); + vkGetDeviceQueue(device.getNativeObject(), familyIndex, queueIndex, ptr); + queue = new VkQueue(ptr.get(0), device.getNativeObject()); + } + } + + public void submit(VkSubmitInfo.Buffer info) { + submit(info, null); + } + + public void submit(VkSubmitInfo.Buffer info, Fence fence) { + check(vkQueueSubmit(queue, info, fence != null ? fence.getNativeObject() : VK_NULL_HANDLE), + "Failed to submit to queue."); + } + + public void waitIdle() { + vkQueueWaitIdle(queue); + } + + public LogicalDevice getDevice() { + return device; + } + + public VkQueue getQueue() { + return queue; + } + + public int getFamilyIndex() { + return familyIndex; + } + + public int getQueueIndex() { + return queueIndex; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/commands/QueueFamilies.java b/jme3-core/src/main/java/com/jme3/vulkan/commands/QueueFamilies.java new file mode 100644 index 0000000000..0290b5d5b3 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/commands/QueueFamilies.java @@ -0,0 +1,23 @@ +package com.jme3.vulkan.commands; + +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.devices.PhysicalDevice; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkDeviceQueueCreateInfo; +import org.lwjgl.vulkan.VkQueueFamilyProperties; + +import java.nio.IntBuffer; + +public interface QueueFamilies { + + boolean populate(PhysicalDevice device, VkQueueFamilyProperties.Buffer properties); + + VkDeviceQueueCreateInfo.Buffer createLogicalBuffers(MemoryStack stack); + + void createQueues(LogicalDevice device); + + boolean isComplete(); + + IntBuffer getSwapchainConcurrentBuffers(MemoryStack stack); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/commands/TransientCommandBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/commands/TransientCommandBuffer.java new file mode 100644 index 0000000000..b2de94614a --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/commands/TransientCommandBuffer.java @@ -0,0 +1,64 @@ +package com.jme3.vulkan.commands; + +import com.jme3.vulkan.sync.SyncGroup; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkCommandBufferBeginInfo; +import org.lwjgl.vulkan.VkSubmitInfo; + +import static org.lwjgl.vulkan.VK10.*; + +public class TransientCommandBuffer extends CommandBuffer { + + private boolean active = false; + + public TransientCommandBuffer(CommandPool pool) { + super(pool); + } + + @Override + public void begin() { + if (recording) { + throw new IllegalStateException("Command buffer already recording."); + } + if (active) { + throw new IllegalStateException("One-time command buffer has already been used."); + } + try (MemoryStack stack = MemoryStack.stackPush()) { + VkCommandBufferBeginInfo begin = VkCommandBufferBeginInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO) + .flags(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + vkBeginCommandBuffer(buffer, begin); + } + recording = true; + } + + @Override + public void submit(SyncGroup sync) { + if (recording) { + throw new IllegalStateException("Command buffer is still recording."); + } + try (MemoryStack stack = MemoryStack.stackPush()) { + VkSubmitInfo.Buffer submit = VkSubmitInfo.calloc(1, stack) + .sType(VK_STRUCTURE_TYPE_SUBMIT_INFO) + .pCommandBuffers(stack.pointers(buffer)); + if (sync.containsWaits()) { + submit.waitSemaphoreCount(sync.getWaits().length) + .pWaitSemaphores(sync.toWaitBuffer(stack)) + .pWaitDstStageMask(sync.toDstStageBuffer(stack)); + } + if (sync.containsSignals()) { + submit.pSignalSemaphores(sync.toSignalBuffer(stack)); + } + pool.getQueue().submit(submit, sync.getFence()); + pool.getQueue().waitIdle(); + vkFreeCommandBuffers(pool.getDevice().getNativeObject(), pool.getNativeObject(), buffer); + } + active = false; + recording = false; + } + + public boolean isActive() { + return active; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/descriptors/BaseDescriptorWriter.java b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/BaseDescriptorWriter.java new file mode 100644 index 0000000000..3440148f8b --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/BaseDescriptorWriter.java @@ -0,0 +1,41 @@ +package com.jme3.vulkan.descriptors; + +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkWriteDescriptorSet; + +public class BaseDescriptorWriter implements DescriptorSetWriter { + + private final Descriptor type; + private final int binding, arrayElement, descriptorCount; + + public BaseDescriptorWriter(Descriptor type, int binding, int arrayElement, int descriptorCount) { + this.type = type; + this.binding = binding; + this.arrayElement = arrayElement; + this.descriptorCount = descriptorCount; + } + + @Override + public void populateWrite(MemoryStack stack, VkWriteDescriptorSet write) { + write.descriptorType(type.getEnum()).dstBinding(binding) + .dstArrayElement(arrayElement) + .descriptorCount(descriptorCount); + } + + public Descriptor getType() { + return type; + } + + public int getBinding() { + return binding; + } + + public int getArrayElement() { + return arrayElement; + } + + public int getDescriptorCount() { + return descriptorCount; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/descriptors/BufferDescriptor.java b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/BufferDescriptor.java new file mode 100644 index 0000000000..a8cd7e58b3 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/BufferDescriptor.java @@ -0,0 +1,37 @@ +package com.jme3.vulkan.descriptors; + +import com.jme3.vulkan.buffers.GpuBuffer; +import org.lwjgl.vulkan.VkDescriptorBufferInfo; + +public class BufferDescriptor { + + private final GpuBuffer buffer; + private final long offset, range; + + public BufferDescriptor(GpuBuffer buffer) { + this(buffer, 0, buffer.size().getBytes()); + } + + public BufferDescriptor(GpuBuffer buffer, long offset, long range) { + this.buffer = buffer; + this.offset = offset; + this.range = range; + } + + public void fillDescriptorInfo(VkDescriptorBufferInfo info) { + info.buffer(buffer.getId()).offset(offset).range(range); + } + + public GpuBuffer getBuffer() { + return buffer; + } + + public long getOffset() { + return offset; + } + + public long getRange() { + return range; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/descriptors/BufferSetWriter.java b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/BufferSetWriter.java new file mode 100644 index 0000000000..f0b27d7d61 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/BufferSetWriter.java @@ -0,0 +1,27 @@ +package com.jme3.vulkan.descriptors; + +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkDescriptorBufferInfo; +import org.lwjgl.vulkan.VkWriteDescriptorSet; + +public class BufferSetWriter extends BaseDescriptorWriter { + + private final BufferDescriptor[] descriptors; + + public BufferSetWriter(Descriptor type, int binding, int arrayElement, BufferDescriptor... descriptors) { + super(type, binding, arrayElement, descriptors.length); + this.descriptors = descriptors; + } + + @Override + public void populateWrite(MemoryStack stack, VkWriteDescriptorSet write) { + super.populateWrite(stack, write); + VkDescriptorBufferInfo.Buffer info = VkDescriptorBufferInfo.calloc(descriptors.length, stack); + for (BufferDescriptor d : descriptors) { + d.fillDescriptorInfo(info.get()); + } + info.flip(); + write.pBufferInfo(info); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/descriptors/Descriptor.java b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/Descriptor.java new file mode 100644 index 0000000000..a29f83829f --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/Descriptor.java @@ -0,0 +1,37 @@ +package com.jme3.vulkan.descriptors; + +import com.jme3.vulkan.util.IntEnum; + +import java.util.HashMap; +import java.util.Map; + +import static org.lwjgl.vulkan.VK10.*; + +public enum Descriptor implements IntEnum { + + UniformBuffer(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER), + UniformBufferDynamic(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC), + StorageBuffer(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER), + StorageBufferDynamic(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC), + CombinedImageSampler(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER), + Sampler(VK_DESCRIPTOR_TYPE_SAMPLER), + SampledImage(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE), + InputAttachment(VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT), + StorageImage(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE), + StorageTexelBuffer(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER), + UniformTexelBuffer(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER); + + private final int vkEnum; + + Descriptor(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + + private static final Map> custom = new HashMap<>(); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/descriptors/DescriptorPool.java b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/DescriptorPool.java new file mode 100644 index 0000000000..5485f1347b --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/DescriptorPool.java @@ -0,0 +1,113 @@ +package com.jme3.vulkan.descriptors; + +import com.jme3.renderer.vulkan.VulkanUtils; +import com.jme3.util.natives.Native; +import com.jme3.util.natives.NativeReference; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.util.Flag; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.*; + +import java.nio.LongBuffer; + +import static com.jme3.renderer.vulkan.VulkanUtils.check; +import static org.lwjgl.vulkan.VK10.*; + +public class DescriptorPool implements Native { + + public enum Create implements Flag { + + FreeDescriptorSets(VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT); + + private final int vkEnum; + + Create(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int bits() { + return vkEnum; + } + + } + + private final LogicalDevice device; + private final NativeReference ref; + private final Flag flags; + private final long id; + + public DescriptorPool(LogicalDevice device, int sets, PoolSize... sizes) { + this(device, sets, Flag.empty(), sizes); + } + + public DescriptorPool(LogicalDevice device, int sets, Flag flags, PoolSize... sizes) { + this.device = device; + this.flags = flags; + try (MemoryStack stack = MemoryStack.stackPush()) { + VkDescriptorPoolCreateInfo create = VkDescriptorPoolCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO) + .pPoolSizes(PoolSize.aggregate(stack, sizes)) + .maxSets(sets) + .flags(this.flags.bits()); + LongBuffer idBuf = stack.mallocLong(1); + check(vkCreateDescriptorPool(device.getNativeObject(), create, null, idBuf), + "Failed to create descriptor pool."); + id = idBuf.get(0); + } + ref = Native.get().register(this); + device.getNativeReference().addDependent(ref); + } + + @Override + public Long getNativeObject() { + return id; + } + + @Override + public Runnable createNativeDestroyer() { + return () -> vkDestroyDescriptorPool(device.getNativeObject(), id, null); + } + + @Override + public void prematureNativeDestruction() {} + + @Override + public NativeReference getNativeReference() { + return ref; + } + + /** + * Allocates a {@link DescriptorSet} for each {@link DescriptorSetLayout} provided. + * + * @param layouts layouts to allocate DescriptorSets with + * @return allocated DescriptorSets, in the same order as {@code layouts} + */ + public DescriptorSet[] allocateSets(DescriptorSetLayout... layouts) { + assert layouts.length > 0 : "Must specify at least one set layout."; + // layouts length = number of descriptor sets created + DescriptorSet[] sets = new DescriptorSet[layouts.length]; + try (MemoryStack stack = MemoryStack.stackPush()) { + VkDescriptorSetAllocateInfo allocate = VkDescriptorSetAllocateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO) + .descriptorPool(id) + .pSetLayouts(VulkanUtils.accumulate(stack, layouts)); + LongBuffer setBuf = stack.mallocLong(layouts.length); + check(vkAllocateDescriptorSets(device.getNativeObject(), allocate, setBuf), + "Failed to allocate descriptor sets."); + for (int i = 0; i < setBuf.limit(); i++) { + sets[i] = new DescriptorSet(device, this, layouts[i], setBuf.get(i)); + } + } + return sets; + } + + public void reset() { + vkResetDescriptorPool(device.getNativeObject(), id, 0); + } + + public Flag getFlags() { + return flags; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/descriptors/DescriptorSet.java b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/DescriptorSet.java new file mode 100644 index 0000000000..11c8f8cced --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/DescriptorSet.java @@ -0,0 +1,67 @@ +package com.jme3.vulkan.descriptors; + +import com.jme3.util.natives.Native; +import com.jme3.util.natives.AbstractNative; +import com.jme3.vulkan.devices.LogicalDevice; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkWriteDescriptorSet; + +import static org.lwjgl.vulkan.VK10.*; + +public class DescriptorSet extends AbstractNative { + + private final LogicalDevice device; + private final DescriptorPool pool; + private final DescriptorSetLayout layout; + + public DescriptorSet(LogicalDevice device, DescriptorPool pool, DescriptorSetLayout layout, long id) { + this.device = device; + this.pool = pool; + this.layout = layout; + this.object = id; + ref = Native.get().register(this); + pool.getNativeReference().addDependent(ref); + } + + @Override + public Runnable createNativeDestroyer() { + return () -> { + if (pool.getFlags().contains(DescriptorPool.Create.FreeDescriptorSets)) { + try (MemoryStack stack = MemoryStack.stackPush()) { + vkFreeDescriptorSets(device.getNativeObject(), pool.getNativeObject(), stack.longs(object)); + } + } + }; + } + + public void write(DescriptorSetWriter... writers) { + try (MemoryStack stack = MemoryStack.stackPush()) { + VkWriteDescriptorSet.Buffer write = VkWriteDescriptorSet.calloc(writers.length, stack); + for (DescriptorSetWriter w : writers) { + w.populateWrite(stack, write.get() + .sType(VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET) + .dstSet(object)); + } + write.flip(); + vkUpdateDescriptorSets(device.getNativeObject(), write, null); + } + } + + @Deprecated + public long getId() { + return object; + } + + public LogicalDevice getDevice() { + return device; + } + + public DescriptorPool getPool() { + return pool; + } + + public DescriptorSetLayout getLayout() { + return layout; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/descriptors/DescriptorSetLayout.java b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/DescriptorSetLayout.java new file mode 100644 index 0000000000..5123c13447 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/DescriptorSetLayout.java @@ -0,0 +1,101 @@ +package com.jme3.vulkan.descriptors; + +import com.jme3.util.IntMap; +import com.jme3.util.natives.AbstractNative; +import com.jme3.util.natives.Native; +import com.jme3.util.natives.NativeReference; +import com.jme3.vulkan.devices.LogicalDevice; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkDescriptorSetLayoutBinding; +import org.lwjgl.vulkan.VkDescriptorSetLayoutCreateInfo; + +import java.nio.LongBuffer; +import java.util.Arrays; +import java.util.Objects; + +import static com.jme3.renderer.vulkan.VulkanUtils.check; +import static org.lwjgl.vulkan.VK10.*; + +public class DescriptorSetLayout extends AbstractNative { + + private final LogicalDevice device; + private final IntMap bindings; + + private DescriptorSetLayout(LogicalDevice device, IntMap bindings) { + this.device = device; + this.bindings = bindings; + } + + public DescriptorSetLayout(LogicalDevice device, SetLayoutBinding... bindings) { + this.device = device; + this.bindings = new IntMap<>(); + for (SetLayoutBinding b : bindings) { + if (this.bindings.put(b.getBinding(), b) != null) { + throw new IllegalArgumentException("Duplicate binding index."); + } + } + } + + @Override + public Runnable createNativeDestroyer() { + return () -> vkDestroyDescriptorSetLayout(device.getNativeObject(), object, null); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + DescriptorSetLayout that = (DescriptorSetLayout) o; + if (device != that.device || bindings.size() != that.bindings.size()) return false; + for (IntMap.Entry b : bindings) { + if (!b.getValue().equals(that.bindings.get(b.getKey()))) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + return Objects.hash(device, bindings); + } + + public DescriptorSetLayout build(MemoryStack stack) { + if (isBuilt()) { + return this; + } + VkDescriptorSetLayoutBinding.Buffer layoutBindings = VkDescriptorSetLayoutBinding.calloc(bindings.size(), stack); + for (IntMap.Entry b : bindings) { + b.getValue().fillLayoutBinding(layoutBindings.get()); + } + layoutBindings.flip(); + VkDescriptorSetLayoutCreateInfo create = VkDescriptorSetLayoutCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO) + .pBindings(layoutBindings); + LongBuffer idBuf = stack.mallocLong(1); + check(vkCreateDescriptorSetLayout(device.getNativeObject(), create, null, idBuf), + "Failed to create descriptor set layout."); + object = idBuf.get(0); + ref = Native.get().register(DescriptorSetLayout.this); + device.getNativeReference().addDependent(ref); + return this; + } + + public DescriptorSetLayout build() { + try (MemoryStack stack = MemoryStack.stackPush()) { + return build(stack); + } + } + + public DescriptorSetLayout copy() { + return new DescriptorSetLayout(device, bindings); + } + + public IntMap getBindings() { + return bindings; + } + + public boolean isBuilt() { + return ref != null; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/descriptors/DescriptorSetWriter.java b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/DescriptorSetWriter.java new file mode 100644 index 0000000000..f21740f2de --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/DescriptorSetWriter.java @@ -0,0 +1,10 @@ +package com.jme3.vulkan.descriptors; + +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkWriteDescriptorSet; + +public interface DescriptorSetWriter { + + void populateWrite(MemoryStack stack, VkWriteDescriptorSet write); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/descriptors/ImageDescriptor.java b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/ImageDescriptor.java new file mode 100644 index 0000000000..7991494f61 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/ImageDescriptor.java @@ -0,0 +1,40 @@ +package com.jme3.vulkan.descriptors; + +import com.jme3.vulkan.images.*; +import org.lwjgl.vulkan.VkDescriptorImageInfo; + +public class ImageDescriptor { + + private final VulkanImageView view; + private final Sampler sampler; + private final VulkanImage.Layout layout; + + public ImageDescriptor(VulkanTexture texture, VulkanImage.Layout layout) { + this(texture.getView(), texture, layout); + } + + public ImageDescriptor(VulkanImageView view, Sampler sampler, VulkanImage.Layout layout) { + this.view = view; + this.sampler = sampler; + this.layout = layout; + } + + public void fillDescriptorInfo(VkDescriptorImageInfo info) { + info.imageView(view.getNativeObject()) + .sampler(sampler.getNativeObject()) + .imageLayout(layout.getEnum()); + } + + public VulkanImageView getView() { + return view; + } + + public Sampler getSampler() { + return sampler; + } + + public VulkanImage.Layout getLayout() { + return layout; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/descriptors/ImageSetWriter.java b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/ImageSetWriter.java new file mode 100644 index 0000000000..b3d7d0c789 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/ImageSetWriter.java @@ -0,0 +1,27 @@ +package com.jme3.vulkan.descriptors; + +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkDescriptorImageInfo; +import org.lwjgl.vulkan.VkWriteDescriptorSet; + +public class ImageSetWriter extends BaseDescriptorWriter { + + private final ImageDescriptor[] descriptors; + + public ImageSetWriter(Descriptor type, int binding, int arrayElement, ImageDescriptor... descriptors) { + super(type, binding, arrayElement, descriptors.length); + this.descriptors = descriptors; + } + + @Override + public void populateWrite(MemoryStack stack, VkWriteDescriptorSet write) { + super.populateWrite(stack, write); + VkDescriptorImageInfo.Buffer info = VkDescriptorImageInfo.calloc(descriptors.length, stack); + for (ImageDescriptor d : descriptors) { + d.fillDescriptorInfo(info.get()); + } + info.flip(); + write.pImageInfo(info); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/descriptors/PoolSize.java b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/PoolSize.java new file mode 100644 index 0000000000..976f7214b5 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/PoolSize.java @@ -0,0 +1,34 @@ +package com.jme3.vulkan.descriptors; + +import com.jme3.vulkan.util.IntEnum; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkDescriptorPoolSize; + +public class PoolSize { + + private final IntEnum type; + private final int size; + + public PoolSize(IntEnum type, int size) { + this.type = type; + this.size = size; + } + + public IntEnum getType() { + return type; + } + + public int getSize() { + return size; + } + + public static VkDescriptorPoolSize.Buffer aggregate(MemoryStack stack, PoolSize... sizes) { + VkDescriptorPoolSize.Buffer buffer = VkDescriptorPoolSize.calloc(sizes.length, stack); + for (PoolSize poolSize : sizes) { + buffer.get().set(poolSize.type.getEnum(), poolSize.size); + } + buffer.flip(); + return buffer; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/descriptors/SetLayoutBinding.java b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/SetLayoutBinding.java new file mode 100644 index 0000000000..33af4d6809 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/descriptors/SetLayoutBinding.java @@ -0,0 +1,67 @@ +package com.jme3.vulkan.descriptors; + +import com.jme3.vulkan.shader.ShaderStage; +import com.jme3.vulkan.util.Flag; +import com.jme3.vulkan.util.IntEnum; +import org.lwjgl.vulkan.VkDescriptorSetLayoutBinding; + +import java.util.Objects; + +public class SetLayoutBinding { + + private final IntEnum type; + private final int binding, descriptors; + private final Flag stages; + + public SetLayoutBinding(IntEnum type, int binding, int descriptors) { + this(type, binding, descriptors, ShaderStage.All); + } + + public SetLayoutBinding(IntEnum type, int binding, int descriptors, Flag stages) { + this.type = type; + this.binding = binding; + this.descriptors = descriptors; + this.stages = stages; + } + + @SuppressWarnings("DataFlowIssue") + public void fillLayoutBinding(VkDescriptorSetLayoutBinding layoutBinding) { + layoutBinding.descriptorType(type.getEnum()) + .binding(binding) + .descriptorCount(descriptors) + .stageFlags(stages.bits()) + .pImmutableSamplers(null); + } + + public IntEnum getType() { + return type; + } + + public int getBinding() { + return binding; + } + + public int getDescriptors() { + return descriptors; + } + + public Flag getStages() { + return stages; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + SetLayoutBinding that = (SetLayoutBinding) o; + return binding == that.binding + && descriptors == that.descriptors + && Objects.equals(type, that.type) + && Objects.equals(stages, that.stages); + } + + @Override + public int hashCode() { + return Objects.hash(type, binding, descriptors, stages); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/devices/AbstractPhysicalDevice.java b/jme3-core/src/main/java/com/jme3/vulkan/devices/AbstractPhysicalDevice.java new file mode 100644 index 0000000000..327e75b447 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/devices/AbstractPhysicalDevice.java @@ -0,0 +1,107 @@ +package com.jme3.vulkan.devices; + +import com.jme3.vulkan.Format; +import com.jme3.vulkan.FormatFeature; +import com.jme3.vulkan.VulkanInstance; +import com.jme3.vulkan.images.VulkanImage; +import com.jme3.vulkan.memory.MemoryProp; +import com.jme3.vulkan.surface.Surface; +import com.jme3.vulkan.util.Flag; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.*; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +import static com.jme3.renderer.vulkan.VulkanUtils.enumerateBuffer; +import static org.lwjgl.vulkan.VK10.*; + +public abstract class AbstractPhysicalDevice implements PhysicalDevice { + + private final VulkanInstance instance; + private final VkPhysicalDevice physicalDevice; + + public AbstractPhysicalDevice(VulkanInstance instance, long id) { + this.instance = instance; + this.physicalDevice = new VkPhysicalDevice(id, instance.getNativeObject()); + } + + @Override + public VulkanInstance getInstance() { + return instance; + } + + @Override + public VkPhysicalDevice getDeviceHandle() { + return physicalDevice; + } + + @Override + public VkQueueFamilyProperties.Buffer getQueueFamilyProperties(MemoryStack stack) { + return enumerateBuffer(stack, n -> VkQueueFamilyProperties.calloc(n, stack), (count, buffer) + -> vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, count, buffer)); + } + + @Override + public VkPhysicalDeviceProperties getProperties(MemoryStack stack) { + VkPhysicalDeviceProperties props = VkPhysicalDeviceProperties.malloc(stack); + vkGetPhysicalDeviceProperties(physicalDevice, props); + return props; + } + + @Override + public VkPhysicalDeviceFeatures getFeatures(MemoryStack stack) { + VkPhysicalDeviceFeatures features = VkPhysicalDeviceFeatures.malloc(stack); + vkGetPhysicalDeviceFeatures(physicalDevice, features); + return features; + } + + @Override + public VkExtensionProperties.Buffer getExtensionProperties(MemoryStack stack) { + return enumerateBuffer(stack, n -> VkExtensionProperties.malloc(n, stack), (count, buffer) -> + vkEnumerateDeviceExtensionProperties(physicalDevice, (ByteBuffer)null, count, buffer)); + } + + @Override + public VkPhysicalDeviceMemoryProperties getMemoryProperties(MemoryStack stack) { + VkPhysicalDeviceMemoryProperties mem = VkPhysicalDeviceMemoryProperties.malloc(stack); + vkGetPhysicalDeviceMemoryProperties(physicalDevice, mem); + return mem; + } + + @Override + public int findSupportedMemoryType(MemoryStack stack, int types, Flag flags) { + VkPhysicalDeviceMemoryProperties mem = getMemoryProperties(stack); + for (int i = 0; i < mem.memoryTypeCount(); i++) { + if ((types & (1 << i)) != 0 && (mem.memoryTypes().get(i).propertyFlags() & flags.bits()) != 0) { + return i; + } + } + throw new NullPointerException("Suitable memory type not found."); + } + + @Override + public Format findSupportedFormat(VulkanImage.Tiling tiling, Flag features, Format... candidates) { + VkFormatProperties props = VkFormatProperties.create(); + for (Format f : candidates) { + vkGetPhysicalDeviceFormatProperties(physicalDevice, f.getVkEnum(), props); + if ((tiling == VulkanImage.Tiling.Linear && features.contains(props.linearTilingFeatures())) + || (tiling == VulkanImage.Tiling.Optimal && features.contains(props.optimalTilingFeatures()))) { + return f; + } + } + throw new NullPointerException("Failed to find supported format."); + } + + @Override + public boolean querySwapchainSupport(Surface surface) { + try (MemoryStack stack = MemoryStack.stackPush()) { + IntBuffer count = stack.mallocInt(1); + KHRSurface.vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface.getNativeObject(), count, null); + if (count.get(0) <= 0) return false; + KHRSurface.vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface.getNativeObject(), count, null); + return count.get(0) > 0; + } + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/devices/DeviceExtension.java b/jme3-core/src/main/java/com/jme3/vulkan/devices/DeviceExtension.java new file mode 100644 index 0000000000..35eca346d3 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/devices/DeviceExtension.java @@ -0,0 +1,65 @@ +package com.jme3.vulkan.devices; + +import java.util.Objects; +import java.util.Set; + +public class DeviceExtension { + + private final String name; + private final Float success, fail; + + public DeviceExtension(String name) { + this(name, 1f, null); + } + + public DeviceExtension(String name, Float success) { + this(name, success, null); + } + + public DeviceExtension(String name, Float success, Float fail) { + this.name = name; + this.success = success; + this.fail = fail; + } + + public Float evaluate(Set extensions) { + return extensions.contains(name) ? success : fail; + } + + public String getName() { + return name; + } + + public Float getSuccessWeight() { + return success; + } + + public Float getFailWeight() { + return fail; + } + + public boolean isRejectOnFailure() { + return fail == null; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + DeviceExtension that = (DeviceExtension) o; + return Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hashCode(name); + } + + public static DeviceExtension critical(String name) { + return new DeviceExtension(name, 1f, null); + } + + public static DeviceExtension optional(String name, float successWeight) { + return new DeviceExtension(name, successWeight); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/devices/DeviceFeature.java b/jme3-core/src/main/java/com/jme3/vulkan/devices/DeviceFeature.java new file mode 100644 index 0000000000..886c2ccf5f --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/devices/DeviceFeature.java @@ -0,0 +1,24 @@ +package com.jme3.vulkan.devices; + +import org.lwjgl.vulkan.VkPhysicalDeviceFeatures; + +public interface DeviceFeature { + + void enableFeature(VkPhysicalDeviceFeatures features); + + Float evaluateFeatureSupport(VkPhysicalDeviceFeatures features); + + static DeviceFeature anisotropy(Float pass, boolean rejectOnFail) { + return new DeviceFeature() { + @Override + public void enableFeature(VkPhysicalDeviceFeatures features) { + features.samplerAnisotropy(true); + } + @Override + public Float evaluateFeatureSupport(VkPhysicalDeviceFeatures features) { + return features.samplerAnisotropy() ? pass : (rejectOnFail ? null : 0f); + } + }; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/devices/DeviceFilter.java b/jme3-core/src/main/java/com/jme3/vulkan/devices/DeviceFilter.java new file mode 100644 index 0000000000..a59a15b69c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/devices/DeviceFilter.java @@ -0,0 +1,84 @@ +package com.jme3.vulkan.devices; + +import com.jme3.vulkan.surface.Surface; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkExtensionProperties; + +import java.util.Arrays; +import java.util.Collection; + +public interface DeviceFilter { + + Float evaluateDevice(PhysicalDevice device); + + static DeviceExtensionSupport extensions(String... exts) { + return new DeviceExtensionSupport(Arrays.asList(exts)); + } + + static DeviceExtensionSupport extensions(Collection exts) { + return new DeviceExtensionSupport(exts); + } + + static DeviceSwapchainSupport swapchain(Surface surface) { + return new DeviceSwapchainSupport(surface); + } + + static DeviceAnisotropySupport anisotropy() { + return new DeviceAnisotropySupport(); + } + + class DeviceExtensionSupport implements DeviceFilter { + + private final Collection extensions; + + public DeviceExtensionSupport(Collection extensions) { + this.extensions = extensions; + } + + @Override + public Float evaluateDevice(PhysicalDevice device) { + try (MemoryStack stack = MemoryStack.stackPush()) { + VkExtensionProperties.Buffer exts = device.getExtensionProperties(stack); + if (extensions.stream().allMatch(e -> exts.stream().anyMatch( + p -> p.extensionNameString().equals(e)))) { + return 0f; + } + return null; + } + } + + } + + class DeviceSwapchainSupport implements DeviceFilter { + + private final Surface surface; + + public DeviceSwapchainSupport(Surface surface) { + this.surface = surface; + } + + @Override + public Float evaluateDevice(PhysicalDevice device) { + if (device.querySwapchainSupport(surface)) { + return 0f; + } + return null; + } + + } + + class DeviceAnisotropySupport implements DeviceFilter { + + @Override + public Float evaluateDevice(PhysicalDevice device) { + try (MemoryStack stack = MemoryStack.stackPush()) { + if (device.getFeatures(stack).samplerAnisotropy()) { + return 0f; + } + return null; + } + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/devices/DeviceProperty.java b/jme3-core/src/main/java/com/jme3/vulkan/devices/DeviceProperty.java new file mode 100644 index 0000000000..f2919af8ae --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/devices/DeviceProperty.java @@ -0,0 +1,13 @@ +package com.jme3.vulkan.devices; + +import org.lwjgl.vulkan.VkPhysicalDeviceProperties; + +public interface DeviceProperty { + + Float evaluateDeviceProperties(VkPhysicalDeviceProperties properties); + + static DeviceProperty maxAnisotropy(float threshold) { + return p -> p.limits().maxSamplerAnisotropy() >= threshold ? 0f : null; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/devices/GeneralPhysicalDevice.java b/jme3-core/src/main/java/com/jme3/vulkan/devices/GeneralPhysicalDevice.java new file mode 100644 index 0000000000..50c73a46b7 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/devices/GeneralPhysicalDevice.java @@ -0,0 +1,93 @@ +package com.jme3.vulkan.devices; + +import com.jme3.vulkan.commands.Queue; +import com.jme3.vulkan.VulkanInstance; +import com.jme3.vulkan.surface.Surface; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.KHRSurface; +import org.lwjgl.vulkan.VkDeviceQueueCreateInfo; +import org.lwjgl.vulkan.VkQueueFamilyProperties; + +import java.nio.IntBuffer; + +import static org.lwjgl.vulkan.VK10.*; + +public class GeneralPhysicalDevice extends AbstractPhysicalDevice implements GraphicalDevice, PresentDevice { + + private final Surface surface; + private Integer graphicsIndex, presentIndex; + private Queue graphics, present; + + public GeneralPhysicalDevice(VulkanInstance instance, Surface surface, long id) { + super(instance, id); + this.surface = surface; + } + + @Override + public boolean populateQueueFamilyIndices() { + try (MemoryStack stack = MemoryStack.stackPush()) { + VkQueueFamilyProperties.Buffer properties = getQueueFamilyProperties(stack); + IntBuffer ibuf = stack.callocInt(1); + for (int i = 0; i < properties.limit(); i++) { + VkQueueFamilyProperties props = properties.get(i); + int flags = props.queueFlags(); + if (graphicsIndex == null && (flags & VK_QUEUE_GRAPHICS_BIT) > 0) { + graphicsIndex = i; + } else if (presentIndex == null) { + KHRSurface.vkGetPhysicalDeviceSurfaceSupportKHR(getDeviceHandle(), + i, surface.getNativeObject(), ibuf); + if (ibuf.get(0) == VK_TRUE) { + presentIndex = i; + } + } + if (allQueuesAvailable()) { + return true; + } + } + } + return allQueuesAvailable(); + } + + @Override + public VkDeviceQueueCreateInfo.Buffer createQueueFamilyInfo(MemoryStack stack) { + VkDeviceQueueCreateInfo.Buffer create = VkDeviceQueueCreateInfo.calloc(2, stack); + create.get(0).sType(VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO) + .queueFamilyIndex(graphicsIndex) + .pQueuePriorities(stack.floats(1f)); + create.get(1).sType(VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO) + .queueFamilyIndex(presentIndex) + .pQueuePriorities(stack.floats(1f)); + return create; + } + + @Override + public void createQueues(LogicalDevice device) { + graphics = new Queue(device, graphicsIndex, 0); + present = new Queue(device, presentIndex, 0); + } + + @Override + public Queue getGraphics() { + return graphics; + } + + @Override + public Queue getPresent() { + return present; + } + + @Override + public Queue getCompute() { + return graphics; + } + + @Override + public Queue getDataTransfer() { + return graphics; + } + + public boolean allQueuesAvailable() { + return graphicsIndex != null && presentIndex != null; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/devices/GraphicalDevice.java b/jme3-core/src/main/java/com/jme3/vulkan/devices/GraphicalDevice.java new file mode 100644 index 0000000000..cae4a8fb73 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/devices/GraphicalDevice.java @@ -0,0 +1,9 @@ +package com.jme3.vulkan.devices; + +import com.jme3.vulkan.commands.Queue; + +public interface GraphicalDevice extends PhysicalDevice { + + Queue getGraphics(); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/devices/LogicalDevice.java b/jme3-core/src/main/java/com/jme3/vulkan/devices/LogicalDevice.java new file mode 100644 index 0000000000..f1c352575d --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/devices/LogicalDevice.java @@ -0,0 +1,229 @@ +package com.jme3.vulkan.devices; + +import com.jme3.util.AbstractBuilder; +import com.jme3.util.natives.Native; +import com.jme3.vulkan.VulkanInstance; +import com.jme3.util.natives.AbstractNative; +import com.jme3.vulkan.commands.CommandPool; +import com.jme3.vulkan.commands.Queue; +import com.jme3.vulkan.util.Flag; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.*; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +import static com.jme3.renderer.vulkan.VulkanUtils.*; +import static org.lwjgl.vulkan.VK10.*; + +public class LogicalDevice extends AbstractNative { + + private final VulkanInstance instance; + private final Set enabledExtensions = new HashSet<>(); + private final VkPhysicalDeviceFeatures enabledFeatures = VkPhysicalDeviceFeatures.calloc(); + private final Map> pools = new ConcurrentHashMap<>(); + private T physical; + + public LogicalDevice(VulkanInstance instance) { + this.instance = instance; + } + + @Override + public Runnable createNativeDestroyer() { + return () -> { + vkDestroyDevice(object, null); + enabledFeatures.free(); + }; + } + + public void waitIdle() { + vkDeviceWaitIdle(object); + } + + public T getPhysicalDevice() { + return physical; + } + + public Set getEnabledExtensions() { + return Collections.unmodifiableSet(enabledExtensions); + } + + public VkPhysicalDeviceFeatures getEnabledFeatures() { + return enabledFeatures; + } + + public CommandPool getShortTermPool(Queue queue) { + return getPool(queue, CommandPool.Create.Transient); + } + + public CommandPool getLongTermPool(Queue queue) { + return getPool(queue, CommandPool.Create.ResetCommandBuffer); + } + + public CommandPool getPool(Queue queue, Flag flags) { + if (queue.getDevice() != this) { + throw new IllegalArgumentException("Queue must belong to this device."); + } + Collection p = pools.computeIfAbsent(Thread.currentThread(), + t -> new ArrayList<>()); + for (CommandPool pool : p) { + if (pool.getQueue() == queue && pool.getFlags().contains(flags)) { + return pool; + } + } + CommandPool pool = new CommandPool(queue, flags); + p.add(pool); + return pool; + } + + public Builder build(Function deviceFactory) { + return new Builder(deviceFactory); + } + + public class Builder extends AbstractBuilder { + + private final Function deviceFactory; + private final Set extensions = new HashSet<>(); + private final Collection features = new ArrayList<>(); + private final Collection filters = new ArrayList<>(); + private boolean enableAllFeatures = false; + + public Builder(Function deviceFactory) { + this.deviceFactory = deviceFactory; + } + + @Override + protected void build() { + findSuitablePhysicalDevice(); + VkPhysicalDeviceFeatures supportedFeatures = physical.getFeatures(stack); + if (enableAllFeatures) { + enabledFeatures.set(supportedFeatures); + } else for (DeviceFeature f : features) { + if (f.evaluateFeatureSupport(supportedFeatures) != null) { + f.enableFeature(enabledFeatures); + } + } + VkDeviceCreateInfo create = VkDeviceCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO) + .pQueueCreateInfos(physical.createQueueFamilyInfo(stack)) + .pEnabledFeatures(enabledFeatures); + if (!extensions.isEmpty()) { + PointerBuffer exts = stack.mallocPointer(extensions.size()); + for (DeviceExtension e : extensions) { + enabledExtensions.add(e.getName()); + exts.put(stack.UTF8(e.getName())); + } + create.ppEnabledExtensionNames(exts.flip()); + } + Set layers = physical.getInstance().getLayers(); + if (!layers.isEmpty()) { + PointerBuffer lyrs = stack.mallocPointer(layers.size()); + for (String l : layers) { + lyrs.put(stack.UTF8(l)); + } + create.ppEnabledLayerNames(lyrs.flip()); + } + PointerBuffer ptr = stack.mallocPointer(1); + check(vkCreateDevice(physical.getDeviceHandle(), create, null, ptr), + "Failed to create logical device."); + object = new VkDevice(ptr.get(0), physical.getDeviceHandle(), create); + ref = Native.get().register(LogicalDevice.this); + physical.getInstance().getNativeReference().addDependent(ref); + physical.createQueues(LogicalDevice.this); + } + + private void findSuitablePhysicalDevice() { + PointerBuffer devices = enumerateBuffer(stack, stack::mallocPointer, + (count, buffer) -> check( + vkEnumeratePhysicalDevices(instance.getNativeObject(), count, buffer), + "Failed to enumerate physical devices.")); + physical = null; + float topWeight = Float.NEGATIVE_INFINITY; + deviceLoop: for (T d : iteratePointers(devices, deviceFactory::apply)) { + if (!d.populateQueueFamilyIndices()) { + continue; + } + // attempting to evaluate all devices with only one memory stack + // results in an out of memory error + try (MemoryStack stack = MemoryStack.stackPush()) { + float deviceWeight = 0f; + // extensions + VkExtensionProperties.Buffer supportedExts = d.getExtensionProperties(stack); + Set extSet = new HashSet<>(); + supportedExts.stream().forEach(e -> extSet.add(e.extensionNameString())); + for (DeviceExtension ext : extensions) { + Float weight = ext.evaluate(extSet); + if (weight == null) { + continue deviceLoop; + } + deviceWeight += weight; + } + // features + VkPhysicalDeviceFeatures ftrs = d.getFeatures(stack); + for (DeviceFeature f : features) { + Float weight = f.evaluateFeatureSupport(ftrs); + if (weight == null) { + continue deviceLoop; + } + deviceWeight += weight; + } + // miscellaneous filters + for (DeviceFilter f : filters) { + Float weight = f.evaluateDevice(d); + if (weight == null) { + continue deviceLoop; + } + deviceWeight += weight; + } + // compare + if (deviceWeight > topWeight) { + physical = d; + topWeight = deviceWeight; + } + } + } + if (physical == null) { + throw new NullPointerException("Failed to find suitable physical device."); + } + } + + public void addExtension(DeviceExtension extension) { + extensions.add(extension); + } + + public void addCriticalExtension(String name) { + addExtension(DeviceExtension.critical(name)); + } + + public void addOptionalExtension(String name, float successWeight) { + addExtension(DeviceExtension.optional(name, successWeight)); + } + + public void addFeature(DeviceFeature feature) { + features.add(feature); + } + + public void addFilter(DeviceFilter filter) { + filters.add(filter); + } + + public void setEnableAllFeatures(boolean enableAllFeatures) { + this.enableAllFeatures = enableAllFeatures; + } + + } + + private static class EvaluatedDevice { + + public final T device; + public final Collection features = new ArrayList<>(); + + private EvaluatedDevice(T device) { + this.device = device; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/devices/PhysicalDevice.java b/jme3-core/src/main/java/com/jme3/vulkan/devices/PhysicalDevice.java new file mode 100644 index 0000000000..3bb7fe1b51 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/devices/PhysicalDevice.java @@ -0,0 +1,46 @@ +package com.jme3.vulkan.devices; + +import com.jme3.vulkan.Format; +import com.jme3.vulkan.FormatFeature; +import com.jme3.vulkan.commands.Queue; +import com.jme3.vulkan.images.VulkanImage; +import com.jme3.vulkan.memory.MemoryProp; +import com.jme3.vulkan.surface.Surface; +import com.jme3.vulkan.VulkanInstance; +import com.jme3.vulkan.util.Flag; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.*; + +public interface PhysicalDevice { + + boolean populateQueueFamilyIndices(); + + VkDeviceQueueCreateInfo.Buffer createQueueFamilyInfo(MemoryStack stack); + + void createQueues(LogicalDevice device); + + VulkanInstance getInstance(); + + VkPhysicalDevice getDeviceHandle(); + + VkQueueFamilyProperties.Buffer getQueueFamilyProperties(MemoryStack stack); + + VkPhysicalDeviceProperties getProperties(MemoryStack stack); + + VkPhysicalDeviceFeatures getFeatures(MemoryStack stack); + + VkExtensionProperties.Buffer getExtensionProperties(MemoryStack stack); + + VkPhysicalDeviceMemoryProperties getMemoryProperties(MemoryStack stack); + + int findSupportedMemoryType(MemoryStack stack, int types, Flag flags); + + Format findSupportedFormat(VulkanImage.Tiling tiling, Flag features, Format... candidates); + + boolean querySwapchainSupport(Surface surface); + + Queue getCompute(); + + Queue getDataTransfer(); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/devices/PresentDevice.java b/jme3-core/src/main/java/com/jme3/vulkan/devices/PresentDevice.java new file mode 100644 index 0000000000..9f10f520e3 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/devices/PresentDevice.java @@ -0,0 +1,9 @@ +package com.jme3.vulkan.devices; + +import com.jme3.vulkan.commands.Queue; + +public interface PresentDevice extends PhysicalDevice { + + Queue getPresent(); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/frames/SingleResource.java b/jme3-core/src/main/java/com/jme3/vulkan/frames/SingleResource.java new file mode 100644 index 0000000000..16b8bc862d --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/frames/SingleResource.java @@ -0,0 +1,68 @@ +package com.jme3.vulkan.frames; + +import java.util.Iterator; + +@Deprecated +public class SingleResource implements VersionedResource { + + private T resource; + + public SingleResource() {} + + public SingleResource(T resource) { + this.resource = resource; + } + + @Override + public void set(T resource) { + this.resource = resource; + } + + @Override + public void set(int frame, T resource) { + this.resource = resource; + } + + @Override + public T get() { + return resource; + } + + @Override + public T get(int frame) { + return resource; + } + + @Override + public int getNumResources() { + return 1; + } + + @Override + public Iterator iterator() { + return new IteratorImpl<>(resource); + } + + private static class IteratorImpl implements Iterator { + + private T value; + + public IteratorImpl(T value) { + this.value = value; + } + + @Override + public boolean hasNext() { + return value != null; + } + + @Override + public T next() { + T v = value; + value = null; + return v; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/frames/UpdateFrame.java b/jme3-core/src/main/java/com/jme3/vulkan/frames/UpdateFrame.java new file mode 100644 index 0000000000..a7a012bd63 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/frames/UpdateFrame.java @@ -0,0 +1,7 @@ +package com.jme3.vulkan.frames; + +public interface UpdateFrame { + + void update(UpdateFrameManager frames, float tpf); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/frames/UpdateFrameManager.java b/jme3-core/src/main/java/com/jme3/vulkan/frames/UpdateFrameManager.java new file mode 100644 index 0000000000..27ac5d0f0a --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/frames/UpdateFrameManager.java @@ -0,0 +1,39 @@ +package com.jme3.vulkan.frames; + +import com.jme3.vulkan.update.Command; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.IntFunction; + +public class UpdateFrameManager { + + private final List frames = new ArrayList<>(); + private int currentFrame = 0; + + public UpdateFrameManager(int frames, IntFunction generator) { + for (int i = 0; i < frames; i++) { + this.frames.add(generator.apply(i)); + } + } + + public void update(float tpf) { + frames.get(currentFrame).update(this, tpf); + if (++currentFrame >= frames.size()) { + currentFrame = 0; + } + } + + public int getTotalFrames() { + return frames.size(); + } + + public int getCurrentFrame() { + return currentFrame; + } + + public T getFrame(int index) { + return frames.get(index); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/frames/VersionedResource.java b/jme3-core/src/main/java/com/jme3/vulkan/frames/VersionedResource.java new file mode 100644 index 0000000000..19e8dc01bd --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/frames/VersionedResource.java @@ -0,0 +1,46 @@ +package com.jme3.vulkan.frames; + +/** + * Immutably maps frame indices to resources. + * + * @param resource type + */ +public interface VersionedResource extends Iterable { + + /** + * Sets the resource for the current frame. + * + * @param resource resource to assign (not null) + */ + @Deprecated + default void set(T resource) {} + + /** + * Sets the resource for the specified frame. + * + * @param frame frame to assign to (not negative) + * @param resource resource to assign (not null) + */ + @Deprecated + default void set(int frame, T resource) {} + + /** + * Gets the resource for the current frame. Must return the same value + * over the course of a frame. + */ + T get(); + + /** + * Gets the resource for the specified frame index. + * + * @param frame frame index (not negative) + * @return resource version mapped to the frame index + */ + T get(int frame); + + /** + * Gets the number of unique resources this resource maintains. + */ + int getNumResources(); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/images/AddressMode.java b/jme3-core/src/main/java/com/jme3/vulkan/images/AddressMode.java new file mode 100644 index 0000000000..e131b3cb1e --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/images/AddressMode.java @@ -0,0 +1,25 @@ +package com.jme3.vulkan.images; + +import com.jme3.vulkan.util.IntEnum; + +import static org.lwjgl.vulkan.VK10.*; + +public enum AddressMode implements IntEnum { + + ClampToBorder(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER), + ClampToEdge(VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE), + Repeat(VK_SAMPLER_ADDRESS_MODE_REPEAT), + MirroredRepeat(VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT); + + private final int vkEnum; + + AddressMode(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/images/BasicVulkanImage.java b/jme3-core/src/main/java/com/jme3/vulkan/images/BasicVulkanImage.java new file mode 100644 index 0000000000..7a7966b873 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/images/BasicVulkanImage.java @@ -0,0 +1,242 @@ +package com.jme3.vulkan.images; + +import com.jme3.util.AbstractBuilder; +import com.jme3.util.natives.Native; +import com.jme3.util.natives.NativeReference; +import com.jme3.util.natives.AbstractNative; +import com.jme3.vulkan.Format; +import com.jme3.vulkan.SharingMode; +import com.jme3.vulkan.commands.CommandBuffer; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.memory.MemoryProp; +import com.jme3.vulkan.memory.MemoryRegion; +import com.jme3.vulkan.util.Flag; +import com.jme3.vulkan.util.IntEnum; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkImageCreateInfo; +import org.lwjgl.vulkan.VkImageMemoryBarrier; +import org.lwjgl.vulkan.VkMemoryRequirements; + +import java.nio.LongBuffer; + +import static com.jme3.renderer.vulkan.VulkanUtils.*; +import static org.lwjgl.vulkan.VK10.*; + +public class BasicVulkanImage extends AbstractNative implements VulkanImage { + + private final LogicalDevice device; + private final IntEnum type; + private MemoryRegion memory; + + private int width, height, depth; + private int mipmaps, layers; + private Flag usage; + private Format format = Format.RGBA8_SRGB; + private IntEnum tiling = Tiling.Optimal; + private IntEnum sharing = SharingMode.Exclusive; + private Layout layout = Layout.Undefined; + + public BasicVulkanImage(LogicalDevice device, IntEnum type) { + this.device = device; + this.type = type; + width = height = depth = 1; + mipmaps = layers = 1; + } + + @Override + public LogicalDevice getDevice() { + return device; + } + + @Override + public long getId() { + return object; + } + + @Override + public IntEnum getType() { + return type; + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public int getDepth() { + return depth; + } + + @Override + public int getMipmaps() { + return mipmaps; + } + + @Override + public int getLayers() { + return layers; + } + + @Override + public Flag getUsage() { + return usage; + } + + @Override + public Format getFormat() { + return format; + } + + @Override + public IntEnum getTiling() { + return tiling; + } + + @Override + public IntEnum getSharingMode() { + return sharing; + } + + @Override + public void addNativeDependent(NativeReference ref) { + this.ref.addDependent(ref); + } + + @Override + public Runnable createNativeDestroyer() { + return () -> vkDestroyImage(device.getNativeObject(), object, null); + } + + public void transitionLayout(CommandBuffer commands, Layout dstLayout) { + try (MemoryStack stack = MemoryStack.stackPush()) { + //int[] args = VulkanImage.Layout.getTransferArguments(srcLayout, dstLayout); + VkImageMemoryBarrier.Buffer barrier = VkImageMemoryBarrier.calloc(1, stack) + .sType(VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER) + .oldLayout(layout.getEnum()) + .newLayout(dstLayout.getEnum()) + .srcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) + .dstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED) // for transfering queue ownership + .image(object) + .srcAccessMask(layout.getAccessHint().bits()) + .dstAccessMask(dstLayout.getAccessHint().bits()); + barrier.subresourceRange() + .baseMipLevel(0) + .levelCount(mipmaps) + .baseArrayLayer(0) + .layerCount(layers) + .aspectMask(format.getAspects().bits()); + vkCmdPipelineBarrier(commands.getBuffer(), layout.getStageHint().bits(), dstLayout.getStageHint().bits(), 0, null, null, barrier); + layout = dstLayout; + } + } + + public Builder build() { + return new Builder(); + } + + public class Builder extends AbstractBuilder { + + private Flag mem; + + @Override + protected void build() { + VkImageCreateInfo create = VkImageCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO) + .imageType(type.getEnum()) + .mipLevels(mipmaps) + .arrayLayers(layers) + .format(format.getVkEnum()) + .tiling(tiling.getEnum()) + .initialLayout(layout.getEnum()) + .usage(usage.bits()) + .samples(VK_SAMPLE_COUNT_1_BIT) // todo: multisampling + .sharingMode(sharing.getEnum()); + create.extent().width(width).height(height).depth(depth); + LongBuffer idBuf = stack.mallocLong(1); + check(vkCreateImage(device.getNativeObject(), create, null, idBuf), + "Failed to create image."); + object = idBuf.get(0); + VkMemoryRequirements memReq = VkMemoryRequirements.malloc(stack); + vkGetImageMemoryRequirements(device.getNativeObject(), object, memReq); + memory = new MemoryRegion(device, memReq.size()); + try (MemoryRegion.Builder m = memory.build()) { + m.setFlags(mem); + m.setUsableMemoryTypes(memReq.memoryTypeBits()); + } + memory.bind(BasicVulkanImage.this, 0); + ref = Native.get().register(BasicVulkanImage.this); + device.getNativeReference().addDependent(ref); + } + + public void setWidth(int w) { + width = w; + } + + public void setHeight(int h) { + height = h; + } + + public void setDepth(int d) { + depth = d; + } + + public void setSize(int w) { + width = w; + } + + public void setSize(int w, int h) { + width = w; + height = h; + } + + public void setSize(int w, int h, int d) { + width = w; + height = h; + depth = d; + } + + public void setNumMipmaps(int m) { + mipmaps = m; + } + + public void setNumLayers(int l) { + layers = l; + } + + public void setUsage(Flag u) { + usage = u; + } + + public void setMemoryProps(Flag m) { + this.mem = m; + } + + public void setFormat(Format f) { + format = f; + } + + public void setTiling(IntEnum t) { + tiling = t; + } + + public void setLayout(Layout l) { + layout = l; + } + + public Flag getMemoryProps() { + return mem; + } + + public IntEnum getLayout() { + return layout; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/images/BorderColor.java b/jme3-core/src/main/java/com/jme3/vulkan/images/BorderColor.java new file mode 100644 index 0000000000..def7e52452 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/images/BorderColor.java @@ -0,0 +1,27 @@ +package com.jme3.vulkan.images; + +import com.jme3.vulkan.util.IntEnum; + +import static org.lwjgl.vulkan.VK10.*; + +public enum BorderColor implements IntEnum { + + FloatOpaqueBlack(VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK), + FloatOpaqueWhite(VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE), + FloatTransparentBlack(VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK), + IntOpaqueBlack(VK_BORDER_COLOR_INT_OPAQUE_BLACK), + IntOpaqueWhite(VK_BORDER_COLOR_INT_OPAQUE_WHITE), + IntTransparentBlack(VK_BORDER_COLOR_INT_TRANSPARENT_BLACK); + + private final int vkEnum; + + BorderColor(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/images/Filter.java b/jme3-core/src/main/java/com/jme3/vulkan/images/Filter.java new file mode 100644 index 0000000000..aeacf554b0 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/images/Filter.java @@ -0,0 +1,40 @@ +package com.jme3.vulkan.images; + +import com.jme3.texture.GlTexture; +import com.jme3.vulkan.util.IntEnum; + +import java.util.Objects; + +import static org.lwjgl.vulkan.VK10.*; + +public enum Filter implements IntEnum { + + Linear(VK_FILTER_LINEAR), + Nearest(VK_FILTER_NEAREST); + + private final int vkEnum; + + Filter(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + + public static Filter of(GlTexture.MinFilter min) { + switch (min) { + case BilinearNearestMipMap: + case BilinearNoMipMaps: + case Trilinear: return Linear; + default: return Nearest; + } + } + + public static Filter of(GlTexture.MagFilter mag) { + if (mag == GlTexture.MagFilter.Bilinear) return Linear; + else return Nearest; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/images/GpuImage.java b/jme3-core/src/main/java/com/jme3/vulkan/images/GpuImage.java new file mode 100644 index 0000000000..ee9ede4dd8 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/images/GpuImage.java @@ -0,0 +1,46 @@ +package com.jme3.vulkan.images; + +import com.jme3.texture.GlTexture; +import com.jme3.vulkan.Format; +import com.jme3.vulkan.util.IntEnum; + +import static org.lwjgl.vulkan.VK10.*; + +public interface GpuImage { + + enum Type implements IntEnum { + + OneDemensional(VK_IMAGE_TYPE_1D), + TwoDemensional(VK_IMAGE_TYPE_2D), + ThreeDemensional(VK_IMAGE_TYPE_3D); + + private final int vkEnum; + + Type(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + + } + + long getId(); + + IntEnum getType(); + + int getWidth(); + + int getHeight(); + + int getDepth(); + + int getMipmaps(); + + int getLayers(); + + Format getFormat(); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/images/ImageUsage.java b/jme3-core/src/main/java/com/jme3/vulkan/images/ImageUsage.java new file mode 100644 index 0000000000..3492344c09 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/images/ImageUsage.java @@ -0,0 +1,29 @@ +package com.jme3.vulkan.images; + +import com.jme3.vulkan.util.Flag; + +import static org.lwjgl.vulkan.VK10.*; + +public enum ImageUsage implements Flag { + + TransferDst(VK_IMAGE_USAGE_TRANSFER_DST_BIT), + TransferSrc(VK_IMAGE_USAGE_TRANSFER_SRC_BIT), + ColorAttachment(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT), + Sampled(VK_IMAGE_USAGE_SAMPLED_BIT), + Storage(VK_IMAGE_USAGE_STORAGE_BIT), + DepthStencilAttachment(VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT), + InputAttachment(VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT), + TransientAttachment(VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT); + + private final int bits; + + ImageUsage(int bits) { + this.bits = bits; + } + + @Override + public int bits() { + return bits; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/images/MipmapMode.java b/jme3-core/src/main/java/com/jme3/vulkan/images/MipmapMode.java new file mode 100644 index 0000000000..b8dd674b54 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/images/MipmapMode.java @@ -0,0 +1,32 @@ +package com.jme3.vulkan.images; + +import com.jme3.texture.GlTexture; +import com.jme3.vulkan.util.IntEnum; + +import static org.lwjgl.vulkan.VK10.*; + +public enum MipmapMode implements IntEnum { + + Linear(VK_SAMPLER_MIPMAP_MODE_LINEAR), + Nearest(VK_SAMPLER_MIPMAP_MODE_NEAREST); + + private final int vkEnum; + + MipmapMode(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + + public static MipmapMode of(GlTexture.MinFilter min) { + switch (min) { + case NearestLinearMipMap: + case Trilinear: return Linear; + default: return Nearest; + } + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/images/Sampler.java b/jme3-core/src/main/java/com/jme3/vulkan/images/Sampler.java new file mode 100644 index 0000000000..ee255ea8e2 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/images/Sampler.java @@ -0,0 +1,224 @@ +package com.jme3.vulkan.images; + +import com.jme3.util.AbstractBuilder; +import com.jme3.util.natives.Native; +import com.jme3.util.natives.AbstractNative; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.pipeline.CompareOp; +import com.jme3.vulkan.util.IntEnum; +import org.lwjgl.vulkan.VkPhysicalDeviceProperties; +import org.lwjgl.vulkan.VkSamplerCreateInfo; + +import java.nio.LongBuffer; +import java.util.Objects; + +import static com.jme3.renderer.vulkan.VulkanUtils.check; +import static org.lwjgl.vulkan.VK10.*; + +public class Sampler extends AbstractNative { + + public static final int U = 0, V = 1, W = 2; + public static final float DISABLE_ANISOTROPY = 0f; + + private final LogicalDevice device; + private IntEnum mipmapMode = MipmapMode.Nearest; + private IntEnum min = Filter.Linear; + private IntEnum mag = Filter.Linear; + private final IntEnum[] edgeModes = {AddressMode.ClampToEdge, AddressMode.ClampToEdge, AddressMode.ClampToEdge}; + private float anisotropy = Float.MAX_VALUE; + private IntEnum borderColor = BorderColor.FloatOpaqueBlack; + private IntEnum compare = CompareOp.Always; + private float mipLodBias = 0f; + private float minLod = 0f; + private float maxLod = VK_LOD_CLAMP_NONE; + private boolean unnormalizedCoords = false; + + public Sampler(LogicalDevice device) { + this.device = device; + } + + @Override + public Runnable createNativeDestroyer() { + return () -> vkDestroySampler(device.getNativeObject(), object, null); + } + + public LogicalDevice getDevice() { + return device; + } + + public IntEnum getMipmapMode() { + return mipmapMode; + } + + public IntEnum getMinFilter() { + return min; + } + + public IntEnum getMagFilter() { + return mag; + } + + public IntEnum[] getEdgeModes() { + return edgeModes; + } + + public float getAnisotropy() { + return anisotropy; + } + + public boolean isAnisotropyEnabled() { + return anisotropy > DISABLE_ANISOTROPY; + } + + public IntEnum getBorderColor() { + return borderColor; + } + + public IntEnum getCompare() { + return compare; + } + + public float getMipLodBias() { + return mipLodBias; + } + + public float getMinLod() { + return minLod; + } + + public float getMaxLod() { + return maxLod; + } + + public boolean isUnnormalizedCoords() { + return unnormalizedCoords; + } + + public Builder build() { + return new Builder(); + } + + public class Builder extends AbstractBuilder { + + private Builder() {} + + @Override + protected void build() { + VkPhysicalDeviceProperties props = device.getPhysicalDevice().getProperties(stack); + VkSamplerCreateInfo create = VkSamplerCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO) + .minFilter(min.getEnum()) + .magFilter(mag.getEnum()) + .addressModeU(edgeModes[U].getEnum()) + .addressModeV(edgeModes[V].getEnum()) + .addressModeW(edgeModes[W].getEnum()) + .anisotropyEnable(anisotropy > DISABLE_ANISOTROPY) + .maxAnisotropy(Math.min(anisotropy, props.limits().maxSamplerAnisotropy())) + .borderColor(borderColor.getEnum()) + .unnormalizedCoordinates(unnormalizedCoords) + .compareEnable(compare != null) + .compareOp(IntEnum.get(compare, CompareOp.Always).getEnum()) + .mipmapMode(mipmapMode.getEnum()) + .mipLodBias(mipLodBias) + .minLod(minLod) + .maxLod(maxLod); + LongBuffer idBuf = stack.mallocLong(1); + check(vkCreateSampler(device.getNativeObject(), create, null, idBuf), + "Failed to create sampler."); + object = idBuf.get(0); + ref = Native.get().register(Sampler.this); + device.getNativeReference().addDependent(ref); + } + + public void setMipmapMode(IntEnum m) { + mipmapMode = Objects.requireNonNull(m); + } + + public void setMinFilter(IntEnum f) { + min = Objects.requireNonNull(f); + } + + public void setMagFilter(IntEnum f) { + mag = Objects.requireNonNull(f); + } + + public void setMinMagFilters(IntEnum min, IntEnum mag) { + setMagFilter(min); + setMagFilter(mag); + } + + public void setEdgeMode(int axis, IntEnum a) { + if (axis < 0 || axis >= edgeModes.length) { + throw new IndexOutOfBoundsException("Invalid axis index (" + axis + "). " + + "Must be 0 (U), 1 (V), or 2 (W)."); + } + edgeModes[axis] = Objects.requireNonNull(a); + } + + public void setEdgeModeU(IntEnum u) { + setEdgeMode(U, u); + } + + public void setEdgeModeV(IntEnum v) { + setEdgeMode(V, v); + } + + public void setEdgeModeW(IntEnum w) { + setEdgeMode(W, w); + } + + public void setEdgeModes(IntEnum u, IntEnum v, IntEnum w) { + setEdgeMode(U, u); + setEdgeMode(V, v); + setEdgeMode(W, w); + } + + public void setEdgeModes(IntEnum e) { + setEdgeMode(U, e); + setEdgeMode(V, e); + setEdgeMode(W, e); + } + + public void setAnisotropy(float a) { + anisotropy = a; + } + + public void maxAnisotropy() { + setAnisotropy(Float.MAX_VALUE); + } + + public void disableAnisotropy() { + setAnisotropy(DISABLE_ANISOTROPY); + } + + public void setBorderColor(IntEnum c) { + borderColor = Objects.requireNonNull(c); + } + + public void setCompare(IntEnum c) { + compare = Objects.requireNonNull(c); + } + + public void setMipLodBias(float bias) { + mipLodBias = bias; + } + + public void setMinLod(float lod) { + minLod = lod; + } + + public void setMaxLod(float lod) { + maxLod = lod; + } + + public void disableMaxLod() { + setMaxLod(VK_LOD_CLAMP_NONE); + } + + public void setUnnormalizedCoords(boolean u) { + unnormalizedCoords = u; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/images/VulkanImage.java b/jme3-core/src/main/java/com/jme3/vulkan/images/VulkanImage.java new file mode 100644 index 0000000000..12c46e4bb7 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/images/VulkanImage.java @@ -0,0 +1,175 @@ +package com.jme3.vulkan.images; + +import com.jme3.util.natives.NativeReference; +import com.jme3.vulkan.SharingMode; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.pipeline.Access; +import com.jme3.vulkan.pipeline.PipelineStage; +import com.jme3.vulkan.util.Flag; +import com.jme3.vulkan.util.IntEnum; +import org.lwjgl.vulkan.KHRSwapchain; + +import static org.lwjgl.vulkan.VK10.*; + +public interface VulkanImage extends GpuImage { + + enum Layout implements IntEnum { + + Undefined(VK_IMAGE_LAYOUT_UNDEFINED, + Flag.empty(), PipelineStage.TopOfPipe), + General(VK_IMAGE_LAYOUT_GENERAL, + Flag.empty(), Flag.empty()), // not sure how this layout should be treated for transitions + PreInitialized(VK_IMAGE_LAYOUT_PREINITIALIZED), + ColorAttachmentOptimal(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + Flag.of(Access.ColorAttachmentRead, Access.ColorAttachmentWrite), PipelineStage.ColorAttachmentOutput), + DepthStencilAttachmentOptimal(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + Flag.of(Access.DepthStencilAttachmentRead, Access.DepthStencilAttachmentWrite), PipelineStage.EarlyFragmentTests), + DepthStencilReadOnlyOptimal(VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, + Access.DepthStencilAttachmentRead, PipelineStage.EarlyFragmentTests), + TransferSrcOptimal(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + Access.TransferRead, PipelineStage.Transfer), + TransferDstOptimal(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + Access.TransferWrite, PipelineStage.Transfer), + ShaderReadOnlyOptimal(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + Access.ShaderRead, PipelineStage.FragmentShader), + PresentSrc(KHRSwapchain.VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + + private final int vkEnum; + private final Flag access; + private final Flag stage; + + Layout(int vkEnum) { + this.vkEnum = vkEnum; + access = Flag.empty(); + stage = Flag.empty(); + } + + Layout(int vkEnum, Flag access, Flag stage) { + this.vkEnum = vkEnum; + this.access = access; + this.stage = stage; + } + + @Override + public int getEnum() { + return vkEnum; + } + + public Flag getAccessHint() { + return access; + } + + public Flag getStageHint() { + return stage; + } + + @Deprecated + @SuppressWarnings("SwitchStatementWithTooFewBranches") + public static int[] getTransferArguments(Layout srcLayout, Layout dstLayout) { + // output array format: {srcAccessMask, dstAccessMask, srcStage, dstStage} + switch (srcLayout) { + case Undefined: switch (dstLayout) { + case TransferDstOptimal: return new int[] { + 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT}; + case DepthStencilAttachmentOptimal: return new int[] { + 0, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT}; + } break; + case TransferDstOptimal: switch (dstLayout) { + case ShaderReadOnlyOptimal: return new int[] { + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT}; + } break; + } + throw new UnsupportedOperationException("Unsupported layout transition: " + srcLayout + " to " + dstLayout); + } + + } + + enum Tiling implements IntEnum { + + Optimal(VK_IMAGE_TILING_OPTIMAL), + Linear(VK_IMAGE_TILING_LINEAR); + + private final int vkEnum; + + Tiling(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + + } + + enum Load implements IntEnum { + + Clear(VK_ATTACHMENT_LOAD_OP_CLEAR), + Load(VK_ATTACHMENT_LOAD_OP_LOAD), + DontCare(VK_ATTACHMENT_LOAD_OP_DONT_CARE); + + private final int vkEnum; + + Load(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + + } + + enum Store implements IntEnum { + + Store(VK_ATTACHMENT_STORE_OP_STORE), + DontCare(VK_ATTACHMENT_STORE_OP_DONT_CARE); + + private final int vkEnum; + + Store(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + + } + + enum Aspect implements Flag { + + Color(VK_IMAGE_ASPECT_COLOR_BIT), + Depth(VK_IMAGE_ASPECT_DEPTH_BIT), + Stencil(VK_IMAGE_ASPECT_STENCIL_BIT), + MetaData(VK_IMAGE_ASPECT_METADATA_BIT); + + private final int bits; + + Aspect(int bits) { + this.bits = bits; + } + + @Override + public int bits() { + return bits; + } + + } + + LogicalDevice getDevice(); + + Flag getUsage(); + + IntEnum getTiling(); + + IntEnum getSharingMode(); + + void addNativeDependent(NativeReference ref); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/images/VulkanImageLoader.java b/jme3-core/src/main/java/com/jme3/vulkan/images/VulkanImageLoader.java new file mode 100644 index 0000000000..ecbd02d317 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/images/VulkanImageLoader.java @@ -0,0 +1,275 @@ +package com.jme3.vulkan.images; + +import com.jme3.asset.*; +import com.jme3.util.BufferUtils; +import com.jme3.vulkan.Format; +import com.jme3.vulkan.buffers.BasicVulkanBuffer; +import com.jme3.vulkan.buffers.BufferUsage; +import com.jme3.vulkan.memory.MemoryProp; +import com.jme3.vulkan.memory.MemorySize; +import com.jme3.vulkan.commands.CommandBuffer; +import com.jme3.vulkan.commands.CommandPool; +import com.jme3.vulkan.sync.SyncGroup; +import com.jme3.vulkan.util.Flag; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkBufferImageCopy; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.DataBufferUShort; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; + +import static org.lwjgl.vulkan.VK10.*; + +public class VulkanImageLoader implements AssetLoader { + + @Override + public Object load(AssetInfo info) throws IOException { + if (ImageIO.getImageReadersBySuffix(info.getKey().getExtension()) != null) { + boolean flip = ((Key)info.getKey()).isFlip(); + try (InputStream stream = info.openStream(); BufferedInputStream bin = new BufferedInputStream(stream)) { + ImageData data = load(bin, flip); + if (data == null) { + throw new AssetLoadException("The given image cannot be loaded " + info.getKey()); + } + if (info.getKey() instanceof ImageKey) { + return loadGpuImage(((ImageKey)info.getKey()).getPool(), data); + } + return data; + } + } + throw new AssetLoadException("Image extension " + info.getKey().getExtension() + " is not supported."); + } + + public ImageData load(InputStream in, boolean flip) throws IOException { + ImageIO.setUseCache(false); + BufferedImage img = ImageIO.read(in); + if (img == null){ + return null; + } + return load(img, flip); + } + + public ImageData load(BufferedImage img, boolean flipY) { + int width = img.getWidth(); + int height = img.getHeight(); + byte[] data = (byte[])extractImageData(img); + switch (img.getType()) { + case BufferedImage.TYPE_4BYTE_ABGR: { // most common in PNG images w/ alpha + if (flipY) { + flipImage(data, width, height, 32); + } + ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * 4); + buffer.put(data); + return new ImageData(buffer, width, height, Format.ABGR8_SRGB); + } + case BufferedImage.TYPE_3BYTE_BGR: { // most common in JPEG images + if (flipY) { + flipImage(data, width, height, 24); + } + ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * 3); + buffer.put(data); + return new ImageData(buffer, width, height, Format.BGR8_SRGB); + } + case BufferedImage.TYPE_BYTE_GRAY: { // grayscale fonts + if (flipY) { + flipImage(data, width, height, 8); + } + ByteBuffer buffer = BufferUtils.createByteBuffer(width * height); + buffer.put(data); + return new ImageData(buffer, width, height, Format.R8_SRGB); + } + } + if (img.getTransparency() == Transparency.OPAQUE) { + ByteBuffer buffer = BufferUtils.createByteBuffer(img.getWidth() * img.getHeight() * 4); + for (int y = 0; y < height; y++) { + int ny = y; + if (flipY) { + ny = height - y - 1; + } + for (int x = 0; x < width; x++) { + int rgb = img.getRGB(x, ny); + byte r = (byte) ((rgb & 0x00FF0000) >> 16); + byte g = (byte) ((rgb & 0x0000FF00) >> 8); + byte b = (byte) ((rgb & 0x000000FF)); + byte a = Byte.MAX_VALUE; + buffer.put(r).put(g).put(b).put(a); + } + } + buffer.flip(); + return new ImageData(buffer, width, height, Format.RGBA8_SRGB); + } else { + ByteBuffer buffer = BufferUtils.createByteBuffer(img.getWidth() * img.getHeight() * 4); + for (int y = 0; y < height; y++) { + int ny = y; + if (flipY) { + ny = height - y - 1; + } + for (int x = 0; x < width; x++) { + int rgb = img.getRGB(x,ny); + byte a = (byte) ((rgb & 0xFF000000) >> 24); + byte r = (byte) ((rgb & 0x00FF0000) >> 16); + byte g = (byte) ((rgb & 0x0000FF00) >> 8); + byte b = (byte) ((rgb & 0x000000FF)); + buffer.put(r).put(g).put(b).put(a); + } + } + buffer.flip(); + return new ImageData(buffer, width, height, Format.RGBA8_SRGB); + } + } + + private Object extractImageData(BufferedImage img) { + DataBuffer buf = img.getRaster().getDataBuffer(); + switch (buf.getDataType()) { + case DataBuffer.TYPE_BYTE: { + DataBufferByte byteBuf = (DataBufferByte) buf; + return byteBuf.getData(); + } + case DataBuffer.TYPE_USHORT: { + DataBufferUShort shortBuf = (DataBufferUShort) buf; + return shortBuf.getData(); + } + default: throw new UnsupportedOperationException("Image data type not supported: " + buf.getDataType()); + } + } + + private void flipImage(byte[] img, int width, int height, int bpp) { + int scSz = (width * bpp) / 8; + byte[] sln = new byte[scSz]; + for (int y1 = 0; y1 < height / 2; y1++) { + int y2 = height - y1 - 1; + System.arraycopy(img, y1 * scSz, sln, 0, scSz); + System.arraycopy(img, y2 * scSz, img, y1 * scSz, scSz); + System.arraycopy(sln, 0, img, y2 * scSz, scSz); + } + } + + private BasicVulkanImage loadGpuImage(CommandPool transferPool, ImageData data) { + try (MemoryStack stack = MemoryStack.stackPush()) { + BasicVulkanBuffer staging = new BasicVulkanBuffer( + transferPool.getDevice(), MemorySize.bytes(data.getBuffer().limit())); + try (BasicVulkanBuffer.Builder s = staging.build()) { + s.setUsage(BufferUsage.TransferSrc); + s.setMemFlags(Flag.of(MemoryProp.HostVisible, MemoryProp.HostCoherent)); + } + staging.copy(data.getBuffer()); + BasicVulkanImage image = new BasicVulkanImage(transferPool.getDevice(), VulkanImage.Type.TwoDemensional); + try (BasicVulkanImage.Builder i = image.build()) { + i.setSize(data.getWidth(), data.getHeight()); + i.setFormat(data.getFormat()); + i.setTiling(VulkanImage.Tiling.Optimal); + i.setUsage(Flag.of(ImageUsage.TransferDst, ImageUsage.Sampled)); + i.setMemoryProps(MemoryProp.DeviceLocal); + } + CommandBuffer commands = transferPool.allocateTransientCommandBuffer(); + commands.begin(); + image.transitionLayout(commands, VulkanImage.Layout.TransferDstOptimal); + VkBufferImageCopy.Buffer region = VkBufferImageCopy.calloc(1, stack) + .bufferOffset(0) + .bufferRowLength(0) // padding bytes + .bufferImageHeight(0); // padding bytes + region.imageSubresource().aspectMask(VulkanImage.Aspect.Color.bits()) + .mipLevel(0) + .baseArrayLayer(0) + .layerCount(1); + region.imageOffset().set(0, 0, 0); + region.imageExtent().set(data.getWidth(), data.getHeight(), 1); + vkCmdCopyBufferToImage(commands.getBuffer(), staging.getId(), + image.getNativeObject(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, region); + image.transitionLayout(commands, VulkanImage.Layout.ShaderReadOnlyOptimal); + commands.endAndSubmit(SyncGroup.ASYNC); + transferPool.getQueue().waitIdle(); + return image; + } + } + + public static Key key(String name) { + return new Key<>(name); + } + + public static ImageKey key(CommandPool pool, String name) { + return new ImageKey(pool, name); + } + + public static class Key extends AssetKey { + + private boolean flip; + + public Key(String name) { + this(name, false); + } + + public Key(String name, boolean flip) { + super(name); + this.flip = flip; + } + + public void setFlip(boolean flip) { + this.flip = flip; + } + + public boolean isFlip() { + return flip; + } + + } + + public static class ImageKey extends Key { + + private final CommandPool pool; + + public ImageKey(CommandPool pool, String name) { + super(name); + this.pool = pool; + } + + public ImageKey(CommandPool pool, String name, boolean flip) { + super(name, flip); + this.pool = pool; + } + + public CommandPool getPool() { + return pool; + } + + } + + public static class ImageData { + + private final ByteBuffer buffer; + private final int width, height; + private final Format format; + + public ImageData(ByteBuffer buffer, int width, int height, Format format) { + this.buffer = buffer; + this.width = width; + this.height = height; + this.format = format; + } + + public ByteBuffer getBuffer() { + return buffer; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public Format getFormat() { + return format; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/images/VulkanImageView.java b/jme3-core/src/main/java/com/jme3/vulkan/images/VulkanImageView.java new file mode 100644 index 0000000000..d2ea1ddf10 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/images/VulkanImageView.java @@ -0,0 +1,172 @@ +package com.jme3.vulkan.images; + +import com.jme3.texture.ImageView; +import com.jme3.util.AbstractBuilder; +import com.jme3.util.natives.Native; +import com.jme3.util.natives.AbstractNative; +import com.jme3.vulkan.Swizzle; +import com.jme3.vulkan.util.Flag; +import com.jme3.vulkan.util.IntEnum; +import org.lwjgl.vulkan.VkImageViewCreateInfo; + +import java.nio.LongBuffer; + +import static com.jme3.renderer.vulkan.VulkanUtils.check; +import static org.lwjgl.vulkan.VK10.*; + +public class VulkanImageView extends AbstractNative implements ImageView { + + private final VulkanImage image; + private final IntEnum type; + + private IntEnum swizzleR = Swizzle.R; + private IntEnum swizzleG = Swizzle.G; + private IntEnum swizzleB = Swizzle.B; + private IntEnum swizzleA = Swizzle.A; + private Flag aspect = VulkanImage.Aspect.Color; + private int baseMipmap = 0; + private int mipmapCount = 1; + private int baseLayer = 0; + private int layerCount = 1; + + public VulkanImageView(VulkanImage image, IntEnum type) { + this.image = image; + this.type = type; + } + + @Override + public Runnable createNativeDestroyer() { + return () -> vkDestroyImageView(image.getDevice().getNativeObject(), object, null); + } + + @Override + public long getId() { + return object; + } + + @Override + public VulkanImage getImage() { + return image; + } + + @Override + public IntEnum getViewType() { + return type; + } + + @Override + public int getBaseMipmap() { + return baseMipmap; + } + + @Override + public int getMipmapCount() { + return mipmapCount; + } + + @Override + public int getBaseLayer() { + return baseLayer; + } + + @Override + public int getLayerCount() { + return layerCount; + } + + public IntEnum getSwizzleR() { + return swizzleR; + } + + public IntEnum getSwizzleG() { + return swizzleG; + } + + public IntEnum getSwizzleB() { + return swizzleB; + } + + public IntEnum getSwizzleA() { + return swizzleA; + } + + public Flag getAspect() { + return aspect; + } + + public Builder build() { + return new Builder(); + } + + public class Builder extends AbstractBuilder { + + @Override + protected void build() { + VkImageViewCreateInfo create = VkImageViewCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO) + .image(image.getId()) + .viewType(type.getEnum()) + .format(image.getFormat().getVkEnum()); + create.components() + .r(swizzleR.getEnum()) + .g(swizzleG.getEnum()) + .b(swizzleB.getEnum()) + .a(swizzleA.getEnum()); + create.subresourceRange() + .aspectMask(aspect.bits()) + .baseMipLevel(baseMipmap) + .levelCount(mipmapCount) + .baseArrayLayer(baseLayer) + .layerCount(layerCount); + LongBuffer idBuf = stack.mallocLong(1); + check(vkCreateImageView(image.getDevice().getNativeObject(), create, null, idBuf), + "Failed to create image view."); + object = idBuf.get(0); + ref = Native.get().register(VulkanImageView.this); + image.addNativeDependent(ref); + } + + public void allMipmaps() { + baseMipmap = 0; + mipmapCount = image.getMipmaps(); + } + + public void setSwizzleR(IntEnum swizzleR) { + VulkanImageView.this.swizzleR = swizzleR; + } + + public void setSwizzleG(IntEnum swizzleG) { + VulkanImageView.this.swizzleG = swizzleG; + } + + public void setSwizzleB(IntEnum swizzleB) { + VulkanImageView.this.swizzleB = swizzleB; + } + + public void setSwizzleA(IntEnum swizzleA) { + VulkanImageView.this.swizzleA = swizzleA; + } + + public void setAspect(Flag aspect) { + VulkanImageView.this.aspect = aspect; + } + + public void setBaseMipmap(int baseMipmap) { + VulkanImageView.this.baseMipmap = baseMipmap; + } + + public void setMipmapCount(int mipmapCount) { + VulkanImageView.this.mipmapCount = mipmapCount; + } + + public void setBaseLayer(int baseLayer) { + VulkanImageView.this.baseLayer = baseLayer; + } + + public void setLayerCount(int layerCount) { + VulkanImageView.this.layerCount = layerCount; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/images/VulkanTexture.java b/jme3-core/src/main/java/com/jme3/vulkan/images/VulkanTexture.java new file mode 100644 index 0000000000..e057f9976f --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/images/VulkanTexture.java @@ -0,0 +1,27 @@ +package com.jme3.vulkan.images; + +import com.jme3.texture.Texture; +import com.jme3.vulkan.devices.LogicalDevice; + +import java.util.Objects; + +public class VulkanTexture extends Sampler implements Texture { + + private final VulkanImageView image; + + public VulkanTexture(LogicalDevice device, VulkanImageView image) { + super(device); + this.image = Objects.requireNonNull(image); + } + + @Override + public long getId() { + return object; + } + + @Override + public VulkanImageView getView() { + return image; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/material/MatrixTransformMaterial.java b/jme3-core/src/main/java/com/jme3/vulkan/material/MatrixTransformMaterial.java new file mode 100644 index 0000000000..7e9cac0dcb --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/material/MatrixTransformMaterial.java @@ -0,0 +1,25 @@ +package com.jme3.vulkan.material; + +import com.jme3.vulkan.descriptors.Descriptor; +import com.jme3.vulkan.descriptors.DescriptorPool; +import com.jme3.vulkan.material.uniforms.BufferUniform; +import com.jme3.vulkan.shader.ShaderStage; + +/** + * Material specifically for storing matrix transforms for a geometry. + */ +public class MatrixTransformMaterial extends NewMaterial { + + private final BufferUniform transforms = new BufferUniform("Transforms", + Descriptor.UniformBuffer, 0, ShaderStage.Vertex); + + public MatrixTransformMaterial(DescriptorPool pool) { + super(pool); + addSet(0, transforms); + } + + public BufferUniform getTransforms() { + return transforms; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/material/NewMaterial.java b/jme3-core/src/main/java/com/jme3/vulkan/material/NewMaterial.java new file mode 100644 index 0000000000..aa71cfb190 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/material/NewMaterial.java @@ -0,0 +1,169 @@ +package com.jme3.vulkan.material; + +import com.jme3.dev.NotFullyImplemented; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.texture.Texture; +import com.jme3.vulkan.buffers.GpuBuffer; +import com.jme3.vulkan.commands.CommandBuffer; +import com.jme3.vulkan.descriptors.*; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.material.uniforms.BufferUniform; +import com.jme3.vulkan.material.uniforms.Uniform; +import com.jme3.vulkan.mesh.MeshDescription; +import com.jme3.vulkan.pipeline.Pipeline; +import com.jme3.vulkan.pipeline.cache.PipelineCache; +import com.jme3.vulkan.pipeline.states.BasePipelineState; +import com.jme3.vulkan.pipeline.states.PipelineState; +import org.lwjgl.system.MemoryStack; + +import java.io.IOException; +import java.nio.LongBuffer; +import java.util.*; + +/** + * Relates shader uniform values to sets and bindings. + */ +public class NewMaterial implements VkMaterial { + + private final DescriptorPool pool; + private final Map uniformSets = new HashMap<>(); + private final Map> uniformLookup = new HashMap<>(); + private final BitSet usedSetSlots = new BitSet(); + private final Map> techniques = new HashMap<>(); + private BasePipelineState additionalState; + + public NewMaterial(DescriptorPool pool) { + this.pool = pool; + } + + @Override + public boolean bind(CommandBuffer cmd, Pipeline pipeline) { + LinkedList availableLayouts = new LinkedList<>( + pipeline.getLayout().getDescriptorSetLayouts()); + try (MemoryStack stack = MemoryStack.stackPush()) { + LongBuffer sets = stack.mallocLong(uniformSets.size()); + for (int i = usedSetSlots.nextSetBit(0), offset = i; i >= 0;) { + DescriptorSet acquired = uniformSets.get(i).acquire(pool, availableLayouts); + if (acquired == null) { + return false; // material is not supported by the pipeline + } + sets.put(acquired.getNativeObject()); + int next = usedSetSlots.nextSetBit(i + 1); + // if there are no more sets remaining, or the next position skips over at + // least one index, bind the current descriptor sets + if (next < 0 || next > i + 1) { + sets.flip(); + vkCmdBindDescriptorSets(cmd.getBuffer(), pipeline.getBindPoint().getEnum(), + pipeline.getLayout().getNativeObject(), offset, sets, null); + sets.clear(); + offset = next; + } + i = next; + } + } + return true; + } + + @Override + public Pipeline selectPipeline(PipelineCache cache, MeshDescription mesh, + String forcedTechnique, PipelineState overrideState) { + BasePipelineState state = techniques.get(forcedTechnique); + return state.selectPipeline(cache, mesh); + } + + @Override + public void setUniform(String name, GpuBuffer buffer) { + BufferUniform u = getUniform(name); + u.set(buffer); + } + + @Override + public void setTexture(String name, Texture texture) { + Uniform u = getUniform(name); + u.set(texture); + } + + @Override + @NotFullyImplemented + public void setParam(String uniform, String param, Object value) { + Uniform u = getUniform(uniform); + GpuBuffer buffer = u.get(); + //buffer.map(Structure::new).set(param, value); + //buffer.unmap(); + } + + @Override + @NotFullyImplemented + public void clearParam(String uniform, String param) { + Uniform u = getUniform(uniform); + // clear parameter + } + + @Override + @NotFullyImplemented + public T getParam(String uniform, String name) { + Uniform u = getUniform(uniform); + // get parameter + return null; + } + + @Override + public Texture getTexture(String name) { + Uniform t = getUniform(name); + return t != null ? t.get() : null; + } + + @Override + public void write(JmeExporter ex) throws IOException { + throw new UnsupportedOperationException("Exporting not yet supported."); + } + + @Override + public void read(JmeImporter im) throws IOException { + throw new UnsupportedOperationException("Importing not yet supported."); + } + + @SuppressWarnings("unchecked") + public T getUniform(String name) { + // Not sure if caching the results is really worth it... + Uniform uniform = uniformLookup.get(name); + if (uniform != null) { + return (T)uniform; + } + for (UniformSet set : uniformSets.values()) { + for (Uniform u : set) { + if (name.equals(u.getName())) { + uniformLookup.put(u.getName(), u); + return (T)u; + } + } + } + return null; + } + + protected UniformSet addSet(UniformSet set) { + if (uniformSets.put(set.getSetIndex(), set) != null) { + throw new IllegalArgumentException("Set index already occupied: " + set.getSetIndex()); + } + usedSetSlots.set(set.getSetIndex()); + return set; + } + + protected UniformSet addSet(int index, Uniform... uniforms) { + return addSet(new UniformSet(index, uniforms)); + } + + public void setTechnique(String name, BasePipelineState state) { + techniques.put(name, state); + } + + public DescriptorSetLayout[] createLayouts(LogicalDevice device) { + return uniformSets.values().stream().map(u -> u.createLayout(device)).toArray(DescriptorSetLayout[]::new); + } + + public Map getSets() { + return Collections.unmodifiableMap(uniformSets); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/material/TestMaterial.java b/jme3-core/src/main/java/com/jme3/vulkan/material/TestMaterial.java new file mode 100644 index 0000000000..2a384bc1e2 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/material/TestMaterial.java @@ -0,0 +1,22 @@ +package com.jme3.vulkan.material; + +import com.jme3.vulkan.descriptors.DescriptorPool; +import com.jme3.vulkan.images.VulkanImage; +import com.jme3.vulkan.material.uniforms.TextureUniform; +import com.jme3.vulkan.shader.ShaderStage; + +public class TestMaterial extends NewMaterial { + + private final TextureUniform baseColorMap = new TextureUniform( + "BaseColorMap", VulkanImage.Layout.ShaderReadOnlyOptimal, 1, ShaderStage.Fragment); + + public TestMaterial(DescriptorPool pool) { + super(pool); + addSet(0, baseColorMap); + } + + public TextureUniform getBaseColorMap() { + return baseColorMap; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/material/UniformSet.java b/jme3-core/src/main/java/com/jme3/vulkan/material/UniformSet.java new file mode 100644 index 0000000000..850d1e32ff --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/material/UniformSet.java @@ -0,0 +1,149 @@ +package com.jme3.vulkan.material; + +import com.jme3.util.IntMap; +import com.jme3.vulkan.descriptors.*; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.material.uniforms.Uniform; + +import java.util.*; + +public class UniformSet implements Iterable { + + private final int setIndex; + private final Uniform[] uniforms; + private final Collection configs = new ArrayList<>(); + private int configCacheTimeout = 60; + + public UniformSet(int setIndex, Uniform... uniforms) { + this.setIndex = setIndex; + this.uniforms = uniforms; + // ensure duplicate binding indices are not present + BitSet bindings = new BitSet(); + for (Uniform u : uniforms) { + int i = u.getBindingIndex(); + if (bindings.get(i)) { + throw new IllegalArgumentException("Duplicate binding index in set " + setIndex + ": " + u.getBindingIndex()); + } + bindings.set(i); + } + } + + @Override + public Iterator iterator() { + return new IteratorImpl(); + } + + public DescriptorSet acquire(DescriptorPool pool, List availableLayouts) { + configs.removeIf(UniformConfig::cycleTimeout); + DescriptorSetWriter[] writers = Arrays.stream(uniforms).map(Uniform::createWriter).toArray(DescriptorSetWriter[]::new); + UniformConfig data = configs.stream().filter(f -> f.isCurrent(writers)).findAny().orElse(null); + if (data == null) { + configs.add(data = new UniformConfig(writers)); + } + return data.get(pool, availableLayouts); + } + + public DescriptorSetLayout createLayout(LogicalDevice device) { + SetLayoutBinding[] bindings = new SetLayoutBinding[uniforms.length]; + for (int i = 0; i < uniforms.length; i++) { + bindings[i] = uniforms[i].createBinding(); + } + return new DescriptorSetLayout(device, bindings); + } + + public void setConfigCacheTimeout(int configCacheTimeout) { + this.configCacheTimeout = configCacheTimeout; + } + + public int getSetIndex() { + return setIndex; + } + + public int getConfigCacheTimeout() { + return configCacheTimeout; + } + + /** + * Maps {@link DescriptorSetLayout DescriptorSetLayouts} to {@link DescriptorSet DescriptorSets} + * by the ID of the DescriptorSetLayout for a particular combination of uniform values. If no + * available layout has a mapped set, a new set is allocated with an available compatible layout + * and mapped with it. + */ + private class UniformConfig { + + private final DescriptorSetWriter[] writers; + private final Map sets = new HashMap<>(); + private int timeout = configCacheTimeout; + + public UniformConfig(DescriptorSetWriter[] writers) { + this.writers = writers; + } + + public boolean cycleTimeout() { + return timeout-- <= 0; + } + + public boolean isCurrent(DescriptorSetWriter[] writers) { + for (int i = 0; i < writers.length; i++) { + if (!this.writers[i].equals(writers[i])) { + return false; + } + } + return true; + } + + public DescriptorSet get(DescriptorPool pool, List availableLayouts) { + timeout = configCacheTimeout; + // check layouts against cached sets + for (Iterator it = availableLayouts.iterator(); it.hasNext();) { + DescriptorSetLayout layout = it.next(); + DescriptorSet set = sets.get(layout.getNativeObject()); + if (set != null) { + it.remove(); + return set; + } + } + // check layouts for a compatible layout to create a new set + for (Iterator it = availableLayouts.iterator(); it.hasNext();) { + DescriptorSetLayout layout = it.next(); + if (isLayoutCompatible(layout)) { + it.remove(); + DescriptorSet set = pool.allocateSets(layout)[0]; + sets.put(set.getLayout().getNativeObject(), set); + set.write(writers); + return set; + } + } + return null; + } + + private boolean isLayoutCompatible(DescriptorSetLayout layout) { + for (Uniform u : uniforms) { + for (IntMap.Entry b : layout.getBindings()) { + if (u.isBindingCompatible(b.getValue())) { + return true; + } + } + } + return false; + } + + } + + private class IteratorImpl implements Iterator { + + private int index = 0; + + @Override + public boolean hasNext() { + return index < uniforms.length; + } + + @Override + public Uniform next() { + return uniforms[index++]; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/material/VkMaterial.java b/jme3-core/src/main/java/com/jme3/vulkan/material/VkMaterial.java new file mode 100644 index 0000000000..36f753d31c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/material/VkMaterial.java @@ -0,0 +1,17 @@ +package com.jme3.vulkan.material; + +import com.jme3.material.Material; +import com.jme3.vulkan.commands.CommandBuffer; +import com.jme3.vulkan.mesh.MeshDescription; +import com.jme3.vulkan.pipeline.Pipeline; +import com.jme3.vulkan.pipeline.cache.PipelineCache; +import com.jme3.vulkan.pipeline.states.PipelineState; + +public interface VkMaterial extends Material { + + boolean bind(CommandBuffer cmd, Pipeline pipeline); + + Pipeline selectPipeline(PipelineCache cache, MeshDescription mesh, + String forcedTechnique, PipelineState overrideState); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/material/uniforms/AbstractUniform.java b/jme3-core/src/main/java/com/jme3/vulkan/material/uniforms/AbstractUniform.java new file mode 100644 index 0000000000..75362ff658 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/material/uniforms/AbstractUniform.java @@ -0,0 +1,63 @@ +package com.jme3.vulkan.material.uniforms; + +import com.fasterxml.jackson.databind.JsonNode; +import com.jme3.vulkan.descriptors.Descriptor; +import com.jme3.vulkan.descriptors.SetLayoutBinding; +import com.jme3.vulkan.frames.VersionedResource; +import com.jme3.vulkan.shader.ShaderStage; +import com.jme3.vulkan.util.Flag; +import com.jme3.vulkan.util.FlagParser; +import com.jme3.vulkan.util.IntEnum; +import com.jme3.vulkan.util.ReflectionArgs; + +import java.util.Objects; + +public abstract class AbstractUniform implements Uniform { + + protected final String name; + protected final IntEnum type; + protected final int bindingIndex; + protected final Flag stages; + protected T value; + + public AbstractUniform(String name, IntEnum type, int bindingIndex, Flag stages) { + this.name = name; + this.type = type; + this.bindingIndex = bindingIndex; + this.stages = stages; + } + + @Override + public SetLayoutBinding createBinding() { + return new SetLayoutBinding(type, bindingIndex, 1, stages); + } + + @Override + public String getName() { + return name; + } + + @Override + public int getBindingIndex() { + return bindingIndex; + } + + @Override + public void set(T value) { + this.value = value; + } + + @Override + public T get() { + return value; + } + + public IntEnum getType() { + return type; + } + + public Flag getStages() { + return stages; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/material/uniforms/BufferUniform.java b/jme3-core/src/main/java/com/jme3/vulkan/material/uniforms/BufferUniform.java new file mode 100644 index 0000000000..7db5b433eb --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/material/uniforms/BufferUniform.java @@ -0,0 +1,74 @@ +package com.jme3.vulkan.material.uniforms; + +import com.jme3.vulkan.buffers.*; +import com.jme3.vulkan.descriptors.Descriptor; +import com.jme3.vulkan.descriptors.DescriptorSetWriter; +import com.jme3.vulkan.descriptors.SetLayoutBinding; +import com.jme3.vulkan.shader.ShaderStage; +import com.jme3.vulkan.util.Flag; +import com.jme3.vulkan.util.IntEnum; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkDescriptorBufferInfo; +import org.lwjgl.vulkan.VkWriteDescriptorSet; + +import java.util.*; + +public class BufferUniform extends AbstractUniform { + + public BufferUniform(String name, IntEnum type, int bindingIndex, Flag stages) { + super(name, type, bindingIndex, stages); + } + + @Override + public DescriptorSetWriter createWriter() { + if (value == null) { + throw new NullPointerException("Cannot write null value."); + } + return new Writer(value); + } + + @Override + public boolean isBindingCompatible(SetLayoutBinding binding) { + return type.is(binding.getType()) + && bindingIndex == binding.getBinding() + && binding.getDescriptors() == 1; + } + + private class Writer implements DescriptorSetWriter { + + private final long id; + private final long bytes; + + private Writer(GpuBuffer buffer) { + this.id = buffer.getId(); + this.bytes = buffer.size().getBytes(); + } + + @Override + public void populateWrite(MemoryStack stack, VkWriteDescriptorSet write) { + VkDescriptorBufferInfo.Buffer info = VkDescriptorBufferInfo.calloc(1, stack) + .buffer(id) + .offset(0L) + .range(bytes); + write.pBufferInfo(info) + .descriptorCount(1) + .dstArrayElement(0) + .dstBinding(bindingIndex) + .descriptorType(type.getEnum()); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + Writer writer = (Writer) o; + return id == writer.id && bytes == writer.bytes; + } + + @Override + public int hashCode() { + return Objects.hash(id, bytes); + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/material/uniforms/TextureUniform.java b/jme3-core/src/main/java/com/jme3/vulkan/material/uniforms/TextureUniform.java new file mode 100644 index 0000000000..6715172d09 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/material/uniforms/TextureUniform.java @@ -0,0 +1,81 @@ +package com.jme3.vulkan.material.uniforms; + +import com.jme3.texture.Texture; +import com.jme3.vulkan.descriptors.Descriptor; +import com.jme3.vulkan.descriptors.DescriptorSetWriter; +import com.jme3.vulkan.descriptors.SetLayoutBinding; +import com.jme3.vulkan.images.VulkanImage; +import com.jme3.vulkan.shader.ShaderStage; +import com.jme3.vulkan.util.Flag; +import com.jme3.vulkan.util.IntEnum; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkDescriptorImageInfo; +import org.lwjgl.vulkan.VkWriteDescriptorSet; + +import java.util.Objects; + +public class TextureUniform extends AbstractUniform { + + private final IntEnum layout; + + public TextureUniform(String name, IntEnum layout, int bindingIndex, Flag stages) { + super(name, Descriptor.CombinedImageSampler, bindingIndex, stages); + this.layout = layout; + } + + @Override + public DescriptorSetWriter createWriter() { + if (value == null) { + throw new NullPointerException("Cannot write null value."); + } + return new Writer(value); + } + + @Override + public boolean isBindingCompatible(SetLayoutBinding binding) { + return type.is(binding.getType()) + && bindingIndex == binding.getBinding() + && binding.getDescriptors() == 1; + } + + public IntEnum getLayout() { + return layout; + } + + private class Writer implements DescriptorSetWriter { + + private final long samplerId, viewId; + + public Writer(Texture texture) { + this.samplerId = texture.getId(); + this.viewId = texture.getView().getId(); + } + + @Override + public void populateWrite(MemoryStack stack, VkWriteDescriptorSet write) { + VkDescriptorImageInfo.Buffer info = VkDescriptorImageInfo.calloc(1, stack) + .imageView(viewId) + .sampler(samplerId) + .imageLayout(layout.getEnum()); + write.pImageInfo(info) + .descriptorType(type.getEnum()) + .dstBinding(bindingIndex) + .dstArrayElement(0) + .descriptorCount(1); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + Writer writer = (Writer) o; + return samplerId == writer.samplerId && viewId == writer.viewId; + } + + @Override + public int hashCode() { + return Objects.hash(samplerId, viewId); + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/material/uniforms/Uniform.java b/jme3-core/src/main/java/com/jme3/vulkan/material/uniforms/Uniform.java new file mode 100644 index 0000000000..32d155f60b --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/material/uniforms/Uniform.java @@ -0,0 +1,64 @@ +package com.jme3.vulkan.material.uniforms; + +import com.jme3.vulkan.descriptors.DescriptorSetWriter; +import com.jme3.vulkan.descriptors.SetLayoutBinding; +import com.jme3.vulkan.frames.VersionedResource; + +public interface Uniform { + + /** + * Gets the name by which this uniform is identified. + */ + String getName(); + + /** + * Creates a {@link DescriptorSetWriter} that can be used to write + * a snapshot of the uniform's current value into the + * {@link com.jme3.vulkan.descriptors.DescriptorSet DescriptorSet}. + * + *

The returned writer is expected to implement + * {@link Object#equals(Object) equals} so that writers can be compared + * for DescriptorSet caching.

+ * + * @return writer + */ + DescriptorSetWriter createWriter(); + + /** + * Tests if the {@link SetLayoutBinding} is compatible with this uniform, + * indicating that this uniform may be bound to the binding which it represents. + * Bindings that previously tested as compatible should always be compatible with + * this uniform. + * + * @param binding binding to test + * @return true if the binding is compatible + */ + boolean isBindingCompatible(SetLayoutBinding binding); + + /** + * Creates a new {@link SetLayoutBinding} that is completely (and always will be) + * {@link #isBindingCompatible(SetLayoutBinding) compatible} with this uniform. + * + * @return new set layout binding that is compatible with this uniform + */ + SetLayoutBinding createBinding(); + + /** + * Sets the {@link VersionedResource} that will provide the uniform value. + */ + void set(T value); + + /** + * Returns the {@link VersionedResource} supplying the uniform value. + */ + T get(); + + /** + * The binding this uniform is targeting. Should be unique among all + * uniforms within a single {@link com.jme3.vulkan.material.UniformSet UniformSet}. + * + * @return the index of the target binding + */ + int getBindingIndex(); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/memory/MemoryProp.java b/jme3-core/src/main/java/com/jme3/vulkan/memory/MemoryProp.java new file mode 100644 index 0000000000..cd7a9a3d8d --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/memory/MemoryProp.java @@ -0,0 +1,26 @@ +package com.jme3.vulkan.memory; + +import com.jme3.vulkan.util.Flag; + +import static org.lwjgl.vulkan.VK10.*; + +public enum MemoryProp implements Flag { + + HostVisible(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT), + HostCoherent(VK_MEMORY_PROPERTY_HOST_COHERENT_BIT), + HostCached(VK_MEMORY_PROPERTY_HOST_CACHED_BIT), + DeviceLocal(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT), + LazilyAllocated(VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT); + + private final int vkEnum; + + MemoryProp(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int bits() { + return vkEnum; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/memory/MemoryRegion.java b/jme3-core/src/main/java/com/jme3/vulkan/memory/MemoryRegion.java new file mode 100644 index 0000000000..990b23f087 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/memory/MemoryRegion.java @@ -0,0 +1,131 @@ +package com.jme3.vulkan.memory; + +import com.jme3.util.AbstractBuilder; +import com.jme3.util.natives.AbstractNative; +import com.jme3.util.natives.Native; +import com.jme3.vulkan.buffers.GpuBuffer; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.images.GpuImage; +import com.jme3.vulkan.util.Flag; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.vulkan.VkMemoryAllocateInfo; + +import java.nio.*; +import java.util.concurrent.atomic.AtomicBoolean; + +import static com.jme3.renderer.vulkan.VulkanUtils.*; +import static org.lwjgl.vulkan.VK10.*; + +public class MemoryRegion extends AbstractNative { + + private final LogicalDevice device; + private final long size; + private final AtomicBoolean mapped = new AtomicBoolean(false); + private final PointerBuffer mapping = MemoryUtil.memCallocPointer(1); + private Flag flags = MemoryProp.DeviceLocal; + private int type; + + public MemoryRegion(LogicalDevice device, long size) { + this.device = device; + this.size = size; + } + + @Override + public Runnable createNativeDestroyer() { + return () -> { + vkFreeMemory(device.getNativeObject(), object, null); + MemoryUtil.memFree(mapping); + }; + } + + public void bind(GpuBuffer buffer, long offset) { + check(vkBindBufferMemory(device.getNativeObject(), buffer.getId(), object, offset), + "Failed to bind buffer memory."); + } + + public void bind(GpuImage image, long offset) { + check(vkBindImageMemory(device.getNativeObject(), image.getId(), object, offset), + "Failed to bind image memory."); + } + + public PointerBuffer map() { + return map(0L, VK_WHOLE_SIZE); + } + + public PointerBuffer map(long offset) { + return map(offset, VK_WHOLE_SIZE); + } + + public PointerBuffer map(long offset, long size) { + if (!flags.contains(MemoryProp.HostVisible)) { + throw new IllegalStateException("Cannot map memory that is not host visible."); + } + if (mapped.getAndSet(true)) { + throw new IllegalStateException("Memory already mapped."); + } + vkMapMemory(device.getNativeObject(), object, offset, size, 0, mapping); + return mapping; + } + + public void unmap() { + if (!mapped.getAndSet(false)) { + throw new IllegalStateException("Memory is not mapped."); + } + mapping.put(0, VK_NULL_HANDLE); + vkUnmapMemory(device.getNativeObject(), object); + } + + public long getSize() { + return size; + } + + public Flag getFlags() { + return flags; + } + + public int getType() { + return type; + } + + public Builder build() { + return new Builder(); + } + + public class Builder extends AbstractBuilder { + + private int usableMemoryTypes = 0; + + @Override + protected void build() { + if (usableMemoryTypes == 0) { + throw new IllegalStateException("No usable memory types specified."); + } + type = device.getPhysicalDevice().findSupportedMemoryType(stack, usableMemoryTypes, flags); + VkMemoryAllocateInfo allocate = VkMemoryAllocateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO) + .allocationSize(size) + .memoryTypeIndex(type); + LongBuffer idBuf = stack.mallocLong(1); + check(vkAllocateMemory(device.getNativeObject(), allocate, null, idBuf), + "Failed to allocate buffer memory."); + object = idBuf.get(0); + ref = Native.get().register(MemoryRegion.this); + device.getNativeReference().addDependent(ref); + } + + public void setFlags(Flag flags) { + MemoryRegion.this.flags = flags; + } + + public void setUsableMemoryTypes(int usableMemoryTypes) { + this.usableMemoryTypes = usableMemoryTypes; + } + + public int getUsableMemoryTypes() { + return usableMemoryTypes; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/memory/MemorySize.java b/jme3-core/src/main/java/com/jme3/vulkan/memory/MemorySize.java new file mode 100644 index 0000000000..5418fa4e31 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/memory/MemorySize.java @@ -0,0 +1,125 @@ +package com.jme3.vulkan.memory; + +import com.jme3.util.BufferUtils; +import com.jme3.vulkan.buffers.NioBuffer; + +import java.nio.Buffer; +import java.util.Objects; + +public class MemorySize { + + public static final MemorySize ZERO = new MemorySize(0, 1); + + private final int elements; + private final int bytesPerElement; + private final int bytes; + + public MemorySize(int elements, int bytesPerElement) { + this.elements = elements; + this.bytesPerElement = bytesPerElement; + this.bytes = this.elements * this.bytesPerElement; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + MemorySize that = (MemorySize) o; + return elements == that.elements && bytesPerElement == that.bytesPerElement; + } + + @Override + public int hashCode() { + return Objects.hash(elements, bytesPerElement); + } + + public int getElements() { + return elements; + } + + public int getBytesPerElement() { + return bytesPerElement; + } + + public int getBytes() { + return bytes; + } + + public int getBytes(int padding) { + return bytes + padding * bytesPerElement; + } + + public int getShorts() { + return bytes / Short.BYTES; + } + + public int getShorts(int padding) { + return getBytes(padding) / Short.BYTES; + } + + public int getInts() { + return bytes / Integer.BYTES; + } + + public int getInts(int padding) { + return getBytes(padding) / Integer.BYTES; + } + + public int getFloats() { + return bytes / Float.BYTES; + } + + public int getFloats(int padding) { + return getBytes(padding) / Float.BYTES; + } + + public int getDoubles() { + return bytes / Double.BYTES; + } + + public int getDoubles(int padding) { + return getBytes(padding) / Double.BYTES; + } + + public int getLongs() { + return bytes / Long.BYTES; + } + + public int getLongs(int padding) { + return getBytes(padding) / Long.BYTES; + } + + public static MemorySize bytes(int elements) { + return new MemorySize(elements, 1); + } + + public static MemorySize shorts(int elements) { + return new MemorySize(elements, Short.BYTES); + } + + public static MemorySize ints(int elements) { + return new MemorySize(elements, Integer.BYTES); + } + + public static MemorySize floats(int elements) { + return new MemorySize(elements, Float.BYTES); + } + + public static MemorySize doubles(int elements) { + return new MemorySize(elements, Double.BYTES); + } + + public static MemorySize longs(int elements) { + return new MemorySize(elements, Long.BYTES); + } + + /** + * + * @param bytes total number of bytes + * @param bytesPerElement number of bytes per element + * @return memory size reflecting {@code bytes} and {@code bytesPerElement} + */ + public static MemorySize dynamic(int bytes, int bytesPerElement) { + return new MemorySize(bytes / bytesPerElement, bytesPerElement); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/AdaptiveMesh.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/AdaptiveMesh.java new file mode 100644 index 0000000000..e42c76b109 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/AdaptiveMesh.java @@ -0,0 +1,161 @@ +package com.jme3.vulkan.mesh; + +import com.jme3.bounding.BoundingBox; +import com.jme3.bounding.BoundingVolume; +import com.jme3.collision.bih.BIHTree; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.vulkan.buffers.AsyncBufferHandler; +import com.jme3.vulkan.buffers.GpuBuffer; +import com.jme3.vulkan.commands.CommandBuffer; +import com.jme3.vulkan.mesh.attribute.Attribute; +import com.jme3.vulkan.mesh.attribute.Position; +import com.jme3.vulkan.util.IntEnum; +import org.lwjgl.system.MemoryStack; + +import java.nio.LongBuffer; +import java.util.*; + +import static org.lwjgl.vulkan.VK10.*; + +public class AdaptiveMesh { + + private final MeshLayout layout; + private final Queue lods = new PriorityQueue<>(); + private final List vertexBuffers = new ArrayList<>(); + private int vertices, instances; + private BoundingVolume volume = new BoundingBox(); + private BIHTree collisionTree; + + public AdaptiveMesh(MeshLayout layout, int vertices, int instances) { + this.layout = layout; + this.vertices = vertices; + this.instances = instances; + } + + public void render(CommandBuffer cmd, float distance) { + try (MemoryStack stack = MemoryStack.stackPush()) { + LongBuffer verts = stack.mallocLong(vertexBuffers.size()); + LongBuffer offsets = stack.mallocLong(vertexBuffers.size()); + for (VertexBinding b : layout) { + VertexBuffer vb = getVertexBuffer(b); + if (vb == null) { + vb = createVertexBuffer(b, b.createBuffer(getElementsOf(b.getInputRate()))); + } + verts.put(vb.getData().getBuffer().getId()); + offsets.put(vb.getBinding().getOffset()); + } + verts.flip(); + offsets.flip(); + vkCmdBindVertexBuffers(cmd.getBuffer(), 0, verts, offsets); + } + if (!lods.isEmpty()) { + LodBuffer lod = selectLod(distance); + vkCmdBindIndexBuffer(cmd.getBuffer(), lod.getId(), 0, IndexType.of(lod).getEnum()); + vkCmdDrawIndexed(cmd.getBuffer(), lod.size().getElements(), instances, 0, 0, 0); + } else { + vkCmdDraw(cmd.getBuffer(), vertices, instances, 0, 0); + } + } + + public T mapAttribute(String name) { + return layout.mapAttribute(this, name); + } + + public T mapAttribute(GlVertexBuffer.Type name) { + return layout.mapAttribute(this, name); + } + + public void addLod(LodBuffer lod) { + lods.add(lod); + } + + public VertexBuffer createVertexBuffer(VertexBinding binding, GpuBuffer buffer) { + VertexBuffer vb = new VertexBuffer(binding, buffer); + vertexBuffers.add(vb); + return vb; + } + + public VertexBuffer getBuffer(int binding) { + return vertexBuffers.get(binding); + } + + public VertexBuffer getVertexBuffer(VertexBinding binding) { + return vertexBuffers.stream().filter(vb -> vb.getBinding() == binding).findAny().orElse(null); + } + + public void computeBounds(BoundingVolume bounds) { + Position pos = mapAttribute(GlVertexBuffer.Type.Position); + volume.computeFromPoints(pos); + pos.unmap(); + } + + public void setVertices(int vertices) { + if (this.vertices != vertices) { + this.vertices = vertices; + for (VertexBuffer vb : vertexBuffers) { + if (vb.getBinding().getInputRate().is(InputRate.Vertex)) { + vb.buffer.getBuffer().resize(vertices); + } + } + } + } + + public void setInstances(int instances) { + if (this.instances != instances) { + this.instances = instances; + for (VertexBuffer vb : vertexBuffers) { + if (vb.getBinding().getInputRate().is(InputRate.Instance)) { + vb.buffer.getBuffer().resize(instances); + } + } + } + } + + public MeshLayout getLayout() { + return layout; + } + + public int getVertices() { + return vertices; + } + + public int getInstances() { + return instances; + } + + protected LodBuffer selectLod(float distance) { + LodBuffer usable = lods.peek(); + for (LodBuffer l : lods) { + if (distance < l.getOptimalDistance()) { + break; + } + usable = l; + } + return usable; + } + + public int getElementsOf(IntEnum rate) { + return rate.is(InputRate.Instance) ? instances : vertices; + } + + public static class VertexBuffer { + + private final VertexBinding binding; + private final AsyncBufferHandler buffer = new AsyncBufferHandler<>(); + + private VertexBuffer(VertexBinding binding, GpuBuffer buffer) { + this.binding = binding; + this.buffer.setBuffer(buffer); + } + + public VertexBinding getBinding() { + return binding; + } + + public AsyncBufferHandler getData() { + return buffer; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/AttributeMapping.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/AttributeMapping.java new file mode 100644 index 0000000000..6ee3a9b2c8 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/AttributeMapping.java @@ -0,0 +1,10 @@ +package com.jme3.vulkan.mesh; + +import com.jme3.vulkan.buffers.Mappable; +import com.jme3.vulkan.mesh.attribute.Attribute; + +public interface AttributeMapping { + + T map(Mappable vertices, int size, int stride, int offset); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/AttributeModifier.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/AttributeModifier.java new file mode 100644 index 0000000000..991673fd12 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/AttributeModifier.java @@ -0,0 +1,10 @@ +package com.jme3.vulkan.mesh; + +public interface AttributeModifier extends AutoCloseable, VertexReader, VertexWriter { + + @Override + void close(); + + void setUpdateNeeded(); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/DataAccess.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/DataAccess.java new file mode 100644 index 0000000000..81b3fdb2ca --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/DataAccess.java @@ -0,0 +1,7 @@ +package com.jme3.vulkan.mesh; + +public enum DataAccess { + + Stream, Dynamic, Static; + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/IndexType.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/IndexType.java new file mode 100644 index 0000000000..e7fa3b213c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/IndexType.java @@ -0,0 +1,38 @@ +package com.jme3.vulkan.mesh; + +import com.jme3.vulkan.buffers.GpuBuffer; +import com.jme3.vulkan.util.IntEnum; + +import static org.lwjgl.vulkan.VK10.*; + +public enum IndexType implements IntEnum { + + UInt32(VK_INDEX_TYPE_UINT32, 2), + UInt16(VK_INDEX_TYPE_UINT16, 4); + + private final int vkEnum; + private final int bytes; + + IndexType(int vkEnum, int bytes) { + this.vkEnum = vkEnum; + this.bytes = bytes; + } + + @Override + public int getEnum() { + return vkEnum; + } + + public int getBytes() { + return bytes; + } + + public static IndexType of(GpuBuffer buffer) { + if (buffer.size().getBytesPerElement() <= UInt16.bytes) { + return UInt16; + } else { + return UInt32; + } + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/InputRate.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/InputRate.java new file mode 100644 index 0000000000..6fbda3315b --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/InputRate.java @@ -0,0 +1,23 @@ +package com.jme3.vulkan.mesh; + +import com.jme3.vulkan.util.IntEnum; + +import static org.lwjgl.vulkan.VK10.*; + +public enum InputRate implements IntEnum { + + Vertex(VK_VERTEX_INPUT_RATE_VERTEX), + Instance(VK_VERTEX_INPUT_RATE_INSTANCE); + + private final int vkEnum; + + InputRate(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/LodBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/LodBuffer.java new file mode 100644 index 0000000000..2763a45b85 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/LodBuffer.java @@ -0,0 +1,53 @@ +package com.jme3.vulkan.mesh; + +import com.jme3.dev.NotFullyImplemented; +import com.jme3.vulkan.buffers.GpuBuffer; +import com.jme3.vulkan.memory.MemorySize; +import org.lwjgl.PointerBuffer; + +@NotFullyImplemented +public class LodBuffer implements GpuBuffer, Comparable { + + private final GpuBuffer buffer; + private final float optimalDistance; + + public LodBuffer(GpuBuffer buffer, float optimalDistance) { + this.buffer = buffer; + this.optimalDistance = optimalDistance; + } + + @Override + public PointerBuffer map(int offset, int size) { + return buffer.map(offset, size); + } + + @Override + public long getId() { + return buffer.getId(); + } + + @Override + public boolean resize(MemorySize size) { + return buffer.resize(size); + } + + @Override + public MemorySize size() { + return buffer.size(); + } + + @Override + public void unmap() { + buffer.unmap(); + } + + @Override + public int compareTo(LodBuffer o) { + return Float.compare(optimalDistance, o.optimalDistance); + } + + public float getOptimalDistance() { + return optimalDistance; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/MeshDescription.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/MeshDescription.java new file mode 100644 index 0000000000..71dc4138c8 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/MeshDescription.java @@ -0,0 +1,110 @@ +package com.jme3.vulkan.mesh; + +import com.jme3.vulkan.Format; +import com.jme3.vulkan.util.IntEnum; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkVertexInputAttributeDescription; +import org.lwjgl.vulkan.VkVertexInputBindingDescription; + +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Describes the layout of vertex bindings and attributes, and simultaneously + * acts as a compatability layer between a mesh and a mesh control. + */ +public class MeshDescription implements Iterable { + + private final List bindings = new ArrayList<>(); + private final AtomicBoolean built = new AtomicBoolean(false); + + @Override + public Iterator iterator() { + return bindings.iterator(); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + MeshDescription that = (MeshDescription) o; + return Objects.equals(bindings, that.bindings); + } + + @Override + public int hashCode() { + return Objects.hashCode(bindings); + } + + public VkVertexInputBindingDescription.Buffer getBindingInfo(MemoryStack stack) { + VkVertexInputBindingDescription.Buffer binds = VkVertexInputBindingDescription.calloc(bindings.size(), stack); + for (OldVertexBinding b : bindings) { + binds.get().binding(b.getBindingIndex()) + .stride(b.getStride()) + .inputRate(b.getRate().getEnum()); + } + binds.flip(); + return binds; + } + + public VkVertexInputAttributeDescription.Buffer getAttributeInfo(MemoryStack stack) { + int size = 0; + for (OldVertexBinding b : bindings) { + size += b.getAttributes().size(); + } + VkVertexInputAttributeDescription.Buffer attr = VkVertexInputAttributeDescription.calloc(size, stack); + for (OldVertexBinding binding : bindings) { + for (VertexAttribute a : binding) { + attr.get().binding(binding.getBindingIndex()) + .location(a.getLocation()) + .format(a.getFormat().getVkEnum()) + .offset(a.getOffset()); + } + } + attr.flip(); + return attr; + } + + public OldVertexBinding getBinding(int index) { + return bindings.get(index); + } + + public VertexAttribute getAttribute(String name) { + for (OldVertexBinding b : bindings) { + VertexAttribute a = b.getAttribute(name); + if (a != null) return a; + } + return null; + } + + public List getBindings() { + return Collections.unmodifiableList(bindings); + } + + public Builder build() { + if (built.getAndSet(true)) { + throw new IllegalStateException("Cannot be modified after initial setup."); + } + return new Builder(); + } + + public class Builder implements AutoCloseable { + + @Override + public void close() {} + + public OldVertexBinding addBinding(IntEnum rate) { + OldVertexBinding binding = new OldVertexBinding(bindings.size(), rate); + bindings.add(binding); + return binding; + } + + public VertexAttribute addAttribute(OldVertexBinding binding, String name, Format format, int location) { + if (binding.getBindingIndex() >= bindings.size() || bindings.get(binding.getBindingIndex()) != binding) { + throw new IllegalArgumentException("Vertex binding does not belong to this mesh description."); + } + return binding.addAttribute(name, format, location); + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/MeshLayout.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/MeshLayout.java new file mode 100644 index 0000000000..6015acdb3e --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/MeshLayout.java @@ -0,0 +1,91 @@ +package com.jme3.vulkan.mesh; + +import com.jme3.scene.GlVertexBuffer; +import com.jme3.vulkan.mesh.attribute.Attribute; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkVertexInputAttributeDescription; +import org.lwjgl.vulkan.VkVertexInputBindingDescription; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +public class MeshLayout implements Iterable { + + private final List bindings = new ArrayList<>(); + + public T mapAttribute(AdaptiveMesh mesh, String name) { + for (VertexBinding b : bindings) { + AdaptiveMesh.VertexBuffer vb = mesh.getVertexBuffer(b); + int elements = mesh.getElementsOf(b.getInputRate()); + if (vb == null) { + vb = mesh.createVertexBuffer(b, b.createBuffer(elements)); + } + T attr = b.mapAttribute(name, vb.getData(), elements); + if (attr != null) return attr; + } + throw new IllegalArgumentException("\"" + name + "\" is not an existing attribute."); + } + + public T mapAttribute(AdaptiveMesh mesh, GlVertexBuffer.Type name) { + return mapAttribute(mesh, name.name()); + } + + public void addBinding(VertexBinding binding) { + bindings.add(binding); + } + + public VkVertexInputBindingDescription.Buffer getBindingInfo(MemoryStack stack) { + VkVertexInputBindingDescription.Buffer binds = VkVertexInputBindingDescription.calloc(bindings.size(), stack); + int i = 0; + for (VertexBinding vb : bindings) { + binds.get().binding(i++) + .stride(vb.getStride()) + .inputRate(vb.getInputRate().getEnum()); + } + binds.flip(); + return binds; + } + + public VkVertexInputAttributeDescription.Buffer getAttributeInfo(MemoryStack stack) { + int size = 0; + for (VertexBinding vb : bindings) { + size += vb.getNumAttributes(); + } + VkVertexInputAttributeDescription.Buffer attr = VkVertexInputAttributeDescription.calloc(size, stack); + int binding = 0; + for (VertexBinding vb : bindings) { + int location = 0; + int offset = 0; + for (VertexBinding.NamedAttribute a : vb) { + attr.get().binding(binding) + .location(location++) + .format(a.getFormat().getVkEnum()) + .offset(offset); + offset += a.getFormat().getTotalBytes(); + } + binding++; + } + attr.flip(); + return attr; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + MeshLayout that = (MeshLayout) o; + return Objects.equals(bindings, that.bindings); + } + + @Override + public int hashCode() { + return Objects.hashCode(bindings); + } + + @Override + public Iterator iterator() { + return bindings.iterator(); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/MeshTriangle.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/MeshTriangle.java new file mode 100644 index 0000000000..613ee6f94a --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/MeshTriangle.java @@ -0,0 +1,157 @@ +package com.jme3.vulkan.mesh; + +import org.lwjgl.BufferUtils; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.Struct; +import org.lwjgl.system.StructBuffer; +import org.lwjgl.vulkan.VkViewport; + +import java.nio.ByteBuffer; + +import static org.lwjgl.system.MemoryUtil.*; + +public class MeshTriangle extends Struct { + + private static final int SIZEOF, ALIGNOF; + private static final int[] INDICES = new int[3]; + + static { + Layout layout = __struct( + __member(Integer.BYTES), + __member(Integer.BYTES), + __member(Integer.BYTES) + ); + SIZEOF = layout.getSize(); + ALIGNOF = layout.getAlignment(); + INDICES[0] = layout.offsetof(0); + INDICES[1] = layout.offsetof(1); + INDICES[2] = layout.offsetof(2); + } + + protected MeshTriangle(long address, ByteBuffer container) { + super(address, container); + } + + @Override + protected MeshTriangle create(long address, ByteBuffer container) { + return new MeshTriangle(address, container); + } + + @Override + public int sizeof() { + return SIZEOF; + } + + public MeshTriangle index(int i, int index) { + nindex(address, i, index); + return this; + } + + public int index(int i) { + return nindex(address, i); + } + + public static void nindex(long address, int i, int index) { + memPutInt(address + INDICES[i], index); + } + + public static int nindex(long address, int i) { + return memGetInt(address + INDICES[i]); + } + + public static MeshTriangle malloc() { + return new MeshTriangle(nmemAllocChecked(SIZEOF), null); + } + + public static MeshTriangle calloc() { + return new MeshTriangle(nmemCallocChecked(1, SIZEOF), null); + } + + public static MeshTriangle create() { + ByteBuffer container = BufferUtils.createByteBuffer(SIZEOF); + return new MeshTriangle(memAddress(container), container); + } + + public static MeshTriangle create(long address) { + return new MeshTriangle(address, null); + } + + public static MeshTriangle createSafe(long address) { + return address == NULL ? null : create(address); + } + + public static Buffer malloc(int capacity) { + return new Buffer(nmemAllocChecked(__checkMalloc(capacity, SIZEOF)), capacity); + } + + public static Buffer calloc(int capacity) { + return new Buffer(nmemCallocChecked(capacity, SIZEOF), capacity); + } + + public static Buffer create(int capacity) { + ByteBuffer container = __create(capacity, SIZEOF); + return new Buffer(memAddress(container), container, -1, 0, capacity, capacity); + } + + public static Buffer create(long address, int capacity) { + return new Buffer(address, capacity); + } + + public static Buffer createSafe(long address, int capacity) { + return address == NULL ? null : new Buffer(address, capacity); + } + + public static MeshTriangle malloc(MemoryStack stack) { + return new MeshTriangle(stack.nmalloc(ALIGNOF, SIZEOF), null); + } + + public static MeshTriangle calloc(MemoryStack stack) { + return new MeshTriangle(stack.ncalloc(ALIGNOF, 1, SIZEOF), null); + } + + public static Buffer malloc(int capacity, MemoryStack stack) { + return new Buffer(stack.nmalloc(ALIGNOF, capacity * SIZEOF), capacity); + } + + public static Buffer calloc(int capacity, MemoryStack stack) { + return new Buffer(stack.ncalloc(ALIGNOF, capacity, SIZEOF), capacity); + } + + public static class Buffer extends StructBuffer { + + private static final MeshTriangle ELEMENT_FACTORY = MeshTriangle.create(-1L); + + public Buffer(ByteBuffer container) { + super(container, container.remaining() / SIZEOF); + } + + public Buffer(long address, int cap) { + super(address, null, -1, 0, cap, cap); + } + + protected Buffer(ByteBuffer container, int remaining) { + super(container, remaining); + } + + protected Buffer(long address, ByteBuffer container, int mark, int position, int limit, int capacity) { + super(address, container, mark, position, limit, capacity); + } + + @Override + protected MeshTriangle getElementFactory() { + return null; + } + + @Override + protected Buffer self() { + return null; + } + + @Override + protected Buffer create(long address, @org.jspecify.annotations.Nullable ByteBuffer container, int mark, int position, int limit, int capacity) { + return null; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/MyCustomMesh.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/MyCustomMesh.java new file mode 100644 index 0000000000..def608274e --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/MyCustomMesh.java @@ -0,0 +1,47 @@ +package com.jme3.vulkan.mesh; + +import com.jme3.math.Vector3f; +import com.jme3.scene.GlVertexBuffer.Type; +import com.jme3.vulkan.buffers.*; +import com.jme3.vulkan.buffers.GpuBuffer; +import com.jme3.vulkan.buffers.generate.BufferGenerator; +import com.jme3.vulkan.memory.MemorySize; + +import java.nio.ShortBuffer; + +public class MyCustomMesh extends AdaptiveMesh { + + private static final short[] INDICES = {0, 2, 3, 0, 1, 2}; + + public MyCustomMesh(MeshDescription description, + BufferGenerator generator, + Vector3f normal, Vector3f up, float width, float height, float centerX, float centerY) { + super(description, generator); + GpuBuffer indices = generator.createBuffer(MemorySize.shorts(6), BufferUsage.Index, DataAccess.Static); + indexBuffers.put(0, indices); + ShortBuffer iBuf = indices.mapShorts(); + iBuf.put(INDICES); + indices.unmap(); + Vector3f x = normal.cross(up); + Vector3f y = normal.cross(x); + Vector3f tempX = new Vector3f(); + Vector3f tempY = new Vector3f(); + try (AttributeModifier position = modify(Type.Position.name()); + AttributeModifier normals = modify(Type.Normal.name()); + AttributeModifier texCoord = modify(Type.TexCoord.name())) { + position.putVector3(0, 0, x.mult(-width * centerX, tempX).addLocal(y.mult(height * (1f - centerY), tempY))); + position.putVector3(1, 0, x.mult(width * (1.0f - centerX), tempX).addLocal(y.mult(height * (1f - centerY), tempY))); + position.putVector3(2, 0, x.mult(width * (1.0f - centerX), tempX).addLocal(y.mult(-height * centerY, tempY))); + position.putVector3(3, 0, x.mult(-width * centerX, tempX).addLocal(y.mult(-height * centerY, tempY))); + normals.putVector3(0, 0, normal); + normals.putVector3(1, 0, normal); + normals.putVector3(2, 0, normal); + normals.putVector3(3, 0, normal); + texCoord.putVector2(0, 0, 0f, 0f); + texCoord.putVector2(1, 0, 1f, 0f); + texCoord.putVector2(2, 0, 1f, 1f); + texCoord.putVector2(3, 0, 0f, 1f); + } + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/NullAttributeModifier.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/NullAttributeModifier.java new file mode 100644 index 0000000000..525f5ae1bb --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/NullAttributeModifier.java @@ -0,0 +1,232 @@ +package com.jme3.vulkan.mesh; + +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector2f; +import com.jme3.math.Vector3f; +import com.jme3.math.Vector4f; + +import java.nio.*; + +public class NullAttributeModifier implements AttributeModifier { + + public NullAttributeModifier() {} + + @Override + public void close() {} + + @Override + public void setUpdateNeeded() {} + + @Override + public int capacity() { + return 0; + } + + @Override + public int limit() { + return 0; + } + + @Override + public int components() { + return 0; + } + + @Override + public AttributeModifier limit(int vertex) { + return this; + } + + @Override + public AttributeModifier putByte(int vertex, int component, byte value) { + return this; + } + + @Override + public AttributeModifier putShort(int vertex, int component, short value) { + return this; + } + + @Override + public AttributeModifier putInt(int vertex, int component, int value) { + return this; + } + + @Override + public AttributeModifier putFloat(int vertex, int component, float value) { + return this; + } + + @Override + public AttributeModifier putDouble(int vertex, int component, double value) { + return this; + } + + @Override + public AttributeModifier putLong(int vertex, int component, long value) { + return this; + } + + @Override + public AttributeModifier putNumber(int vertex, int component, Number value) { + return this; + } + + @Override + public AttributeModifier putVector2(int vertex, int baseComponent, Vector2f value) { + return this; + } + + @Override + public AttributeModifier putVector2(int vertex, int baseComponent, float x, float y) { + return this; + } + + @Override + public AttributeModifier putVector3(int vertex, int baseComponent, Vector3f value) { + return this; + } + + @Override + public AttributeModifier putVector3(int vertex, int baseComponent, float x, float y, float z) { + return this; + } + + @Override + public AttributeModifier putVector4(int vertex, int baseComponent, Vector4f value) { + return putVector4(vertex, baseComponent, value.x, value.y, value.z, value.w); + } + + @Override + public AttributeModifier putVector4(int vertex, int baseComponent, float x, float y, float z, float w) { + return this; + } + + @Override + public AttributeModifier putColor(int vertex, int baseComponent, ColorRGBA value) { + return this; + } + + @Override + public AttributeModifier putBytes(int baseVertex, int baseComponent, byte[] values) { + return this; + } + + @Override + public AttributeModifier putBytes(int baseVertex, int baseComponent, ByteBuffer values) { + return this; + } + + @Override + public AttributeModifier putShorts(int baseVertex, int baseComponent, short[] values) { + return this; + } + + @Override + public AttributeModifier putShorts(int baseVertex, int baseComponent, ShortBuffer values) { + return this; + } + + @Override + public AttributeModifier putInts(int baseVertex, int baseComponent, int[] values) { + return this; + } + + @Override + public AttributeModifier putInts(int baseVertex, int baseComponent, IntBuffer values) { + return this; + } + + @Override + public AttributeModifier putFloats(int baseVertex, int baseComponent, float[] values) { + return this; + } + + @Override + public AttributeModifier putFloats(int baseVertex, int baseComponent, FloatBuffer values) { + return this; + } + + @Override + public AttributeModifier putDoubles(int baseVertex, int baseComponent, double[] values) { + return this; + } + + @Override + public AttributeModifier putDoubles(int baseVertex, int baseComponent, DoubleBuffer values) { + return this; + } + + @Override + public AttributeModifier putLongs(int baseVertex, int baseComponent, long[] values) { + return this; + } + + @Override + public AttributeModifier putLongs(int baseVertex, int baseComponent, LongBuffer values) { + return this; + } + + @Override + public byte getByte(int vertex, int component) { + return 0; + } + + @Override + public short getShort(int vertex, int component) { + return 0; + } + + @Override + public int getInt(int vertex, int component) { + return 0; + } + + @Override + public float getFloat(int vertex, int component) { + return 0f; + } + + @Override + public double getDouble(int vertex, int component) { + return 0.0; + } + + @Override + public long getLong(int vertex, int component) { + return 0L; + } + + @Override + public Vector2f getVector2(int vertex, int baseComponent, Vector2f store) { + if (store == null) { + store = new Vector2f(); + } + return store.set(0f, 0f); + } + + @Override + public Vector3f getVector3(int vertex, int baseComponent, Vector3f store) { + if (store == null) { + store = new Vector3f(); + } + return store.set(0f, 0f, 0f); + } + + @Override + public Vector4f getVector4(int vertex, int baseComponent, Vector4f store) { + if (store == null) { + store = new Vector4f(); + } + return store.set(0f, 0f, 0f, 0f); + } + + @Override + public ColorRGBA getColor(int vertex, int baseComponent, ColorRGBA store) { + if (store == null) { + store = new ColorRGBA(); + } + return store.set(0f, 0f, 0f, 0f); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/OldVertexBinding.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/OldVertexBinding.java new file mode 100644 index 0000000000..f1235d3bf0 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/OldVertexBinding.java @@ -0,0 +1,67 @@ +package com.jme3.vulkan.mesh; + +import com.jme3.vulkan.Format; +import com.jme3.vulkan.util.IntEnum; + +import java.util.*; + +public class OldVertexBinding implements Iterable { + + private final int binding; + private final IntEnum rate; + private final Map attributes = new HashMap<>(); + private int stride = 0; + + public OldVertexBinding(int binding, IntEnum rate) { + this.binding = binding; + this.rate = Objects.requireNonNull(rate); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + OldVertexBinding that = (OldVertexBinding) o; + return binding == that.binding + && stride == that.stride + && rate.is(that.rate) + && Objects.equals(attributes, that.attributes); + } + + @Override + public int hashCode() { + return Objects.hash(binding, rate, attributes, stride); + } + + protected VertexAttribute addAttribute(String name, Format format, int location) { + VertexAttribute attr = new VertexAttribute(this, name, format, location, stride); + attributes.put(name, attr); + stride += format.getTotalBytes(); + return attr; + } + + public int getBindingIndex() { + return binding; + } + + public int getStride() { + return stride; + } + + public IntEnum getRate() { + return rate; + } + + public VertexAttribute getAttribute(String name) { + return attributes.get(name); + } + + public Map getAttributes() { + return Collections.unmodifiableMap(attributes); + } + + @Override + public Iterator iterator() { + return attributes.values().iterator(); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/VertexAttribute.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/VertexAttribute.java new file mode 100644 index 0000000000..b21167b1a0 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/VertexAttribute.java @@ -0,0 +1,65 @@ +package com.jme3.vulkan.mesh; + +import com.jme3.vulkan.Format; + +import java.util.Objects; + +public class VertexAttribute { + + // todo: remove + public static final String POSITION = "jme_position"; + public static final String NORMALS = "jme_normals"; + public static final String TEXCOORD = "jme_texCoord"; + public static final String COLOR = "jme_color"; + + private final OldVertexBinding binding; + private final String name; + private final Format format; + private final int location; + private final int offset; + + public VertexAttribute(OldVertexBinding binding, String name, Format format, int location, int offset) { + this.binding = Objects.requireNonNull(binding); + this.name = Objects.requireNonNull(name); + this.format = Objects.requireNonNull(format); + this.location = location; + this.offset = offset; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + VertexAttribute that = (VertexAttribute) o; + return location == that.location + && offset == that.offset + && binding.getBindingIndex() == that.binding.getBindingIndex() + && Objects.equals(name, that.name) + && format == that.format; + } + + @Override + public int hashCode() { + return Objects.hash(binding, name, format, location, offset); + } + + public String getName() { + return name; + } + + public OldVertexBinding getBinding() { + return binding; + } + + public Format getFormat() { + return format; + } + + public int getLocation() { + return location; + } + + public int getOffset() { + return offset; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/VertexBinding.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/VertexBinding.java new file mode 100644 index 0000000000..2e7ee31cc0 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/VertexBinding.java @@ -0,0 +1,84 @@ +package com.jme3.vulkan.mesh; + +import com.jme3.scene.GlVertexBuffer; +import com.jme3.vulkan.Format; +import com.jme3.vulkan.buffers.GpuBuffer; +import com.jme3.vulkan.buffers.Mappable; +import com.jme3.vulkan.mesh.attribute.Attribute; +import com.jme3.vulkan.util.IntEnum; + +import java.util.Objects; + +public interface VertexBinding extends Iterable { + + T mapAttribute(String name, Mappable vertices, int size); + + GpuBuffer createBuffer(int elements); + + void setOffset(long offset); + + long getOffset(); + + int getStride(); + + IntEnum getInputRate(); + + int getNumAttributes(); + + class NamedAttribute { + + private final String name; + private final Format format; + private final AttributeMapping mapping; + private final int offset; + + public NamedAttribute(String name, Format format, AttributeMapping mapping, int offset) { + this.name = name; + this.format = format; + this.mapping = mapping; + this.offset = offset; + } + + public String getName() { + return name; + } + + public AttributeMapping getMapping() { + return mapping; + } + + public Format getFormat() { + return format; + } + + public int getOffset() { + return offset; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + NamedAttribute that = (NamedAttribute) o; + return offset == that.offset && format == that.format; + } + + @Override + public int hashCode() { + return Objects.hash(format, offset); + } + + } + + interface Builder { + + Builder add(String name, Format format, AttributeMapping mapping); + + VertexBinding build(); + + default Builder add(GlVertexBuffer.Type name, Format format, AttributeMapping mapping) { + return add(name.name(), format, mapping); + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/VertexReader.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/VertexReader.java new file mode 100644 index 0000000000..98bb513efd --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/VertexReader.java @@ -0,0 +1,203 @@ +package com.jme3.vulkan.mesh; + +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector2f; +import com.jme3.math.Vector3f; +import com.jme3.math.Vector4f; + +import java.nio.*; + +public interface VertexReader { + + int capacity(); + + int limit(); + + int components(); + + byte getByte(int vertex, int component); + + short getShort(int vertex, int component); + + int getInt(int vertex, int component); + + float getFloat(int vertex, int component); + + double getDouble(int vertex, int component); + + long getLong(int vertex, int component); + + default Vector2f getVector2(int vertex, int baseComponent, Vector2f store) { + if (store == null) { + store = new Vector2f(); + } + return store.set( + getFloat(vertex, baseComponent++), + getFloat(vertex, baseComponent )); + } + + default Vector3f getVector3(int vertex, int baseComponent, Vector3f store) { + if (store == null) { + store = new Vector3f(); + } + return store.set( + getFloat(vertex, baseComponent++), + getFloat(vertex, baseComponent++), + getFloat(vertex, baseComponent )); + } + + default Vector4f getVector4(int vertex, int baseComponent, Vector4f store) { + if (store == null) { + store = new Vector4f(); + } + return store.set( + getFloat(vertex, baseComponent++), + getFloat(vertex, baseComponent++), + getFloat(vertex, baseComponent++), + getFloat(vertex, baseComponent )); + } + + default ColorRGBA getColor(int vertex, int baseComponent, ColorRGBA store) { + if (store == null) { + store = new ColorRGBA(); + } + return store.set( + getFloat(vertex, baseComponent++), + getFloat(vertex, baseComponent++), + getFloat(vertex, baseComponent++), + getFloat(vertex, baseComponent )); + } + + default byte[] getBytes(int baseVertex, int baseComponent, byte[] store) { + for (int i = 0; i < store.length; i++) { + store[i] = getByte(baseVertex, baseComponent); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return store; + } + + default ByteBuffer getBytes(int baseVertex, int baseComponent, ByteBuffer store) { + for (int i = 0; i < store.limit(); i++) { + store.put(i, getByte(baseVertex, baseComponent)); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return store; + } + + default short[] getShorts(int baseVertex, int baseComponent, short[] store) { + for (int i = 0; i < store.length; i++) { + store[i] = getShort(baseVertex, baseComponent); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return store; + } + + default ShortBuffer getShorts(int baseVertex, int baseComponent, ShortBuffer store) { + for (int i = 0; i < store.limit(); i++) { + store.put(i, getShort(baseVertex, baseComponent)); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return store; + } + + default int[] getInts(int baseVertex, int baseComponent, int[] store) { + for (int i = 0; i < store.length; i++) { + store[i] = getInt(baseVertex, baseComponent); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return store; + } + + default IntBuffer getInts(int baseVertex, int baseComponent, IntBuffer store) { + for (int i = 0; i < store.limit(); i++) { + store.put(i, getInt(baseVertex, baseComponent)); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return store; + } + + default float[] getFloats(int baseVertex, int baseComponent, float[] store) { + for (int i = 0; i < store.length; i++) { + store[i] = getFloat(baseVertex, baseComponent); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return store; + } + + default FloatBuffer getFloats(int baseVertex, int baseComponent, FloatBuffer store) { + for (int i = 0; i < store.limit(); i++) { + store.put(i, getFloat(baseVertex, baseComponent)); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return store; + } + + default double[] getDoubles(int baseVertex, int baseComponent, double[] store) { + for (int i = 0; i < store.length; i++) { + store[i] = getDouble(baseVertex, baseComponent); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return store; + } + + default DoubleBuffer getDoubles(int baseVertex, int baseComponent, DoubleBuffer store) { + for (int i = 0; i < store.limit(); i++) { + store.put(i, getDouble(baseVertex, baseComponent)); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return store; + } + + default long[] getLongs(int baseVertex, int baseComponent, long[] store) { + for (int i = 0; i < store.length; i++) { + store[i] = getLong(baseVertex, baseComponent); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return store; + } + + default LongBuffer getLongs(int baseVertex, int baseComponent, LongBuffer store) { + for (int i = 0; i < store.limit(); i++) { + store.put(i, getLong(baseVertex, baseComponent)); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return store; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/VertexWriter.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/VertexWriter.java new file mode 100644 index 0000000000..a9fda260e0 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/VertexWriter.java @@ -0,0 +1,206 @@ +package com.jme3.vulkan.mesh; + +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector2f; +import com.jme3.math.Vector3f; +import com.jme3.math.Vector4f; + +import java.nio.*; + +public interface VertexWriter extends VertexReader { + + VertexWriter limit(int vertex); + + VertexWriter putByte(int vertex, int component, byte value); + + VertexWriter putShort(int vertex, int component, short value); + + VertexWriter putInt(int vertex, int component, int value); + + VertexWriter putFloat(int vertex, int component, float value); + + VertexWriter putDouble(int vertex, int component, double value); + + VertexWriter putLong(int vertex, int component, long value); + + VertexWriter putNumber(int vertex, int component, Number number); + + default VertexWriter putVector2(int vertex, int baseComponent, float x, float y) { + putFloat(vertex, baseComponent++, x); + putFloat(vertex, baseComponent , y); + return this; + } + + default VertexWriter putVector3(int vertex, int baseComponent, float x, float y, float z) { + putFloat(vertex, baseComponent++, x); + putFloat(vertex, baseComponent++, y); + putFloat(vertex, baseComponent , z); + return this; + } + + default VertexWriter putVector4(int vertex, int baseComponent, float x, float y, float z, float w) { + putFloat(vertex, baseComponent++, x); + putFloat(vertex, baseComponent++, y); + putFloat(vertex, baseComponent++, z); + putFloat(vertex, baseComponent , w); + return this; + } + + default VertexWriter putVector2(int vertex, int baseComponent, Vector2f value) { + return putVector2(vertex, baseComponent, value.x, value.y); + } + + default VertexWriter putVector3(int vertex, int baseComponent, Vector3f value) { + return putVector3(vertex, baseComponent, value.x, value.y, value.z); + } + + default VertexWriter putVector4(int vertex, int baseComponent, Vector4f value) { + return putVector4(vertex, baseComponent, value.x, value.y, value.z, value.w); + } + + default VertexWriter putColor(int vertex, int baseComponent, ColorRGBA value) { + return putVector4(vertex, baseComponent, value.r, value.g, value.b, value.a); + } + + default VertexWriter putBytes(int baseVertex, int baseComponent, byte... values) { + for (byte v : values) { + putByte(baseVertex, baseComponent, v); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return this; + } + + default VertexWriter putBytes(int baseVertex, int baseComponent, ByteBuffer values) { + for (int i = 0; i < values.limit(); i++) { + putByte(baseVertex, baseComponent, values.get(i)); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return this; + } + + default VertexWriter putShorts(int baseVertex, int baseComponent, short... values) { + for (short v : values) { + putShort(baseVertex, baseComponent, v); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return this; + } + + default VertexWriter putShorts(int baseVertex, int baseComponent, ShortBuffer values) { + for (int i = 0; i < values.limit(); i++) { + putShort(baseVertex, baseComponent, values.get(i)); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return this; + } + + default VertexWriter putInts(int baseVertex, int baseComponent, int... values) { + for (int v : values) { + putInt(baseVertex, baseComponent, v); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return this; + } + + default VertexWriter putInts(int baseVertex, int baseComponent, IntBuffer values) { + for (int i = 0; i < values.limit(); i++) { + putInt(baseVertex, baseComponent, values.get(i)); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return this; + } + + default VertexWriter putFloats(int baseVertex, int baseComponent, float... values) { + for (float v : values) { + putFloat(baseVertex, baseComponent, v); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return this; + } + + default VertexWriter putFloats(int baseVertex, int baseComponent, FloatBuffer values) { + for (int i = 0; i < values.limit(); i++) { + putFloat(baseVertex, baseComponent, values.get(i)); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return this; + } + + default VertexWriter putDoubles(int baseVertex, int baseComponent, double... values) { + for (double v : values) { + putDouble(baseVertex, baseComponent, v); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return this; + } + + default VertexWriter putDoubles(int baseVertex, int baseComponent, DoubleBuffer values) { + for (int i = 0; i < values.limit(); i++) { + putDouble(baseVertex, baseComponent, values.get(i)); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return this; + } + + default VertexWriter putLongs(int baseVertex, int baseComponent, long... values) { + for (long v : values) { + putLong(baseVertex, baseComponent, v); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return this; + } + + default VertexWriter putLongs(int baseVertex, int baseComponent, LongBuffer values) { + for (int i = 0; i < values.limit(); i++) { + putLong(baseVertex, baseComponent, values.get(i)); + if (++baseComponent >= components()) { + baseComponent = 0; + baseVertex++; + } + } + return this; + } + + default VertexWriter putReader(VertexReader reader, int srcVertex, int dstVertex, int srcComponent, int dstComponent, int vertices, int components) { + for (int v = 0; v < vertices; v++) { + for (int c = 0; c < components; c++) { + putDouble(srcVertex + v, srcComponent + c, reader.getDouble(dstVertex + v, dstComponent + c)); + } + } + return this; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/VkMesh.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/VkMesh.java new file mode 100644 index 0000000000..869f3f8aba --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/VkMesh.java @@ -0,0 +1,15 @@ +package com.jme3.vulkan.mesh; + +import com.jme3.scene.Mesh; +import com.jme3.vulkan.commands.CommandBuffer; + +// todo: rename to VulkanMesh; for whatever reason, IntelliJ refuses to recognize VulkanMesh as a valid type +public interface VkMesh extends Mesh { + + void bind(CommandBuffer cmd, int lod); + + void render(CommandBuffer cmd, int lod); + + MeshDescription getDescription(); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/VulkanAttributeModifier.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/VulkanAttributeModifier.java new file mode 100644 index 0000000000..82c761c149 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/VulkanAttributeModifier.java @@ -0,0 +1,350 @@ +package com.jme3.vulkan.mesh; + +import com.jme3.vulkan.Format; + +import java.nio.*; + +public class VulkanAttributeModifier implements AttributeModifier { + + private final VertexBinding vertex; + private final VertexAttribute attribute; + private final ByteBuffer buffer; + + public VulkanAttributeModifier(VertexBinding vertex, VertexAttribute attribute) { + this.vertex = vertex; + this.attribute = attribute; + buffer = vertex.mapBytes(); + } + + @Override + public void close() { + vertex.unmap(); + } + + @Override + public void setUpdateNeeded() {} + + public int vertexToPosition(int vertex) { + return attribute.getBinding().getStride() * vertex + attribute.getOffset(); + } + + public int positionToVertex(int position) { + return (position - attribute.getOffset()) / attribute.getBinding().getStride(); + } + + @Override + public int capacity() { + return buffer.capacity() / attribute.getBinding().getStride(); + } + + @Override + public int limit() { + return positionToVertex(buffer.limit()); + } + + @Override + public int components() { + return attribute.getFormat().getNumComponents(); + } + + @Override + public AttributeModifier limit(int vertex) { + buffer.limit(vertexToPosition(vertex)); + return this; + } + + @Override + public AttributeModifier putByte(int vertex, int component, byte value) { + attribute.getFormat().getComponent(component).putByte(buffer, vertexToPosition(vertex), value); + return this; + } + + @Override + public AttributeModifier putShort(int vertex, int component, short value) { + attribute.getFormat().getComponent(component).putShort(buffer, vertexToPosition(vertex), value); + return this; + } + + @Override + public AttributeModifier putInt(int vertex, int component, int value) { + attribute.getFormat().getComponent(component).putInt(buffer, vertexToPosition(vertex), value); + return this; + } + + @Override + public AttributeModifier putFloat(int vertex, int component, float value) { + attribute.getFormat().getComponent(component).putFloat(buffer, vertexToPosition(vertex), value); + return this; + } + + @Override + public AttributeModifier putDouble(int vertex, int component, double value) { + attribute.getFormat().getComponent(component).putDouble(buffer, vertexToPosition(vertex), value); + return this; + } + + @Override + public AttributeModifier putLong(int vertex, int component, long value) { + attribute.getFormat().getComponent(component).putLong(buffer, vertexToPosition(vertex), value); + return this; + } + + @Override + public AttributeModifier putNumber(int vertex, int component, Number value) { + if (value instanceof Byte) { + return putByte(vertex, component, value.byteValue()); + } else if (value instanceof Short) { + return putShort(vertex, component, value.shortValue()); + } else if (value instanceof Integer) { + return putInt(vertex, component, value.intValue()); + } else if (value instanceof Float) { + return putFloat(vertex, component, value.floatValue()); + } else if (value instanceof Double) { + return putDouble(vertex, component, value.doubleValue()); + } else if (value instanceof Long) { + return putLong(vertex, component, value.longValue()); + } + return this; + } + + @Override + public AttributeModifier putVector2(int vertex, int baseComponent, float x, float y) { + vertex = vertexToPosition(vertex); + Format f = attribute.getFormat(); + f.getComponent(baseComponent++).putFloat(buffer, vertex, x); + f.getComponent(baseComponent ).putFloat(buffer, vertex, y); + return this; + } + + @Override + public AttributeModifier putVector3(int vertex, int baseComponent, float x, float y, float z) { + vertex = vertexToPosition(vertex); + Format f = attribute.getFormat(); + f.getComponent(baseComponent++).putFloat(buffer, vertex, x); + f.getComponent(baseComponent++).putFloat(buffer, vertex, y); + f.getComponent(baseComponent ).putFloat(buffer, vertex, z); + return this; + } + + @Override + public AttributeModifier putVector4(int vertex, int baseComponent, float x, float y, float z, float w) { + vertex = vertexToPosition(vertex); + Format f = attribute.getFormat(); + f.getComponent(baseComponent++).putFloat(buffer, vertex, x); + f.getComponent(baseComponent++).putFloat(buffer, vertex, y); + f.getComponent(baseComponent++).putFloat(buffer, vertex, z); + f.getComponent(baseComponent ).putFloat(buffer, vertex, w); + return this; + } + + @Override + public AttributeModifier putBytes(int baseVertex, int baseComponent, byte... values) { + Format f = attribute.getFormat(); + int vertPos = vertexToPosition(baseVertex); + for (byte v : values) { + f.getComponent(baseComponent).putByte(buffer, vertPos, v); + if (++baseComponent >= f.getNumComponents()) { + baseComponent = 0; + vertPos = vertexToPosition(++baseVertex); + } + } + return this; + } + + @Override + public AttributeModifier putBytes(int baseVertex, int baseComponent, ByteBuffer values) { + Format f = attribute.getFormat(); + int vertPos = vertexToPosition(baseVertex); + int bufPos = values.position(); + while (values.hasRemaining()) { + f.getComponent(baseComponent).putByte(buffer, vertPos, values.get()); + if (++baseComponent >= f.getNumComponents()) { + baseComponent = 0; + vertPos = vertexToPosition(++baseVertex); + } + } + values.position(bufPos); + return this; + } + + @Override + public AttributeModifier putShorts(int baseVertex, int baseComponent, short... values) { + Format f = attribute.getFormat(); + int vertPos = vertexToPosition(baseVertex); + for (short v : values) { + f.getComponent(baseComponent).putShort(buffer, vertPos, v); + if (++baseComponent >= f.getNumComponents()) { + baseComponent = 0; + vertPos = vertexToPosition(++baseVertex); + } + } + return this; + } + + @Override + public AttributeModifier putShorts(int baseVertex, int baseComponent, ShortBuffer values) { + Format f = attribute.getFormat(); + int vertPos = vertexToPosition(baseVertex); + int bufPos = values.position(); + while (values.hasRemaining()) { + f.getComponent(baseComponent).putShort(buffer, vertPos, values.get()); + if (++baseComponent >= f.getNumComponents()) { + baseComponent = 0; + vertPos = vertexToPosition(++baseVertex); + } + } + values.position(bufPos); + return this; + } + + @Override + public AttributeModifier putInts(int baseVertex, int baseComponent, int... values) { + Format f = attribute.getFormat(); + int vertPos = vertexToPosition(baseVertex); + for (int v : values) { + f.getComponent(baseComponent).putInt(buffer, vertPos, v); + if (++baseComponent >= f.getNumComponents()) { + baseComponent = 0; + vertPos = vertexToPosition(++baseVertex); + } + } + return this; + } + + @Override + public AttributeModifier putInts(int baseVertex, int baseComponent, IntBuffer values) { + Format f = attribute.getFormat(); + int vertPos = vertexToPosition(baseVertex); + int bufPos = values.position(); + while (values.hasRemaining()) { + f.getComponent(baseComponent).putInt(buffer, vertPos, values.get()); + if (++baseComponent >= f.getNumComponents()) { + baseComponent = 0; + vertPos = vertexToPosition(++baseVertex); + } + } + values.position(bufPos); + return this; + } + + @Override + public AttributeModifier putFloats(int baseVertex, int baseComponent, float... values) { + Format f = attribute.getFormat(); + int vertPos = vertexToPosition(baseVertex); + for (float v : values) { + f.getComponent(baseComponent).putFloat(buffer, vertPos, v); + if (++baseComponent >= f.getNumComponents()) { + baseComponent = 0; + vertPos = vertexToPosition(++baseVertex); + } + } + return this; + } + + @Override + public AttributeModifier putFloats(int baseVertex, int baseComponent, FloatBuffer values) { + Format f = attribute.getFormat(); + int vertPos = vertexToPosition(baseVertex); + int bufPos = values.position(); + while (values.hasRemaining()) { + f.getComponent(baseComponent).putFloat(buffer, vertPos, values.get()); + if (++baseComponent >= f.getNumComponents()) { + baseComponent = 0; + vertPos = vertexToPosition(++baseVertex); + } + } + values.position(bufPos); + return this; + } + + @Override + public AttributeModifier putDoubles(int baseVertex, int baseComponent, double... values) { + Format f = attribute.getFormat(); + int vertPos = vertexToPosition(baseVertex); + for (double v : values) { + f.getComponent(baseComponent).putDouble(buffer, vertPos, v); + if (++baseComponent >= f.getNumComponents()) { + baseComponent = 0; + vertPos = vertexToPosition(++baseVertex); + } + } + return this; + } + + @Override + public AttributeModifier putDoubles(int baseVertex, int baseComponent, DoubleBuffer values) { + Format f = attribute.getFormat(); + int vertPos = vertexToPosition(baseVertex); + int bufPos = values.position(); + while (values.hasRemaining()) { + f.getComponent(baseComponent).putDouble(buffer, vertPos, values.get()); + if (++baseComponent >= f.getNumComponents()) { + baseComponent = 0; + vertPos = vertexToPosition(++baseVertex); + } + } + values.position(bufPos); + return this; + } + + @Override + public AttributeModifier putLongs(int baseVertex, int baseComponent, long... values) { + Format f = attribute.getFormat(); + int vertPos = vertexToPosition(baseVertex); + for (long v : values) { + f.getComponent(baseComponent).putLong(buffer, vertPos, v); + if (++baseComponent >= f.getNumComponents()) { + baseComponent = 0; + vertPos = vertexToPosition(++baseVertex); + } + } + return this; + } + + @Override + public AttributeModifier putLongs(int baseVertex, int baseComponent, LongBuffer values) { + Format f = attribute.getFormat(); + int vertPos = vertexToPosition(baseVertex); + int bufPos = values.position(); + while (values.hasRemaining()) { + f.getComponent(baseComponent).putLong(buffer, vertPos, values.get()); + if (++baseComponent >= f.getNumComponents()) { + baseComponent = 0; + vertPos = vertexToPosition(++baseVertex); + } + } + values.position(bufPos); + return this; + } + + @Override + public byte getByte(int vertex, int component) { + return attribute.getFormat().getComponent(component).getByte(buffer, vertexToPosition(vertex)); + } + + @Override + public short getShort(int vertex, int component) { + return attribute.getFormat().getComponent(component).getShort(buffer, vertexToPosition(vertex)); + } + + @Override + public int getInt(int vertex, int component) { + return attribute.getFormat().getComponent(component).getInt(buffer, vertexToPosition(vertex)); + } + + @Override + public float getFloat(int vertex, int component) { + return attribute.getFormat().getComponent(component).getFloat(buffer, vertexToPosition(vertex)); + } + + @Override + public double getDouble(int vertex, int component) { + return attribute.getFormat().getComponent(component).getDouble(buffer, vertexToPosition(vertex)); + } + + @Override + public long getLong(int vertex, int component) { + return attribute.getFormat().getComponent(component).getLong(buffer, vertexToPosition(vertex)); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/VulkanVertexBinding.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/VulkanVertexBinding.java new file mode 100644 index 0000000000..5e67266d22 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/VulkanVertexBinding.java @@ -0,0 +1,115 @@ +package com.jme3.vulkan.mesh; + +import com.jme3.vulkan.Format; +import com.jme3.vulkan.buffers.AdaptiveVulkanBuffer; +import com.jme3.vulkan.buffers.GpuBuffer; +import com.jme3.vulkan.buffers.Mappable; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.memory.MemorySize; +import com.jme3.vulkan.mesh.attribute.Attribute; +import com.jme3.vulkan.util.IntEnum; + +import java.util.*; + +public class VulkanVertexBinding implements VertexBinding { + + private final LogicalDevice device; + private final List attributes = new ArrayList<>(); + private final IntEnum rate; + private int stride = 0; + private long offset = 0L; + + protected VulkanVertexBinding(LogicalDevice device, IntEnum rate) { + this.device = device; + this.rate = rate; + } + + @Override + public T mapAttribute(String name, Mappable vertices, int size) { + for (NamedAttribute a : attributes) { + if (a.getName().equals(name)) { + return (T)a.getMapping().map(vertices, size, getStride(), a.getOffset()); + } + } + return null; + } + + @Override + public GpuBuffer createBuffer(int elements) { + return new AdaptiveVulkanBuffer(device, new MemorySize(elements, stride)); + } + + @Override + public void setOffset(long offset) { + this.offset = offset; + } + + @Override + public int getStride() { + return stride; + } + + @Override + public IntEnum getInputRate() { + return rate; + } + + @Override + public int getNumAttributes() { + return attributes.size(); + } + + @Override + public long getOffset() { + return offset; + } + + @Override + public Iterator iterator() { + return attributes.iterator(); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + VulkanVertexBinding that = (VulkanVertexBinding)o; + return stride == that.stride + && Objects.equals(attributes, that.attributes) + && Objects.equals(rate, that.rate); + } + + @Override + public int hashCode() { + return Objects.hash(attributes, rate, stride); + } + + public static BuilderImpl create(LogicalDevice device, IntEnum rate) { + return new VulkanVertexBinding(device, rate).build(); + } + + private BuilderImpl build() { + return new BuilderImpl(); + } + + public class BuilderImpl implements VertexBinding.Builder { + + private final Set usedAttrNames = new HashSet<>(); + + @Override + public BuilderImpl add(String name, Format format, AttributeMapping mapping) { + if (!usedAttrNames.add(name)) { + throw new IllegalArgumentException("\"" + name + "\" is already used as an attribute name."); + } + attributes.add(new NamedAttribute(name, format, mapping, stride)); + stride += format.getTotalBytes(); + return this; + } + + @Override + public VulkanVertexBinding build() { + return VulkanVertexBinding.this; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/attribute/AbstractAttribute.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/attribute/AbstractAttribute.java new file mode 100644 index 0000000000..8e71696b90 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/attribute/AbstractAttribute.java @@ -0,0 +1,116 @@ +package com.jme3.vulkan.mesh.attribute; + +import com.jme3.vulkan.buffers.Mappable; + +import java.nio.ByteBuffer; +import java.util.Iterator; + +public abstract class AbstractAttribute implements Attribute { + + private final Mappable vertices; + private final int size, stride, offset; + private ByteBuffer buffer; + + public AbstractAttribute(Mappable vertices, int size, int stride, int offset) { + this.vertices = vertices; + this.size = size; + this.stride = stride; + this.offset = offset; + this.buffer = vertices.mapBytes(); + } + + @Override + public void unmap() { + if (buffer == null) { + throw new IllegalStateException("Attribute has already been unmapped."); + } + buffer = null; + vertices.unmap(); + } + + protected ByteBuffer getBuffer() { + return buffer; + } + + protected ByteBuffer getBuffer(int element) { + return buffer.position(element * stride + offset); + } + + @Override + public Iterator iterator() { + if (vertices == null) { + throw new IllegalStateException("Cannot iterate over unmapped attribute."); + } + return new IteratorImpl<>(this, null, size); + } + + public IteratorImpl iterator(T store) { + if (vertices == null) { + throw new IllegalStateException("Cannot iterate over unmapped attribute."); + } + return new IteratorImpl<>(this, store, size); + } + + public IndexIterator indices() { + return new IndexIterator(size); + } + + public static class IteratorImpl implements Iterator, Iterable { + + private final Attribute attr; + private final T store; + private final int size; + private int index = 0; + + public IteratorImpl(Attribute attr, T store, int size) { + this.attr = attr; + this.store = store; + this.size = size; + } + + @Override + public Iterator iterator() { + this.index = 0; + return this; + } + + @Override + public boolean hasNext() { + return index < size; + } + + @Override + public T next() { + return attr.get(index++, store); + } + + } + + public static class IndexIterator implements Iterator, Iterable { + + private final int size; + private int index = 0; + + public IndexIterator(int size) { + this.size = size; + } + + @Override + public Iterator iterator() { + this.index = 0; + return this; + } + + @Override + public boolean hasNext() { + return index < size; + } + + @Override + public Integer next() { + return index++; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/attribute/Attribute.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/attribute/Attribute.java new file mode 100644 index 0000000000..5ef40e0597 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/attribute/Attribute.java @@ -0,0 +1,28 @@ +package com.jme3.vulkan.mesh.attribute; + +public interface Attribute extends Iterable { + + void unmap(); + + void set(int element, T value); + + T get(int element); + + default void set(int startElement, T[] values) { + for (int i = 0; i < values.length; i++) { + set(startElement + i, values[i]); + } + } + + default T get(int element, T store) { + return get(element); + } + + default T[] get(int startElement, T[] store) { + for (int i = 0; i < store.length; i++) { + store[i] = get(startElement + i, store[i]); + } + return store; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/attribute/BufferAttribute.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/attribute/BufferAttribute.java new file mode 100644 index 0000000000..c44e0fb328 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/attribute/BufferAttribute.java @@ -0,0 +1,36 @@ +package com.jme3.vulkan.mesh.attribute; + +import com.jme3.vulkan.Format; +import com.jme3.vulkan.buffers.Mappable; +import org.lwjgl.system.MemoryUtil; + +import java.nio.Buffer; +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; + +public abstract class BufferAttribute extends AbstractAttribute { + + public BufferAttribute(Mappable vertices, int size, int stride, int offset) { + super(vertices, size, stride, offset); + } + + public void set(int baseElement, B values) { + ByteBuffer buf = getBuffer(baseElement); + int length = values.remaining() * Float.BYTES; + if (length > buf.remaining()) { + throw new BufferOverflowException(); + } + MemoryUtil.memCopy(MemoryUtil.memAddress(values), MemoryUtil.memAddress(buf), length); + } + + public B get(int baseElement, B store) { + ByteBuffer buf = getBuffer(baseElement); + int length = store.remaining() * Float.BYTES; + if (length > buf.remaining()) { + throw new BufferOverflowException(); + } + MemoryUtil.memCopy(MemoryUtil.memAddress(buf), MemoryUtil.memAddress(store), length); + return store; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/mesh/attribute/Position.java b/jme3-core/src/main/java/com/jme3/vulkan/mesh/attribute/Position.java new file mode 100644 index 0000000000..c3242b1265 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/mesh/attribute/Position.java @@ -0,0 +1,52 @@ +package com.jme3.vulkan.mesh.attribute; + +import com.jme3.math.Vector3f; +import com.jme3.vulkan.Format; +import com.jme3.vulkan.buffers.Mappable; + +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; + +public class Position extends BufferAttribute { + + public Position(Mappable vertices, int size, int stride, int offset) { + super(vertices, size, stride, offset); + } + + @Override + public void set(int element, Vector3f value) { + set(element, value.x, value.y, value.z); + } + + @Override + public Vector3f get(int element) { + return get(element, (Vector3f)null); + } + + @Override + public Vector3f get(int element, Vector3f store) { + store = Vector3f.storage(store); + ByteBuffer buf = getBuffer(element); + return store.set(buf.getFloat(), buf.getFloat(), buf.getFloat()); + } + + public void set(int element, float x, float y, float z) { + getBuffer(element).putFloat(x).putFloat(y).putFloat(z); + } + + public void set(int baseElement, float[] values) { + ByteBuffer buf = getBuffer(baseElement); + for (float v : values) { + buf.putFloat(v); + } + } + + public float[] get(int baseElement, float[] store) { + ByteBuffer buf = getBuffer(baseElement); + for (int i = 0; i < store.length; i++) { + store[i] = buf.getFloat(); + } + return store; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pass/Attachment.java b/jme3-core/src/main/java/com/jme3/vulkan/pass/Attachment.java new file mode 100644 index 0000000000..ee1ca290df --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pass/Attachment.java @@ -0,0 +1,144 @@ +package com.jme3.vulkan.pass; + +import com.jme3.math.ColorRGBA; +import com.jme3.vulkan.Format; +import com.jme3.vulkan.images.VulkanImage; +import org.lwjgl.vulkan.VkAttachmentDescription; + +public class Attachment { + + private final int position; + private final Format format; + private final int samples; + private VulkanImage.Load load = VulkanImage.Load.Clear; + private VulkanImage.Store store = VulkanImage.Store.Store; + private VulkanImage.Load stencilLoad = VulkanImage.Load.DontCare; + private VulkanImage.Store stencilStore = VulkanImage.Store.DontCare; + private VulkanImage.Layout initialLayout = VulkanImage.Layout.Undefined; + private VulkanImage.Layout finalLayout = VulkanImage.Layout.General; + private final ColorRGBA clearColor = ColorRGBA.Black.clone(); + private float clearDepth = 1f; + private int clearStencil = 0; + + protected Attachment(int position, Format format, int samples) { + this.position = position; + this.format = format; + this.samples = samples; + } + + protected Attachment(int position, Attachment base) { + this.position = position; + this.format = base.format; + this.samples = base.samples; + this.load = base.load; + this.store = base.store; + this.stencilLoad = base.stencilLoad; + this.stencilStore = base.stencilStore; + this.initialLayout = base.initialLayout; + this.finalLayout = base.finalLayout; + } + + public AttachmentReference createReference(VulkanImage.Layout layout) { + return new AttachmentReference(this, layout); + } + + public void fillStruct(VkAttachmentDescription struct) { + struct.format(format.getVkEnum()) + .samples(samples) + .loadOp(load.getEnum()) + .storeOp(store.getEnum()) + .stencilLoadOp(stencilLoad.getEnum()) + .stencilStoreOp(stencilStore.getEnum()) + .initialLayout(initialLayout.getEnum()) + .finalLayout(finalLayout.getEnum()); + } + + public void setLoad(VulkanImage.Load load) { + this.load = load; + } + + public void setStencilLoad(VulkanImage.Load stencilLoad) { + this.stencilLoad = stencilLoad; + } + + public void setStore(VulkanImage.Store store) { + this.store = store; + } + + public void setStencilStore(VulkanImage.Store stencilStore) { + this.stencilStore = stencilStore; + } + + public void setInitialLayout(VulkanImage.Layout initialLayout) { + this.initialLayout = initialLayout; + } + + public void setFinalLayout(VulkanImage.Layout finalLayout) { + this.finalLayout = finalLayout; + } + + public void setClearColor(ColorRGBA color) { + this.clearColor.set(color); + } + + public void setClearDepth(float clearDepth) { + this.clearDepth = clearDepth; + } + + public void setClearStencil(int clearStencil) { + this.clearStencil = clearStencil; + } + + public int getPosition() { + return position; + } + + public Format getFormat() { + return format; + } + + public int getSamples() { + return samples; + } + + public VulkanImage.Load getLoad() { + return load; + } + + public VulkanImage.Load getStencilLoad() { + return stencilLoad; + } + + public VulkanImage.Store getStore() { + return store; + } + + public VulkanImage.Store getStencilStore() { + return stencilStore; + } + + public VulkanImage.Layout getInitialLayout() { + return initialLayout; + } + + public VulkanImage.Layout getFinalLayout() { + return finalLayout; + } + + public ColorRGBA getClearColor() { + return clearColor; + } + + public float getClearDepth() { + return clearDepth; + } + + public int getClearStencil() { + return clearStencil; + } + + public boolean isCompatible(Attachment a) { + return position == a.position && format == a.format && samples == a.samples; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pass/AttachmentReference.java b/jme3-core/src/main/java/com/jme3/vulkan/pass/AttachmentReference.java new file mode 100644 index 0000000000..b45412dd13 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pass/AttachmentReference.java @@ -0,0 +1,49 @@ +package com.jme3.vulkan.pass; + +import com.jme3.vulkan.images.VulkanImage; +import org.lwjgl.vulkan.VK10; +import org.lwjgl.vulkan.VkAttachmentReference; + +/** + * Immutable reference to an {@link Attachment}. + */ +public class AttachmentReference { + + private final Attachment attachment; + private final VulkanImage.Layout layout; + + protected AttachmentReference(Attachment attachment, VulkanImage.Layout layout) { + this.attachment = attachment; + this.layout = layout; + } + + public void fillStruct(VkAttachmentReference struct) { + struct.attachment(getAttachmentPosition()) + .layout(layout.getEnum()); + } + + public Attachment getAttachment() { + return attachment; + } + + public VulkanImage.Layout getLayout() { + return layout; + } + + public int getAttachmentPosition() { + return attachment != null ? attachment.getPosition() : VK10.VK_ATTACHMENT_UNUSED; + } + + public boolean isUnused() { + return attachment == null; + } + + public boolean isCompatible(AttachmentReference ref) { + return isUnused() == ref.isUnused() && (isUnused() || attachment.isCompatible(ref.attachment)) && layout == ref.layout; + } + + public static AttachmentReference unused(VulkanImage.Layout layout) { + return new AttachmentReference(null, layout); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pass/RenderPass.java b/jme3-core/src/main/java/com/jme3/vulkan/pass/RenderPass.java new file mode 100644 index 0000000000..376f1c7a8e --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pass/RenderPass.java @@ -0,0 +1,269 @@ +package com.jme3.vulkan.pass; + +import com.jme3.util.AbstractBuilder; +import com.jme3.util.natives.Native; +import com.jme3.vulkan.Format; +import com.jme3.vulkan.commands.CommandBuffer; +import com.jme3.util.natives.AbstractNative; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.pipeline.framebuffer.VulkanFrameBuffer; +import com.jme3.vulkan.pipeline.PipelineBindPoint; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.*; + +import java.nio.LongBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; + +import static com.jme3.renderer.vulkan.VulkanUtils.*; +import static org.lwjgl.vulkan.VK10.*; + +public class RenderPass extends AbstractNative { + + private final LogicalDevice device; + private final List attachments = new ArrayList<>(); + private final List subpasses = new ArrayList<>(); + private final List dependencies = new ArrayList<>(); + private boolean built = false; + + public RenderPass(LogicalDevice device) { + this.device = device; + } + + @Override + public Runnable createNativeDestroyer() { + return () -> vkDestroyRenderPass(device.getNativeObject(), object, null); + } + + public void begin(CommandBuffer cmd, VulkanFrameBuffer fbo) { + begin(cmd, fbo, true); + } + + public void begin(CommandBuffer cmd, VulkanFrameBuffer fbo, boolean inline) { + assert fbo.getAttachments().size() < attachments.size() : "FrameBuffer does not contain enough attachments."; + try (MemoryStack stack = MemoryStack.stackPush()) { + VkClearValue.Buffer clear = VkClearValue.calloc(attachments.size(), stack); + for (Attachment a : attachments) { + VkClearValue clr = clear.get(); + clr.color().float32(a.getClearColor().toBuffer(stack)); + clr.depthStencil().set(a.getClearDepth(), a.getClearStencil()); + } + clear.flip(); + VkRenderPassBeginInfo begin = VkRenderPassBeginInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO) + .renderPass(object) + .framebuffer(fbo.getNativeObject()) + .clearValueCount(clear.limit()) + .pClearValues(clear); + begin.renderArea().offset().set(0, 0); + begin.renderArea().extent().width(fbo.getWidth()).height(fbo.getHeight()); + vkCmdBeginRenderPass(cmd.getBuffer(), begin, inline ? VK_SUBPASS_CONTENTS_INLINE : VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); + } + } + + public void nextSubpass(CommandBuffer cmd) { + nextSubpass(cmd, true); + } + + public void nextSubpass(CommandBuffer cmd, boolean inline) { + vkCmdNextSubpass(cmd.getBuffer(), inline ? VK_SUBPASS_CONTENTS_INLINE : VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS); + } + + public void end(CommandBuffer cmd) { + vkCmdEndRenderPass(cmd.getBuffer()); + } + + public LogicalDevice getDevice() { + return device; + } + + public List getAttachments() { + return Collections.unmodifiableList(attachments); + } + + public List getSubpasses() { + return Collections.unmodifiableList(subpasses); + } + + public List getDependencies() { + return Collections.unmodifiableList(dependencies); + } + + public boolean isCompatible(RenderPass pass) { + if (this == pass) { + return true; + } + if (attachments.size() != pass.attachments.size()) { + return false; + } + if (subpasses.size() != pass.subpasses.size()) { + return false; + } + if (dependencies.size() != pass.dependencies.size()) { + return false; + } + for (int i = 0; i < attachments.size(); i++) { + if (!attachments.get(i).isCompatible(pass.attachments.get(i))) { + return false; + } + } + for (int i = 0; i < subpasses.size(); i++) { + if (!subpasses.get(i).isCompatible(pass.subpasses.get(i))) { + return false; + } + } + for (int i = 0; i < dependencies.size(); i++) { + if (!dependencies.get(i).isCompatible(pass.dependencies.get(i))) { + return false; + } + } + return true; + } + + public Builder build() { + if (built) { + throw new IllegalStateException("Render pass has already been built or is being built."); + } + built = true; + return new Builder(); + } + + public Builder buildCopyOf(RenderPass base) { + return new Builder(base); + } + + public class Builder extends AbstractBuilder { + + public Builder() {} + + public Builder(RenderPass base) { + for (Attachment a : base.attachments) { + attachments.add(new Attachment(attachments.size(), a)); + } + for (Subpass s : base.subpasses) { + subpasses.add(new Subpass(RenderPass.this, subpasses.size(), s, attachments)); + } + for (SubpassDependency d : base.dependencies) { + dependencies.add(new SubpassDependency(d, subpasses)); + } + } + + @Override + protected void build() { + VkRenderPassCreateInfo create = VkRenderPassCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO); + if (!attachments.isEmpty()) { + VkAttachmentDescription.Buffer buf = VkAttachmentDescription.calloc(attachments.size(), stack); + for (Attachment a : attachments) { + a.fillStruct(buf.get()); + } + create.pAttachments(buf.flip()); + } + if (!subpasses.isEmpty()) { + VkSubpassDescription.Buffer buf = VkSubpassDescription.calloc(subpasses.size(), stack); + for (Subpass s : subpasses) { + s.fillStruct(stack, buf.get()); + } + create.pSubpasses(buf.flip()); + } + if (!dependencies.isEmpty()) { + VkSubpassDependency.Buffer buf = VkSubpassDependency.calloc(dependencies.size(), stack); + for (SubpassDependency d : dependencies) { + d.fillStruct(buf.get()); + } + create.pDependencies(buf.flip()); + } + LongBuffer idBuf = stack.mallocLong(1); + check(vkCreateRenderPass(device.getNativeObject(), create, null, idBuf), "Failed to create render pass."); + object = idBuf.get(0); + ref = Native.get().register(RenderPass.this); + device.getNativeReference().addDependent(ref); + } + + public Attachment createAttachment(Format format, int samples) { + Attachment a = new Attachment(attachments.size(), format, samples); + attachments.add(a); + return a; + } + + public Attachment createAttachment(Format format, int samples, Consumer config) { + Attachment a = createAttachment(format, samples); + config.accept(a); + return a; + } + + public Attachment getAttachment(int i) { + return attachments.get(i); + } + + public Attachment getAttachment(int i, Consumer config) { + Attachment a = getAttachment(i); + config.accept(a); + return a; + } + + public List getAttachments() { + return Collections.unmodifiableList(attachments); + } + + public Subpass createSubpass(PipelineBindPoint bindPoint) { + Subpass p = new Subpass(RenderPass.this, subpasses.size(), bindPoint); + subpasses.add(p); + return p; + } + + public Subpass createSubpass(PipelineBindPoint bindPoint, Consumer config) { + Subpass p = createSubpass(bindPoint); + config.accept(p); + return p; + } + + public Subpass getSubpass(int i) { + return subpasses.get(i); + } + + public Subpass getSubpass(int i, Consumer config) { + Subpass p = getSubpass(i); + config.accept(p); + return p; + } + + public List getSubpasses() { + return Collections.unmodifiableList(subpasses); + } + + public SubpassDependency createDependency(Subpass src, Subpass dst) { + SubpassDependency d = new SubpassDependency(src, dst); + dependencies.add(d); + return d; + } + + public SubpassDependency createDependency(Subpass src, Subpass dst, Consumer config) { + SubpassDependency d = createDependency(src, dst); + config.accept(d); + return d; + } + + public SubpassDependency getDependency(int i) { + return dependencies.get(i); + } + + public SubpassDependency getDependency(int i, Consumer config) { + SubpassDependency d = getDependency(i); + config.accept(d); + return d; + } + + public List getDependencies() { + return Collections.unmodifiableList(dependencies); + } + + public MemoryStack getStack() { + return stack; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pass/Subpass.java b/jme3-core/src/main/java/com/jme3/vulkan/pass/Subpass.java new file mode 100644 index 0000000000..8319ec58a8 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pass/Subpass.java @@ -0,0 +1,202 @@ +package com.jme3.vulkan.pass; + +import com.jme3.vulkan.pipeline.PipelineBindPoint; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkAttachmentReference; +import org.lwjgl.vulkan.VkSubpassDescription; + +import java.nio.IntBuffer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Immutable definition of a subpass within a render pass. + */ +public class Subpass { + + private final RenderPass pass; + private final int position; + private final PipelineBindPoint bindPoint; + private final List color = new ArrayList<>(); + private final List input = new ArrayList<>(); + private final List resolve = new ArrayList<>(); + private final List preserve = new ArrayList<>(); + private AttachmentReference depthStencil; + + protected Subpass(RenderPass pass, int position, PipelineBindPoint bindPoint) { + this.pass = pass; + this.position = position; + this.bindPoint = bindPoint; + } + + protected Subpass(RenderPass pass, int position, Subpass base, List attachments) { + this.pass = pass; + this.position = position; + this.bindPoint = base.bindPoint; + transferRefs(base.color, color, attachments); + transferRefs(base.input, input, attachments); + transferRefs(base.resolve, resolve, attachments); + transferRefs(base.preserve, preserve, attachments); + if (base.depthStencil != null) { + if (!base.depthStencil.isUnused()) { + depthStencil = attachments.get(base.depthStencil.getAttachment().getPosition()) + .createReference(base.depthStencil.getLayout()); + } else { + depthStencil = AttachmentReference.unused(base.depthStencil.getLayout()); + } + } + } + + private void transferRefs(List srcRefs, List dstRefs, List attachments) { + for (AttachmentReference r : srcRefs) { + if (!r.isUnused()) { + dstRefs.add(attachments.get(r.getAttachment().getPosition()).createReference(r.getLayout())); + } else { + dstRefs.add(AttachmentReference.unused(r.getLayout())); + } + } + } + + public void fillStruct(MemoryStack stack, VkSubpassDescription struct) { + struct.pipelineBindPoint(bindPoint.getEnum()); + if (!color.isEmpty()) { + struct.colorAttachmentCount(color.size()); + struct.pColorAttachments(getColorReferences(stack)); + } + if (depthStencil != null) { + struct.pDepthStencilAttachment(getDepthStencil(stack)); + } + if (!input.isEmpty()) { + struct.pInputAttachments(getInputReferences(stack)); + } + if (!resolve.isEmpty()) { + struct.pResolveAttachments(getResolveReferences(stack)); + } + if (!preserve.isEmpty()) { + struct.pPreserveAttachments(getPreserveIndices(stack)); + } + } + + public void addColorAttachment(AttachmentReference ref) { + color.add(ref); + } + + public void addInputAttachment(AttachmentReference ref) { + input.add(ref); + } + + public void addResolveAttachment(AttachmentReference ref) { + resolve.add(ref); + } + + public void addPreserveAttachment(AttachmentReference ref) { + resolve.add(ref); + } + + public void setDepthStencilAttachment(AttachmentReference depthStencil) { + this.depthStencil = depthStencil; + } + + public RenderPass getPass() { + return pass; + } + + public AttachmentReference getDepthStencil() { + return depthStencil; + } + + public List getColor() { + return color; + } + + public List getInput() { + return input; + } + + public List getResolve() { + return resolve; + } + + public List getPreserve() { + return preserve; + } + + public VkAttachmentReference getDepthStencil(MemoryStack stack) { + VkAttachmentReference ref = VkAttachmentReference.calloc(stack); + depthStencil.fillStruct(ref); + return ref; + } + + private VkAttachmentReference.Buffer getReferenceBuffer(MemoryStack stack, Collection refs) { + VkAttachmentReference.Buffer att = VkAttachmentReference.calloc(color.size(), stack); + for (AttachmentReference ref : refs) { + ref.fillStruct(att.get()); + } + return att.flip(); + } + + public VkAttachmentReference.Buffer getColorReferences(MemoryStack stack) { + return getReferenceBuffer(stack, color); + } + + public VkAttachmentReference.Buffer getInputReferences(MemoryStack stack) { + return getReferenceBuffer(stack, input); + } + + public VkAttachmentReference.Buffer getResolveReferences(MemoryStack stack) { + return getReferenceBuffer(stack, resolve); + } + + public IntBuffer getPreserveIndices(MemoryStack stack) { + IntBuffer indices = stack.mallocInt(preserve.size()); + for (AttachmentReference ref : preserve) { + indices.put(ref.getAttachmentPosition()); + } + indices.flip(); + return indices; + } + + public int getPosition() { + return position; + } + + public PipelineBindPoint getBindPoint() { + return bindPoint; + } + + public boolean hasDepthStencil() { + return depthStencil != null; + } + + public boolean isCompatible(Subpass pass) { + return bindPoint == pass.bindPoint + && hasDepthStencil() == pass.hasDepthStencil() + && (!hasDepthStencil() || depthStencil.isCompatible(pass.depthStencil)) + && compareReferenceLists(color, pass.color) + && compareReferenceLists(input, pass.input) + && compareReferenceLists(resolve, pass.resolve) + && compareReferenceLists(preserve, pass.preserve); + } + + private boolean compareReferenceLists(List list1, List list2) { + int lower = Math.min(list1.size(), list2.size()); + int higher = Math.max(list1.size(), list2.size()); + for (int i = 0; i < lower; i++) { + if (!list1.get(i).isCompatible(list2.get(i))) { + return false; + } + } + for (int i = lower; i < higher; i++) { + if (i < list1.size()) { + if (!list1.get(i).isUnused()) { + return false; + } + } else if (!list2.get(i).isUnused()) { + return false; + } + } + return true; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pass/SubpassDependency.java b/jme3-core/src/main/java/com/jme3/vulkan/pass/SubpassDependency.java new file mode 100644 index 0000000000..ba9da6f616 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pass/SubpassDependency.java @@ -0,0 +1,100 @@ +package com.jme3.vulkan.pass; + +import com.jme3.vulkan.pipeline.Access; +import com.jme3.vulkan.pipeline.PipelineStage; +import com.jme3.vulkan.util.Flag; +import org.lwjgl.vulkan.VK10; +import org.lwjgl.vulkan.VkSubpassDependency; + +import java.util.List; + +/** + * Immutable definition of a render pass dependency. + */ +public class SubpassDependency { + + private final Subpass srcSubpass, dstSubpass; + private Flag srcStageMask, dstStageMask; + private Flag srcAccessMask, dstAccessMask; + + protected SubpassDependency(Subpass srcSubpass, Subpass dstSubpass) { + this.srcSubpass = srcSubpass; + this.dstSubpass = dstSubpass; + } + + protected SubpassDependency(SubpassDependency base, List subpasses) { + srcSubpass = base.srcSubpass != null ? subpasses.get(base.srcSubpass.getPosition()) : null; + dstSubpass = base.dstSubpass != null ? subpasses.get(base.dstSubpass.getPosition()) : null; + this.srcStageMask = base.srcStageMask; + this.srcAccessMask = base.srcAccessMask; + this.dstStageMask = base.dstStageMask; + this.dstAccessMask = base.dstAccessMask; + } + + public void fillStruct(VkSubpassDependency struct) { + struct.srcSubpass(srcSubpass != null ? srcSubpass.getPosition() : VK10.VK_SUBPASS_EXTERNAL) + .dstSubpass(dstSubpass != null ? dstSubpass.getPosition() : VK10.VK_SUBPASS_EXTERNAL) + .srcStageMask(srcStageMask.bits()) + .srcAccessMask(srcAccessMask.bits()) + .dstStageMask(dstStageMask.bits()) + .dstAccessMask(dstAccessMask.bits()); + } + + public void setSrcStageMask(Flag srcStageMask) { + this.srcStageMask = srcStageMask; + } + + public void setSrcAccessMask(Flag srcAccessMask) { + this.srcAccessMask = srcAccessMask; + } + + public void setDstStageMask(Flag dstStageMask) { + this.dstStageMask = dstStageMask; + } + + public void setDstAccessMask(Flag dstAccessMask) { + this.dstAccessMask = dstAccessMask; + } + + public Subpass getSrcSubpass() { + return srcSubpass; + } + + public Subpass getDstSubpass() { + return dstSubpass; + } + + public Flag getSrcStageMask() { + return srcStageMask; + } + + public Flag getSrcAccessMask() { + return srcAccessMask; + } + + public Flag getDstStageMask() { + return dstStageMask; + } + + public Flag getDstAccessMask() { + return dstAccessMask; + } + + public boolean isSourceExternal() { + return srcSubpass == null; + } + + public boolean isDestinationExternal() { + return dstSubpass == null; + } + + public boolean isCompatible(SubpassDependency dependency) { + return srcSubpass.getPosition() == dependency.srcSubpass.getPosition() + && dstSubpass.getPosition() == dependency.dstSubpass.getPosition() + && srcStageMask == dependency.srcStageMask + && srcAccessMask == dependency.srcAccessMask + && dstStageMask == dependency.dstStageMask + && dstAccessMask == dependency.dstAccessMask; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/AbstractVulkanPipeline.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/AbstractVulkanPipeline.java new file mode 100644 index 0000000000..c2eef20db9 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/AbstractVulkanPipeline.java @@ -0,0 +1,91 @@ +package com.jme3.vulkan.pipeline; + +import com.jme3.util.natives.AbstractNative; +import com.jme3.vulkan.commands.CommandBuffer; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.util.Flag; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +import static org.lwjgl.vulkan.VK10.*; +import static org.lwjgl.vulkan.VK10.vkCmdBindPipeline; + +public abstract class AbstractVulkanPipeline extends AbstractNative implements Pipeline { + + public static final String DEFAULT_SHADER_ENTRY_POINT = "main"; + private static final AtomicInteger nextSortId = new AtomicInteger(0); + + public enum Create implements Flag { + + Derivative(VK_PIPELINE_CREATE_DERIVATIVE_BIT), + AllowDerivatives(VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT); + + private final int bits; + + Create(int bits) { + this.bits = bits; + } + + @Override + public int bits() { + return bits; + } + + } + + protected final LogicalDevice device; + protected final PipelineLayout layout; + protected final PipelineBindPoint bindPoint; + protected final Pipeline parent; + private final int sortId; + + public AbstractVulkanPipeline(LogicalDevice device, PipelineLayout layout, PipelineBindPoint bindPoint, Pipeline parent) { + this.device = device; + this.layout = layout; + this.bindPoint = bindPoint; + this.parent = parent; + this.sortId = nextSortId.getAndIncrement(); + } + + @Override + public Runnable createNativeDestroyer() { + return () -> vkDestroyPipeline(device.getNativeObject(), object, null); + } + + @Override + public void bind(CommandBuffer cmd) { + vkCmdBindPipeline(cmd.getBuffer(), bindPoint.getEnum(), object); + } + + @Override + public boolean isMaterialEquivalent(Pipeline other) { + if (!(other instanceof AbstractVulkanPipeline)) return false; + AbstractVulkanPipeline that = (AbstractVulkanPipeline)other; + return this == that || (bindPoint.is(other.getBindPoint()) && getLayout() == that.getLayout()); + } + + @Override + public PipelineBindPoint getBindPoint() { + return bindPoint; + } + + @Override + public Pipeline getParent() { + return parent; + } + + @Override + public int getSortId() { + return sortId; + } + + public LogicalDevice getDevice() { + return device; + } + + public PipelineLayout getLayout() { + return layout; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/Access.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/Access.java new file mode 100644 index 0000000000..ade9f57a7a --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/Access.java @@ -0,0 +1,38 @@ +package com.jme3.vulkan.pipeline; + +import com.jme3.vulkan.util.Flag; + +import static org.lwjgl.vulkan.VK10.*; + +public enum Access implements Flag { + + ColorAttachmentWrite(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT), + ColorAttachmentRead(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT), + HostWrite(VK_ACCESS_HOST_WRITE_BIT), + HostRead(VK_ACCESS_HOST_READ_BIT), + IndexRead(VK_ACCESS_INDEX_READ_BIT), + DepthStencilAttachmentWrite(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT), + DepthStencilAttachmentRead(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT), + IndirectCommandRead(VK_ACCESS_INDIRECT_COMMAND_READ_BIT), + InputAttachmentRead(VK_ACCESS_INPUT_ATTACHMENT_READ_BIT), + MemoryWrite(VK_ACCESS_MEMORY_WRITE_BIT), + MemoryRead(VK_ACCESS_MEMORY_READ_BIT), + ShaderWrite(VK_ACCESS_SHADER_WRITE_BIT), + ShaderRead(VK_ACCESS_SHADER_READ_BIT), + TransferWrite(VK_ACCESS_TRANSFER_WRITE_BIT), + TransferRead(VK_ACCESS_TRANSFER_READ_BIT), + UniformRead(VK_ACCESS_UNIFORM_READ_BIT), + VertexAttributeRead(VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT); + + private final int vkEnum; + + Access(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int bits() { + return vkEnum; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/CompareOp.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/CompareOp.java new file mode 100644 index 0000000000..9ec1849e2f --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/CompareOp.java @@ -0,0 +1,29 @@ +package com.jme3.vulkan.pipeline; + +import com.jme3.vulkan.util.IntEnum; + +import static org.lwjgl.vulkan.VK10.*; + +public enum CompareOp implements IntEnum { + + LessOrEqual(VK_COMPARE_OP_LESS_OR_EQUAL), + Always(VK_COMPARE_OP_ALWAYS), + Equal(VK_COMPARE_OP_EQUAL), + Greater(VK_COMPARE_OP_GREATER), + Less(VK_COMPARE_OP_LESS), + GreaterOrEqual(VK_COMPARE_OP_GREATER_OR_EQUAL), + Never(VK_COMPARE_OP_NEVER), + NotEqual(VK_COMPARE_OP_NOT_EQUAL); + + private final int vkEnum; + + CompareOp(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/CullMode.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/CullMode.java new file mode 100644 index 0000000000..1ea3d31226 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/CullMode.java @@ -0,0 +1,25 @@ +package com.jme3.vulkan.pipeline; + +import com.jme3.vulkan.util.Flag; + +import static org.lwjgl.vulkan.VK10.*; + +public enum CullMode implements Flag { + + None(VK_CULL_MODE_NONE), + Back(VK_CULL_MODE_BACK_BIT), + Front(VK_CULL_MODE_FRONT_BIT), + FrontAndBack(VK_CULL_MODE_FRONT_AND_BACK); + + private final int bits; + + CullMode(int bits) { + this.bits = bits; + } + + @Override + public int bits() { + return bits; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/DynamicState.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/DynamicState.java new file mode 100644 index 0000000000..b4200cec58 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/DynamicState.java @@ -0,0 +1,30 @@ +package com.jme3.vulkan.pipeline; + +import com.jme3.vulkan.util.IntEnum; + +import static org.lwjgl.vulkan.VK10.*; + +public enum DynamicState implements IntEnum { + + ViewPort(VK_DYNAMIC_STATE_VIEWPORT), + Scissor(VK_DYNAMIC_STATE_SCISSOR), + LineWidth(VK_DYNAMIC_STATE_LINE_WIDTH), + DepthBias(VK_DYNAMIC_STATE_DEPTH_BIAS), + BlendConstants(VK_DYNAMIC_STATE_BLEND_CONSTANTS), + DepthBounds(VK_DYNAMIC_STATE_DEPTH_BOUNDS), + CompareMask(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK), + WriteMask(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK), + StencilReference(VK_DYNAMIC_STATE_STENCIL_REFERENCE); + + private final int vk; + + DynamicState(int vk) { + this.vk = vk; + } + + @Override + public int getEnum() { + return vk; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/FaceWinding.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/FaceWinding.java new file mode 100644 index 0000000000..bc85fd180d --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/FaceWinding.java @@ -0,0 +1,23 @@ +package com.jme3.vulkan.pipeline; + +import com.jme3.vulkan.util.IntEnum; + +import static org.lwjgl.vulkan.VK10.*; + +public enum FaceWinding implements IntEnum { + + Clockwise(VK_FRONT_FACE_CLOCKWISE), + CounterClockwise(VK_FRONT_FACE_COUNTER_CLOCKWISE); + + private final int vkEnum; + + FaceWinding(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/LogicOp.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/LogicOp.java new file mode 100644 index 0000000000..cb41d86577 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/LogicOp.java @@ -0,0 +1,37 @@ +package com.jme3.vulkan.pipeline; + +import com.jme3.vulkan.util.IntEnum; + +import static org.lwjgl.vulkan.VK10.*; + +public enum LogicOp implements IntEnum { + + Copy(VK_LOGIC_OP_COPY), + And(VK_LOGIC_OP_AND), + Or(VK_LOGIC_OP_OR), + None(VK_LOGIC_OP_NO_OP), + Clear(VK_LOGIC_OP_CLEAR), + AndInverted(VK_LOGIC_OP_AND_INVERTED), + AndReverse(VK_LOGIC_OP_AND_REVERSE), + CopyInverted(VK_LOGIC_OP_COPY_INVERTED), + Equivalent(VK_LOGIC_OP_EQUIVALENT), + Invert(VK_LOGIC_OP_INVERT), + Nand(VK_LOGIC_OP_NAND), + Nor(VK_LOGIC_OP_NOR), + OrInverted(VK_LOGIC_OP_OR_INVERTED), + OrReverse(VK_LOGIC_OP_OR_REVERSE), + Set(VK_LOGIC_OP_SET), + Xor(VK_LOGIC_OP_XOR); + + private final int vkEnum; + + LogicOp(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/Pipeline.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/Pipeline.java new file mode 100644 index 0000000000..b1f3bc4cd8 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/Pipeline.java @@ -0,0 +1,28 @@ +package com.jme3.vulkan.pipeline; + +import com.jme3.vulkan.commands.CommandBuffer; +import com.jme3.util.natives.AbstractNative; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.pipeline.states.BasePipelineState; +import com.jme3.vulkan.pipeline.states.PipelineState; +import com.jme3.vulkan.util.Flag; + +import java.util.concurrent.atomic.AtomicLong; + +import static org.lwjgl.vulkan.VK10.*; + +public interface Pipeline { + + BasePipelineState getState(); + + void bind(CommandBuffer cmd); + + boolean isMaterialEquivalent(Pipeline other); + + PipelineBindPoint getBindPoint(); + + Pipeline getParent(); + + int getSortId(); + +} \ No newline at end of file diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/PipelineBindPoint.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/PipelineBindPoint.java new file mode 100644 index 0000000000..572bc7f7bb --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/PipelineBindPoint.java @@ -0,0 +1,23 @@ +package com.jme3.vulkan.pipeline; + +import com.jme3.vulkan.util.IntEnum; + +import static org.lwjgl.vulkan.VK10.*; + +public enum PipelineBindPoint implements IntEnum { + + Graphics(VK_PIPELINE_BIND_POINT_GRAPHICS), + Compute(VK_PIPELINE_BIND_POINT_COMPUTE); + + private final int vkEnum; + + PipelineBindPoint(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/PipelineLayout.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/PipelineLayout.java new file mode 100644 index 0000000000..f355bf6e39 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/PipelineLayout.java @@ -0,0 +1,117 @@ +package com.jme3.vulkan.pipeline; + +import com.jme3.util.AbstractBuilder; +import com.jme3.util.natives.AbstractNative; +import com.jme3.util.natives.Native; +import com.jme3.vulkan.descriptors.DescriptorSetLayout; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.material.NewMaterial; +import org.lwjgl.vulkan.VkPipelineLayoutCreateInfo; + +import java.nio.LongBuffer; +import java.util.*; + +import static com.jme3.renderer.vulkan.VulkanUtils.*; +import static org.lwjgl.vulkan.VK10.*; + +public class PipelineLayout extends AbstractNative { + + private final LogicalDevice device; + private Collection layouts; + + public PipelineLayout(LogicalDevice device, DescriptorSetLayout... layouts) { + this.device = device; + } + + @Override + public Runnable createNativeDestroyer() { + return () -> vkDestroyPipelineLayout(device.getNativeObject(), object, null); + } + + public Collection getDescriptorSetLayouts() { + return Collections.unmodifiableCollection(layouts); + } + + public Builder build() { + return new Builder(); + } + + public class Builder extends AbstractBuilder { + + private final Collection descriptorLayouts = new ArrayList<>(); + private final Collection supportedMaterials = new ArrayList<>(); + + @Override + protected void build() { + Collection descriptors = createDescriptors(); + LongBuffer layoutBuf = stack.mallocLong(descriptors.size()); + for (DescriptorSetLayout l : descriptors) { + layoutBuf.put(l.build(stack).getNativeObject()); + } + layoutBuf.flip(); + VkPipelineLayoutCreateInfo create = VkPipelineLayoutCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO) + .setLayoutCount(layoutBuf.limit()) + .pSetLayouts(layoutBuf); + LongBuffer idBuf = stack.mallocLong(1); + check(vkCreatePipelineLayout(device.getNativeObject(), create, null, idBuf), + "Failed to create pipeline."); + object = idBuf.get(0); + ref = Native.get().register(PipelineLayout.this); + device.getNativeReference().addDependent(ref); + } + + private Collection createDescriptors() { + // The number of duplicates of a particular layout to return is the + // maximum of those duplicates that come from any material. + Map layoutBuckets = new HashMap<>(); + int usedLayouts = 0; + for (NewMaterial m : supportedMaterials) { + DescriptorSetLayout[] layouts = m.createLayouts(device); + for (DescriptorSetLayout l : layouts) { + LayoutCount c = layoutBuckets.get(l); + if (c == null) { + layoutBuckets.put(l, c = new LayoutCount()); + } + if (c.add(l)) { + usedLayouts++; + } + } + layoutBuckets.values().forEach(LayoutCount::reset); + } + layouts.clear(); + layouts = new ArrayList<>(usedLayouts + descriptorLayouts.size()); + layouts.addAll(descriptorLayouts); + layoutBuckets.values().forEach(layouts::addAll); + return layouts; + } + + public void addDescriptorLayout(DescriptorSetLayout layout) { + descriptorLayouts.add(layout); + } + + public void addMaterial(NewMaterial material) { + supportedMaterials.add(material); + } + + } + + private static class LayoutCount extends ArrayList { + + private int material = 0; + + @Override + public boolean add(DescriptorSetLayout l) { + if (++material > size()) { + return super.add(l); + } + return false; + } + + public void reset() { + material = 0; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/PipelineStage.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/PipelineStage.java new file mode 100644 index 0000000000..3e7fa970d9 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/PipelineStage.java @@ -0,0 +1,39 @@ +package com.jme3.vulkan.pipeline; + +import com.jme3.vulkan.util.Flag; + +import static org.lwjgl.vulkan.VK10.*; + +public enum PipelineStage implements Flag { + + TopOfPipe(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT), + ColorAttachmentOutput(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT), + AllCommands(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT), + AllGraphics(VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT), + EarlyFragmentTests(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT), + BottomOfPipe(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT), + ComputeShader(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT), + DrawIndirect(VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT), + FragmentShader(VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT), + GeometryShader(VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT), + Host(VK_PIPELINE_STAGE_HOST_BIT), + LateFragmentTests(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT), + TessellationControlShader(VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT), + TessellationEvalShader(VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT), + Transfer(VK_PIPELINE_STAGE_TRANSFER_BIT), + VertexInput(VK_PIPELINE_STAGE_VERTEX_INPUT_BIT), + VertexShader(VK_PIPELINE_STAGE_VERTEX_SHADER_BIT), + None(0); + + private final int vkEnum; + + PipelineStage(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int bits() { + return vkEnum; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/PolygonMode.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/PolygonMode.java new file mode 100644 index 0000000000..a6c3b78d00 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/PolygonMode.java @@ -0,0 +1,24 @@ +package com.jme3.vulkan.pipeline; + +import com.jme3.vulkan.util.IntEnum; + +import static org.lwjgl.vulkan.VK10.*; + +public enum PolygonMode implements IntEnum { + + Fill(VK_POLYGON_MODE_FILL), + Line(VK_POLYGON_MODE_LINE), + Point(VK_POLYGON_MODE_POINT); + + private final int vkEnum; + + PolygonMode(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/Topology.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/Topology.java new file mode 100644 index 0000000000..11c4b37e97 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/Topology.java @@ -0,0 +1,32 @@ +package com.jme3.vulkan.pipeline; + +import com.jme3.vulkan.util.IntEnum; + +import static org.lwjgl.vulkan.VK10.*; + +public enum Topology implements IntEnum { + + LineList(VK_PRIMITIVE_TOPOLOGY_LINE_LIST), + LineStrip(VK_PRIMITIVE_TOPOLOGY_LINE_STRIP), + TriangleList(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST), + PatchList(VK_PRIMITIVE_TOPOLOGY_PATCH_LIST), + PointList(VK_PRIMITIVE_TOPOLOGY_POINT_LIST), + LineListAdjacency(VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY), + LineStripAdjacency(VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY), + TriangleFan(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN), + TriangleListAdjacency(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY), + TriangleStrip(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP), + TriangleStripAdjacency(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY); + + private final int vkEnum; + + Topology(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/cache/PipelineCache.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/cache/PipelineCache.java new file mode 100644 index 0000000000..334ba0d0eb --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/cache/PipelineCache.java @@ -0,0 +1,109 @@ +package com.jme3.vulkan.pipeline.cache; + +import com.jme3.asset.AssetManager; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.pipeline.Pipeline; +import com.jme3.vulkan.pipeline.states.BasePipelineState; +import com.jme3.vulkan.pipeline.states.IShaderState; +import com.jme3.vulkan.shader.ShaderModule; + +import java.util.*; +import java.util.function.Supplier; + +public class PipelineCache { + + public static final long DEFAULT_TIMEOUT = 2000L; + + private final LogicalDevice device; + private final AssetManager assetManager; + private final Map, CacheElement> pipelines = new HashMap<>(); + private final Map> shaders = new HashMap<>(); + private long timeout = DEFAULT_TIMEOUT; + + public PipelineCache(LogicalDevice device, AssetManager assetManager) { + this.device = device; + this.assetManager = assetManager; + } + + public Supplier acquirePipeline(BasePipelineState state, Pipeline parent) { + CacheElement pipeline = pipelines.get(state); + if (pipeline == null) { + pipeline = new CacheElement<>(); + Collection shaders = new ArrayList<>(state.getPipelineShaderStates().size()); + for (IShaderState s : state.getPipelineShaderStates()) { + CacheElement e = acquireShader(s); + shaders.add(e.get()); + e.addDependent(pipeline); + } + pipeline.set(state.createPipeline(device, parent, shaders)); + pipelines.put(pipeline.peek().getState(), pipeline); + } + return pipeline; + } + + protected CacheElement acquireShader(IShaderState state) { + CacheElement shader = shaders.get(state); + if (shader == null) { + shader = new CacheElement<>(new ShaderModule(device, assetManager, state)); + shaders.put(state, shader); + } + return shader; + } + + public void flush() { + long current = System.currentTimeMillis(); + pipelines.values().removeIf(p -> p.isIdle(current)); + shaders.values().removeIf(s -> s.isIdle(current)); + } + + public void clear() { + pipelines.clear(); + shaders.clear(); + } + + public void setTimeout(long timeout) { + this.timeout = timeout; + } + + public long getTimeout() { + return timeout; + } + + protected class CacheElement implements Supplier { + + private T object; + private long lastUsed = System.currentTimeMillis(); + private final Collection dependents = new ArrayList<>(); + + public CacheElement() {} + + public CacheElement(T object) { + this.object = object; + } + + public void set(T object) { + this.object = object; + } + + @Override + public T get() { + lastUsed = System.currentTimeMillis(); + return object; + } + + public T peek() { + return object; + } + + public boolean isIdle(long current) { + return Math.abs(current - lastUsed) > timeout + && dependents.stream().allMatch(d -> d.isIdle(current)); + } + + public void addDependent(CacheElement d) { + dependents.add(d); + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/framebuffer/FrameBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/framebuffer/FrameBuffer.java new file mode 100644 index 0000000000..decde05b3b --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/framebuffer/FrameBuffer.java @@ -0,0 +1,27 @@ +package com.jme3.vulkan.pipeline.framebuffer; + +import com.jme3.texture.ImageView; + +import java.util.List; + +public interface FrameBuffer { + + void addColorTarget(T image); + + void setColorTarget(int i, T image); + + void removeColorTarget(int i); + + void removeColorTarget(T image); + + void clearColorTargets(); + + void setDepthTarget(T image); + + List getColorTargets(); + + T getColorTarget(int i); + + T getDepthTarget(); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/framebuffer/VulkanFrameBuffer.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/framebuffer/VulkanFrameBuffer.java new file mode 100644 index 0000000000..6fc378a346 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/framebuffer/VulkanFrameBuffer.java @@ -0,0 +1,192 @@ +package com.jme3.vulkan.pipeline.framebuffer; + +import com.jme3.util.natives.AbstractNative; +import com.jme3.util.natives.Native; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.images.VulkanImage; +import com.jme3.vulkan.images.VulkanImageView; +import com.jme3.vulkan.pass.RenderPass; +import com.jme3.vulkan.util.Flag; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkFramebufferCreateInfo; + +import java.nio.LongBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static com.jme3.renderer.vulkan.VulkanUtils.*; +import static org.lwjgl.vulkan.VK14.*; + +public class VulkanFrameBuffer extends AbstractNative implements FrameBuffer { + + public enum Create implements Flag { + + Imageless(VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT); + + private final int bits; + + Create(int bits) { + this.bits = bits; + } + + @Override + public int bits() { + return bits; + } + + } + + private final LogicalDevice device; + private final RenderPass compat; + private final int width, height, layers; + private final List colorTargets = new ArrayList<>(); + private VulkanImageView depthTarget; + private Flag flags = Flag.empty(); + private boolean updateNeeded = true; + + public VulkanFrameBuffer(LogicalDevice device, RenderPass compat, int width, int height, int layers) { + this.device = device; + this.compat = compat; + this.width = width; + this.height = height; + this.layers = layers; + } + + @Override + public Long getNativeObject() { + if (updateNeeded) { + build(); + } + return super.getNativeObject(); + } + + @Override + public Runnable createNativeDestroyer() { + return () -> vkDestroyFramebuffer(device.getNativeObject(), object, null); + } + + @Override + public void addColorTarget(VulkanImageView image) { + assert image.getAspect().contains(VulkanImage.Aspect.Color) : "Image must have a color aspect."; + colorTargets.add(image); + updateNeeded = false; + } + + @Override + public void setColorTarget(int i, VulkanImageView image) { + assert image.getAspect().contains(VulkanImage.Aspect.Color) : "Image must have a color aspect."; + if (colorTargets.set(i, image) != image) { + updateNeeded = true; + } + } + + @Override + public void removeColorTarget(int i) { + if (i < colorTargets.size()) { + colorTargets.remove(i); + updateNeeded = true; + } + } + + @Override + public void removeColorTarget(VulkanImageView image) { + if (colorTargets.removeIf(c -> c == image)) { + updateNeeded = true; + } + } + + @Override + public void clearColorTargets() { + if (!colorTargets.isEmpty()) { + colorTargets.clear(); + updateNeeded = true; + } + } + + @Override + public void setDepthTarget(VulkanImageView image) { + assert image == null || image.getAspect().contains(VulkanImage.Aspect.Depth) : "Image must have a depth aspect."; + if (this.depthTarget != image) { + this.depthTarget = image; + updateNeeded = true; + } + } + + @Override + public List getColorTargets() { + return Collections.unmodifiableList(colorTargets); + } + + @Override + public VulkanImageView getColorTarget(int i) { + return colorTargets.get(i); + } + + @Override + public VulkanImageView getDepthTarget() { + return depthTarget; + } + + public void setFlags(Flag flags) { + if (!Flag.is(this.flags, Objects.requireNonNull(flags))) { + this.flags = flags; + updateNeeded = true; + } + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public int getLayers() { + return layers; + } + + public Flag getFlags() { + return flags; + } + + public int getNumTargets() { + return colorTargets.size() + (depthTarget != null ? 1 : 0); + } + + protected void build() { + if (ref != null) { + ref.destroy(); + ref = null; + } + try (MemoryStack stack = MemoryStack.stackPush()) { + LongBuffer att = stack.mallocLong(getNumTargets()); + for (VulkanImageView c : colorTargets) { + if (c == null) { + throw new NullPointerException("Cannot attach null color target."); + } + att.put(c.getId()); + } + if (depthTarget != null) { + att.put(depthTarget.getId()); + } + att.flip(); + VkFramebufferCreateInfo create = VkFramebufferCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO) + .flags(flags.bits()) + .renderPass(compat.getNativeObject()) + .pAttachments(att) + .width(width).height(height) + .layers(layers); + LongBuffer idBuf = stack.mallocLong(1); + check(vkCreateFramebuffer(device.getNativeObject(), create, null, idBuf)); + object = idBuf.get(0); + } + ref = Native.get().register(VulkanFrameBuffer.this); + device.getNativeReference().addDependent(ref); + updateNeeded = false; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/graphics/ColorBlendAttachment.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/graphics/ColorBlendAttachment.java new file mode 100644 index 0000000000..abea0819e7 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/graphics/ColorBlendAttachment.java @@ -0,0 +1,158 @@ +package com.jme3.vulkan.pipeline.graphics; + +import org.lwjgl.vulkan.VkPipelineColorBlendAttachmentState; + +import java.util.Objects; + +import static org.lwjgl.vulkan.VK10.*; + +public class ColorBlendAttachment { + + private int colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT + | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + private boolean blend = false; + private int srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + private int dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; + private int colorBlendOp = VK_BLEND_OP_ADD; + private int srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + private int dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + private int alphaBlendOp = VK_BLEND_OP_ADD; + protected long version = 0L; + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + ColorBlendAttachment that = (ColorBlendAttachment) o; + return colorWriteMask == that.colorWriteMask + && blend == that.blend + && srcColorBlendFactor == that.srcColorBlendFactor + && dstColorBlendFactor == that.dstColorBlendFactor + && colorBlendOp == that.colorBlendOp + && srcAlphaBlendFactor == that.srcAlphaBlendFactor + && dstAlphaBlendFactor == that.dstAlphaBlendFactor + && alphaBlendOp == that.alphaBlendOp; + } + + @Override + public int hashCode() { + return Objects.hash(colorWriteMask, blend, srcColorBlendFactor, dstColorBlendFactor, colorBlendOp, + srcAlphaBlendFactor, dstAlphaBlendFactor, alphaBlendOp); + } + + public ColorBlendAttachment copy() { + ColorBlendAttachment copy = new ColorBlendAttachment(); + copy.colorWriteMask = colorWriteMask; + copy.blend = blend; + copy.srcColorBlendFactor = srcColorBlendFactor; + copy.dstColorBlendFactor = dstColorBlendFactor; + copy.colorBlendOp = colorBlendOp; + copy.srcAlphaBlendFactor = srcAlphaBlendFactor; + copy.dstAlphaBlendFactor = dstAlphaBlendFactor; + copy.alphaBlendOp = alphaBlendOp; + return copy; + } + + public long getCurrentVersion() { + return version; + } + + public void writeToStruct(VkPipelineColorBlendAttachmentState struct) { + struct.colorWriteMask(colorWriteMask) + .blendEnable(blend) + .srcColorBlendFactor(srcColorBlendFactor) + .dstColorBlendFactor(dstColorBlendFactor) + .colorBlendOp(colorBlendOp) + .srcAlphaBlendFactor(srcAlphaBlendFactor) + .dstAlphaBlendFactor(dstAlphaBlendFactor) + .alphaBlendOp(alphaBlendOp); + } + + public void setColorWriteMask(int colorWriteMask) { + if (this.colorWriteMask != colorWriteMask) { + this.colorWriteMask = colorWriteMask; + version++; + } + } + + public void setBlend(boolean blend) { + if (this.blend != blend) { + this.blend = blend; + version++; + } + } + + public void setSrcColorBlendFactor(int srcColorBlendFactor) { + if (this.srcColorBlendFactor != srcColorBlendFactor) { + this.srcColorBlendFactor = srcColorBlendFactor; + version++; + } + } + + public void setDstColorBlendFactor(int dstColorBlendFactor) { + if (this.dstColorBlendFactor != dstColorBlendFactor) { + this.dstColorBlendFactor = dstColorBlendFactor; + version++; + } + } + + public void setColorBlendOp(int colorBlendOp) { + if (this.colorBlendOp != colorBlendOp) { + this.colorBlendOp = colorBlendOp; + version++; + } + } + + public void setSrcAlphaBlendFactor(int srcAlphaBlendFactor) { + if (this.srcAlphaBlendFactor != srcAlphaBlendFactor) { + this.srcAlphaBlendFactor = srcAlphaBlendFactor; + version++; + } + } + + public void setDstAlphaBlendFactor(int dstAlphaBlendFactor) { + if (this.dstAlphaBlendFactor != dstAlphaBlendFactor) { + this.dstAlphaBlendFactor = dstAlphaBlendFactor; + version++; + } + } + + public void setAlphaBlendOp(int alphaBlendOp) { + if (this.alphaBlendOp != alphaBlendOp) { + this.alphaBlendOp = alphaBlendOp; + version++; + } + } + + public int getColorWriteMask() { + return colorWriteMask; + } + + public boolean isBlend() { + return blend; + } + + public int getSrcColorBlendFactor() { + return srcColorBlendFactor; + } + + public int getDstColorBlendFactor() { + return dstColorBlendFactor; + } + + public int getColorBlendOp() { + return colorBlendOp; + } + + public int getSrcAlphaBlendFactor() { + return srcAlphaBlendFactor; + } + + public int getDstAlphaBlendFactor() { + return dstAlphaBlendFactor; + } + + public int getAlphaBlendOp() { + return alphaBlendOp; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/graphics/GraphicsPipeline.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/graphics/GraphicsPipeline.java new file mode 100644 index 0000000000..0bdfd1dcd6 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/graphics/GraphicsPipeline.java @@ -0,0 +1,62 @@ +package com.jme3.vulkan.pipeline.graphics; + +import com.jme3.util.natives.Native; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.pass.Subpass; +import com.jme3.vulkan.pipeline.AbstractVulkanPipeline; +import com.jme3.vulkan.pipeline.Pipeline; +import com.jme3.vulkan.pipeline.PipelineBindPoint; +import com.jme3.vulkan.pipeline.PipelineLayout; +import com.jme3.vulkan.pipeline.states.BasePipelineState; +import com.jme3.vulkan.pipeline.states.PipelineState; +import com.jme3.vulkan.shader.ShaderModule; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.*; + +import java.nio.LongBuffer; +import java.util.Collection; + +import static com.jme3.renderer.vulkan.VulkanUtils.check; +import static org.lwjgl.vulkan.VK10.*; + +public class GraphicsPipeline extends AbstractVulkanPipeline { + + private final BasePipelineState state; + + /** + * Creates a new GraphicsPipeline with the given state. + * + * @param device logical device + * @param state graphics state (alias created) + */ + public GraphicsPipeline(LogicalDevice device, + PipelineLayout layout, + Pipeline parent, + BasePipelineState state, + Collection shaders) { + super(device, layout, PipelineBindPoint.Graphics, parent); + this.state = state.copy(); // create alias to avoid outside mutations mucking things up + try (MemoryStack stack = MemoryStack.stackPush()) { + VkGraphicsPipelineCreateInfo.Buffer create = VkGraphicsPipelineCreateInfo.calloc(1, stack) + .sType(VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO); + state.fill(stack, create.get(), shaders); + if (parent != null) { + create.basePipelineHandle(parent.getNativeObject()); + } + create.flip(); + LongBuffer idBuf = stack.mallocLong(1); + // todo: look into pipeline caching in the vulkan API + check(vkCreateGraphicsPipelines(device.getNativeObject(), VK_NULL_HANDLE, create, null, idBuf), + "Failed to create graphics pipeline"); + object = idBuf.get(0); + } + ref = Native.get().register(GraphicsPipeline.this); + device.getNativeReference().addDependent(ref); + } + + @Override + public BasePipelineState getState() { + return state; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/graphics/GraphicsState.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/graphics/GraphicsState.java new file mode 100644 index 0000000000..c3f393281c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/graphics/GraphicsState.java @@ -0,0 +1,830 @@ +package com.jme3.vulkan.pipeline.graphics; + +import com.jme3.util.Versionable; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.mesh.MeshDescription; +import com.jme3.vulkan.pass.Subpass; +import com.jme3.vulkan.pipeline.*; +import com.jme3.vulkan.pipeline.cache.PipelineCache; +import com.jme3.vulkan.pipeline.states.*; +import com.jme3.vulkan.shader.ShaderModule; +import com.jme3.vulkan.shader.ShaderStage; +import com.jme3.vulkan.util.Flag; +import com.jme3.vulkan.util.IntEnum; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.*; + +import java.nio.IntBuffer; +import java.util.*; +import java.util.function.Supplier; + +import static org.lwjgl.vulkan.VK10.*; + +public class GraphicsState implements BasePipelineState, Versionable { + + protected static final int BASE_INDEX = 0; + + // graphics + protected static final int CREATE_FLAGS = BASE_INDEX; + private Subpass subpass; + private PipelineLayout layout; + private Flag flags = Flag.empty(); + + // shaders + private final Map shaders = new HashMap<>(); + + // vertex input + private MeshDescription mesh; // not mutated the normal way + + // input assembly + protected static final int TOPOLOGY = BASE_INDEX + 1; + protected static final int PRIMITIVE_RESTART = BASE_INDEX + 2; + private IntEnum topology = Topology.TriangleList; + private Boolean primitiveRestart = false; + + // depth stencil + protected static final int DEPTH_TEST = BASE_INDEX + 3; + protected static final int DEPTH_WRITE = BASE_INDEX + 4; + protected static final int DEPTH_BOUNDS_TEST = BASE_INDEX + 5; + protected static final int STENCIL_TEST = BASE_INDEX + 6; + protected static final int DEPTH_COMPARE = BASE_INDEX + 7; + private boolean depthTest = true; + private boolean depthWrite = true; + private boolean depthBoundsTest = false; + private boolean stencilTest = false; + private IntEnum depthCompare = CompareOp.LessOrEqual; + + // rasterization + protected static final int POLYGON_MODE = BASE_INDEX + 8; + protected static final int CULL_MODE = BASE_INDEX + 9; + protected static final int FACE_WINDING = BASE_INDEX + 10; + protected static final int LINE_WIDTH = BASE_INDEX + 11; + protected static final int DEPTH_CLAMP = BASE_INDEX + 12; + protected static final int RASTERIZER_DISCARD = BASE_INDEX + 13; + protected static final int DEPTH_BIAS = BASE_INDEX + 14; + private IntEnum polygonMode = PolygonMode.Fill; + private Flag cullMode = CullMode.Back; + private IntEnum faceWinding = FaceWinding.Clockwise; + private float lineWidth = 1f; + private boolean depthClamp = false; + private boolean rasterizerDiscard = false; + private boolean depthBias = false; + + // multisample + protected static final int RASTERIZATION_SAMPLES = BASE_INDEX + 15; + protected static final int SAMPLE_SHADING = BASE_INDEX + 16; + private int rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + private boolean sampleShading = false; + + // color blend + protected static final int BLEND_LOGIC_ENABLED = 17; + protected static final int BLEND_LOGIC = 18; + private final List blendAttachments = new ArrayList<>(); + private boolean blendLogicEnabled = false; + private IntEnum blendLogic = LogicOp.Copy; + + // viewport + private final List viewports = new ArrayList<>(); + private final List scissors = new ArrayList<>(); + + // dynamic + private final Set dynamicStates = new HashSet<>(); + + // management + protected static final int LAST_USED_INDEX = BLEND_LOGIC; + protected final Map supportedPipelines = new HashMap<>(); + protected final BitSet applied = new BitSet(); + protected long version = 0L; + + public GraphicsState() {} + + @Override + public VkGraphicsPipelineCreateInfo create(MemoryStack stack) { + return VkGraphicsPipelineCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO); + } + + @Override + public VkGraphicsPipelineCreateInfo fill(MemoryStack stack, VkGraphicsPipelineCreateInfo struct, Collection shaders) { + struct.flags(getFlags().bits()) + .layout(layout.getNativeObject()) + .renderPass(subpass.getPass().getNativeObject()) + .subpass(subpass.getPosition()); + if (!shaders.isEmpty()) { + VkPipelineShaderStageCreateInfo.Buffer stageBuf = VkPipelineShaderStageCreateInfo.calloc(shaders.size(), stack); + for (ShaderModule s : shaders) { + fillShaderInfo(stack, s, stageBuf.get().sType(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO)); + } + stageBuf.flip(); + struct.stageCount(stageBuf.limit()).pStages(stageBuf); + } + return struct.pVertexInputState(createVertexInput(stack)) + .pInputAssemblyState(createInputAssembly(stack)) + .pDepthStencilState(createDepthStencil(stack)) + .pRasterizationState(createRasterization(stack)) + .pMultisampleState(createMultisample(stack)) + .pColorBlendState(createColorBlend(stack)) + .pViewportState(createViewport(stack)) + .pDynamicState(createDynamic(stack)); + } + + @Override + public GraphicsState copy(GraphicsState store) { + if (store == null) { + store = new GraphicsState(); + } + store.subpass = subpass; + store.layout = layout; + store.mesh = mesh; + store.topology = topology; + store.primitiveRestart = primitiveRestart; + store.depthTest = depthTest; + store.depthWrite = depthWrite; + store.depthBoundsTest = depthBoundsTest; + store.stencilTest = stencilTest; + store.depthCompare = depthCompare; + store.polygonMode = polygonMode; + store.cullMode = cullMode; + store.faceWinding = faceWinding; + store.lineWidth = lineWidth; + store.depthClamp = depthClamp; + store.rasterizerDiscard = rasterizerDiscard; + store.rasterizationSamples = rasterizationSamples; + store.sampleShading = sampleShading; + store.blendLogicEnabled = blendLogicEnabled; + store.blendLogic = blendLogic; + store.viewports.addAll(viewports); + store.scissors.addAll(scissors); + store.dynamicStates.addAll(dynamicStates); + store.shaders.putAll(shaders); + store.blendAttachments.addAll(blendAttachments); + return store; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + GraphicsState that = (GraphicsState) o; + return depthTest == that.depthTest + && depthWrite == that.depthWrite + && depthBoundsTest == that.depthBoundsTest + && stencilTest == that.stencilTest + && Float.compare(lineWidth, that.lineWidth) == 0 + && depthClamp == that.depthClamp + && rasterizerDiscard == that.rasterizerDiscard + && depthBias == that.depthBias + && rasterizationSamples == that.rasterizationSamples + && sampleShading == that.sampleShading + && blendLogicEnabled == that.blendLogicEnabled + && Objects.equals(subpass, that.subpass) + && Objects.equals(layout, that.layout) + && Objects.equals(flags, that.flags) + && Objects.equals(mesh, that.mesh) + && Objects.equals(topology, that.topology) + && Objects.equals(primitiveRestart, that.primitiveRestart) + && Objects.equals(depthCompare, that.depthCompare) + && Objects.equals(polygonMode, that.polygonMode) + && Objects.equals(cullMode, that.cullMode) + && Objects.equals(faceWinding, that.faceWinding) + && Objects.equals(blendAttachments, that.blendAttachments) + && Objects.equals(blendLogic, that.blendLogic) + && Objects.equals(viewports, that.viewports) + && Objects.equals(scissors, that.scissors) + && Objects.equals(dynamicStates, that.dynamicStates) + && Objects.equals(shaders, that.shaders); + } + + @Override + public int hashCode() { + return Objects.hash(subpass, layout, flags, mesh, topology, primitiveRestart, depthTest, depthWrite, + depthBoundsTest, stencilTest, depthCompare, polygonMode, cullMode, faceWinding, lineWidth, depthClamp, + rasterizerDiscard, depthBias, rasterizationSamples, sampleShading, blendAttachments, blendLogicEnabled, + blendLogic, viewports, scissors, dynamicStates, shaders); + } + + @Override + public Pipeline selectPipeline(PipelineCache cache, MeshDescription mesh) { + this.mesh = mesh; + CachedPipeline result = supportedPipelines.get(mesh); + if (result == null || result.version != version) { + // find an up-to-date pipeline for a different mesh to be the parent, + // otherwise let the cache take care of it + Pipeline parent = supportedPipelines.values().stream() + .filter(p -> p.version == version) + .findAny() + .map(CachedPipeline::getPipeline).orElse(null); + result = new CachedPipeline(cache.acquirePipeline(this, parent), version); + supportedPipelines.put(mesh, result); + } + return result.getPipeline(); + } + + @Override + public GraphicsPipeline createPipeline(LogicalDevice device, Pipeline parent, Collection shaders) { + if (layout == null) { + throw new NullPointerException("Pipeline layout is not defined."); + } + return new GraphicsPipeline(device, layout, parent, this, shaders); + } + + @Override + public Collection getPipelineShaderStates() { + return Collections.unmodifiableCollection(shaders.values()); + } + + @Override + public long getVersionNumber() { + return version; + } + + @Override + public GraphicsState override(GraphicsState state, GraphicsState store) { + if (store == null) { + store = new GraphicsState(); + } + store.subpass = subpass != null ? subpass : state.subpass; + store.layout = layout != null ? layout : state.layout; + store.flags = override(state.flags, flags, CREATE_FLAGS); + store.topology = override(state.topology, topology, TOPOLOGY); + store.primitiveRestart = override(state.primitiveRestart, primitiveRestart, PRIMITIVE_RESTART); + store.depthTest = override(state.depthTest, depthTest, DEPTH_TEST); + store.depthWrite = override(state.depthWrite, depthWrite, DEPTH_WRITE); + store.depthBoundsTest = override(state.depthBoundsTest, depthBoundsTest, DEPTH_BOUNDS_TEST); + store.stencilTest = override(state.stencilTest, stencilTest, STENCIL_TEST); + store.depthCompare = override(state.depthCompare, depthCompare, DEPTH_COMPARE); + store.polygonMode = override(state.polygonMode, polygonMode, POLYGON_MODE); + store.cullMode = override(state.cullMode, cullMode, CULL_MODE); + store.faceWinding = override(state.faceWinding, faceWinding, FACE_WINDING); + store.lineWidth = override(state.lineWidth, lineWidth, LINE_WIDTH); + store.depthClamp = override(state.depthClamp, depthClamp, DEPTH_CLAMP); + store.rasterizerDiscard = override(state.rasterizerDiscard, rasterizerDiscard, RASTERIZER_DISCARD); + store.depthBias = override(state.depthBias, depthBias, DEPTH_BIAS); + store.rasterizationSamples = override(state.rasterizationSamples, rasterizationSamples, RASTERIZATION_SAMPLES); + store.sampleShading = override(state.sampleShading, sampleShading, SAMPLE_SHADING); + store.blendLogicEnabled = override(state.blendLogicEnabled, blendLogicEnabled, BLEND_LOGIC_ENABLED); + store.blendLogic = override(state.blendLogic, blendLogic, BLEND_LOGIC); + store.blendAttachments.clear(); + override(state.blendAttachments, blendAttachments, store.blendAttachments); + store.dynamicStates.clear(); + store.dynamicStates.addAll(state.dynamicStates); + store.dynamicStates.addAll(dynamicStates); + store.shaders.clear(); + store.shaders.putAll(state.shaders); + store.shaders.putAll(shaders); + override(state.viewports, viewports, store.viewports); + override(state.scissors, scissors, store.scissors); + store.applied.clear(); + store.applied.or(state.applied); + store.applied.or(applied); + return store; + } + + @Override + public final Class getBaseStateClass() { + return GraphicsState.class; + } + + protected void fillShaderInfo(MemoryStack stack, ShaderModule shader, VkPipelineShaderStageCreateInfo struct) { + shader.fill(stack, struct); + } + + protected VkPipelineVertexInputStateCreateInfo createVertexInput(MemoryStack stack) { + return VkPipelineVertexInputStateCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO) + .pVertexBindingDescriptions(mesh.getBindingInfo(stack)) + .pVertexAttributeDescriptions(mesh.getAttributeInfo(stack)); + } + + protected VkPipelineInputAssemblyStateCreateInfo createInputAssembly(MemoryStack stack) { + return VkPipelineInputAssemblyStateCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO) + .topology(topology.getEnum()) + .primitiveRestartEnable(primitiveRestart); + } + + protected VkPipelineDepthStencilStateCreateInfo createDepthStencil(MemoryStack stack) { + return VkPipelineDepthStencilStateCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO) + .depthTestEnable(depthTest) + .depthWriteEnable(depthWrite) + .depthBoundsTestEnable(depthBoundsTest) + .stencilTestEnable(stencilTest) + .depthCompareOp(depthCompare.getEnum()); + } + + protected VkPipelineRasterizationStateCreateInfo createRasterization(MemoryStack stack) { + return VkPipelineRasterizationStateCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO) + .depthClampEnable(depthClamp) + .rasterizerDiscardEnable(rasterizerDiscard) + .polygonMode(polygonMode.getEnum()) + .lineWidth(lineWidth) + .cullMode(cullMode.bits()) + .frontFace(faceWinding.getEnum()) + .depthBiasEnable(depthBias); + } + + protected VkPipelineMultisampleStateCreateInfo createMultisample(MemoryStack stack) { + return VkPipelineMultisampleStateCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO) + .sampleShadingEnable(sampleShading) + .rasterizationSamples(rasterizationSamples); + } + + protected VkPipelineColorBlendStateCreateInfo createColorBlend(MemoryStack stack) { + VkPipelineColorBlendAttachmentState.Buffer attBuf = VkPipelineColorBlendAttachmentState + .calloc(blendAttachments.size(), stack); + for (int i = 0; i < blendAttachments.size(); i++) { + ColorBlendAttachment a = blendAttachments.get(i); + if (a == null) { + throw new NullPointerException("Blend attachment " + i + " is not defined."); + } + a.writeToStruct(attBuf.get()); + } + attBuf.flip(); + return VkPipelineColorBlendStateCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO) + .logicOpEnable(blendLogicEnabled) + .logicOp(blendLogic.getEnum()) + .pAttachments(attBuf); + } + + protected VkPipelineViewportStateCreateInfo createViewport(MemoryStack stack) { + VkViewport.Buffer vpBuf = VkViewport.calloc(viewports.size(), stack); + for (ViewportInfo v : viewports) { + vpBuf.get().x(v.x).y(v.y).width(v.w).height(v.h).minDepth(v.min).maxDepth(v.max); + } + vpBuf.flip(); + VkRect2D.Buffer scissorBuf = VkRect2D.calloc(scissors.size(), stack); + for (ScissorInfo s : scissors) { + VkRect2D e = scissorBuf.get(); + e.offset().set(s.x, s.y); + e.extent().set(s.w, s.h); + } + scissorBuf.flip(); + return VkPipelineViewportStateCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO) + .pViewports(vpBuf) + .pScissors(scissorBuf); + } + + protected VkPipelineDynamicStateCreateInfo createDynamic(MemoryStack stack) { + IntBuffer stateBuf = stack.mallocInt(dynamicStates.size()); + for (Integer t : dynamicStates) { + stateBuf.put(t); + } + stateBuf.flip(); + return VkPipelineDynamicStateCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO) + .pDynamicStates(stateBuf); + } + + protected boolean stateEquals(GraphicsState that) { + return depthTest == that.depthTest + && depthWrite == that.depthWrite + && depthBoundsTest == that.depthBoundsTest + && stencilTest == that.stencilTest + && Float.compare(lineWidth, that.lineWidth) == 0 + && depthClamp == that.depthClamp + && rasterizerDiscard == that.rasterizerDiscard + && depthBias == that.depthBias + && rasterizationSamples == that.rasterizationSamples + && sampleShading == that.sampleShading + && blendLogicEnabled == that.blendLogicEnabled + && Objects.equals(subpass, that.subpass) + && Objects.equals(layout, that.layout) + && Objects.equals(flags, that.flags) + && Objects.equals(mesh, that.mesh) + && Objects.equals(topology, that.topology) + && Objects.equals(primitiveRestart, that.primitiveRestart) + && Objects.equals(depthCompare, that.depthCompare) + && Objects.equals(polygonMode, that.polygonMode) + && Objects.equals(cullMode, that.cullMode) + && Objects.equals(faceWinding, that.faceWinding) + && Objects.equals(blendAttachments, that.blendAttachments) + && Objects.equals(blendLogic, that.blendLogic) + && Objects.equals(viewports, that.viewports) + && Objects.equals(scissors, that.scissors) && Objects.equals(dynamicStates, that.dynamicStates); + } + + protected T override(T val, T override, int i) { + return applied.get(i) ? override : val; + } + + protected void override(List state, List overrides, List store) { + store.clear(); + for (int i = 0, l = Math.max(state.size(), overrides.size()); i < l; i++) { + if (i < overrides.size() && overrides.get(i) != null) { + store.add(overrides.get(i)); + } else if (i < state.size()) { + store.add(state.get(i)); + } else { + store.add(null); + } + } + } + + protected void incrementVersion(PipelineState oldState, PipelineState newState) { + if (oldState != newState) { + // must make sure the new state's version doesn't make this state "travel back in time" + version += 1 + Math.abs(PipelineState.versionOf(oldState) - PipelineState.versionOf(newState)); + } + } + + public void setSubpass(Subpass subpass) { + if (this.subpass != subpass) { + this.subpass = subpass; + version++; + } + } + + public void setLayout(PipelineLayout layout) { + if (this.layout != layout) { + this.layout = layout; + version++; + } + } + + public void setFlags(Flag flags) { + this.flags = set(this.flags, flags, CREATE_FLAGS); + } + + public void addShader(IShaderState shader) { + if (!shader.equals(shaders.put(shader.getStage().getVk(), shader))) { + version++; + } + } + + public IShaderState removeShader(ShaderStage stage) { + return shaders.remove(stage.getVk()); + } + + public void setTopology(IntEnum topology) { + this.topology = set(this.topology, topology, TOPOLOGY); + } + + public void setPrimitiveRestart(Boolean primitiveRestart) { + this.primitiveRestart = set(this.primitiveRestart, primitiveRestart, PRIMITIVE_RESTART); + } + + public void setDepthTest(Boolean depthTest) { + this.depthTest = set(this.depthTest, depthTest, DEPTH_TEST); + } + + public void setDepthWrite(Boolean depthWrite) { + this.depthWrite = set(this.depthWrite, depthWrite, DEPTH_WRITE); + } + + public void setDepthBoundsTest(Boolean depthBoundsTest) { + this.depthBoundsTest = set(this.depthBoundsTest, depthBoundsTest, DEPTH_BOUNDS_TEST); + } + + public void setStencilTest(Boolean stencilTest) { + this.stencilTest = set(this.stencilTest, stencilTest, STENCIL_TEST); + } + + public void setDepthCompare(IntEnum depthCompare) { + this.depthCompare = set(this.depthCompare, depthCompare, DEPTH_COMPARE); + } + + public void setPolygonMode(IntEnum polygonMode) { + this.polygonMode = set(this.polygonMode, polygonMode, POLYGON_MODE); + } + + public void setCullMode(Flag cullMode) { + this.cullMode = set(this.cullMode, cullMode, CULL_MODE); + } + + public void setFaceWinding(IntEnum faceWinding) { + this.faceWinding = set(this.faceWinding, faceWinding, FACE_WINDING); + } + + public void setLineWidth(Float lineWidth) { + this.lineWidth = set(this.lineWidth, lineWidth, LINE_WIDTH, DynamicState.LineWidth); + } + + public void setDepthClamp(Boolean depthClamp) { + this.depthClamp = set(this.depthClamp, depthClamp, DEPTH_CLAMP); + } + + public void setRasterizerDiscard(Boolean rasterizerDiscard) { + this.rasterizerDiscard = set(this.rasterizerDiscard, rasterizerDiscard, RASTERIZER_DISCARD); + } + + public void setDepthBias(Boolean depthBias) { + this.depthBias = set(this.depthBias, depthBias, DEPTH_BIAS, DynamicState.DepthBias); + } + + public void setRasterizationSamples(Integer rasterizationSamples) { + this.rasterizationSamples = set(this.rasterizationSamples, rasterizationSamples, RASTERIZATION_SAMPLES); + } + + public void setSampleShading(Boolean sampleShading) { + this.sampleShading = set(this.sampleShading, sampleShading, SAMPLE_SHADING); + } + + public void setBlendLogicEnabled(Boolean blendLogicEnabled) { + this.blendLogicEnabled = set(this.blendLogicEnabled, blendLogicEnabled, BLEND_LOGIC_ENABLED); + } + + public void setBlendLogic(IntEnum blendLogic) { + this.blendLogic = set(this.blendLogic, blendLogic, BLEND_LOGIC); + } + + public void setBlendAttachment(int i, ColorBlendAttachment attachment) { + while (blendAttachments.size() <= i) { + blendAttachments.add(null); + } + if (!attachment.equals(blendAttachments.set(i, attachment))) { + version++; + } + } + + public ColorBlendAttachment clearBlendAttachment(int i) { + if (i >= blendAttachments.size()) return null; + return blendAttachments.set(i, null); + } + + public void setViewPort(int i, float x, float y, float w, float h, float minDepth, float maxDepth) { + while (viewports.size() <= i) { + viewports.add(null); + } + ViewportInfo vp = new ViewportInfo(x, y, w, h, minDepth, maxDepth); + if (!vp.equals(viewports.set(i, vp)) && !isDynamic(DynamicState.ViewPort)) { + version++; + } + } + + public void setViewPort(int i, float x, float y, float w, float h) { + setViewPort(i, x, y, w, h, 0f, 1f); + } + + public void setViewPort(int i) { + setViewPort(i, 0f, 0f, 128f, 128f, 0f, 1f); + } + + public void setScissor(int i, int x, int y, int w, int h) { + while (scissors.size() <= i) { + scissors.add(null); + } + ScissorInfo scissor = new ScissorInfo(x, y, w, h); + if (!scissor.equals(scissors.set(i, scissor)) && !isDynamic(DynamicState.Scissor)) { + version++; + } + } + + public void setScissor(int i) { + setScissor(i, 0, 0, 128, 128); + } + + public void addDynamic(IntEnum dynamic) { + if (dynamicStates.add(dynamic.getEnum())) { + version++; + } + } + + public void removeDynamic(IntEnum dynamic) { + if (dynamicStates.remove(dynamic.getEnum())) { + version++; + } + } + + public Subpass getSubpass() { + return subpass; + } + + public PipelineLayout getLayout() { + return layout; + } + + public Flag getFlags() { + return flags; + } + + public Map getShaders() { + return Collections.unmodifiableMap(shaders); + } + + public IShaderState getShader(ShaderStage stage) { + return shaders.get(stage.getVk()); + } + + public IntEnum getTopology() { + return topology; + } + + public boolean isPrimitiveRestart() { + return primitiveRestart; + } + + public boolean isDepthTest() { + return depthTest; + } + + public boolean isDepthWrite() { + return depthWrite; + } + + public boolean isDepthBoundsTest() { + return depthBoundsTest; + } + + public boolean isStencilTest() { + return stencilTest; + } + + public IntEnum getDepthCompare() { + return depthCompare; + } + + public IntEnum getPolygonMode() { + return polygonMode; + } + + public Flag getCullMode() { + return cullMode; + } + + public IntEnum getFaceWinding() { + return faceWinding; + } + + public float getLineWidth() { + return lineWidth; + } + + public boolean isDepthClamp() { + return depthClamp; + } + + public boolean isRasterizerDiscard() { + return rasterizerDiscard; + } + + public boolean isDepthBias() { + return depthBias; + } + + public int getRasterizationSamples() { + return rasterizationSamples; + } + + public boolean isSampleShading() { + return sampleShading; + } + + public List getBlendAttachments() { + return Collections.unmodifiableList(blendAttachments); + } + + public boolean isBlendLogicEnabled() { + return blendLogicEnabled; + } + + public IntEnum getBlendLogic() { + return blendLogic; + } + + public Set getDynamicStates() { + return Collections.unmodifiableSet(dynamicStates); + } + + public boolean isDynamic(IntEnum dynamic) { + return dynamicStates.contains(dynamic.getEnum()); + } + + protected T get(T val, T defVal) { + return val != null ? val : defVal; + } + + protected T set(T oldVal, T newVal, int i) { + applied.set(i); + if (oldVal != newVal) version++; + return newVal; + } + + protected T set(T oldVal, T newVal, int i, IntEnum dynamic) { + applied.set(i); + if (oldVal != newVal && !isDynamic(dynamic)) version++; + return newVal; + } + + protected T setEq(T oldVal, T newVal, int i) { + applied.set(i); + if (!Objects.equals(oldVal, newVal)) version++; + return newVal; + } + + protected Flag set(Flag oldVal, Flag newVal, int i) { + applied.set(i); + if (!Flag.is(oldVal, newVal)) version++; + return newVal; + } + + protected IntEnum set(IntEnum oldVal, IntEnum newVal, int i) { + applied.set(i); + if (!IntEnum.is(oldVal, newVal)) version++; + return newVal; + } + + protected boolean changed(Object oldVal, Object newVal, Object defVal) { + return get(oldVal, defVal) != get(newVal, defVal); + } + + protected boolean objectChanged(Object oldVal, Object newVal, Object defVal) { + return !get(oldVal, defVal).equals(get(newVal, defVal)); + } + + protected boolean flagChanged(Flag oldVal, Flag newVal, Flag defVal) { + return !get(oldVal, defVal).is(get(newVal, defVal)); + } + + protected boolean enumChanged(IntEnum oldVal, IntEnum newVal, IntEnum defVal) { + return !get(oldVal, defVal).is(get(newVal, defVal)); + } + + protected static class CachedPipeline { + + public final Supplier pipeline; + public final long version; + + public CachedPipeline(Supplier pipeline, long version) { + this.pipeline = pipeline; + this.version = version; + } + + public Pipeline getPipeline() { + return pipeline.get(); + } + + public long getVersion() { + return version; + } + + } + + /** + * Immutable viewport information. + */ + protected static class ViewportInfo { + + public final float x, y, w, h; + public final float min, max; + + public ViewportInfo(float x, float y, float w, float h, float min, float max) { + this.x = x; + this.y = y; + this.w = w; + this.h = h; + this.min = min; + this.max = max; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + ViewportInfo that = (ViewportInfo) o; + return Float.compare(x, that.x) == 0 + && Float.compare(y, that.y) == 0 + && Float.compare(w, that.w) == 0 + && Float.compare(h, that.h) == 0 + && Float.compare(min, that.min) == 0 + && Float.compare(max, that.max) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(x, y, w, h, min, max); + } + + } + + /** + * Immutable scissor information. + */ + protected static class ScissorInfo { + + public final int x, y, w, h; + + public ScissorInfo(int x, int y, int w, int h) { + this.x = x; + this.y = y; + this.w = w; + this.h = h; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + ScissorInfo that = (ScissorInfo) o; + return x == that.x && y == that.y && w == that.w && h == that.h; + } + + @Override + public int hashCode() { + return Objects.hash(x, y, w, h); + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/states/BasePipelineState.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/states/BasePipelineState.java new file mode 100644 index 0000000000..1677de332d --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/states/BasePipelineState.java @@ -0,0 +1,141 @@ +package com.jme3.vulkan.pipeline.states; + +import com.jme3.util.Versionable; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.mesh.MeshDescription; +import com.jme3.vulkan.pipeline.Pipeline; +import com.jme3.vulkan.pipeline.cache.PipelineCache; +import com.jme3.vulkan.shader.ShaderModule; +import org.lwjgl.system.MemoryStack; + +import java.util.Collection; +import java.util.Map; + +public interface BasePipelineState extends Versionable { + + /** + * Creates a struct that can hold this state. Only the {@code sType} + * field should be set by this method. + * + * @param stack memory stack + * @return struct that may contain this state + */ + T create(MemoryStack stack); + + /** + * Fill {@code struct} with this state. + * + * @param stack memory stack + * @param struct struct to fill + * @param shaders {@link #getPipelineShaderStates() shaders} associated with this state + * @return filled struct + */ + T fill(MemoryStack stack, T struct, Collection shaders); + + /** + * Selects a pipeline to use based on the current state. + * + * @param cache cache that may be used for selection + * @param mesh mesh description to select with + * @return selected pipeline + */ + Pipeline selectPipeline(PipelineCache cache, MeshDescription mesh); + + /** + * Creates a new pipeline based on the current state. + * + * @param device logical device + * @param parent the new pipeline's parent (can be null) + * @param shaders shaders created from {@link #getPipelineShaderStates()} to use in pipeline creation + * @return new pipeline + */ + Pipeline createPipeline(LogicalDevice device, Pipeline parent, Collection shaders); + + /** + * + * @return shader states + */ + Collection getPipelineShaderStates(); + + /** + * Create a copy of this state. + * + * @return copy stores the copy result (or null to create a new state) + */ + SELF copy(SELF store); + + /** + * Overrides the parameters of {@code state} according to which + * parameters of this state have been set. + * + * @param state state to override + * @param store state to store the result (or null to create a new state) + * @return result of override operation + */ + SELF override(SELF state, SELF store); + + /** + * Returns the base class representing {@link SELF}. + * + * @return class of {@link SELF} + */ + Class getBaseStateClass(); + + /** + * {@link #copy(BasePipelineState) Copies} this state to a new state. + * + * @return state copying this state + */ + default SELF copy() { + return copy(null); + } + + /** + * {@link #override(BasePipelineState, BasePipelineState) Overrides} {@code state} + * with this state's parameters. + * + * @param state state to override + * @return result of override operation + */ + default SELF override(SELF state) { + return override(state, null); + } + + /** + * {@link #copy(BasePipelineState) Copies} this state to {@code store} if {@code store} is an instance + * of {@link #getBaseStateClass()}. Otherwise a new state is created + * to hold the copy. + * + * @param store stores the copy of this state, if possible (or null to create a new state) + * @return copy of this state + */ + @SuppressWarnings("unchecked") + default SELF copyAnonymous(BasePipelineState store) { + if (store == null || getBaseStateClass().isAssignableFrom(store.getClass())) { + return copy((SELF)store); + } else { + return copy(null); + } + } + + /** + * {@link #override(BasePipelineState, BasePipelineState) Overrides} {@code state} with this + * state's parameters. If {@code state} is not an instance of {@link #getBaseStateClass()}, + * then this state's parameters are {@link #copyAnonymous(BasePipelineState) copied anonymously} + * to {@code store}. + * + * @param state state to override (not null) + * @param store state to store the override operation (or null to create a new state) + * @return result of the override operation + */ + @SuppressWarnings("unchecked") + default SELF overrideAnonymous(BasePipelineState state, BasePipelineState store) { + if (getBaseStateClass().isAssignableFrom(state.getClass()) + && (store == null || getBaseStateClass().isAssignableFrom(store.getClass()))) { + return override((SELF)state, (SELF)store); + } else { + return copyAnonymous(store); + } + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/states/IShaderState.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/states/IShaderState.java new file mode 100644 index 0000000000..6caa658a5b --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/states/IShaderState.java @@ -0,0 +1,46 @@ +package com.jme3.vulkan.pipeline.states; + +import com.jme3.vulkan.shader.ShaderStage; + +import java.util.Arrays; +import java.util.Objects; + +public class IShaderState { + + private final String assetName; + private final String entryPoint; + private final ShaderStage stage; + + public IShaderState(String assetName, String entryPoint, ShaderStage stage) { + this.assetName = assetName; + this.entryPoint = entryPoint; + this.stage = stage; + } + + public String getAssetName() { + return assetName; + } + + public String getEntryPoint() { + return entryPoint; + } + + public ShaderStage getStage() { + return stage; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + IShaderState that = (IShaderState) o; + return Objects.equals(assetName, that.assetName) + && Objects.equals(entryPoint, that.entryPoint) + && Objects.equals(stage, that.stage); + } + + @Override + public int hashCode() { + return Objects.hash(assetName, entryPoint, stage); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/pipeline/states/PipelineState.java b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/states/PipelineState.java new file mode 100644 index 0000000000..e0e0406963 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/pipeline/states/PipelineState.java @@ -0,0 +1,69 @@ +package com.jme3.vulkan.pipeline.states; + +import org.lwjgl.system.MemoryStack; + +/** + * + * @param + * @deprecated use {@link BasePipelineState} + */ +@Deprecated +public interface PipelineState { + + /** + * Creates the struct but does not fill the struct with + * this state's information. Only the {@code sType} field + * should be written by this method. + * + * @param stack memory stack + * @return struct + */ + T create(MemoryStack stack); + + /** + * Fills {@code struct} with this state's information. + * + * @param stack memory stack + * @param struct struct to fill + * @return filled struct + */ + T fill(MemoryStack stack, T struct); + + /** + * Creates a copy of this state. + * + * @return copy + */ + PipelineState copy(); + + /** + * Returns the version number representing the current state. + * If any state is changed, the version number returned by + * this method increases. + * + * @return version number for the current state (not negative) + */ + long getCurrentVersion(); + + /** + * Creates and fills a new struct with this state's information. + * + * @param stack memory stack + * @return created and filled struct + */ + default T fill(MemoryStack stack) { + return fill(stack, create(stack)); + } + + /** + * Gets the {@link #getCurrentVersion() version} of {@code state}. + * If {@code state} is null {@code 0L} is returned. + * + * @param state state to get version of + * @return version of the state, or 0 if state is null + */ + static long versionOf(PipelineState state) { + return state != null ? state.getCurrentVersion() : 0L; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/render/BatchElement.java b/jme3-core/src/main/java/com/jme3/vulkan/render/BatchElement.java new file mode 100644 index 0000000000..01ff285722 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/render/BatchElement.java @@ -0,0 +1,25 @@ +package com.jme3.vulkan.render; + +import com.jme3.material.Material; +import com.jme3.renderer.Camera; +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.vulkan.pipeline.Pipeline; + +public interface BatchElement { + + float computeDistanceSq(); + + float computeDistance(); + + Camera getCamera(); + + Geometry getGeometry(); + + Material getMaterial(); + + Mesh getMesh(); + + Pipeline getPipeline(); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/render/GeometryBatch.java b/jme3-core/src/main/java/com/jme3/vulkan/render/GeometryBatch.java new file mode 100644 index 0000000000..1861ebba86 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/render/GeometryBatch.java @@ -0,0 +1,151 @@ +package com.jme3.vulkan.render; + +import com.jme3.material.Material; +import com.jme3.renderer.Camera; +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.scene.Spatial; +import com.jme3.vulkan.commands.CommandBuffer; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.PriorityQueue; + +public abstract class GeometryBatch implements Iterable { + + protected final Camera camera; + protected final PriorityQueue queue; + protected String forcedTechnique = null; + protected Material forcedMaterial = null; + protected Mesh forcedMesh = null; + + public GeometryBatch(Camera camera, Comparator comparator) { + this.camera = camera; + this.queue = new PriorityQueue<>(comparator); + } + + public abstract void render(CommandBuffer cmd); + + protected abstract boolean fastAdd(Geometry geometry); + + public boolean add(Geometry geometry) { + return add(geometry, true, true); + } + + public boolean add(Geometry geometry, boolean respectCullHints, boolean frustumCulling) { + Spatial.CullHint h = geometry.getCullHint(); + if ((!respectCullHints || h != Spatial.CullHint.Always) + && (!frustumCulling + || (respectCullHints && h == Spatial.CullHint.Never) + || frustumIntersect(geometry) != Camera.FrustumIntersect.Outside)) { + return fastAdd(geometry); + } + return false; + } + + public void addAll(Spatial spatial) { + addAll(spatial, true, true); + } + + public void addAll(Spatial spatial, boolean respectCullHints, boolean frustumCulling) { + for (Spatial.GraphIterator it = spatial.iterator(); it.hasNext();) { + Spatial child = it.next(); + if (respectCullHints) { + if (child.getCullHint() == Spatial.CullHint.Always) { + it.skipChildren(); + continue; + } else if (child.getCullHint() == Spatial.CullHint.Never) { + fastAddAll(child); + it.skipChildren(); + continue; // current spatial is handled by the nested addAll call + } + } + if (frustumCulling && (!respectCullHints || child.getCullHint() == Spatial.CullHint.Dynamic)) { + Camera.FrustumIntersect intersect = frustumIntersect(child); + if (intersect == Camera.FrustumIntersect.Outside) { + it.skipChildren(); + continue; + } else if (intersect == Camera.FrustumIntersect.Inside) { + addAll(child, respectCullHints, false); + it.skipChildren(); + continue; // current spatial is handled by the nested addAll call + } + } + if (child instanceof Geometry) { + fastAdd((Geometry)child); + } + } + } + + public void fastAddAll(Spatial spatial) { + for (Spatial child : spatial) { + if (child instanceof Geometry) { + fastAdd((Geometry)child); + } + } + } + + public int addAll(Iterable geometries) { + return addAll(geometries, true, true); + } + + public int addAll(Iterable geometries, boolean respectCullHints, boolean frustumCulling) { + int count = 0; + for (Geometry g : geometries) { + if (add(g, respectCullHints, frustumCulling)) { + count++; + } + } + return count; + } + + protected Camera.FrustumIntersect frustumIntersect(Spatial spatial) { + return camera.contains(spatial.getWorldBound()); + } + + public void clear() { + queue.clear(); + } + + public int size() { + return queue.size(); + } + + public boolean isEmpty() { + return queue.isEmpty(); + } + + public void setForcedTechnique(String forcedTechnique) { + this.forcedTechnique = forcedTechnique; + } + + public void setForcedMaterial(Material forcedMaterial) { + this.forcedMaterial = forcedMaterial; + } + + public void setForcedMesh(Mesh forcedMesh) { + this.forcedMesh = forcedMesh; + } + + public Camera getCamera() { + return camera; + } + + public String getForcedTechnique() { + return forcedTechnique; + } + + public Material getForcedMaterial() { + return forcedMaterial; + } + + public Mesh getForcedMesh() { + return forcedMesh; + } + + @Override + public Iterator iterator() { + return queue.iterator(); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/render/GlGeometryBatch.java b/jme3-core/src/main/java/com/jme3/vulkan/render/GlGeometryBatch.java new file mode 100644 index 0000000000..f57c74dd76 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/render/GlGeometryBatch.java @@ -0,0 +1,224 @@ +package com.jme3.vulkan.render; + +import com.jme3.light.LightList; +import com.jme3.material.*; +import com.jme3.math.FastMath; +import com.jme3.math.Vector3f; +import com.jme3.renderer.*; +import com.jme3.scene.Geometry; +import com.jme3.scene.GlMesh; +import com.jme3.scene.Mesh; +import com.jme3.scene.Spatial; +import com.jme3.shader.Shader; +import com.jme3.shader.Uniform; +import com.jme3.util.ListMap; +import com.jme3.util.SafeArrayList; +import com.jme3.vulkan.commands.CommandBuffer; +import com.jme3.vulkan.pipeline.Pipeline; + +import java.util.Comparator; + +public class GlGeometryBatch extends GeometryBatch { + + private final RenderManager renderManager; + private final boolean ortho; + private final RenderState mergedState = new RenderState(); + private final LightList filteredLights = new LightList(null); + private RenderState forcedState = null; + private LightList forcedLights = null; + + public GlGeometryBatch(RenderManager renderManager, Camera camera, boolean ortho, Comparator comparator) { + super(camera, comparator); + this.renderManager = renderManager; + this.ortho = ortho; + } + + @Override + public void render(CommandBuffer cmd) { + renderManager.setCamera(camera, ortho); + Renderer renderer = renderManager.getRenderer(); + for (Element e : queue) { + if (e.getPipeline().getDef().isNoRender()) { + continue; + } + updateRenderState(e.getGeometry(), e.getState(), + e.getAdditionalState(), renderer, e.getPipeline().getDef()); + SafeArrayList overrides = e.getGeometry().getWorldMatParamOverrides(); + LightList lights = renderManager.filterLights(e.getGeometry(), e.getLights(), filteredLights); + Shader shader = e.getPipeline().makeCurrent(renderManager, overrides, + renderManager.getForcedMatParams(), lights, renderer.getCaps()); + clearUniformsSetByCurrent(shader); + renderManager.updateUniformBindings(shader); + GlMaterial.BindUnits units = e.getMaterial().updateShaderMaterialParameters( + renderer, shader, overrides, renderManager.getForcedMatParams()); + resetUniformsNotSetByCurrent(shader); + e.getPipeline().render(renderManager, shader, e.getGeometry(), e.getMesh(), lights, units); + } + } + + private void updateRenderState(Geometry geometry, RenderState forcedState, RenderState additionalState, + Renderer renderer, TechniqueDef techniqueDef) { + RenderState finalRenderState; + if (forcedState != null) { + finalRenderState = mergedState.copyFrom(forcedState); + } else if (techniqueDef.getRenderState() != null) { + finalRenderState = mergedState.copyFrom(RenderState.DEFAULT); + finalRenderState = techniqueDef.getRenderState().copyMergedTo(additionalState, finalRenderState); + } else { + finalRenderState = mergedState.copyFrom(RenderState.DEFAULT); + finalRenderState = RenderState.DEFAULT.copyMergedTo(additionalState, finalRenderState); + } + // test if the face cull mode should be flipped before render + if (finalRenderState.isFaceCullFlippable() && isNormalsBackward(geometry.getWorldScale())) { + finalRenderState.flipFaceCull(); + } + renderer.applyRenderState(finalRenderState); + } + + private boolean isNormalsBackward(Vector3f scalar) { + // count number of negative scalar vector components + int n = 0; + if (scalar.x < 0) n++; + if (scalar.y < 0) n++; + if (scalar.z < 0) n++; + // An odd number of negative components means the normal vectors + // are backward to what they should be. + return n == 1 || n == 3; + } + + private void clearUniformsSetByCurrent(Shader shader) { + ListMap uniforms = shader.getUniformMap(); + int size = uniforms.size(); + for (int i = 0; i < size; i++) { + Uniform u = uniforms.getValue(i); + u.clearSetByCurrentMaterial(); + } + } + + private void resetUniformsNotSetByCurrent(Shader shader) { + ListMap uniforms = shader.getUniformMap(); + int size = uniforms.size(); + for (int i = 0; i < size; i++) { + Uniform u = uniforms.getValue(i); + if (!u.isSetByCurrentMaterial()) { + if (u.getName().charAt(0) != 'g') { + // Don't reset world globals! + // The benefits gained from this are very minimal + // and cause lots of matrix -> FloatBuffer conversions. + u.clearValue(); + } + } + } + } + + @Override + protected boolean fastAdd(Geometry geometry) { + return queue.add(new Element(geometry)); + } + + @Override + protected Camera.FrustumIntersect frustumIntersect(Spatial spatial) { + if (ortho) { + return camera.containsGui(spatial.getWorldBound()) ? + Camera.FrustumIntersect.Intersects : Camera.FrustumIntersect.Outside; + } else return camera.contains(spatial.getWorldBound()); + } + + public void setForcedState(RenderState forcedState) { + this.forcedState = forcedState; + } + + public void setForcedLights(LightList forcedLights) { + this.forcedLights = forcedLights; + } + + public RenderState getForcedState() { + return forcedState; + } + + public LightList getForcedLights() { + return forcedLights; + } + + public class Element implements BatchElement { + + private final Geometry geometry; + private final GlMaterial material; + private final GlMesh mesh; + private final Technique technique; + private final RenderState state; + private final RenderState additionalState; + private final LightList lights; + private float distanceSq = Float.NaN; + private float distance = Float.NaN; + + public Element(Geometry geometry) { + this.geometry = geometry; + Material mat = forcedMaterial != null ? forcedMaterial : geometry.getMaterial(); + if (!(mat instanceof GlMaterial)) { + throw new ClassCastException("Cannot render " + mat.getClass() + " in an OpenGL context."); + } + this.material = (GlMaterial)mat; + Mesh m = forcedMesh != null ? forcedMesh : geometry.getMesh(); + if (!(m instanceof GlMesh)) { + throw new ClassCastException("Cannot render " + m.getClass() + " in an OpenGL context."); + } + this.mesh = (GlMesh)m; + this.technique = this.material.selectTechnique(forcedTechnique, renderManager); + this.state = forcedState != null ? forcedState : renderManager.getForcedRenderState(); + this.additionalState = this.material.getAdditionalRenderState(); + this.lights = forcedLights != null ? forcedLights : this.geometry.getWorldLightList(); + } + + @Override + public float computeDistanceSq() { + if (!Float.isNaN(distanceSq)) return distanceSq; + else return (distanceSq = geometry.getWorldTranslation().distanceSquared(camera.getLocation())); + } + + @Override + public float computeDistance() { + if (!Float.isNaN(distance)) return distance; + else return (distance = FastMath.sqrt(computeDistanceSq())); + } + + @Override + public Camera getCamera() { + return camera; + } + + @Override + public Geometry getGeometry() { + return geometry; + } + + @Override + public GlMaterial getMaterial() { + return material; + } + + @Override + public GlMesh getMesh() { + return mesh; + } + + @Override + public Technique getPipeline() { + return technique; + } + + public RenderState getState() { + return state; + } + + public RenderState getAdditionalState() { + return additionalState; + } + + public LightList getLights() { + return lights; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/render/VulkanGeometryBatch.java b/jme3-core/src/main/java/com/jme3/vulkan/render/VulkanGeometryBatch.java new file mode 100644 index 0000000000..3e49e39855 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/render/VulkanGeometryBatch.java @@ -0,0 +1,133 @@ +package com.jme3.vulkan.render; + +import com.jme3.material.*; +import com.jme3.math.FastMath; +import com.jme3.renderer.Camera; +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.vulkan.material.VkMaterial; +import com.jme3.vulkan.mesh.VkMesh; +import com.jme3.vulkan.commands.CommandBuffer; +import com.jme3.vulkan.material.NewMaterial; +import com.jme3.vulkan.pipeline.Pipeline; +import com.jme3.vulkan.pipeline.PipelineBindPoint; +import com.jme3.vulkan.pipeline.cache.PipelineCache; + +import java.util.Comparator; + +public class VulkanGeometryBatch extends GeometryBatch { + + private final PipelineCache cache; + + public VulkanGeometryBatch(PipelineCache cache, Camera camera, Comparator comparator) { + super(camera, comparator); + this.cache = cache; + } + + @Override + public void render(CommandBuffer cmd) { + Pipeline currentPipeline = null; + VkMaterial currentMaterial = null; + VkMesh currentMesh = null; + for (Element e : queue) { + // I'm not sure if it's safe or optimal to allow controls to muck with + // geometry/material/mesh at this point, but this would technically + // be the location to do this. + //e.getGeometry().runControlRender(rm, cmd, camera); + + // Provide transform matrices to the material. This requires + // that the appropriate buffers be updated on or after this point. + e.getGeometry().updateMatrixTransforms(camera); + + // bind the selected pipeline if necessary + if (e.getPipeline() != currentPipeline) { + if (!e.getPipeline().isMaterialEquivalent(currentPipeline)) { + currentMaterial = null; // must explicitly bind next material + } + (currentPipeline = e.getPipeline()).bind(cmd); + } + + // bind the material if necessary + if (e.getMaterial() != currentMaterial && !(currentMaterial = e.getMaterial()).bind(cmd, currentPipeline)) { + throw new IllegalStateException("Attempted to bind material with an incompatible pipeline."); + } + + // bind the mesh if necessary + if (currentMesh != e.getMesh()) { + (currentMesh = e.getMesh()).bind(cmd, e.getGeometry().getLodLevel()); + } + + // render + currentMesh.render(cmd, e.getGeometry().getLodLevel()); + } + } + + @Override + protected boolean fastAdd(Geometry geometry) { + return queue.add(new Element(geometry)); + } + + public class Element implements BatchElement { + + private final Geometry geometry; + private final VkMaterial material; + private final VkMesh mesh; + private final Pipeline pipeline; + private float distanceSq = Float.NaN; + private float distance = Float.NaN; + + private Element(Geometry geometry) { + this.geometry = geometry; + Material mat = forcedMaterial != null ? forcedMaterial : this.geometry.getMaterial(); + if (!(mat instanceof NewMaterial)) { + throw new ClassCastException("Cannot render " + mat.getClass() + " in a Vulkan context."); + } + this.material = (VkMaterial)mat; + Mesh m = forcedMesh != null ? forcedMesh : this.geometry.getMesh(); + if (!(m instanceof VkMesh)) { + throw new ClassCastException("Cannot render " + mat.getClass() + " in a Vulkan context."); + } + this.mesh = (VkMesh)m; + this.pipeline = this.material.selectPipeline(cache, this.mesh.getDescription(), forcedTechnique, null); + } + + @Override + public float computeDistanceSq() { + if (!Float.isNaN(distanceSq)) return distanceSq; + return (distanceSq = camera.getLocation().distanceSquared(geometry.getWorldTranslation())); + } + + @Override + public float computeDistance() { + if (!Float.isNaN(distance)) return distance; + return (distance = FastMath.sqrt(computeDistanceSq())); + } + + @Override + public Camera getCamera() { + return camera; + } + + @Override + public Geometry getGeometry() { + return geometry; + } + + @Override + public VkMaterial getMaterial() { + return material; + } + + @Override + public VkMesh getMesh() { + return mesh; + } + + @Override + public Pipeline getPipeline() { + return pipeline; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/shader/ConstantDefine.java b/jme3-core/src/main/java/com/jme3/vulkan/shader/ConstantDefine.java new file mode 100644 index 0000000000..2f33b85b9c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/shader/ConstantDefine.java @@ -0,0 +1,33 @@ +package com.jme3.vulkan.shader; + +public class ConstantDefine implements Define { + + private final String name; + private final String value; + + public ConstantDefine(String name, String value) { + this.name = name; + this.value = value; + } + + @Override + public String getDefineName() { + return name; + } + + @Override + public String getDefineValue() { + return value; + } + + @Override + public boolean isDefineActive() { + return true; + } + + @Override + public long getVersionNumber() { + return 0L; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/shader/Define.java b/jme3-core/src/main/java/com/jme3/vulkan/shader/Define.java new file mode 100644 index 0000000000..e79914238c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/shader/Define.java @@ -0,0 +1,29 @@ +package com.jme3.vulkan.shader; + +import com.jme3.util.Versionable; + +public interface Define extends Versionable { + + /** + * Gets the define as named in the shader. + * + * @return define name + */ + String getDefineName(); + + /** + * Gets the value assigned to the define in the shader. + * + * @return define value, as a string + */ + String getDefineValue(); + + /** + * Returns true if this define is active and should be + * included in the shader. + * + * @return true if active + */ + boolean isDefineActive(); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/shader/ShaderDefines.java b/jme3-core/src/main/java/com/jme3/vulkan/shader/ShaderDefines.java new file mode 100644 index 0000000000..8c2ba04510 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/shader/ShaderDefines.java @@ -0,0 +1,55 @@ +package com.jme3.vulkan.shader; + +import com.jme3.util.Version; +import com.jme3.util.Versionable; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Stores the defines that are to be used in a shader. If the defines + * change, the shader will need to be re-compiled, or another existing + * shader used. + */ +public class ShaderDefines implements Versionable { + + private final Map> defines = new HashMap<>(); + private long version = 0L; + + @Override + public long getVersionNumber() { + for (Version d : defines.values()) { + if (d.update()) version++; + } + return version; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + ShaderDefines that = (ShaderDefines) o; + return Objects.equals(defines, that.defines); + } + + @Override + public int hashCode() { + return Objects.hashCode(defines); + } + + public ShaderDefines snapshot() { + ShaderDefines copy = new ShaderDefines(); + for (Map.Entry> d : defines.entrySet()) { + copy.defines.put(d.getKey(), new Version<>(new ConstantDefine( + d.getValue().get().getDefineName(), d.getValue().get().getDefineValue()))); + } + return copy; + } + + public void snapshot(Map store) { + for (Map.Entry> d : defines.entrySet()) { + store.put(d.getKey(), d.getValue().get().getDefineValue()); + } + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/shader/ShaderModule.java b/jme3-core/src/main/java/com/jme3/vulkan/shader/ShaderModule.java new file mode 100644 index 0000000000..bffa04e6ac --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/shader/ShaderModule.java @@ -0,0 +1,77 @@ +package com.jme3.vulkan.shader; + +import com.jme3.asset.AssetManager; +import com.jme3.util.natives.AbstractNative; +import com.jme3.util.natives.Native; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.pipeline.states.IShaderState; +import com.jme3.vulkan.shaderc.ShadercLoader; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkPipelineShaderStageCreateInfo; +import org.lwjgl.vulkan.VkShaderModuleCreateInfo; + +import java.nio.ByteBuffer; +import java.nio.LongBuffer; +import java.util.Objects; + +import static com.jme3.renderer.vulkan.VulkanUtils.*; +import static org.lwjgl.vulkan.VK10.*; + +public class ShaderModule extends AbstractNative { + + private final LogicalDevice device; + private final ShaderStage stage; + private final String entryPoint; + + public ShaderModule(LogicalDevice device, AssetManager assetManager, IShaderState state) { + this.device = device; + this.stage = state.getStage(); + this.entryPoint = state.getEntryPoint(); + ByteBuffer code = assetManager.loadAsset(ShadercLoader.key(state.getAssetName(), state.getStage(), state.getEntryPoint())); + try (MemoryStack stack = MemoryStack.stackPush()) { + VkShaderModuleCreateInfo create = VkShaderModuleCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO) + .pCode(code); + LongBuffer idBuf = stack.mallocLong(1); + check(vkCreateShaderModule(device.getNativeObject(), create, null, idBuf), + "Failed to create shader module."); + object = idBuf.get(0); + } + ref = Native.get().register(this); + device.getNativeReference().addDependent(ref); + } + + @Override + public Runnable createNativeDestroyer() { + return () -> vkDestroyShaderModule(device.getNativeObject(), object, null); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + ShaderModule that = (ShaderModule) o; + return device == that.device && Objects.equals(object, that.object); + } + + @Override + public int hashCode() { + return Objects.hash(device, object); + } + + public VkPipelineShaderStageCreateInfo fill(MemoryStack stack, VkPipelineShaderStageCreateInfo struct) { + return struct.module(object).stage(stage.getVk()).pName(stack.UTF8(entryPoint)); + } + + public LogicalDevice getDevice() { + return device; + } + + public ShaderStage getStage() { + return stage; + } + + public String getEntryPoint() { + return entryPoint; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/shader/ShaderProgram.java b/jme3-core/src/main/java/com/jme3/vulkan/shader/ShaderProgram.java new file mode 100644 index 0000000000..03f87ba7e0 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/shader/ShaderProgram.java @@ -0,0 +1,5 @@ +package com.jme3.vulkan.shader; + +public class ShaderProgram { + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/shader/ShaderStage.java b/jme3-core/src/main/java/com/jme3/vulkan/shader/ShaderStage.java new file mode 100644 index 0000000000..0d0e9cecf4 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/shader/ShaderStage.java @@ -0,0 +1,65 @@ +package com.jme3.vulkan.shader; + +import com.jme3.vulkan.util.Flag; +import com.jme3.vulkan.util.IntEnum; + +import java.util.Objects; + +import static org.lwjgl.util.shaderc.Shaderc.*; +import static org.lwjgl.vulkan.VK10.*; + +public final class ShaderStage implements Flag, IntEnum { + + public static final ShaderStage All = new ShaderStage(VK_SHADER_STAGE_ALL, -1); + public static final ShaderStage AllGraphics = new ShaderStage(VK_SHADER_STAGE_ALL_GRAPHICS, -1); + public static final ShaderStage Vertex = new ShaderStage(VK_SHADER_STAGE_VERTEX_BIT, shaderc_vertex_shader); + public static final ShaderStage Geometry = new ShaderStage(VK_SHADER_STAGE_GEOMETRY_BIT, shaderc_geometry_shader); + public static final ShaderStage TessellationEval = new ShaderStage(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, shaderc_tess_evaluation_shader); + public static final ShaderStage TessellationControl = new ShaderStage(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, shaderc_tess_control_shader); + public static final ShaderStage Fragment = new ShaderStage(VK_SHADER_STAGE_FRAGMENT_BIT, shaderc_fragment_shader); + public static final ShaderStage Compute = new ShaderStage(VK_SHADER_STAGE_COMPUTE_BIT, shaderc_compute_shader); + + private final int vk; + private final int shaderc; + + ShaderStage(int vk, int shaderc) { + this.vk = vk; + this.shaderc = shaderc; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + ShaderStage integers = (ShaderStage) o; + return vk == integers.vk && shaderc == integers.shaderc; + } + + @Override + public int hashCode() { + return Objects.hash(vk, shaderc); + } + + @Override + public int bits() { + return vk; + } + + @Override + public int getEnum() { + return vk; + } + + @Override + public boolean is(int bits) { + return Flag.super.is(bits); + } + + public int getVk() { + return vk; + } + + public int getShaderc() { + return shaderc; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/shader/UniformTestStruct.java b/jme3-core/src/main/java/com/jme3/vulkan/shader/UniformTestStruct.java new file mode 100644 index 0000000000..55a851d9c0 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/shader/UniformTestStruct.java @@ -0,0 +1,103 @@ +package com.jme3.vulkan.shader; + +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.NativeResource; +import org.lwjgl.system.Struct; +import org.lwjgl.system.StructBuffer; + +import java.nio.ByteBuffer; + +public class UniformTestStruct extends Struct { + + public static final int SIZEOF; + public static final int ALIGNOF; + + public static final int X, Y, Z, W; + + static { + Layout layout = __struct( + __member(Float.BYTES), + __member(Float.BYTES), + __member(Float.BYTES), + __member(Float.BYTES) + ); + SIZEOF = layout.getSize(); + ALIGNOF = layout.getAlignment(); + X = layout.offsetof(0); + Y = layout.offsetof(1); + Z = layout.offsetof(2); + W = layout.offsetof(3); + } + + public UniformTestStruct(ByteBuffer container) { + this(MemoryUtil.memAddress(container), __checkContainer(container, SIZEOF)); + } + + protected UniformTestStruct(long address, ByteBuffer container) { + super(address, container); + } + + @Override + protected UniformTestStruct create(long address, ByteBuffer container) { + return new UniformTestStruct(address, container); + } + + @Override + public int sizeof() { + return SIZEOF; + } + + public float x() { + return nx(address()); + } + + public void x(float x) { + nx(address(), x); + } + + public static float nx(long struct) { + return MemoryUtil.memGetFloat(struct + X); + } + + public static void nx(long struct, float x) { + MemoryUtil.memPutFloat(struct + X, x); + } + + public static UniformTestStruct calloc() { + return new UniformTestStruct(MemoryUtil.nmemCallocChecked(1, SIZEOF), null); + } + + public static UniformTestStruct create(long address) { + return new UniformTestStruct(address, null); + } + + public static class Buffer extends StructBuffer implements NativeResource { + + private static final UniformTestStruct FACTORY = new UniformTestStruct(-1L, null); + + protected Buffer(ByteBuffer container, int remaining) { + super(container, remaining); + } + + protected Buffer(long address, ByteBuffer container, int mark, int position, int limit, int capacity) { + super(address, container, mark, position, limit, capacity); + } + + @Override + protected UniformTestStruct getElementFactory() { + return null; + } + + @Override + protected Buffer self() { + return this; + } + + @Override + protected Buffer create(long address, ByteBuffer container, int mark, int position, int limit, int capacity) { + return new Buffer(address, container, mark, position, limit, capacity); + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/shaderc/ShaderType.java b/jme3-core/src/main/java/com/jme3/vulkan/shaderc/ShaderType.java new file mode 100644 index 0000000000..847c69c071 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/shaderc/ShaderType.java @@ -0,0 +1,37 @@ +package com.jme3.vulkan.shaderc; + +import org.lwjgl.util.shaderc.Shaderc; +import org.lwjgl.vulkan.VK10; + +public enum ShaderType { + + Vertex(VK10.VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, VK10.VK_SHADER_STAGE_VERTEX_BIT, Shaderc.shaderc_vertex_shader), + Fragment(VK10.VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK10.VK_SHADER_STAGE_FRAGMENT_BIT, Shaderc.shaderc_fragment_shader), + Tessellation(VK10.VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT, VK10.VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, Shaderc.shaderc_tess_evaluation_shader), + TessellationControl(VK10.VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT, VK10.VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, Shaderc.shaderc_tess_control_shader), + Geometry(VK10.VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT, VK10.VK_SHADER_STAGE_GEOMETRY_BIT, Shaderc.shaderc_geometry_shader), + Compute(VK10.VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK10.VK_SHADER_STAGE_COMPUTE_BIT, Shaderc.shaderc_compute_shader); + + private final int vulkanPipeline; + private final int vulkanShader; + private final int shaderc; + + ShaderType(int vulkanPipeline, int vulkanShader, int shaderc) { + this.vulkanPipeline = vulkanPipeline; + this.vulkanShader = vulkanShader; + this.shaderc = shaderc; + } + + public int getVulkanPipeline() { + return vulkanPipeline; + } + + public int getVulkanShader() { + return vulkanShader; + } + + public int getShaderc() { + return shaderc; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/shaderc/ShadercLoader.java b/jme3-core/src/main/java/com/jme3/vulkan/shaderc/ShadercLoader.java new file mode 100644 index 0000000000..ebfdb0b2e8 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/shaderc/ShadercLoader.java @@ -0,0 +1,169 @@ +package com.jme3.vulkan.shaderc; + +import com.jme3.asset.AssetInfo; +import com.jme3.asset.AssetKey; +import com.jme3.asset.AssetLoader; +import com.jme3.util.natives.AbstractNative; +import com.jme3.util.natives.Native; +import com.jme3.vulkan.shader.ShaderModule; +import com.jme3.vulkan.shader.ShaderStage; +import com.jme3.vulkan.util.IntEnum; +import jme3tools.shader.ShaderDebug; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.util.shaderc.Shaderc; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.lwjgl.system.MemoryUtil.NULL; + +public class ShadercLoader implements AssetLoader { + + public enum Optimization implements IntEnum { + + None(Shaderc.shaderc_optimization_level_zero), + Size(Shaderc.shaderc_optimization_level_size), + Performance(Shaderc.shaderc_optimization_level_performance); + + private final int shadercEnum; + + Optimization(int shadercEnum) { + this.shadercEnum = shadercEnum; + } + + @Override + public int getEnum() { + return shadercEnum; + } + + } + + private static final Logger LOG = Logger.getLogger(ShadercLoader.class.getName()); + private static final Compiler compiler = new Compiler(); + private static final Map shaders = new HashMap<>(); + + @Override + public Object load(AssetInfo assetInfo) throws IOException { + if (!(assetInfo.getKey() instanceof Key)) { + throw new IllegalArgumentException("Requires " + Key.class.getName()); + } + Key key = (Key)assetInfo.getKey(); + try (InputStream in = assetInfo.openStream()) { + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + StringBuilder code = new StringBuilder(); + for (String line; (line = reader.readLine()) != null; ) { + code.append(line).append('\n'); + } + return compile(key, code.toString()); + } + } + + public static ByteBuffer compile(Key key, String code) { + try (MemoryStack stack = MemoryStack.stackPush()) { + long preprocessed = Shaderc.shaderc_compile_into_preprocessed_text(compiler.getNativeObject(), code, + key.getStage().getShaderc(), key.getName(), key.getEntryPoint(), NULL); + handleCompileCodes(preprocessed, key.getName(), code); + ByteBuffer bytecode = Objects.requireNonNull(Shaderc.shaderc_result_get_bytes(preprocessed)); + long options = Shaderc.shaderc_compile_options_initialize(); + //Shaderc.shaderc_compile_options_add_macro_definition(options, "MACRO_NAME", "156"); + Shaderc.shaderc_compile_options_set_optimization_level(options, key.getOptimization().getEnum()); + long compiled = Shaderc.shaderc_compile_into_spv(compiler.getNativeObject(), bytecode, + key.getStage().getShaderc(), stack.UTF8(key.getName()), stack.UTF8(key.getName()), options); + Shaderc.shaderc_compile_options_release(options); + handleCompileCodes(compiled, key.getName(), code); + return Shaderc.shaderc_result_get_bytes(compiled); + } + } + + private static void handleCompileCodes(long result, String name, String code) { + int status = Shaderc.shaderc_result_get_compilation_status(result); + if (status != Shaderc.shaderc_compilation_status_success) { + LOG.log(Level.SEVERE, "Bad compile of\n{0}", ShaderDebug.formatShaderSource(code)); + throw new RuntimeException("Failed to compile " + name + ":\n" + + Shaderc.shaderc_result_get_error_message(result)); + } + long warnings = Shaderc.shaderc_result_get_num_warnings(result); + if (warnings > 0) { + LOG.warning("Compiled with " + warnings + " warning" + (warnings == 1 ? "" : "s") + ": " + name); + } else { + LOG.fine("Compiled with no warnings: " + name); + } + } + + public static Key key(String name, ShaderStage stage) { + return new Key(name, stage, "main"); + } + + public static Key key(String name, ShaderStage stage, String entry) { + return new Key(name, stage, entry); + } + + private static class Compiler extends AbstractNative { + + private Compiler() { + object = Shaderc.shaderc_compiler_initialize(); + if (object == NULL) { + throw new NullPointerException("Unable to initialize Shaderc compiler."); + } + ref = Native.get().register(this); + } + + @Override + public Runnable createNativeDestroyer() { + return () -> Shaderc.shaderc_compiler_release(object); + } + + } + + public static class Key extends AssetKey { + + private final ShaderStage stage; + private final String entryPoint; + private IntEnum optimization = Optimization.Performance; + + public Key(String name, ShaderStage stage, String entryPoint) { + super(name); + this.stage = stage; + this.entryPoint = entryPoint; + } + + public void setOptimization(IntEnum optimization) { + this.optimization = optimization; + } + + public ShaderStage getStage() { + return stage; + } + + public String getEntryPoint() { + return entryPoint; + } + + public IntEnum getOptimization() { + return optimization; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + Key key = (Key) o; + return stage == key.stage && Objects.equals(entryPoint, key.entryPoint) && Objects.equals(optimization, key.optimization); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), stage, entryPoint, optimization); + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/struct/MappedStruct.java b/jme3-core/src/main/java/com/jme3/vulkan/struct/MappedStruct.java new file mode 100644 index 0000000000..69e83dbfee --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/struct/MappedStruct.java @@ -0,0 +1,217 @@ +package com.jme3.vulkan.struct; + +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.system.NativeResource; +import org.lwjgl.system.Struct; +import org.lwjgl.system.StructBuffer; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public abstract class MappedStruct > extends Struct implements NativeResource { + + protected MappedStruct(long address, ByteBuffer container) { + super(address, container); + } + + protected abstract MappedLayout getLayout(); + + @Override + public int sizeof() { + return getLayout().sizeof(); + } + + public void set(String name, Object value) { + getLayout().getMember(name).set(address, value); + } + + public Object get(String name) { + return getLayout().getMember(name).get(address); + } + + protected static class MappedLayout { + + private final Map members = new HashMap<>(); + private final List memberList = new LinkedList<>(); + private int sizeof, alignof; + + protected void add(String name, MappedMember member) { + members.put(name, member); + memberList.add(member); + } + + public MappedMember getMember(String name) { + return members.get(name); + } + + protected Layout build() { + Struct.Member[] mems = memberList.stream().map(m -> __member(m.size)).toArray(Struct.Member[]::new); + Layout layout = __struct(mems); + sizeof = layout.getSize(); + alignof = layout.getAlignment(); + int i = 0; + for (MappedMember m : memberList) { + m.offset = layout.offsetof(i++); + } + memberList.clear(); + return layout; + } + + public int sizeof() { + return sizeof; + } + + public int alignof() { + return alignof; + } + + } + + protected static abstract class MappedMember { + + protected final int size; + protected int offset; + + public MappedMember(int size) { + this.size = size; + } + + protected abstract void set(long struct, Object value); + + protected abstract T get(long struct); + + public int getSize() { + return size; + } + + public int getOffset() { + return offset; + } + + } + + protected static abstract class MappedBuffer , SELF extends MappedBuffer> + extends StructBuffer implements NativeResource { + + protected MappedBuffer(ByteBuffer container, int remaining) { + super(container, remaining); + } + + public MappedBuffer(long address, ByteBuffer container, int mark, int position, int limit, int capacity) { + super(address, container, mark, position, limit, capacity); + } + + } + + protected static class ByteMember extends MappedMember { + + public ByteMember() { + super(Byte.BYTES); + } + + @Override + protected void set(long struct, Object value) { + MemoryUtil.memPutByte(struct + offset, (byte)value); + } + + @Override + protected Byte get(long struct) { + return MemoryUtil.memGetByte(struct + offset); + } + + } + + protected static class ShortMember extends MappedMember { + + public ShortMember() { + super(Short.BYTES); + } + + @Override + protected void set(long struct, Object value) { + MemoryUtil.memPutShort(struct + offset, (short)value); + } + + @Override + protected Short get(long struct) { + return MemoryUtil.memGetShort(struct + offset); + } + + } + + protected static class IntMember extends MappedMember { + + public IntMember() { + super(Integer.BYTES); + } + + @Override + protected void set(long struct, Object value) { + MemoryUtil.memPutInt(struct + offset, (int)value); + } + + @Override + protected Integer get(long struct) { + return MemoryUtil.memGetInt(struct + offset); + } + + } + + protected static class FloatMember extends MappedMember { + + public FloatMember() { + super(Float.BYTES); + } + + @Override + protected void set(long struct, Object value) { + MemoryUtil.memPutFloat(struct + offset, (float)value); + } + + @Override + protected Float get(long struct) { + return MemoryUtil.memGetFloat(struct + offset); + } + + } + + protected static class DoubleMember extends MappedMember { + + public DoubleMember() { + super(Double.BYTES); + } + + @Override + protected void set(long struct, Object value) { + MemoryUtil.memPutDouble(struct + offset, (double)value); + } + + @Override + protected Double get(long struct) { + return MemoryUtil.memGetDouble(struct + offset); + } + + } + + protected static class LongMember extends MappedMember { + + public LongMember() { + super(Long.BYTES); + } + + @Override + protected void set(long struct, Object value) { + MemoryUtil.memPutLong(struct + offset, (long)value); + } + + @Override + protected Long get(long struct) { + return MemoryUtil.memGetLong(struct + offset); + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/surface/Surface.java b/jme3-core/src/main/java/com/jme3/vulkan/surface/Surface.java new file mode 100644 index 0000000000..957204752f --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/surface/Surface.java @@ -0,0 +1,78 @@ +package com.jme3.vulkan.surface; + +import com.jme3.util.natives.Native; +import com.jme3.util.natives.NativeReference; +import com.jme3.vulkan.VulkanInstance; +import com.jme3.vulkan.devices.DeviceFilter; +import com.jme3.vulkan.devices.PhysicalDevice; +import org.lwjgl.glfw.GLFWVulkan; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.vulkan.KHRSurface; + +import java.nio.IntBuffer; + +import static com.jme3.renderer.vulkan.VulkanUtils.*; + +public class Surface implements Native, DeviceFilter { + + private final VulkanInstance instance; + private final NativeReference ref; + private final long window; + private long id; + + public Surface(VulkanInstance instance, long window) { + this.instance = instance; + this.window = window; + try (MemoryStack stack = MemoryStack.stackPush()) { + id = getLong(stack, ptr -> check(GLFWVulkan.glfwCreateWindowSurface( + instance.getNativeObject(), window, null, ptr), + "Failed to create surface for GLFW window.")); + ref = Native.get().register(this); + instance.getNativeReference().addDependent(ref); + } + } + + @Override + public Float evaluateDevice(PhysicalDevice device) { + try (MemoryStack stack = MemoryStack.stackPush()) { + IntBuffer count = stack.mallocInt(1); + KHRSurface.vkGetPhysicalDeviceSurfaceFormatsKHR(device.getDeviceHandle(), id, count, null); + if (count.get(0) == 0) { + System.out.println("Reject device by surface support (formats)"); + return null; + } + KHRSurface.vkGetPhysicalDeviceSurfacePresentModesKHR(device.getDeviceHandle(), id, count, null); + if (count.get(0) == 0) { + System.out.println("Reject device by surface support (present modes)"); + return null; + } + return 0f; + } + } + + @Override + public Long getNativeObject() { + return id; + } + + @Override + public Runnable createNativeDestroyer() { + return () -> KHRSurface.vkDestroySurfaceKHR(instance.getNativeObject(), id, null); + } + + @Override + public void prematureNativeDestruction() { + id = MemoryUtil.NULL; + } + + @Override + public NativeReference getNativeReference() { + return ref; + } + + public long getWindowHandle() { + return window; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/surface/Swapchain.java b/jme3-core/src/main/java/com/jme3/vulkan/surface/Swapchain.java new file mode 100644 index 0000000000..4b30160aa0 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/surface/Swapchain.java @@ -0,0 +1,435 @@ +package com.jme3.vulkan.surface; + +import com.jme3.texture.ImageView; +import com.jme3.util.AbstractBuilder; +import com.jme3.util.natives.AbstractNative; +import com.jme3.util.natives.Native; +import com.jme3.util.natives.NativeReference; +import com.jme3.vulkan.*; +import com.jme3.vulkan.commands.Queue; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.images.GpuImage; +import com.jme3.vulkan.images.ImageUsage; +import com.jme3.vulkan.images.VulkanImageView; +import com.jme3.vulkan.images.VulkanImage; +import com.jme3.vulkan.pipeline.framebuffer.VulkanFrameBuffer; +import com.jme3.vulkan.pass.RenderPass; +import com.jme3.vulkan.sync.Fence; +import com.jme3.vulkan.sync.Semaphore; +import com.jme3.vulkan.sync.SyncGroup; +import com.jme3.vulkan.util.Extent2; +import com.jme3.vulkan.util.Flag; +import com.jme3.vulkan.util.IntEnum; +import org.lwjgl.glfw.GLFW; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.*; + +import java.nio.IntBuffer; +import java.nio.LongBuffer; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +import static com.jme3.renderer.vulkan.VulkanUtils.*; +import static org.lwjgl.vulkan.VK10.*; + +public class Swapchain extends AbstractNative { + + public enum PresentMode implements IntEnum { + + FirstInFirstOut(KHRSurface.VK_PRESENT_MODE_FIFO_KHR), + FirstInFirstOutRelaxed(KHRSurface.VK_PRESENT_MODE_FIFO_RELAXED_KHR), + Immediate(KHRSurface.VK_PRESENT_MODE_IMMEDIATE_KHR), + Mailbox(KHRSurface.VK_PRESENT_MODE_MAILBOX_KHR); + + private final int vkEnum; + + PresentMode(int vkEnum) { + this.vkEnum = vkEnum; + } + + @Override + public int getEnum() { + return vkEnum; + } + + } + + private final LogicalDevice device; + private final Surface surface; + private final List images = new ArrayList<>(); + private Consumer builder; + private Extent2 extent; + private Format format; + private int imageLayers = 1; + private Flag imageUsage = ImageUsage.ColorAttachment; + + public Swapchain(LogicalDevice device, Surface surface) { + this.device = device; + this.surface = surface; + this.object = VK_NULL_HANDLE; + ref = Native.get().register(this); + device.getNativeReference().addDependent(ref); + surface.getNativeReference().addDependent(ref); + } + + @Override + public Runnable createNativeDestroyer() { + return () -> KHRSwapchain.vkDestroySwapchainKHR(device.getNativeObject(), object, null); + } + + public void createFrameBuffers(RenderPass compat, VulkanImageView depthStencil) { + for (PresentImage img : images) { + img.createFrameBuffer(compat, depthStencil); + } + } + + public PresentImage acquireNextImage(SwapchainUpdater updater, Semaphore semaphore, Fence fence, long timeoutMillis) { + try (MemoryStack stack = MemoryStack.stackPush()) { + IntBuffer i = stack.mallocInt(1); + int code = KHRSwapchain.vkAcquireNextImageKHR(device.getNativeObject(), object, + TimeUnit.MILLISECONDS.toNanos(timeoutMillis), Native.getId(semaphore), Native.getId(fence), i); + if (updater.swapchainOutOfDate(this, code)) { + return null; + } + return images.get(i.get(0)); + } + } + + public void present(Queue presentQueue, PresentImage image, SyncGroup sync) { + int imageIndex = images.indexOf(image); + if (imageIndex < 0) { + throw new IllegalArgumentException("Image does not belong to this swapchain."); + } + try (MemoryStack stack = MemoryStack.stackPush()) { + VkPresentInfoKHR info = VkPresentInfoKHR.calloc(stack) + .sType(KHRSwapchain.VK_STRUCTURE_TYPE_PRESENT_INFO_KHR) + .swapchainCount(1) + .pSwapchains(stack.longs(object)) + .pImageIndices(stack.ints(imageIndex)); + if (sync.containsWaits()) { + info.pWaitSemaphores(sync.toWaitBuffer(stack)); + } + check(KHRSwapchain.vkQueuePresentKHR(presentQueue.getQueue(), info)); + } + } + + public void build(Consumer builder) { + if (builder == null) { + throw new NullPointerException("Builder function cannot be null."); + } + try (Builder b = new Builder()) { + (this.builder = builder).accept(b); + } + } + + public void update() { + build(builder); + } + + public LogicalDevice getDevice() { + return device; + } + + public Surface getSurface() { + return surface; + } + + public List getImages() { + return Collections.unmodifiableList(images); + } + + public Extent2 getExtent() { + return extent; + } + + public Format getFormat() { + return format; + } + + public int getImageLayers() { + return imageLayers; + } + + public Flag getImageUsage() { + return imageUsage; + } + + public class PresentImage implements VulkanImage { + + private final LogicalDevice device; + private final long id; + private final VulkanImageView colorView; + private VulkanFrameBuffer frameBuffer; + + private PresentImage(LogicalDevice device, long id) { + this.device = device; + this.id = id; + colorView = new VulkanImageView(this, ImageView.Type.TwoDemensional); + try (VulkanImageView.Builder v = colorView.build()) { + v.setLayerCount(imageLayers); + } + } + + @Override + public LogicalDevice getDevice() { + return device; + } + + @Override + public long getId() { + return id; + } + + @Override + public IntEnum getType() { + return GpuImage.Type.TwoDemensional; + } + + @Override + public int getWidth() { + return extent.x; + } + + @Override + public int getHeight() { + return extent.y; + } + + @Override + public int getDepth() { + return 1; // swapchain images are always 2D + } + + @Override + public int getMipmaps() { + return 1; // swapchain images always have only 1 mipmap + } + + @Override + public int getLayers() { + return imageLayers; + } + + @Override + public Flag getUsage() { + return imageUsage; + } + + @Override + public Format getFormat() { + return format; + } + + @Override + public VulkanImage.Tiling getTiling() { + return VulkanImage.Tiling.Optimal; + } + + @Override + public IntEnum getSharingMode() { + return SharingMode.Exclusive; + } + + @Override + public void addNativeDependent(NativeReference ref) { + Swapchain.this.ref.addDependent(ref); + } + + public void createFrameBuffer(RenderPass compat, VulkanImageView depthStencil) { + frameBuffer = new VulkanFrameBuffer(getDevice(), compat, extent.x, extent.y, 1); + frameBuffer.addColorTarget(colorView); + frameBuffer.setDepthTarget(depthStencil); + } + + public VulkanFrameBuffer getFrameBuffer() { + return frameBuffer; + } + + } + + public class Builder extends AbstractBuilder { + + private final VkSurfaceCapabilitiesKHR caps; + private final VkSurfaceFormatKHR.Buffer formats; + private final IntBuffer modes; + private final Collection queues = new ArrayList<>(); + + private VkSurfaceFormatKHR selectedFormat; + private VkExtent2D selectedExtent; + private IntEnum selectedMode; + private Integer selectedImageCount; + + private Swapchain base; + + public Builder() { + caps = VkSurfaceCapabilitiesKHR.malloc(stack); + KHRSurface.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device.getPhysicalDevice().getDeviceHandle(), surface.getNativeObject(), caps); + formats = enumerateBuffer(stack, n -> VkSurfaceFormatKHR.malloc(n, stack), (count, buffer) + -> KHRSurface.vkGetPhysicalDeviceSurfaceFormatsKHR(device.getPhysicalDevice().getDeviceHandle(), + surface.getNativeObject(), count, buffer)); + modes = enumerateBuffer(stack, stack::mallocInt, (count, buffer) -> + KHRSurface.vkGetPhysicalDeviceSurfacePresentModesKHR(device.getPhysicalDevice().getDeviceHandle(), + surface.getNativeObject(), count, buffer)); + if (formats == null || modes == null) { + throw new UnsupportedOperationException("Swapchains are not supported by the device."); + } + } + + @Override + protected void build() { + if (selectedFormat == null) { + throw new IllegalStateException("Format not selected."); + } + if (selectedMode == null) { + throw new IllegalStateException("Mode not selected."); + } + if (selectedExtent == null) { + selectExtentByWindow(); + } + if (selectedImageCount == null) { + throw new IllegalStateException("Image count not selected."); + } + if (object != VK_NULL_HANDLE) { + createNativeDestroyer().run(); + object = VK_NULL_HANDLE; + } + VkSurfaceCapabilitiesKHR caps = VkSurfaceCapabilitiesKHR.calloc(stack); + KHRSurface.vkGetPhysicalDeviceSurfaceCapabilitiesKHR( + device.getPhysicalDevice().getDeviceHandle(), surface.getNativeObject(), caps); + format = Format.byVkEnum(selectedFormat.format()); + extent = new Extent2(selectedExtent); + VkSwapchainCreateInfoKHR create = VkSwapchainCreateInfoKHR.calloc(stack) + .sType(KHRSwapchain.VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR) + .surface(surface.getNativeObject()) + .minImageCount(selectedImageCount) + .imageFormat(format.getVkEnum()) + .imageColorSpace(selectedFormat.colorSpace()) + .imageExtent(selectedExtent) + .imageArrayLayers(imageLayers) + .imageUsage(imageUsage.bits()) + .preTransform(caps.currentTransform()) + .compositeAlpha(KHRSurface.VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) + .presentMode(selectedMode.getEnum()) + .clipped(true); + if (base != null) { + create.oldSwapchain(base.getNativeObject()); + } else { + create.oldSwapchain(VK_NULL_HANDLE); + } + if (queues.size() > 1) { + IntBuffer concurrent = stack.mallocInt(queues.size()); + for (Queue q : queues) { + concurrent.put(q.getFamilyIndex()); + } + concurrent.flip(); + create.imageSharingMode(VK_SHARING_MODE_CONCURRENT) + .queueFamilyIndexCount(queues.size()) + .pQueueFamilyIndices(concurrent); + } else { + create.imageSharingMode(VK_SHARING_MODE_EXCLUSIVE); + } + LongBuffer ptr = stack.mallocLong(1); + check(KHRSwapchain.vkCreateSwapchainKHR(device.getNativeObject(), create, null, ptr), + "Failed to create swapchain."); + object = ptr.get(0); + System.out.println("swapchain handle: " + object); + LongBuffer imgs = enumerateBuffer(stack, stack::mallocLong, (c, b) -> + check(KHRSwapchain.vkGetSwapchainImagesKHR(device.getNativeObject(), object, c, b), + "Failed to get swapchain images.")); + Objects.requireNonNull(imgs, "Swapchain contains no images."); + for (int i = 0; i < imgs.limit(); i++) { + images.add(new PresentImage(device, imgs.get(i))); + } + ref.refresh(); + } + + public void setBaseSwapchain(Swapchain base) { + this.base = base; + } + + public void addQueue(Queue queue) { + queues.add(queue); + } + + public VkSurfaceFormatKHR selectFormat(FormatColorSpace... preferredFormats) { + for (VkSurfaceFormatKHR f : formats) { + for (int i = 0; i < preferredFormats.length; i += 2) { + if (f.format() == preferredFormats[i].format.getVkEnum() + && f.colorSpace() == preferredFormats[i].colorSpace.getEnum()) { + return (selectedFormat = f); + } + } + } + return (selectedFormat = formats.get(0)); + } + + @SafeVarargs + public final IntEnum selectMode(IntEnum... preferredModes) { + for (IntEnum m : preferredModes) { + for (int i = 0; i < modes.limit(); i++) { + if (modes.get(i) == m.getEnum()) { + return (selectedMode = m); + } + } + } + return (selectedMode = PresentMode.FirstInFirstOut); + } + + public VkExtent2D selectExtentByWindow() { + if (caps.currentExtent().width() != UINT32_MAX) { + return (selectedExtent = caps.currentExtent()); + } + IntBuffer width = stack.mallocInt(1); + IntBuffer height = stack.mallocInt(1); + GLFW.glfwGetFramebufferSize(surface.getWindowHandle(), width, height); + selectedExtent = VkExtent2D.malloc(stack); + selectedExtent.width(Math.min(Math.max(width.get(0), caps.minImageExtent().width()), caps.maxImageExtent().width())); + selectedExtent.height(Math.min(Math.max(width.get(0), caps.minImageExtent().height()), caps.maxImageExtent().height())); + return selectedExtent; + } + + public int selectImageCount(int preferredCount) { + if (preferredCount < caps.minImageCount()) { + preferredCount = caps.minImageCount(); + } else if (caps.maxImageCount() > 0 && preferredCount > caps.maxImageCount()) { + preferredCount = caps.maxImageCount(); + } + return (selectedImageCount = preferredCount); + } + + public void setImageLayers(int layers) { + Swapchain.this.imageLayers = layers; + } + + public void setImageUsage(Flag usage) { + Swapchain.this.imageUsage = usage; + } + + public int getMinImageCount() { + return caps.minImageCount(); + } + + public int getMaxImageCount() { + return caps.maxImageCount(); + } + + } + + public static FormatColorSpace format(Format format, IntEnum colorSpace) { + return new FormatColorSpace(format, colorSpace); + } + + public static class FormatColorSpace { + + public final Format format; + public final IntEnum colorSpace; + + public FormatColorSpace(Format format, IntEnum colorSpace) { + this.format = format; + this.colorSpace = colorSpace; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/surface/SwapchainUpdater.java b/jme3-core/src/main/java/com/jme3/vulkan/surface/SwapchainUpdater.java new file mode 100644 index 0000000000..42f019a6b6 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/surface/SwapchainUpdater.java @@ -0,0 +1,7 @@ +package com.jme3.vulkan.surface; + +public interface SwapchainUpdater { + + boolean swapchainOutOfDate(Swapchain swapchain, int imageAcquireCode); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/sync/Fence.java b/jme3-core/src/main/java/com/jme3/vulkan/sync/Fence.java new file mode 100644 index 0000000000..cbf49f57c8 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/sync/Fence.java @@ -0,0 +1,88 @@ +package com.jme3.vulkan.sync; + +import com.jme3.util.natives.Native; +import com.jme3.util.natives.NativeReference; +import com.jme3.vulkan.devices.LogicalDevice; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkFenceCreateInfo; + +import java.util.concurrent.TimeUnit; + +import static com.jme3.renderer.vulkan.VulkanUtils.*; +import static org.lwjgl.system.MemoryUtil.NULL; +import static org.lwjgl.vulkan.VK10.*; + +public class Fence implements Native { + + private final LogicalDevice device; + private final NativeReference ref; + private long id; + + public Fence(LogicalDevice device) { + this(device, false); + } + + public Fence(LogicalDevice device, boolean signal) { + this.device = device; + try (MemoryStack stack = MemoryStack.stackPush()) { + VkFenceCreateInfo create = VkFenceCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_FENCE_CREATE_INFO) + .flags(signal ? VK_FENCE_CREATE_SIGNALED_BIT : 0); + id = getLong(stack, ptr -> check(vkCreateFence(device.getNativeObject(), create, null, ptr), + "Failed to create fence.")); + ref = Native.get().register(this); + device.getNativeReference().addDependent(ref); + } + } + + @Override + public Long getNativeObject() { + return id; + } + + @Override + public Runnable createNativeDestroyer() { + return () -> vkDestroyFence(device.getNativeObject(), nonNull(id), null); + } + + @Override + public void prematureNativeDestruction() { + id = NULL; + } + + @Override + public NativeReference getNativeReference() { + return ref; + } + + public String toString() { + return "Fence[" + !isBlocking() + "]"; + } + + public void block(long timeoutMillis) { + check(vkWaitForFences(device.getNativeObject(), id, true, TimeUnit.MILLISECONDS.toNanos(timeoutMillis)), + "Fence wait expired."); + } + + public void blockThenReset(long timeoutMillis) { + block(timeoutMillis); + reset(); + } + + public void reset() { + vkResetFences(device.getNativeObject(), id); + } + + public SyncGroup toGroup() { + return new SyncGroup(this); + } + + public boolean isBlocking() { + return vkGetFenceStatus(device.getNativeObject(), id) != VK_SUCCESS; + } + + public long getId() { + return id; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/sync/Semaphore.java b/jme3-core/src/main/java/com/jme3/vulkan/sync/Semaphore.java new file mode 100644 index 0000000000..c91705a574 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/sync/Semaphore.java @@ -0,0 +1,95 @@ +package com.jme3.vulkan.sync; + +import com.jme3.util.natives.Native; +import com.jme3.util.natives.AbstractNative; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.pipeline.PipelineStage; +import com.jme3.vulkan.util.Flag; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkSemaphoreCreateInfo; + +import java.nio.LongBuffer; +import java.util.concurrent.atomic.AtomicBoolean; + +import static com.jme3.renderer.vulkan.VulkanUtils.*; +import static org.lwjgl.vulkan.VK10.*; + +public class Semaphore extends AbstractNative { + + public static final Semaphore[] EMPTY = new Semaphore[0]; + + private final LogicalDevice device; + private Flag dstStageMask; + private final AtomicBoolean signal = new AtomicBoolean(false); + + public Semaphore(LogicalDevice device) { + this(device, PipelineStage.None); + } + + public Semaphore(LogicalDevice device, Flag dstStageMask) { + this.device = device; + this.dstStageMask = dstStageMask; + try (MemoryStack stack = MemoryStack.stackPush()) { + VkSemaphoreCreateInfo create = VkSemaphoreCreateInfo.calloc(stack) + .sType(VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO); + object = getLong(stack, ptr -> check(vkCreateSemaphore(device.getNativeObject(), create, null, ptr), + "Failed to create semaphore.")); + ref = Native.get().register(this); + device.getNativeReference().addDependent(ref); + } + } + + @Override + public Runnable createNativeDestroyer() { + return () -> vkDestroySemaphore(device.getNativeObject(), nonNull(object), null); + } + + protected void onRegisterSignal() { + if (signal.getAndSet(true)) { + throw new IllegalStateException("Semaphore cannot be registered as a signal twice in a row."); + } + } + + protected boolean onRegisterWait() { + return signal.getAndSet(false); + } + + public void setDstStageMask(Flag dstStageMask) { + this.dstStageMask = dstStageMask; + } + + public void addDstStage(Flag stageBit) { + this.dstStageMask = this.dstStageMask.add(stageBit); + } + + public void removeDstStage(Flag stageBit) { + this.dstStageMask = this.dstStageMask.remove(stageBit); + } + + public Flag getDstStageMask() { + return dstStageMask; + } + + public SyncGroup toGroupWait() { + return new SyncGroup(this, EMPTY); + } + + public SyncGroup toGroupSignal() { + return new SyncGroup(EMPTY, this); + } + + public boolean isRegisteredSignal() { + return signal.get(); + } + + @Deprecated + public long getId() { + return object; + } + + @Deprecated + public LongBuffer toBuffer(MemoryStack stack) { + return stack.longs(object); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/sync/SyncGroup.java b/jme3-core/src/main/java/com/jme3/vulkan/sync/SyncGroup.java new file mode 100644 index 0000000000..891637206c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/sync/SyncGroup.java @@ -0,0 +1,186 @@ +package com.jme3.vulkan.sync; + +import com.jme3.vulkan.devices.LogicalDevice; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VK10; + +import java.nio.IntBuffer; +import java.nio.LongBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class SyncGroup { + + public static final SyncGroup ASYNC = new SyncGroup(); + + private static Semaphore[] toArray(Semaphore s) { + return s != null ? new Semaphore[] {s} : Semaphore.EMPTY; + } + + private Semaphore[] waits; + private Semaphore[] signals; + private Fence fence; + + public SyncGroup() { + this(Semaphore.EMPTY, Semaphore.EMPTY, null); + } + + public SyncGroup(Fence fence) { + this(Semaphore.EMPTY, Semaphore.EMPTY, fence); + } + + public SyncGroup(Semaphore wait, Semaphore signal) { + this(toArray(wait), toArray(signal), null); + } + + public SyncGroup(Semaphore wait, Semaphore[] signals) { + this(toArray(wait), signals, null); + } + + public SyncGroup(Semaphore[] waits, Semaphore signal) { + this(waits, toArray(signal), null); + } + + public SyncGroup(Semaphore[] waits, Semaphore[] signals) { + this(waits, signals, null); + } + + public SyncGroup(Semaphore wait, Semaphore signal, Fence fence) { + this(toArray(wait), toArray(signal), fence); + } + + public SyncGroup(Semaphore wait, Semaphore[] signals, Fence fence) { + this(toArray(wait), signals, fence); + } + + public SyncGroup(Semaphore[] waits, Semaphore signal, Fence fence) { + this(waits, toArray(signal), fence); + } + + public SyncGroup(Semaphore[] waits, Semaphore[] signals, Fence fence) { + this.waits = Objects.requireNonNull(waits); + this.signals = Objects.requireNonNull(signals); + this.fence = fence; + } + + public LongBuffer onRegisterSignal(MemoryStack stack) { + for (Semaphore s : signals) { + s.onRegisterSignal(); + } + return toSignalBuffer(stack); + } + + public LongBuffer onRegisterWait(MemoryStack stack) { + LongBuffer buf = stack.mallocLong(waits.length); + for (Semaphore w : waits) { + if (w.onRegisterWait()) { + buf.put(w.getNativeObject()); + } + } + buf.flip(); + return buf; + } + + public void setWaits(Semaphore... waits) { + this.waits = Objects.requireNonNull(waits); + } + + public void setWaits(SyncGroup sync) { + this.waits = sync.waits; + } + + public Semaphore[] getWaits() { + return waits; + } + + public void setSignals(Semaphore... signals) { + this.signals = Objects.requireNonNull(signals); + } + + public void setSignals(SyncGroup sync) { + this.signals = sync.signals; + } + + public Semaphore[] getSignals() { + return signals; + } + + public void setFence(Fence fence) { + this.fence = fence; + } + + public void setFence(SyncGroup sync) { + this.fence = sync.fence; + } + + public Fence getFence() { + return fence; + } + + public Fence getOrCreateFence(LogicalDevice device) { + return getOrCreateFence(device, false); + } + + public Fence getOrCreateFence(LogicalDevice device, boolean signal) { + if (fence == null) { + fence = new Fence(device, signal); + } + return fence; + } + + public boolean containsWaits() { + return waits.length > 0; + } + + public boolean containsSignals() { + return signals.length > 0; + } + + public boolean containsFence() { + return fence != null; + } + + public LongBuffer toWaitBuffer(MemoryStack stack) { + LongBuffer buf = stack.mallocLong(waits.length); + for (Semaphore w : waits) { + buf.put(w.getNativeObject()); + } + buf.flip(); + return buf; + } + + public IntBuffer toDstStageBuffer(MemoryStack stack) { + IntBuffer buf = stack.mallocInt(waits.length); + for (Semaphore s : waits) { + if (s.getDstStageMask().isEmpty()) { + throw new IllegalStateException("Wait semaphore destination stage mask cannot be empty."); + } + buf.put(s.getDstStageMask().bits()); + } + buf.flip(); + return buf; + } + + public LongBuffer toSignalBuffer(MemoryStack stack) { + LongBuffer buf = stack.mallocLong(signals.length); + for (Semaphore s : signals) { + buf.put(s.getNativeObject()); + } + buf.flip(); + return buf; + } + + public long getFenceHandle() { + return fence != null ? fence.getNativeObject() : VK10.VK_NULL_HANDLE; + } + + public static SyncGroup wait(Semaphore... waits) { + return new SyncGroup(waits, Semaphore.EMPTY); + } + + public static SyncGroup signal(Semaphore... signals) { + return new SyncGroup(Semaphore.EMPTY, signals); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/sync/TaskQueue.java b/jme3-core/src/main/java/com/jme3/vulkan/sync/TaskQueue.java new file mode 100644 index 0000000000..69505c663a --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/sync/TaskQueue.java @@ -0,0 +1,9 @@ +package com.jme3.vulkan.sync; + +import java.util.concurrent.Future; + +public interface TaskQueue { + + void submit(Future task); + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/update/BasicCommandBatch.java b/jme3-core/src/main/java/com/jme3/vulkan/update/BasicCommandBatch.java new file mode 100644 index 0000000000..40f37f1a0b --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/update/BasicCommandBatch.java @@ -0,0 +1,48 @@ +package com.jme3.vulkan.update; + +import com.jme3.vulkan.commands.CommandBuffer; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class BasicCommandBatch implements CommandBatch { + + private final List> commands = new ArrayList<>(); + + @Override + public boolean requiresCommandBuffer(int frame) { + return commands.stream().anyMatch(ref -> { + Command c = ref.get(); + return c != null && c.requiresCommandBuffer(frame); + }); + } + + @Override + public void run(CommandBuffer cmd, int frame) { + for (Iterator> it = commands.iterator(); it.hasNext();) { + Command c = it.next().get(); + if (c == null) { + it.remove(); + } else { + c.run(cmd, frame); + } + } + } + + @Override + public T add(T command) { + commands.add(new WeakReference<>(command)); + return command; + } + + @Override + public void remove(Command command) { + commands.removeIf(ref -> { + Command c = ref.get(); + return c == null || c.removeByCommand(command); + }); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/update/Command.java b/jme3-core/src/main/java/com/jme3/vulkan/update/Command.java new file mode 100644 index 0000000000..14febc1feb --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/update/Command.java @@ -0,0 +1,41 @@ +package com.jme3.vulkan.update; + +import com.jme3.vulkan.commands.CommandBuffer; + +public interface Command { + + /** + * Checks if this command currently requires a {@link CommandBuffer} + * on {@link #run(CommandBuffer, int) run}. If no commands in the + * {@link CommandBatch batch} require a CommandBuffer, then {@code null} + * is passed as the CommandBuffer on run. + * + * @param frame current frame index + * @return true if this command requires a CommandBuffer on run + */ + boolean requiresCommandBuffer(int frame); + + /** + * Runs this command. + * + *

If no commands in the {@link CommandBatch} {@link #requiresCommandBuffer(int) + * require a CommandBuffer} on run, the null is passed as {@code cmd}.

+ * + * @param cmd CommandBuffer to submit Vulkan commands to, or null if no + * commands require a CommandBuffer. + * @param frame current frame index + */ + void run(CommandBuffer cmd, int frame); + + /** + * Checks if this command should be removed if {@code command} + * is submitted for removal. + * + * @param command command asserted for removal + * @return true if this command should be removed + */ + default boolean removeByCommand(Command command) { + return this == command; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/update/CommandBatch.java b/jme3-core/src/main/java/com/jme3/vulkan/update/CommandBatch.java new file mode 100644 index 0000000000..767cc9f3dd --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/update/CommandBatch.java @@ -0,0 +1,30 @@ +package com.jme3.vulkan.update; + +import com.jme3.vulkan.frames.VersionedResource; + +public interface CommandBatch extends Command { + + T add(T command); + + void remove(Command command); + + default T add(int frame, T command) { + add(new FrameLocalCommand(frame, command)); + return command; + } + + default > T addAll(T resource) { + int i = 0; + for (Command c : resource) { + add(i++, c); + } + return resource; + } + + default void removeAll(VersionedResource resource) { + for (Command c : resource) { + remove(c); + } + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/update/CommandRunner.java b/jme3-core/src/main/java/com/jme3/vulkan/update/CommandRunner.java new file mode 100644 index 0000000000..81e435685d --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/update/CommandRunner.java @@ -0,0 +1,61 @@ +package com.jme3.vulkan.update; + +import com.jme3.vulkan.commands.CommandBuffer; +import com.jme3.vulkan.sync.SyncGroup; + +import java.util.function.Consumer; + +public class CommandRunner { + + private int frame; + private Command command; + private CommandBuffer buffer; + + public CommandRunner(int frame, CommandBuffer buffer, Command command) { + this.frame = frame; + this.buffer = buffer; + this.command = command; + } + + public void run(SyncGroup sync, Consumer onCommandBufferUsed) { + if (command.requiresCommandBuffer(frame)) { + if (onCommandBufferUsed != null) { + onCommandBufferUsed.accept(buffer); + } + buffer.resetAndBegin(); + command.run(buffer, frame); + buffer.endAndSubmit(sync != null ? sync : SyncGroup.ASYNC); + } else { + command.run(null, frame); + } + } + + public void run(SyncGroup sync) { + run(sync, null); + } + + public void setTargetFrame(int frame) { + this.frame = frame; + } + + public void setCommand(Command command) { + this.command = command; + } + + public void setCommandBuffer(CommandBuffer buffer) { + this.buffer = buffer; + } + + public int getTargetFrame() { + return frame; + } + + public CommandBuffer getCommandBuffer() { + return buffer; + } + + public Command getCommand() { + return command; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/update/FrameLocalCommand.java b/jme3-core/src/main/java/com/jme3/vulkan/update/FrameLocalCommand.java new file mode 100644 index 0000000000..f0117d234b --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/update/FrameLocalCommand.java @@ -0,0 +1,32 @@ +package com.jme3.vulkan.update; + +import com.jme3.vulkan.commands.CommandBuffer; + +public class FrameLocalCommand implements Command { + + private final int frame; + private final Command delegate; + + public FrameLocalCommand(int frame, Command delegate) { + this.frame = frame; + this.delegate = delegate; + } + + @Override + public boolean requiresCommandBuffer(int frame) { + return this.frame == frame && delegate.requiresCommandBuffer(frame); + } + + @Override + public void run(CommandBuffer cmd, int frame) { + if (this.frame == frame) { + delegate.run(cmd, frame); + } + } + + @Override + public boolean removeByCommand(Command command) { + return this == command || delegate.removeByCommand(command); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/util/Extent2.java b/jme3-core/src/main/java/com/jme3/vulkan/util/Extent2.java new file mode 100644 index 0000000000..f3b99e53eb --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/util/Extent2.java @@ -0,0 +1,58 @@ +package com.jme3.vulkan.util; + +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VkExtent2D; + +public final class Extent2 { + + public int x, y; + + public Extent2() { + this(0, 0); + } + + public Extent2(int x, int y) { + this.x = x; + this.y = y; + } + + public Extent2(VkExtent2D vk) { + this.x = vk.width(); + this.y = vk.height(); + } + + public Extent2 set(int x, int y) { + this.x = x; + this.y = y; + return this; + } + + public Extent2 set(VkExtent2D vk) { + this.x = vk.width(); + this.y = vk.height(); + return this; + } + + public Extent2 setX(int x) { + this.x = x; + return this; + } + + public Extent2 setY(int y) { + this.y = y; + return this; + } + + public VkExtent2D toStruct(MemoryStack stack) { + return VkExtent2D.malloc(stack).set(x, y); + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/util/Flag.java b/jme3-core/src/main/java/com/jme3/vulkan/util/Flag.java new file mode 100644 index 0000000000..46d2d77026 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/util/Flag.java @@ -0,0 +1,151 @@ +package com.jme3.vulkan.util; + +import java.util.Iterator; + +public interface Flag extends Iterable { + + int bits(); + + default int lowestBit() { + return Integer.lowestOneBit(bits()); + } + + default int bitCount() { + return Integer.bitCount(bits()); + } + + default Flag add(Flag flag) { + if (!contains(flag)) { + return new FlagImpl<>(bits() | flag.bits()); + } else return this; + } + + default Flag add(Flag... flags) { + int result = bits(); + for (Flag f : flags) { + result |= f.bits(); + } + return new FlagImpl<>(result); + } + + default Flag remove(Flag flag) { + if (containsAny(flag)) { + return new FlagImpl<>(bits() & ~flag.bits()); + } else return this; + } + + default Flag remove(Flag... flags) { + int result = bits(); + for (Flag f : flags) { + result &= ~f.bits(); + } + return new FlagImpl<>(result); + } + + default boolean contains(Flag flag) { + return contains(flag.bits()); + } + + default boolean contains(int bits) { + return (bits() & bits) == bits; + } + + default boolean containsAny(Flag flag) { + return containsAny(flag.bits()); + } + + default boolean containsAny(int bits) { + return (bits() & bits) > 0; + } + + default boolean isEmpty() { + return bits() == 0; + } + + default boolean is(Flag flag) { + return bits() == flag.bits(); + } + + default boolean is(int bits) { + return bits() == bits; + } + + @Override + default Iterator iterator() { + return new IteratorImpl(bits()); + } + + static Flag of(int bits) { + return new FlagImpl<>(bits); + } + + static Flag empty() { + return of(0); + } + + static Flag of(Flag... flags) { + return new FlagImpl<>(flags); + } + + static int bitsOf(Flag... flags) { + int result = 0; + for (Flag f : flags) { + result |= f.bits(); + } + return result; + } + + static boolean is(Flag f1, Flag f2) { + return f1 == f2 || (f1 != null && f1.is(f2)); + } + + static Flag combine(Flag... flags) { + int bits = 0; + for (Flag f : flags) { + if (f != null) bits |= f.bits(); + } + return of(bits); + } + + class FlagImpl implements Flag { + + private final int bits; + + public FlagImpl(int bits) { + this.bits = bits; + } + + public FlagImpl(Flag... flags) { + this.bits = bitsOf(flags); + } + + @Override + public int bits() { + return bits; + } + + } + + class IteratorImpl implements Iterator { + + private int bits; + + public IteratorImpl(int bits) { + this.bits = bits; + } + + @Override + public boolean hasNext() { + return bits != 0; + } + + @Override + public Integer next() { + int bit = Integer.lowestOneBit(bits); + bits &= ~bit; + return bit; + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/util/FlagParser.java b/jme3-core/src/main/java/com/jme3/vulkan/util/FlagParser.java new file mode 100644 index 0000000000..629cf5220e --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/util/FlagParser.java @@ -0,0 +1,77 @@ +package com.jme3.vulkan.util; + +import com.fasterxml.jackson.databind.JsonNode; + +import java.util.HashMap; +import java.util.Map; + +public class FlagParser { + + private static final Map> custom = new HashMap<>(); + + public static void add(Class clazz, String name, Flag flag) { + custom.computeIfAbsent(clazz, k -> new HashMap<>()).put(name, flag); + } + + public static Object remove(Class clazz, String name) { + Map map = custom.get(clazz); + if (map == null) return null; + Object rmv = map.remove(name); + if (map.isEmpty()) custom.remove(clazz, map); + return rmv; + } + + public static Object get(Class clazz, String name) { + Map map = custom.get(clazz); + if (map == null) return null; + return map.get(name); + } + + @SuppressWarnings("unchecked") + public static Flag parseFlag(Class clazz, String name) { + Object flag = get(clazz, name); + if (flag != null) { + flag = Enum.valueOf(clazz, name); + } + return (Flag)flag; + } + + public static Flag parseFlag(Class clazz, String... names) { + int bits = 0; + for (String n : names) { + bits |= parseFlag(clazz, n).bits(); + } + return Flag.of(bits); + } + + public static Flag parseFlag(Class clazz, JsonNode array) { + if (array == null) { + throw new NullPointerException("Json element cannot be null."); + } + if (!array.isArray()) { + throw new IllegalArgumentException("Json element must be an array."); + } + int bits = 0; + for (int i = 0; i < array.size(); i++) { + bits |= parseFlag(clazz, array.get(i).asText()).bits(); + } + return Flag.of(bits); + } + + public static Flag parseFlag(Class clazz, JsonNode array, Flag defVal) { + if (array == null || !array.isArray()) { + return defVal; + } + return parseFlag(clazz, array); + } + + @SuppressWarnings("unchecked") + public static IntEnum parseEnum(Class clazz, String name) { + Object enm = get(clazz, name); + if (enm != null) { + enm = Enum.valueOf(clazz, name); + } + return (IntEnum)enm; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/util/FloatBufferModifier.java b/jme3-core/src/main/java/com/jme3/vulkan/util/FloatBufferModifier.java new file mode 100644 index 0000000000..14e1d01016 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/util/FloatBufferModifier.java @@ -0,0 +1,133 @@ +package com.jme3.vulkan.util; + +import com.jme3.vulkan.mesh.VertexReader; +import com.jme3.vulkan.mesh.VertexWriter; + +import java.nio.FloatBuffer; + +public class FloatBufferModifier implements VertexReader, VertexWriter { + + private final FloatBuffer buffer; + private final int components; + + public FloatBufferModifier(FloatBuffer buffer, int components) { + this.buffer = buffer; + this.components = components; + } + + public int vertexToPosition(int vertex, int component) { + return vertex * components + component; + } + + @Override + public int capacity() { + return buffer.capacity() / components; + } + + @Override + public int limit() { + return buffer.limit() / components; + } + + @Override + public int components() { + return components; + } + + @Override + public VertexWriter limit(int vertex) { + buffer.limit(vertexToPosition(vertex, 0)); + return this; + } + + @Override + public VertexWriter putByte(int vertex, int component, byte value) { + buffer.put(vertexToPosition(vertex, component), value); + return this; + } + + @Override + public VertexWriter putShort(int vertex, int component, short value) { + buffer.put(vertexToPosition(vertex, component), value); + return this; + } + + @Override + public VertexWriter putInt(int vertex, int component, int value) { + buffer.put(vertexToPosition(vertex, component), value); + return this; + } + + @Override + public VertexWriter putFloat(int vertex, int component, float value) { + buffer.put(vertexToPosition(vertex, component), value); + return this; + } + + @Override + public VertexWriter putDouble(int vertex, int component, double value) { + buffer.put(vertexToPosition(vertex, component), (float)value); + return this; + } + + @Override + public VertexWriter putLong(int vertex, int component, long value) { + buffer.put(vertexToPosition(vertex, component), value); + return this; + } + + @Override + public VertexWriter putNumber(int vertex, int component, Number value) { + buffer.put(vertexToPosition(vertex, component), value.floatValue()); + return this; + } + + @Override + public VertexWriter putFloats(int baseVertex, int baseComponent, float... values) { + int p = buffer.position(); + buffer.position(vertexToPosition(baseVertex, baseComponent)); + buffer.put(values); + buffer.position(p); + return this; + } + + @Override + public VertexWriter putFloats(int baseVertex, int baseComponent, FloatBuffer values) { + int p = buffer.position(); + buffer.position(vertexToPosition(baseVertex, baseComponent)); + buffer.put(values); + buffer.position(p); + return this; + } + + @Override + public byte getByte(int vertex, int component) { + return (byte)buffer.get(vertexToPosition(vertex, component)); + } + + @Override + public short getShort(int vertex, int component) { + return (short)buffer.get(vertexToPosition(vertex, component)); + } + + @Override + public int getInt(int vertex, int component) { + return (int)buffer.get(vertexToPosition(vertex, component)); + } + + @Override + public float getFloat(int vertex, int component) { + return buffer.get(vertexToPosition(vertex, component)); + } + + @Override + public double getDouble(int vertex, int component) { + return buffer.get(vertexToPosition(vertex, component)); + } + + @Override + public long getLong(int vertex, int component) { + return (long)buffer.get(vertexToPosition(vertex, component)); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/util/IntEnum.java b/jme3-core/src/main/java/com/jme3/vulkan/util/IntEnum.java new file mode 100644 index 0000000000..ff636f5167 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/util/IntEnum.java @@ -0,0 +1,60 @@ +package com.jme3.vulkan.util; + +import java.util.Objects; + +public interface IntEnum { + + int getEnum(); + + default boolean is(IntEnum other) { + return other != null && is(other.getEnum()); + } + + default boolean is(int value) { + return getEnum() == value; + } + + static IntEnum get(IntEnum intEnum, IntEnum defEnum) { + return intEnum != null ? intEnum : defEnum; + } + + static int get(IntEnum intEnum, int defEnum) { + return intEnum != null ? intEnum.getEnum() : defEnum; + } + + static IntEnum of(int intEnum) { + return new EnumImpl<>(intEnum); + } + + static boolean is(IntEnum e1, IntEnum e2) { + return e1 == e2 || (e1 != null && e1.is(e2)); + } + + class EnumImpl implements IntEnum { + + private final int intEnum; + + public EnumImpl(int intEnum) { + this.intEnum = intEnum; + } + + @Override + public int getEnum() { + return intEnum; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + EnumImpl that = (EnumImpl) o; + return intEnum == that.intEnum; + } + + @Override + public int hashCode() { + return Objects.hashCode(intEnum); + } + + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/util/ReflectionArgs.java b/jme3-core/src/main/java/com/jme3/vulkan/util/ReflectionArgs.java new file mode 100644 index 0000000000..646c82cacc --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/util/ReflectionArgs.java @@ -0,0 +1,103 @@ +package com.jme3.vulkan.util; + +import com.fasterxml.jackson.databind.JsonNode; +import com.jme3.asset.AssetManager; +import com.jme3.vulkan.buffers.generate.BufferGenerator; +import com.jme3.vulkan.descriptors.Descriptor; +import com.jme3.vulkan.material.uniforms.BufferUniform; +import com.jme3.vulkan.material.uniforms.TextureUniform; +import com.jme3.vulkan.pipeline.graphics.GraphicsState; + +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; + +@Deprecated +public class ReflectionArgs { + + private static final Map> natives = new HashMap<>(); + + private final AssetManager assetManager; + private final BufferGenerator generator; + private final String name; + private final String type; + private final JsonNode properties; + private final JsonNode imports; + + public ReflectionArgs(AssetManager assetManager, BufferGenerator generator, JsonNode imports) { + this(assetManager, generator, null, null, null, imports); + } + + public ReflectionArgs(AssetManager assetManager, BufferGenerator generator, String name, JsonNode properties, JsonNode imports) { + this(assetManager, generator, name, null, properties, imports); + } + + public ReflectionArgs(AssetManager assetManager, BufferGenerator generator, String name, String type, JsonNode properties, JsonNode imports) { + this.assetManager = assetManager; + this.generator = generator; + this.name = name; + this.properties = properties; + this.imports = imports; + if (properties != null) { + JsonNode typeNode = properties.get("type"); + this.type = typeNode != null ? typeNode.asText() : type; + } else { + this.type = null; + } + } + + @SuppressWarnings("unchecked") + public T instantiate() { + try { + JsonNode im = imports.get(type); + if (im != null) { + Class clazz = Class.forName(im.asText()); + return (T)clazz.getDeclaredConstructor(ReflectionArgs.class).newInstance(this); + } + Function factory = natives.get(type); + if (factory != null) { + return (T)factory.apply(this); + } else { + Class clazz = Class.forName(type); + return (T)clazz.getDeclaredConstructor(ReflectionArgs.class).newInstance(this); + } + } catch (ClassNotFoundException + | InvocationTargetException + | InstantiationException + | IllegalAccessException + | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + public ReflectionArgs create(String name, JsonNode properties) { + return new ReflectionArgs(assetManager, generator, name, properties, imports); + } + + public ReflectionArgs create(String name, String type, JsonNode properties) { + return new ReflectionArgs(assetManager, generator, type, name, properties, imports); + } + + public AssetManager getAssetManager() { + return assetManager; + } + + public BufferGenerator getGenerator() { + return generator; + } + + public String getName() { + return name; + } + + public JsonNode getProperties() { + return properties; + } + + public JsonNode getImports() { + return imports; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/vulkan/util/RenderStateToVulkan.java b/jme3-core/src/main/java/com/jme3/vulkan/util/RenderStateToVulkan.java new file mode 100644 index 0000000000..b0988ab6a5 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/vulkan/util/RenderStateToVulkan.java @@ -0,0 +1,84 @@ +package com.jme3.vulkan.util; + +import com.jme3.material.RenderState.*; +import static org.lwjgl.vulkan.VK10.*; + +public class RenderStateToVulkan { + + private static RuntimeException unrecognized(Object state) { + return new UnsupportedOperationException("Unrecognized: " + state); + } + + public static int depthFunc(TestFunction func) { + switch (func) { + case Always: return VK_COMPARE_OP_ALWAYS; + case Equal: return VK_COMPARE_OP_EQUAL; + case Greater: return VK_COMPARE_OP_GREATER; + case Less: return VK_COMPARE_OP_LESS; + case LessOrEqual: return VK_COMPARE_OP_LESS_OR_EQUAL; + case GreaterOrEqual: return VK_COMPARE_OP_GREATER_OR_EQUAL; + case Never: return VK_COMPARE_OP_NEVER; + case NotEqual: return VK_COMPARE_OP_NOT_EQUAL; + default: throw unrecognized(func); + } + } + + public static int blendEquation(BlendEquation eq) { + switch (eq) { + case Add: return VK_BLEND_OP_ADD; + case Subtract: return VK_BLEND_OP_SUBTRACT; + case ReverseSubtract: return VK_BLEND_OP_REVERSE_SUBTRACT; + case Min: return VK_BLEND_OP_MIN; + case Max: return VK_BLEND_OP_MAX; + default: throw unrecognized(eq); + } + } + + public static int blendEquationAlpha(BlendEquationAlpha eqA, BlendEquation eq) { + switch (eqA) { + case InheritColor: return blendEquation(eq); + case Add: return VK_BLEND_OP_ADD; + case Subtract: return VK_BLEND_OP_SUBTRACT; + case ReverseSubtract: return VK_BLEND_OP_REVERSE_SUBTRACT; + case Min: return VK_BLEND_OP_MIN; + case Max: return VK_BLEND_OP_MAX; + default: throw unrecognized(eqA); + } + } + + public static int blendFunc(BlendFunc func) { + switch (func) { + case Zero: return VK_BLEND_FACTOR_ZERO; + case One: return VK_BLEND_FACTOR_ONE; + case Src_Color: return VK_BLEND_FACTOR_SRC_COLOR; + case One_Minus_Src_Color: return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; + case Dst_Color: return VK_BLEND_FACTOR_DST_COLOR; + case One_Minus_Dst_Color: return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; + case Src_Alpha: return VK_BLEND_FACTOR_SRC_ALPHA; + case One_Minus_Src_Alpha: return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + case Dst_Alpha: return VK_BLEND_FACTOR_DST_ALPHA; + case One_Minus_Dst_Alpha: return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; + case Src_Alpha_Saturate: return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; + default: throw unrecognized(func); + } + } + + public static int faceCull(FaceCullMode mode) { + switch (mode) { + case Off: return VK_CULL_MODE_NONE; + case Front: return VK_CULL_MODE_FRONT_BIT; + case Back: return VK_CULL_MODE_BACK_BIT; + case FrontAndBack: return VK_CULL_MODE_FRONT_AND_BACK; + default: throw unrecognized(mode); + } + } + + public static int wireframe(boolean wireframe, int def) { + return wireframe ? VK_POLYGON_MODE_LINE : def; + } + + public static int wireframe(boolean wireframe) { + return wireframe(wireframe, VK_POLYGON_MODE_FILL); + } + +} diff --git a/jme3-core/src/plugins/java/com/jme3/font/plugins/BitmapFontLoader.java b/jme3-core/src/plugins/java/com/jme3/font/plugins/BitmapFontLoader.java index b81253601c..0968b00332 100644 --- a/jme3-core/src/plugins/java/com/jme3/font/plugins/BitmapFontLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/font/plugins/BitmapFontLoader.java @@ -38,7 +38,7 @@ import com.jme3.material.Material; import com.jme3.material.MaterialDef; import com.jme3.material.RenderState.BlendMode; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -86,7 +86,7 @@ private BitmapFont load(AssetManager assetManager, String folder, InputStream in } }else if (tokens[0].equals("page")){ int index = -1; - Texture tex = null; + GlTexture tex = null; for (int i = 1; i < tokens.length; i++){ String token = tokens[i]; @@ -100,8 +100,8 @@ private BitmapFont load(AssetManager assetManager, String folder, InputStream in TextureKey key = new TextureKey(folder + file, true); key.setGenerateMips(false); tex = assetManager.loadTexture(key); - tex.setMagFilter(Texture.MagFilter.Bilinear); - tex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); + tex.setMagFilter(GlTexture.MagFilter.Bilinear); + tex.setMinFilter(GlTexture.MinFilter.BilinearNoMipMaps); } } // set page diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java index e874ed555c..819ccd1476 100644 --- a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java @@ -43,7 +43,7 @@ import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; import com.jme3.shader.*; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; import com.jme3.texture.image.ColorSpace; import com.jme3.util.PlaceholderAssets; @@ -211,7 +211,7 @@ private boolean isTexturePathDeclaredTheTraditionalWay(final List textureValues = tokenizeTextureValue(value); final List textureOptionValues = parseTextureOptions(textureValues); @@ -267,17 +267,17 @@ private Texture parseTextureType(final VarType type, final String value) { switch (type) { case Texture3D: - textureKey.setTextureTypeHint(Texture.Type.ThreeDimensional); + textureKey.setTextureTypeHint(GlTexture.Type.ThreeDimensional); break; case TextureArray: - textureKey.setTextureTypeHint(Texture.Type.TwoDimensionalArray); + textureKey.setTextureTypeHint(GlTexture.Type.TwoDimensionalArray); break; case TextureCubeMap: - textureKey.setTextureTypeHint(Texture.Type.CubeMap); + textureKey.setTextureTypeHint(GlTexture.Type.CubeMap); break; } - Texture texture; + GlTexture texture; try { texture = assetManager.loadTexture(textureKey); @@ -403,7 +403,7 @@ private void readParam(String statement) throws IOException{ defaultValObj = readValue(type, defaultVal); } if(type.isTextureType()){ - materialDef.addMaterialParamTexture(type, name, colorSpace,(Texture)defaultValObj); + materialDef.addMaterialParamTexture(type, name, colorSpace,(GlTexture)defaultValObj); }else{ materialDef.addMaterialParam(type, name, defaultValObj); } @@ -426,7 +426,7 @@ private void readValueParam(String statement) throws IOException{ Object valueObj = readValue(p.getVarType(), split[1]); if (p.getVarType().isTextureType()){ - material.setTextureParam(name, p.getVarType(), (Texture) valueObj); + material.setTextureParam(name, p.getVarType(), (GlTexture) valueObj); }else{ material.setParam(name, p.getVarType(), valueObj); } @@ -857,60 +857,60 @@ protected void initNodesLoader() { private enum TextureOption { /** - * Applies a {@link com.jme3.texture.Texture.MinFilter} to the texture. + * Applies a {@link GlTexture.MinFilter} to the texture. */ Min { @Override public void applyToTextureKey(final String option, final TextureKey textureKey) { - Texture.MinFilter minFilter = Texture.MinFilter.valueOf(option); + GlTexture.MinFilter minFilter = GlTexture.MinFilter.valueOf(option); textureKey.setGenerateMips(minFilter.usesMipMapLevels()); } @Override - public void applyToTexture(final String option, final Texture texture) { - texture.setMinFilter(Texture.MinFilter.valueOf(option)); + public void applyToTexture(final String option, final GlTexture texture) { + texture.setMinFilter(GlTexture.MinFilter.valueOf(option)); } }, /** - * Applies a {@link com.jme3.texture.Texture.MagFilter} to the texture. + * Applies a {@link GlTexture.MagFilter} to the texture. */ Mag { @Override - public void applyToTexture(final String option, final Texture texture) { - texture.setMagFilter(Texture.MagFilter.valueOf(option)); + public void applyToTexture(final String option, final GlTexture texture) { + texture.setMagFilter(GlTexture.MagFilter.valueOf(option)); } }, /** - * Applies a {@link com.jme3.texture.Texture.WrapMode} to the texture. This also supports {@link com.jme3.texture.Texture.WrapAxis} + * Applies a {@link GlTexture.WrapMode} to the texture. This also supports {@link GlTexture.WrapAxis} * by adding "_AXIS" to the texture option. For instance if you wanted to repeat on the S (horizontal) axis, you * would use
WrapRepeat_S
as a texture option. */ Wrap { @Override - public void applyToTexture(final String option, final Texture texture) { + public void applyToTexture(final String option, final GlTexture texture) { final int separatorPosition = option.indexOf("_"); if (separatorPosition >= option.length() - 2) { final String axis = option.substring(separatorPosition + 1); final String mode = option.substring(0, separatorPosition); - final Texture.WrapAxis wrapAxis = Texture.WrapAxis.valueOf(axis); - texture.setWrap(wrapAxis, Texture.WrapMode.valueOf(mode)); + final GlTexture.WrapAxis wrapAxis = GlTexture.WrapAxis.valueOf(axis); + texture.setWrap(wrapAxis, GlTexture.WrapMode.valueOf(mode)); } else { - texture.setWrap(Texture.WrapMode.valueOf(option)); + texture.setWrap(GlTexture.WrapMode.valueOf(option)); } } }, /** - * Applies a {@link com.jme3.texture.Texture.WrapMode#Repeat} to the texture. This is simply an alias for + * Applies a {@link GlTexture.WrapMode#Repeat} to the texture. This is simply an alias for * WrapRepeat, please use WrapRepeat instead if possible as this may become deprecated later on. */ Repeat { @Override - public void applyToTexture(final String option, final Texture texture) { + public void applyToTexture(final String option, final GlTexture texture) { Wrap.applyToTexture("Repeat", texture); } }, @@ -929,7 +929,7 @@ public String getOptionValue(final String option) { return option.substring(name().length()); } - public void applyToTexture(final String option, final Texture texture) { + public void applyToTexture(final String option, final GlTexture texture) { } public void applyToTextureKey(final String option, final TextureKey textureKey) { @@ -964,7 +964,7 @@ public void applyToTextureKey(final TextureKey textureKey) { textureOption.applyToTextureKey(value, textureKey); } - public void applyToTexture(final Texture texture) { + public void applyToTexture(final GlTexture texture) { textureOption.applyToTexture(value, texture); } } diff --git a/jme3-core/src/plugins/java/com/jme3/scene/plugins/MTLLoader.java b/jme3-core/src/plugins/java/com/jme3/scene/plugins/MTLLoader.java index ac186c1606..8853bd0721 100644 --- a/jme3-core/src/plugins/java/com/jme3/scene/plugins/MTLLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/scene/plugins/MTLLoader.java @@ -36,8 +36,8 @@ import com.jme3.material.MaterialList; import com.jme3.material.RenderState.BlendMode; import com.jme3.math.ColorRGBA; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.texture.Texture2D; import com.jme3.util.PlaceholderAssets; import java.io.File; @@ -60,7 +60,7 @@ public class MTLLoader implements AssetLoader { protected String folderName; protected AssetKey key; - protected Texture diffuseMap, normalMap, specularMap, alphaMap; + protected GlTexture diffuseMap, normalMap, specularMap, alphaMap; protected ColorRGBA ambient = new ColorRGBA(); protected ColorRGBA diffuse = new ColorRGBA(); protected ColorRGBA specular = new ColorRGBA(); @@ -167,7 +167,7 @@ protected void startMaterial(String name){ matName = name; } - protected Texture loadTexture(String path){ + protected GlTexture loadTexture(String path){ String[] split = path.trim().split("\\p{javaWhitespace}+"); // will crash if path is an empty string @@ -176,7 +176,7 @@ protected Texture loadTexture(String path){ String name = new File(path).getName(); TextureKey texKey = new TextureKey(folderName + name); texKey.setGenerateMips(true); - Texture texture; + GlTexture texture; try { texture = assetManager.loadTexture(texKey); texture.setWrap(WrapMode.Repeat); diff --git a/jme3-core/src/plugins/java/com/jme3/scene/plugins/OBJLoader.java b/jme3-core/src/plugins/java/com/jme3/scene/plugins/OBJLoader.java index cd2b4a29e8..1a6f9bbf28 100644 --- a/jme3-core/src/plugins/java/com/jme3/scene/plugins/OBJLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/scene/plugins/OBJLoader.java @@ -38,8 +38,8 @@ import com.jme3.math.Vector3f; import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.scene.*; -import com.jme3.scene.Mesh.Mode; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlMesh.Mode; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.scene.mesh.IndexBuffer; import com.jme3.scene.mesh.IndexIntBuffer; import com.jme3.scene.mesh.IndexShortBuffer; @@ -481,22 +481,22 @@ protected Mesh constructMesh(ArrayList faceList){ if (hasNormals){ normBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 3); - m.setBuffer(VertexBuffer.Type.Normal, 3, normBuf); + m.setBuffer(GlVertexBuffer.Type.Normal, 3, normBuf); } if (hasTexCoord){ tcBuf = BufferUtils.createFloatBuffer(vertIndexMap.size() * 2); - m.setBuffer(VertexBuffer.Type.TexCoord, 2, tcBuf); + m.setBuffer(GlVertexBuffer.Type.TexCoord, 2, tcBuf); } IndexBuffer indexBuf = null; if (vertIndexMap.size() >= 65536){ // too many vertices: use IntBuffer instead of ShortBuffer IntBuffer ib = BufferUtils.createIntBuffer(newFaces.size() * 3); - m.setBuffer(VertexBuffer.Type.Index, 3, ib); + m.setBuffer(GlVertexBuffer.Type.Index, 3, ib); indexBuf = new IndexIntBuffer(ib); }else{ ShortBuffer sb = BufferUtils.createShortBuffer(newFaces.size() * 3); - m.setBuffer(VertexBuffer.Type.Index, 3, sb); + m.setBuffer(GlVertexBuffer.Type.Index, 3, sb); indexBuf = new IndexShortBuffer(sb); } @@ -545,7 +545,7 @@ protected Mesh constructMesh(ArrayList faceList){ indexBuf.put(index+2, v2.index); } - m.setBuffer(VertexBuffer.Type.Position, 3, posBuf); + m.setBuffer(GlVertexBuffer.Type.Position, 3, posBuf); // index buffer and others were set on creation m.setStatic(); diff --git a/jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java b/jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java index 6ea0f8d45d..a6b0cd7a8e 100644 --- a/jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java @@ -34,9 +34,9 @@ import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetLoader; import com.jme3.asset.TextureKey; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture; import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; import com.jme3.util.LittleEndien; @@ -125,20 +125,20 @@ public Object load(AssetInfo info) throws IOException { in = new LittleEndien(stream); loadHeader(); if (texture3D) { - textureKey.setTextureTypeHint(Texture.Type.ThreeDimensional); + textureKey.setTextureTypeHint(GlTexture.Type.ThreeDimensional); } else if (depth > 1) { - textureKey.setTextureTypeHint(Texture.Type.CubeMap); + textureKey.setTextureTypeHint(GlTexture.Type.CubeMap); } ArrayList data = readData(textureKey.isFlipY()); - return new Image(pixelFormat, width, height, depth, data, sizes, ColorSpace.sRGB); + return new GlImage(pixelFormat, width, height, depth, data, sizes, ColorSpace.sRGB); } } - public Image load(InputStream stream) throws IOException { + public GlImage load(InputStream stream) throws IOException { in = new LittleEndien(stream); loadHeader(); ArrayList data = readData(false); - return new Image(pixelFormat, width, height, depth, data, sizes, ColorSpace.sRGB); + return new GlImage(pixelFormat, width, height, depth, data, sizes, ColorSpace.sRGB); } private void loadDX10Header() throws IOException { @@ -178,16 +178,16 @@ private void setPixelFormat(int dxgiFormat) throws IOException { pixelFormat = Format.DXT5; break; case DXGIFormat.DXGI_FORMAT_BC4_UNORM: - pixelFormat = Image.Format.RGTC1; + pixelFormat = GlImage.Format.RGTC1; break; case DXGIFormat.DXGI_FORMAT_BC4_SNORM: pixelFormat = Format.SIGNED_RGTC1; break; case DXGIFormat.DXGI_FORMAT_BC5_UNORM: - pixelFormat = Image.Format.RGTC2; + pixelFormat = GlImage.Format.RGTC2; break; case DXGIFormat.DXGI_FORMAT_BC5_SNORM: - pixelFormat = Image.Format.SIGNED_RGTC2; + pixelFormat = GlImage.Format.SIGNED_RGTC2; break; case DXGIFormat.DXGI_FORMAT_BC6H_UF16: pixelFormat = Format.BC6H_UF16; @@ -298,26 +298,26 @@ private void readPixelFormat() throws IOException { case PF_DXT1: bpp = 4; if (is(pfFlags, DDPF_ALPHAPIXELS)) { - pixelFormat = Image.Format.DXT1A; + pixelFormat = GlImage.Format.DXT1A; } else { - pixelFormat = Image.Format.DXT1; + pixelFormat = GlImage.Format.DXT1; } break; case PF_DXT3: bpp = 8; - pixelFormat = Image.Format.DXT3; + pixelFormat = GlImage.Format.DXT3; break; case PF_DXT5: bpp = 8; - pixelFormat = Image.Format.DXT5; + pixelFormat = GlImage.Format.DXT5; break; case PF_ATI1: bpp = 4; - pixelFormat = Image.Format.RGTC1; + pixelFormat = GlImage.Format.RGTC1; break; case PF_ATI2: bpp = 8; - pixelFormat = Image.Format.RGTC2; + pixelFormat = GlImage.Format.RGTC2; break; case PF_DX10: compressed = false; @@ -329,7 +329,7 @@ private void readPixelFormat() throws IOException { case 113: compressed = false; bpp = 64; - pixelFormat = Image.Format.RGBA16F; + pixelFormat = GlImage.Format.RGBA16F; break; case 111: compressed = false; diff --git a/jme3-core/src/plugins/java/com/jme3/texture/plugins/DXTFlipper.java b/jme3-core/src/plugins/java/com/jme3/texture/plugins/DXTFlipper.java index 10a02df4a3..6293c70985 100644 --- a/jme3-core/src/plugins/java/com/jme3/texture/plugins/DXTFlipper.java +++ b/jme3-core/src/plugins/java/com/jme3/texture/plugins/DXTFlipper.java @@ -32,7 +32,7 @@ package com.jme3.texture.plugins; import com.jme3.math.FastMath; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage.Format; import com.jme3.util.BufferUtils; import java.nio.ByteBuffer; import java.nio.ByteOrder; diff --git a/jme3-core/src/plugins/java/com/jme3/texture/plugins/ETCFlipper.java b/jme3-core/src/plugins/java/com/jme3/texture/plugins/ETCFlipper.java index 0809a5f7bb..889ad55c1a 100644 --- a/jme3-core/src/plugins/java/com/jme3/texture/plugins/ETCFlipper.java +++ b/jme3-core/src/plugins/java/com/jme3/texture/plugins/ETCFlipper.java @@ -3,7 +3,7 @@ import java.nio.ByteBuffer; import java.util.logging.Logger; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage.Format; /** * A place-holder for future implementation of ETC texture flipping. diff --git a/jme3-core/src/plugins/java/com/jme3/texture/plugins/HDRLoader.java b/jme3-core/src/plugins/java/com/jme3/texture/plugins/HDRLoader.java index 183049f9ec..b498df6ea6 100644 --- a/jme3-core/src/plugins/java/com/jme3/texture/plugins/HDRLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/texture/plugins/HDRLoader.java @@ -35,8 +35,8 @@ import com.jme3.asset.AssetLoader; import com.jme3.asset.TextureKey; import com.jme3.math.FastMath; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; import java.io.IOException; @@ -215,7 +215,7 @@ private void decodeScanline(InputStream in, int width) throws IOException{ } } - public Image load(InputStream in, boolean flipY) throws IOException{ + public GlImage load(InputStream in, boolean flipY) throws IOException{ float gamma = -1f; float exposure = -1f; @@ -305,7 +305,7 @@ public Image load(InputStream in, boolean flipY) throws IOException{ dataStore.rewind(); //HDR files color data is actually stored in linear space. - return new Image(pixelFormat, width, height, dataStore, ColorSpace.Linear); + return new GlImage(pixelFormat, width, height, dataStore, ColorSpace.Linear); } @Override @@ -317,7 +317,7 @@ public Object load(AssetInfo info) throws IOException { InputStream in = null; try { in = info.openStream(); - Image img = load(in, flip); + GlImage img = load(in, flip); return img; } finally { if (in != null){ diff --git a/jme3-core/src/plugins/java/com/jme3/texture/plugins/ImageFlipper.java b/jme3-core/src/plugins/java/com/jme3/texture/plugins/ImageFlipper.java index 0d5f39d84b..dfc788b9c4 100644 --- a/jme3-core/src/plugins/java/com/jme3/texture/plugins/ImageFlipper.java +++ b/jme3-core/src/plugins/java/com/jme3/texture/plugins/ImageFlipper.java @@ -31,7 +31,7 @@ */ package com.jme3.texture.plugins; -import com.jme3.texture.Image; +import com.jme3.texture.GlImage; import com.jme3.util.BufferUtils; import java.nio.ByteBuffer; @@ -50,8 +50,8 @@ public class ImageFlipper { private ImageFlipper() { } - public static void flipImage(Image img, int index){ - if (img.getFormat().isCompressed()) + public static void flipImage(GlImage img, int index){ + if (img.getGlFormat().isCompressed()) throw new UnsupportedOperationException("Flipping compressed " + "images is unsupported."); @@ -60,7 +60,7 @@ public static void flipImage(Image img, int index){ int halfH = h / 2; // bytes per pixel - int bpp = img.getFormat().getBitsPerPixel() / 8; + int bpp = img.getGlFormat().getBitsPerPixel() / 8; int scanline = w * bpp; ByteBuffer data = img.getData(index); diff --git a/jme3-core/src/plugins/java/com/jme3/texture/plugins/PFMLoader.java b/jme3-core/src/plugins/java/com/jme3/texture/plugins/PFMLoader.java index 859d06fc77..dde12fcef3 100644 --- a/jme3-core/src/plugins/java/com/jme3/texture/plugins/PFMLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/texture/plugins/PFMLoader.java @@ -34,8 +34,8 @@ import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetLoader; import com.jme3.asset.TextureKey; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; import java.io.IOException; @@ -73,7 +73,7 @@ private void flipScanline(byte[] scanline){ } } - private Image load(InputStream in, boolean needYFlip) throws IOException{ + private GlImage load(InputStream in, boolean needYFlip) throws IOException{ Format format = null; String fmtStr = readString(in); @@ -130,7 +130,7 @@ private Image load(InputStream in, boolean needYFlip) throws IOException{ } imageData.rewind(); - return new Image(format, width, height, imageData, null, ColorSpace.Linear); + return new GlImage(format, width, height, imageData, null, ColorSpace.Linear); } @Override diff --git a/jme3-core/src/plugins/java/com/jme3/texture/plugins/TGALoader.java b/jme3-core/src/plugins/java/com/jme3/texture/plugins/TGALoader.java index 13bf130977..edcad585fc 100644 --- a/jme3-core/src/plugins/java/com/jme3/texture/plugins/TGALoader.java +++ b/jme3-core/src/plugins/java/com/jme3/texture/plugins/TGALoader.java @@ -35,8 +35,8 @@ import com.jme3.asset.AssetLoader; import com.jme3.asset.TextureKey; import com.jme3.math.FastMath; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; import com.jme3.util.BufferUtils; import java.io.BufferedInputStream; import java.io.DataInputStream; @@ -81,7 +81,7 @@ public Object load(AssetInfo info) throws IOException { InputStream in = null; try { in = info.openStream(); - Image img = load(in, flip); + GlImage img = load(in, flip); return img; } finally { if (in != null) { @@ -104,7 +104,7 @@ public Object load(AssetInfo info) throws IOException { * image, either as a RGB888 or RGBA8888 * @throws java.io.IOException if an I/O error occurs */ - public static Image load(InputStream in, boolean flip) throws IOException { + public static GlImage load(InputStream in, boolean flip) throws IOException { boolean flipH = false; // open a stream to the file @@ -470,7 +470,7 @@ public static Image load(InputStream in, boolean flip) throws IOException { scratch.put(rawData); scratch.rewind(); // Create the Image object - Image textureImage = new Image(); + GlImage textureImage = new GlImage(); textureImage.setFormat(format); textureImage.setWidth(width); textureImage.setHeight(height); diff --git a/jme3-core/src/plugins/java/com/jme3/texture/plugins/ktx/KTXLoader.java b/jme3-core/src/plugins/java/com/jme3/texture/plugins/ktx/KTXLoader.java index 9cd78b35a3..259a263487 100644 --- a/jme3-core/src/plugins/java/com/jme3/texture/plugins/ktx/KTXLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/texture/plugins/ktx/KTXLoader.java @@ -37,7 +37,7 @@ import com.jme3.renderer.Caps; import com.jme3.renderer.opengl.GLImageFormat; import com.jme3.renderer.opengl.GLImageFormats; -import com.jme3.texture.Image; +import com.jme3.texture.GlImage; import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; import com.jme3.util.LittleEndien; @@ -80,7 +80,7 @@ public Object load(AssetInfo info) throws IOException { InputStream in = null; try { in = info.openStream(); - Image img = load(in); + GlImage img = load(in); return img; } finally { if (in != null) { @@ -89,7 +89,7 @@ public Object load(AssetInfo info) throws IOException { } } - private Image load(InputStream stream) { + private GlImage load(InputStream stream) { byte[] fileId = new byte[12]; @@ -149,7 +149,7 @@ private Image load(InputStream stream) { int nbSlices = Math.max(numberOfFaces,numberOfArrayElements); - Image.Format imgFormat = getImageFormat(glFormat, glInternalFormat, glType); + GlImage.Format imgFormat = getImageFormat(glFormat, glInternalFormat, glType); log.log(Level.FINE, "img format {0}", imgFormat.toString()); @@ -159,7 +159,7 @@ private Image load(InputStream stream) { int[] mipMapSizes = new int[numberOfMipmapLevels]; - Image image = createImage(nbSlices, byteBuffersSize, imgFormat, pixelWidth, pixelHeight, pixelDepth); + GlImage image = createImage(nbSlices, byteBuffersSize, imgFormat, pixelWidth, pixelHeight, pixelDepth); byte[] pixelData = new byte[bytePerPixel]; @@ -258,12 +258,12 @@ private int computeBuffersSize(int numberOfMipmapLevels, int pixelWidth, int pix * @param depth * @return */ - private Image createImage(int nbSlices, int byteBuffersSize, Image.Format imgFormat, int pixelWidth, int pixelHeight, int depth) { + private GlImage createImage(int nbSlices, int byteBuffersSize, GlImage.Format imgFormat, int pixelWidth, int pixelHeight, int depth) { ArrayList imageData = new ArrayList<>(nbSlices); for (int i = 0; i < nbSlices; i++) { imageData.add(BufferUtils.createByteBuffer(byteBuffersSize)); } - Image image = new Image(imgFormat, pixelWidth, pixelHeight, depth, imageData, ColorSpace.sRGB); + GlImage image = new GlImage(imgFormat, pixelWidth, pixelHeight, depth, imageData, ColorSpace.sRGB); return image; } @@ -334,7 +334,7 @@ private boolean checkFileIdentifier(byte[] b) { * @param glType * @return */ - private Image.Format getImageFormat(int glFormat, int glInternalFormat, int glType) { + private GlImage.Format getImageFormat(int glFormat, int glInternalFormat, int glType) { EnumSet caps = EnumSet.allOf(Caps.class); GLImageFormat[][] formats = GLImageFormats.getFormatsForCaps(caps); for (GLImageFormat[] format : formats) { @@ -343,7 +343,7 @@ private Image.Format getImageFormat(int glFormat, int glInternalFormat, int glTy if (glImgFormat != null) { if (glImgFormat.format == glFormat && glImgFormat.dataType == glType) { if (glFormat == glInternalFormat || glImgFormat.internalFormat == glInternalFormat) { - return Image.Format.values()[j]; + return GlImage.Format.values()[j]; } } } diff --git a/jme3-core/src/plugins/java/com/jme3/texture/plugins/ktx/KTXWriter.java b/jme3-core/src/plugins/java/com/jme3/texture/plugins/ktx/KTXWriter.java index 116ec05df2..1d4366f79f 100644 --- a/jme3-core/src/plugins/java/com/jme3/texture/plugins/ktx/KTXWriter.java +++ b/jme3-core/src/plugins/java/com/jme3/texture/plugins/ktx/KTXWriter.java @@ -34,8 +34,8 @@ import com.jme3.renderer.Caps; import com.jme3.renderer.opengl.GLImageFormat; import com.jme3.renderer.opengl.GLImageFormats; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; import com.jme3.texture.Texture3D; import com.jme3.texture.TextureArray; @@ -83,7 +83,7 @@ public KTXWriter(String path) { * @param image the image to write * @param fileName the name of the file to write */ - public void write(Image image, String fileName) { + public void write(GlImage image, String fileName) { write(image, Texture2D.class, fileName); } @@ -97,7 +97,7 @@ public void write(Image image, String fileName) { * @param textureType the texture type * @param fileName the name of the file to write */ - public void write(Image image, Class textureType, String fileName) { + public void write(GlImage image, Class textureType, String fileName) { FileOutputStream outs = null; try { @@ -110,7 +110,7 @@ public void write(Image image, Class textureType, String file out.write(fileIdentifier); //endianness out.writeInt(0x04030201); - GLImageFormat format = getGlFormat(image.getFormat()); + GLImageFormat format = getGlFormat(image.getGlFormat()); //glType out.writeInt(format.dataType); //glTypeSize @@ -181,7 +181,7 @@ public void write(Image image, Class textureType, String file if (image.hasMipmaps()) { imageSize = image.getMipMapSizes()[mipLevel]; } else { - imageSize = width * height * image.getFormat().getBitsPerPixel() / 8; + imageSize = width * height * image.getGlFormat().getBitsPerPixel() / 8; } out.writeInt(imageSize); @@ -263,7 +263,7 @@ private byte[] getByteBufferArray(ByteBuffer byteBuffer, int size) { * @param format * @return */ - private GLImageFormat getGlFormat(Image.Format format) { + private GLImageFormat getGlFormat(GlImage.Format format) { EnumSet caps = EnumSet.allOf(Caps.class); GLImageFormat[][] formats = GLImageFormats.getFormatsForCaps(caps); return formats[0][format.ordinal()]; diff --git a/jme3-core/src/test/java/com/jme3/cinematic/CinematicTest.java b/jme3-core/src/test/java/com/jme3/cinematic/CinematicTest.java index 400dbaa9f9..98f2540c78 100644 --- a/jme3-core/src/test/java/com/jme3/cinematic/CinematicTest.java +++ b/jme3-core/src/test/java/com/jme3/cinematic/CinematicTest.java @@ -31,10 +31,6 @@ */ package com.jme3.cinematic; -import com.jme3.animation.AnimControl; -import com.jme3.animation.Animation; -import com.jme3.cinematic.events.AnimationEvent; -import com.jme3.scene.Node; import org.junit.Test; /** @@ -51,19 +47,5 @@ public void clearEmpty() { Cinematic sut = new Cinematic(); sut.clear(); } - - /** - * No ClassCastException when clear() a Cinematic with AnimationEvent - */ - @Test - public void clearAnimationEvent() { - Cinematic sut = new Cinematic(); - Node model = new Node("model"); - AnimControl ac = new AnimControl(); - ac.addAnim(new Animation("animName", 1.0f)); - model.addControl(ac); - sut.enqueueCinematicEvent(new AnimationEvent(model, "animName")); - sut.initialize(null, null); - sut.clear(); - } + } diff --git a/jme3-core/src/test/java/com/jme3/light/LightFilterTest.java b/jme3-core/src/test/java/com/jme3/light/LightFilterTest.java index 80c75f0b7b..571c21669c 100644 --- a/jme3-core/src/test/java/com/jme3/light/LightFilterTest.java +++ b/jme3-core/src/test/java/com/jme3/light/LightFilterTest.java @@ -57,7 +57,7 @@ private void checkFilteredLights(int expected) { geom.updateGeometricState(); filter.setCamera(cam); // setCamera resets the intersection cache list.clear(); - filter.filterLights(geom, list); + filter.filterLights(geom, geom.getWorldLightList(), list); assert list.size() == expected; } diff --git a/jme3-core/src/test/java/com/jme3/material/MaterialMatParamTest.java b/jme3-core/src/test/java/com/jme3/material/MaterialMatParamTest.java index 9b572e8e93..a11db8906b 100644 --- a/jme3-core/src/test/java/com/jme3/material/MaterialMatParamTest.java +++ b/jme3-core/src/test/java/com/jme3/material/MaterialMatParamTest.java @@ -50,8 +50,8 @@ import com.jme3.shader.DefineList; import com.jme3.system.NullRenderer; import com.jme3.system.TestUtil; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; import java.util.ArrayList; import static org.junit.Assert.assertEquals; @@ -445,7 +445,7 @@ public void setShader(Shader shader) { } @Override - public void setTexture(int unit, Texture texture) { + public void setTexture(int unit, GlTexture texture) { MaterialMatParamTest.this.usedTextures[unit] = texture; } }; @@ -453,7 +453,7 @@ public void setTexture(int unit, Texture texture) { private boolean evaluated = false; private Shader usedShader = null; - private final Texture[] usedTextures = new Texture[32]; + private final GlTexture[] usedTextures = new GlTexture[32]; private void inputMp(MatParam... params) { if (evaluated) { @@ -530,7 +530,7 @@ private void evaluateTechniqueDef() { Assert.assertTrue(evaluated); } - private void outTextures(Texture... textures) { + private void outTextures(GlTexture... textures) { for (int i = 0; i < usedTextures.length; i++) { if (i < textures.length) { Assert.assertSame(textures[i], usedTextures[i]); diff --git a/jme3-core/src/test/java/com/jme3/material/MaterialTest.java b/jme3-core/src/test/java/com/jme3/material/MaterialTest.java index 6edd3ed34a..8b5eaf7407 100644 --- a/jme3-core/src/test/java/com/jme3/material/MaterialTest.java +++ b/jme3-core/src/test/java/com/jme3/material/MaterialTest.java @@ -39,9 +39,9 @@ import com.jme3.shader.VarType; import com.jme3.system.NullRenderer; import com.jme3.system.TestUtil; -import com.jme3.texture.Image; +import com.jme3.texture.GlImage; import com.jme3.texture.Texture2D; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage.Format; import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; @@ -130,8 +130,8 @@ public void testSelectNamedTechnique_GLSL150Cap() { @Test public void testForcedColorSpace(){ - Image img=new Image(Format.RGBA8,2,2,BufferUtils.createByteBuffer(16),null,ColorSpace.sRGB); - Image img2=new Image(Format.RGBA8,2,2,BufferUtils.createByteBuffer(16),null,ColorSpace.sRGB); + GlImage img=new GlImage(Format.RGBA8,2,2,BufferUtils.createByteBuffer(16),null,ColorSpace.sRGB); + GlImage img2=new GlImage(Format.RGBA8,2,2,BufferUtils.createByteBuffer(16),null,ColorSpace.sRGB); Texture2D tx=new Texture2D(img); Texture2D tx2=new Texture2D(img2); diff --git a/jme3-core/src/test/java/com/jme3/material/plugins/J3MLoaderTest.java b/jme3-core/src/test/java/com/jme3/material/plugins/J3MLoaderTest.java index 7126d94fa8..5af7845993 100644 --- a/jme3-core/src/test/java/com/jme3/material/plugins/J3MLoaderTest.java +++ b/jme3-core/src/test/java/com/jme3/material/plugins/J3MLoaderTest.java @@ -9,7 +9,7 @@ import com.jme3.material.MaterialDef; import com.jme3.renderer.Caps; import com.jme3.shader.VarType; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import java.io.IOException; import java.util.EnumSet; import org.junit.Before; @@ -84,8 +84,8 @@ public void multipleSameNamedTechniques_shouldBeSupported() throws IOException { public void oldStyleTextureParameters_shouldBeSupported() throws Exception { when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/texture-parameters-oldstyle.j3m")); - final Texture textureOldStyle = Mockito.mock(Texture.class); - final Texture textureOldStyleUsingQuotes = Mockito.mock(Texture.class); + final GlTexture textureOldStyle = Mockito.mock(GlTexture.class); + final GlTexture textureOldStyleUsingQuotes = Mockito.mock(GlTexture.class); final TextureKey textureKeyUsingQuotes = setupMockForTexture("OldStyleUsingQuotes", "old style using quotes/texture.png", true, true, textureOldStyleUsingQuotes); final TextureKey textureKeyOldStyle = setupMockForTexture("OldStyle", "old style/texture.png", true, true, textureOldStyle); @@ -94,22 +94,22 @@ public void oldStyleTextureParameters_shouldBeSupported() throws Exception { verify(assetManager).loadTexture(textureKeyUsingQuotes); verify(assetManager).loadTexture(textureKeyOldStyle); - verify(textureOldStyle).setWrap(Texture.WrapMode.Repeat); - verify(textureOldStyleUsingQuotes).setWrap(Texture.WrapMode.Repeat); + verify(textureOldStyle).setWrap(GlTexture.WrapMode.Repeat); + verify(textureOldStyleUsingQuotes).setWrap(GlTexture.WrapMode.Repeat); } @Test public void newStyleTextureParameters_shouldBeSupported() throws Exception { when(assetInfo.openStream()).thenReturn(J3MLoader.class.getResourceAsStream("/texture-parameters-newstyle.j3m")); - final Texture textureNoParameters = Mockito.mock(Texture.class); - final Texture textureFlip = Mockito.mock(Texture.class); - final Texture textureRepeat = Mockito.mock(Texture.class); - final Texture textureRepeatAxis = Mockito.mock(Texture.class); - final Texture textureMin = Mockito.mock(Texture.class); - final Texture textureMag = Mockito.mock(Texture.class); - final Texture textureCombined = Mockito.mock(Texture.class); - final Texture textureLooksLikeOldStyle = Mockito.mock(Texture.class); + final GlTexture textureNoParameters = Mockito.mock(GlTexture.class); + final GlTexture textureFlip = Mockito.mock(GlTexture.class); + final GlTexture textureRepeat = Mockito.mock(GlTexture.class); + final GlTexture textureRepeatAxis = Mockito.mock(GlTexture.class); + final GlTexture textureMin = Mockito.mock(GlTexture.class); + final GlTexture textureMag = Mockito.mock(GlTexture.class); + final GlTexture textureCombined = Mockito.mock(GlTexture.class); + final GlTexture textureLooksLikeOldStyle = Mockito.mock(GlTexture.class); final TextureKey textureKeyNoParameters = setupMockForTexture("Empty", "empty.png", false, true, textureNoParameters); final TextureKey textureKeyFlip = setupMockForTexture("Flip", "flip.png", true, true, textureFlip); @@ -125,17 +125,17 @@ public void newStyleTextureParameters_shouldBeSupported() throws Exception { verify(assetManager).loadTexture(textureKeyNoParameters); verify(assetManager).loadTexture(textureKeyFlip); - verify(textureRepeat).setWrap(Texture.WrapMode.Repeat); - verify(textureRepeatAxis).setWrap(Texture.WrapAxis.T, Texture.WrapMode.Repeat); - verify(textureMin).setMinFilter(Texture.MinFilter.Trilinear); - verify(textureMag).setMagFilter(Texture.MagFilter.Bilinear); + verify(textureRepeat).setWrap(GlTexture.WrapMode.Repeat); + verify(textureRepeatAxis).setWrap(GlTexture.WrapAxis.T, GlTexture.WrapMode.Repeat); + verify(textureMin).setMinFilter(GlTexture.MinFilter.Trilinear); + verify(textureMag).setMagFilter(GlTexture.MagFilter.Bilinear); - verify(textureCombined).setMagFilter(Texture.MagFilter.Nearest); - verify(textureCombined).setMinFilter(Texture.MinFilter.BilinearNoMipMaps); - verify(textureCombined).setWrap(Texture.WrapMode.Repeat); + verify(textureCombined).setMagFilter(GlTexture.MagFilter.Nearest); + verify(textureCombined).setMinFilter(GlTexture.MinFilter.BilinearNoMipMaps); + verify(textureCombined).setWrap(GlTexture.WrapMode.Repeat); } - private TextureKey setupMockForTexture(final String paramName, final String path, final boolean flipY, boolean generateMips, final Texture texture) { + private TextureKey setupMockForTexture(final String paramName, final String path, final boolean flipY, boolean generateMips, final GlTexture texture) { when(materialDef.getMaterialParam(paramName)).thenReturn(new MatParamTexture(VarType.Texture2D, paramName, texture, null)); final TextureKey textureKey = new TextureKey(path, flipY); diff --git a/jme3-core/src/test/java/com/jme3/renderer/OpaqueComparatorTest.java b/jme3-core/src/test/java/com/jme3/renderer/OpaqueComparatorTest.java index 929281819e..ae99460114 100644 --- a/jme3-core/src/test/java/com/jme3/renderer/OpaqueComparatorTest.java +++ b/jme3-core/src/test/java/com/jme3/renderer/OpaqueComparatorTest.java @@ -41,9 +41,9 @@ import com.jme3.scene.Mesh; import com.jme3.scene.shape.Box; import com.jme3.system.TestUtil; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; @@ -188,9 +188,9 @@ public void testNoSortByParam() { testSort(sameMat1, sameMat2); } - private Texture createTexture(String name) { + private GlTexture createTexture(String name) { ByteBuffer bb = BufferUtils.createByteBuffer(3); - Image image = new Image(Format.RGB8, 1, 1, bb, ColorSpace.sRGB); + GlImage image = new GlImage(Format.RGB8, 1, 1, bb, ColorSpace.sRGB); Texture2D texture = new Texture2D(image); texture.setName(name); return texture; @@ -202,13 +202,13 @@ public void testSortByTexture() { Material texture2Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); Material texture3Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - Texture tex1 = createTexture("A"); + GlTexture tex1 = createTexture("A"); tex1.getImage().setId(1); - Texture tex2 = createTexture("B"); + GlTexture tex2 = createTexture("B"); tex2.getImage().setId(2); - Texture tex3 = createTexture("C"); + GlTexture tex3 = createTexture("C"); tex3.getImage().setId(3); texture1Mat.setName("TexA"); @@ -261,11 +261,11 @@ public void testSortByAll() { Material matBase1 = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); Material matBase2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - Texture texBase = createTexture("BASE"); + GlTexture texBase = createTexture("BASE"); texBase.getImage().setId(1); - Texture tex1 = createTexture("1"); + GlTexture tex1 = createTexture("1"); tex1.getImage().setId(2); - Texture tex2 = createTexture("2"); + GlTexture tex2 = createTexture("2"); tex2.getImage().setId(3); matBase1.setName("BASE"); diff --git a/jme3-core/src/test/java/com/jme3/scene/PhantomTrianglesTest.java b/jme3-core/src/test/java/com/jme3/scene/PhantomTrianglesTest.java index e7f1013f62..4a45fccafd 100644 --- a/jme3-core/src/test/java/com/jme3/scene/PhantomTrianglesTest.java +++ b/jme3-core/src/test/java/com/jme3/scene/PhantomTrianglesTest.java @@ -103,9 +103,9 @@ void createWhiteLines() { 1f, 1f, 0f, 1f, -1f, 0f }; - lineMesh.setBuffer(VertexBuffer.Type.Position, 3, corners); + lineMesh.setBuffer(GlVertexBuffer.Type.Position, 3, corners); short[] indices = new short[]{0, 1, 2, 3}; - lineMesh.setBuffer(VertexBuffer.Type.Index, 2, indices); + lineMesh.setBuffer(GlVertexBuffer.Type.Index, 2, indices); lineMesh.updateBound(); Geometry whiteLines = new Geometry("white lines", lineMesh); Material white = assetManager.loadMaterial("Common/Materials/WhiteColor.j3m"); diff --git a/jme3-core/src/test/java/com/jme3/scene/debug/TestCloneMesh.java b/jme3-core/src/test/java/com/jme3/scene/debug/TestCloneMesh.java index b2a9a7704b..f8f47b82b6 100644 --- a/jme3-core/src/test/java/com/jme3/scene/debug/TestCloneMesh.java +++ b/jme3-core/src/test/java/com/jme3/scene/debug/TestCloneMesh.java @@ -31,15 +31,11 @@ */ package com.jme3.scene.debug; -import com.jme3.animation.Bone; -import com.jme3.animation.Skeleton; import com.jme3.asset.AssetManager; import com.jme3.asset.DesktopAssetManager; import com.jme3.export.binary.BinaryExporter; import com.jme3.math.Vector3f; import com.jme3.util.clone.Cloner; -import java.util.HashMap; -import java.util.Map; import org.junit.Assert; import org.junit.Test; @@ -86,106 +82,6 @@ public void testCloneGrid() { Assert.assertNotEquals(deepClone, saveAndLoad); } - /** - * Test cloning/saving/loading a SkeletonDebugger. - */ - @Test - public void testCloneSkeletonDebugger() { - Bone[] boneArray = new Bone[2]; - boneArray[0] = new Bone("rootBone"); - boneArray[1] = new Bone("leafBone"); - boneArray[0].addChild(boneArray[1]); - Skeleton skeleton = new Skeleton(boneArray); - skeleton.setBindingPose(); - SkeletonDebugger skeletonDebugger - = new SkeletonDebugger("sd", skeleton); - - SkeletonDebugger deepClone = Cloner.deepClone(skeletonDebugger); - Assert.assertNotNull(deepClone); - Assert.assertNotEquals(deepClone, skeletonDebugger); - - AssetManager assetManager = new DesktopAssetManager(); - SkeletonDebugger saveAndLoad - = BinaryExporter.saveAndLoad(assetManager, skeletonDebugger); - Assert.assertNotNull(saveAndLoad); - Assert.assertNotEquals(deepClone, saveAndLoad); - } - - /** - * Test cloning/saving/loading a SkeletonInterBoneWire. See JME issue #1705. - */ - @Test - public void testCloneSkeletonInterBoneWire() { - Bone[] boneArray = new Bone[2]; - boneArray[0] = new Bone("rootBone"); - boneArray[1] = new Bone("leafBone"); - boneArray[0].addChild(boneArray[1]); - Skeleton skeleton = new Skeleton(boneArray); - skeleton.setBindingPose(); - Map boneLengths = new HashMap<>(); - boneLengths.put(0, 2f); - boneLengths.put(1, 1f); - SkeletonInterBoneWire sibw - = new SkeletonInterBoneWire(skeleton, boneLengths); - - SkeletonInterBoneWire deepClone = Cloner.deepClone(sibw); - Assert.assertNotNull(deepClone); - Assert.assertNotEquals(deepClone, sibw); - - AssetManager assetManager = new DesktopAssetManager(); - SkeletonInterBoneWire saveAndLoad - = BinaryExporter.saveAndLoad(assetManager, sibw); - Assert.assertNotNull(saveAndLoad); - Assert.assertNotEquals(deepClone, saveAndLoad); - } - - /** - * Test cloning/saving/loading a SkeletonPoints. See JME issue #1705. - */ - @Test - public void testCloneSkeletonPoints() { - Bone[] boneArray = new Bone[2]; - boneArray[0] = new Bone("rootBone"); - boneArray[1] = new Bone("leafBone"); - boneArray[0].addChild(boneArray[1]); - Skeleton skeleton = new Skeleton(boneArray); - skeleton.setBindingPose(); - SkeletonPoints skeletonPoints = new SkeletonPoints(skeleton); - - SkeletonPoints deepClone = Cloner.deepClone(skeletonPoints); - Assert.assertNotNull(deepClone); - Assert.assertNotEquals(deepClone, skeletonPoints); - - AssetManager assetManager = new DesktopAssetManager(); - SkeletonPoints saveAndLoad - = BinaryExporter.saveAndLoad(assetManager, skeletonPoints); - Assert.assertNotNull(saveAndLoad); - Assert.assertNotEquals(deepClone, saveAndLoad); - } - - /** - * Test cloning/saving/loading a SkeletonWire. See JME issue #1705. - */ - @Test - public void testCloneSkeletonWire() { - Bone[] boneArray = new Bone[2]; - boneArray[0] = new Bone("rootBone"); - boneArray[1] = new Bone("leafBone"); - boneArray[0].addChild(boneArray[1]); - Skeleton skeleton = new Skeleton(boneArray); - SkeletonWire skeletonWire = new SkeletonWire(skeleton); - - SkeletonWire deepClone = Cloner.deepClone(skeletonWire); - Assert.assertNotNull(deepClone); - Assert.assertNotEquals(deepClone, skeletonWire); - - AssetManager assetManager = new DesktopAssetManager(); - SkeletonWire saveAndLoad - = BinaryExporter.saveAndLoad(assetManager, skeletonWire); - Assert.assertNotNull(saveAndLoad); - Assert.assertNotEquals(deepClone, saveAndLoad); - } - /** * Test cloning/saving/loading a WireBox. */ diff --git a/jme3-core/src/test/java/com/jme3/scene/plugins/OBJLoaderTest.java b/jme3-core/src/test/java/com/jme3/scene/plugins/OBJLoaderTest.java index 8fadbedae5..5e60407c15 100644 --- a/jme3-core/src/test/java/com/jme3/scene/plugins/OBJLoaderTest.java +++ b/jme3-core/src/test/java/com/jme3/scene/plugins/OBJLoaderTest.java @@ -39,7 +39,7 @@ import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.system.TestUtil; -import com.jme3.texture.Image; +import com.jme3.texture.GlImage; import org.junit.Before; import org.junit.Test; @@ -100,7 +100,7 @@ private static String toDiffFriendlyString(String indent, Spatial spatial) { public static class PngLoaderStub implements AssetLoader { @Override public Object load(final AssetInfo assetInfo) { - return new Image(); + return new GlImage(); } } } \ No newline at end of file diff --git a/jme3-core/src/test/java/com/jme3/scene/shape/ShapeBoundsTest.java b/jme3-core/src/test/java/com/jme3/scene/shape/ShapeBoundsTest.java index dc923f6f46..8889a24d00 100644 --- a/jme3-core/src/test/java/com/jme3/scene/shape/ShapeBoundsTest.java +++ b/jme3-core/src/test/java/com/jme3/scene/shape/ShapeBoundsTest.java @@ -36,7 +36,7 @@ import com.jme3.bounding.BoundingVolume; import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import org.junit.Assert; import org.junit.Test; @@ -147,9 +147,9 @@ private void testVertices(Geometry geometry) { Assert.assertNotNull(bv); for (int e = 0; e < geometry.getVertexCount(); e++) { - float x = (Float) geometry.getMesh().getBuffer(VertexBuffer.Type.Position).getElementComponent(e, 0); - float y = (Float) geometry.getMesh().getBuffer(VertexBuffer.Type.Position).getElementComponent(e, 1); - float z = (Float) geometry.getMesh().getBuffer(VertexBuffer.Type.Position).getElementComponent(e, 2); + float x = (Float) geometry.getMesh().getBuffer(GlVertexBuffer.Type.Position).getElementComponent(e, 0); + float y = (Float) geometry.getMesh().getBuffer(GlVertexBuffer.Type.Position).getElementComponent(e, 1); + float z = (Float) geometry.getMesh().getBuffer(GlVertexBuffer.Type.Position).getElementComponent(e, 2); Vector3f vertex = new Vector3f(x, y, z); Assert.assertTrue("Vertex outside world bound: " + vertex, bv.intersects(vertex)); } diff --git a/jme3-core/src/test/java/com/jme3/texture/TestIssue2250.java b/jme3-core/src/test/java/com/jme3/texture/TestIssue2250.java index 51d9e9e007..0fde872f09 100644 --- a/jme3-core/src/test/java/com/jme3/texture/TestIssue2250.java +++ b/jme3-core/src/test/java/com/jme3/texture/TestIssue2250.java @@ -54,8 +54,8 @@ public void testIssue2250WithData() { int height = 8; int numBytes = 4 * width * height; ByteBuffer data = BufferUtils.createByteBuffer(numBytes); - Image image1 = new Image( - Image.Format.RGBA8, width, height, data, ColorSpace.Linear); + GlImage image1 = new GlImage( + GlImage.Format.RGBA8, width, height, data, ColorSpace.Linear); image1.setMultiSamples(1); } @@ -71,7 +71,7 @@ public void testIssue2250WithMips() { int[] mipMapSizes = {256, 64, 16, 4}; ArrayList data = new ArrayList<>(); - Image image2 = new Image(Image.Format.RGBA8, width, height, depth, data, + GlImage image2 = new GlImage(GlImage.Format.RGBA8, width, height, depth, data, mipMapSizes, ColorSpace.Linear); image2.setMultiSamples(1); diff --git a/jme3-core/src/test/java/com/jme3/texture/TextureArrayTest.java b/jme3-core/src/test/java/com/jme3/texture/TextureArrayTest.java index bd861b9879..095d94e9cb 100644 --- a/jme3-core/src/test/java/com/jme3/texture/TextureArrayTest.java +++ b/jme3-core/src/test/java/com/jme3/texture/TextureArrayTest.java @@ -36,8 +36,8 @@ import com.jme3.asset.AssetManager; import com.jme3.asset.DesktopAssetManager; import com.jme3.export.binary.BinaryExporter; -import com.jme3.texture.Texture.WrapAxis; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture.WrapAxis; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; import java.nio.ByteBuffer; @@ -51,7 +51,7 @@ public class TextureArrayTest { @Test public void testExportWrapMode() { - List images = new ArrayList<>(); + List images = new ArrayList<>(); images.add(createImage()); images.add(createImage()); TextureArray tex3 = new TextureArray(images); @@ -62,12 +62,12 @@ public void testExportWrapMode() { assertEquals(tex3.getWrap(WrapAxis.T), tex4.getWrap(WrapAxis.T)); } - private Image createImage() { + private GlImage createImage() { int width = 8; int height = 8; int numBytes = 4 * width * height; ByteBuffer data = BufferUtils.createByteBuffer(numBytes); - return new Image(Image.Format.RGBA8, width, height, data, ColorSpace.Linear); + return new GlImage(GlImage.Format.RGBA8, width, height, data, ColorSpace.Linear); } } diff --git a/jme3-core/src/test/java/com/jme3/tools/LodGeneratorTest.java b/jme3-core/src/test/java/com/jme3/tools/LodGeneratorTest.java index cdf1e9fad5..c252c5a7f8 100644 --- a/jme3-core/src/test/java/com/jme3/tools/LodGeneratorTest.java +++ b/jme3-core/src/test/java/com/jme3/tools/LodGeneratorTest.java @@ -41,7 +41,7 @@ import com.jme3.scene.Mesh; import com.jme3.scene.Node; import com.jme3.scene.Spatial; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import com.jme3.scene.shape.Sphere; import com.jme3.system.TestUtil; @@ -69,7 +69,7 @@ public void testInit() { /** * Returns a List of the sizes of the VertexBuff. */ - private int[] getBufferSizes(VertexBuffer[] buffers) { + private int[] getBufferSizes(GlVertexBuffer[] buffers) { int[] result = new int[buffers.length]; for (int i = 0; i < buffers.length; i++) { @@ -85,7 +85,7 @@ private int[] getBufferSizes(VertexBuffer[] buffers) { @Test public void testSphereReductionProportional() { LodGenerator lod = new LodGenerator(sphere()); - VertexBuffer[] buffer = lod.computeLods(LodGenerator.TriangleReductionMethod.PROPORTIONAL, + GlVertexBuffer[] buffer = lod.computeLods(LodGenerator.TriangleReductionMethod.PROPORTIONAL, REDUCTION_VALUES); int[] expected = { 240, 120, 108, 96, 84, 72, 60, 48 }; @@ -100,7 +100,7 @@ public void testSphereReductionProportional() { @Test public void testSphereReductionCollapsCost() { LodGenerator lod = new LodGenerator(sphere()); - VertexBuffer[] buffer = lod.computeLods(LodGenerator.TriangleReductionMethod.COLLAPSE_COST, + GlVertexBuffer[] buffer = lod.computeLods(LodGenerator.TriangleReductionMethod.COLLAPSE_COST, REDUCTION_VALUES); int[] expected = { 240, 6, 2, 1 }; @@ -150,7 +150,7 @@ private Mesh sphere() { public void testMonkeyReductionConstant() { LodGenerator lod = new LodGenerator(monkey()); - VertexBuffer[] buffer = lod.computeLods(LodGenerator.TriangleReductionMethod.CONSTANT, + GlVertexBuffer[] buffer = lod.computeLods(LodGenerator.TriangleReductionMethod.CONSTANT, REDUCTION_VALUES); int[] expected = { 5108 }; @@ -166,7 +166,7 @@ public void testMonkeyReductionConstant() { public void testMonkeyReductionProportional() { LodGenerator lod = new LodGenerator(monkey()); - VertexBuffer[] buffer = lod.computeLods(LodGenerator.TriangleReductionMethod.PROPORTIONAL, + GlVertexBuffer[] buffer = lod.computeLods(LodGenerator.TriangleReductionMethod.PROPORTIONAL, REDUCTION_VALUES); int[] expected = { 5108, 2553, 2298, 2043, 1787, 1531, 1276, 1021 }; @@ -181,7 +181,7 @@ public void testMonkeyReductionProportional() { // @Test public void testMonkeyReductionCollapsCost() { LodGenerator lod = new LodGenerator(monkey()); - VertexBuffer[] buffer = lod.computeLods(LodGenerator.TriangleReductionMethod.COLLAPSE_COST, + GlVertexBuffer[] buffer = lod.computeLods(LodGenerator.TriangleReductionMethod.COLLAPSE_COST, REDUCTION_VALUES); int[] expected = { 5108, 16 }; diff --git a/jme3-core/src/test/java/com/jme3/util/TestIssue1909.java b/jme3-core/src/test/java/com/jme3/util/TestIssue1909.java index 3cc91ef6f6..416cb87cb1 100644 --- a/jme3-core/src/test/java/com/jme3/util/TestIssue1909.java +++ b/jme3-core/src/test/java/com/jme3/util/TestIssue1909.java @@ -33,7 +33,7 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import com.jme3.util.mikktspace.MikktspaceTangentGenerator; import java.nio.FloatBuffer; import org.junit.Assert; @@ -83,15 +83,15 @@ public void testIssue1909() { ); Mesh mesh = new Mesh(); int numAxes = 3; - mesh.setBuffer(VertexBuffer.Type.Normal, numAxes, normals); - mesh.setBuffer(VertexBuffer.Type.Position, numAxes, positions); - mesh.setBuffer(VertexBuffer.Type.TexCoord, 2, uvs); + mesh.setBuffer(GlVertexBuffer.Type.Normal, numAxes, normals); + mesh.setBuffer(GlVertexBuffer.Type.Position, numAxes, positions); + mesh.setBuffer(GlVertexBuffer.Type.TexCoord, 2, uvs); mesh.updateBound(); Geometry testGeometry = new Geometry("testGeometry", mesh); MikktspaceTangentGenerator.generate(testGeometry); - VertexBuffer tangents = mesh.getBuffer(VertexBuffer.Type.Tangent); + GlVertexBuffer tangents = mesh.getBuffer(GlVertexBuffer.Type.Tangent); Assert.assertNotNull(tangents); } } diff --git a/jme3-core/src/test/java/com/jme3/util/TestIssue1919.java b/jme3-core/src/test/java/com/jme3/util/TestIssue1919.java index 87283c1c9a..e1e882de07 100644 --- a/jme3-core/src/test/java/com/jme3/util/TestIssue1919.java +++ b/jme3-core/src/test/java/com/jme3/util/TestIssue1919.java @@ -33,7 +33,7 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import com.jme3.util.mikktspace.MikktspaceTangentGenerator; import java.nio.FloatBuffer; import org.junit.Assert; @@ -69,7 +69,7 @@ public void testLineLoop() { MikktspaceTangentGenerator.generate(testGeometry); Mesh mesh = testGeometry.getMesh(); - VertexBuffer tangents = mesh.getBuffer(VertexBuffer.Type.Tangent); + GlVertexBuffer tangents = mesh.getBuffer(GlVertexBuffer.Type.Tangent); Assert.assertNull(tangents); /// skipped this mesh } @@ -82,7 +82,7 @@ public void testLineStrip() { MikktspaceTangentGenerator.generate(testGeometry); Mesh mesh = testGeometry.getMesh(); - VertexBuffer tangents = mesh.getBuffer(VertexBuffer.Type.Tangent); + GlVertexBuffer tangents = mesh.getBuffer(GlVertexBuffer.Type.Tangent); Assert.assertNull(tangents); /// skipped this mesh } @@ -95,7 +95,7 @@ public void testLines() { MikktspaceTangentGenerator.generate(testGeometry); Mesh mesh = testGeometry.getMesh(); - VertexBuffer tangents = mesh.getBuffer(VertexBuffer.Type.Tangent); + GlVertexBuffer tangents = mesh.getBuffer(GlVertexBuffer.Type.Tangent); Assert.assertNull(tangents); // skipped this mesh } @@ -117,7 +117,7 @@ public void testPoints() { MikktspaceTangentGenerator.generate(testGeometry); Mesh mesh = testGeometry.getMesh(); - VertexBuffer tangents = mesh.getBuffer(VertexBuffer.Type.Tangent); + GlVertexBuffer tangents = mesh.getBuffer(GlVertexBuffer.Type.Tangent); Assert.assertNull(tangents); // skipped this mesh } @@ -130,7 +130,7 @@ public void testTriangles() { MikktspaceTangentGenerator.generate(testGeometry); Mesh mesh = testGeometry.getMesh(); - VertexBuffer tangents = mesh.getBuffer(VertexBuffer.Type.Tangent); + GlVertexBuffer tangents = mesh.getBuffer(GlVertexBuffer.Type.Tangent); Assert.assertNotNull(tangents); // generated tangents } @@ -169,9 +169,9 @@ private Geometry createGeometry(Mesh.Mode mode) { ); Mesh mesh = new Mesh(); mesh.setMode(mode); - mesh.setBuffer(VertexBuffer.Type.Normal, numAxes, normals); - mesh.setBuffer(VertexBuffer.Type.Position, numAxes, positions); - mesh.setBuffer(VertexBuffer.Type.TexCoord, 2, uvs); + mesh.setBuffer(GlVertexBuffer.Type.Normal, numAxes, normals); + mesh.setBuffer(GlVertexBuffer.Type.Position, numAxes, positions); + mesh.setBuffer(GlVertexBuffer.Type.TexCoord, 2, uvs); mesh.updateBound(); Geometry result = new Geometry("testGeometry" + mode, mesh); diff --git a/jme3-core/src/tools/java/jme3tools/optimize/GeometryBatchFactory.java b/jme3-core/src/tools/java/jme3tools/optimize/GeometryBatchFactory.java index 98ccf9c35b..ef0e859d9b 100644 --- a/jme3-core/src/tools/java/jme3tools/optimize/GeometryBatchFactory.java +++ b/jme3-core/src/tools/java/jme3tools/optimize/GeometryBatchFactory.java @@ -5,10 +5,10 @@ import com.jme3.math.Transform; import com.jme3.math.Vector3f; import com.jme3.scene.*; -import com.jme3.scene.Mesh.Mode; -import com.jme3.scene.VertexBuffer.Format; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.VertexBuffer.Usage; +import com.jme3.scene.GlMesh.Mode; +import com.jme3.scene.GlVertexBuffer.Format; +import com.jme3.scene.GlVertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Usage; import com.jme3.scene.mesh.IndexBuffer; import com.jme3.util.BufferUtils; import java.nio.Buffer; @@ -95,9 +95,9 @@ private static void doTransformTangents(FloatBuffer inBuf, int offset, int compo * @param outMesh a Mesh to receive the geometries */ public static void mergeGeometries(Collection geometries, Mesh outMesh) { - int[] compsForBuf = new int[VertexBuffer.Type.values().length]; + int[] compsForBuf = new int[GlVertexBuffer.Type.values().length]; Format[] formatForBuf = new Format[compsForBuf.length]; - boolean[] normForBuf = new boolean[VertexBuffer.Type.values().length]; + boolean[] normForBuf = new boolean[GlVertexBuffer.Type.values().length]; int totalVerts = 0; int totalTris = 0; @@ -133,7 +133,7 @@ public static void mergeGeometries(Collection geometries, Mesh outMesh throw new UnsupportedOperationException(); } - for (VertexBuffer vb : geom.getMesh().getBufferList().getArray()) { + for (GlVertexBuffer vb : geom.getMesh().getBufferList().getArray()) { int currentCompsForBuf = compsForBuf[vb.getBufferType().ordinal()]; if (vb.getBufferType() != Type.Index && currentCompsForBuf != 0 && currentCompsForBuf != vb.getNumComponents()) { throw new UnsupportedOperationException("The geometry " + geom + " buffer " + vb.getBufferType() @@ -173,12 +173,12 @@ public static void mergeGeometries(Collection geometries, Mesh outMesh Buffer data; if (i == Type.Index.ordinal()) { - data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalTris); + data = GlVertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalTris); } else { - data = VertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalVerts); + data = GlVertexBuffer.createBuffer(formatForBuf[i], compsForBuf[i], totalVerts); } - VertexBuffer vb = new VertexBuffer(Type.values()[i]); + GlVertexBuffer vb = new GlVertexBuffer(Type.values()[i]); vb.setupData(Usage.Static, compsForBuf[i], formatForBuf[i], data); vb.setNormalized(normForBuf[i]); outMesh.setBuffer(vb); @@ -196,8 +196,8 @@ public static void mergeGeometries(Collection geometries, Mesh outMesh int geomTriCount = inMesh.getTriangleCount(); for (int bufType = 0; bufType < compsForBuf.length; bufType++) { - VertexBuffer inBuf = inMesh.getBuffer(Type.values()[bufType]); - VertexBuffer outBuf = outMesh.getBuffer(Type.values()[bufType]); + GlVertexBuffer inBuf = inMesh.getBuffer(Type.values()[bufType]); + GlVertexBuffer outBuf = outMesh.getBuffer(Type.values()[bufType]); if (inBuf == null || outBuf == null) { continue; @@ -268,7 +268,7 @@ public static void makeLods(Collection geometries, Mesh outMesh) { for (int i = 0; i < lodLevels; i++) { lodData[i] = new int[lodSize[i]]; } - VertexBuffer[] lods = new VertexBuffer[lodLevels]; + GlVertexBuffer[] lods = new GlVertexBuffer[lodLevels]; int bufferPos[] = new int[lodLevels]; int numOfVertices = 0; @@ -277,7 +277,7 @@ public static void makeLods(Collection geometries, Mesh outMesh) { for (Geometry g : geometries) { numOfVertices = g.getVertexCount(); for (int i = 0; i < lodLevels; i++) { - boolean isShortBuffer = g.getMesh().getLodLevel(i).getFormat() == VertexBuffer.Format.UnsignedShort; + boolean isShortBuffer = g.getMesh().getLodLevel(i).getFormat() == GlVertexBuffer.Format.UnsignedShort; if(isShortBuffer){ ShortBuffer buffer = (ShortBuffer) g.getMesh().getLodLevel(i).getDataReadOnly(); for (int j = 0; j < buffer.limit(); j++) { @@ -296,7 +296,7 @@ public static void makeLods(Collection geometries, Mesh outMesh) { } for (int i = 0; i < lodLevels; i++) { - lods[i] = new VertexBuffer(Type.Index); + lods[i] = new GlVertexBuffer(Type.Index); lods[i].setupData(Usage.Dynamic, 1, Format.UnsignedInt, BufferUtils.createIntBuffer(lodData[i])); } outMesh.setLodLevels(lods); @@ -408,7 +408,7 @@ public static Node optimize(Node scene, boolean useLods) { public static void printMesh(Mesh mesh) { for (int bufType = 0; bufType < Type.values().length; bufType++) { - VertexBuffer outBuf = mesh.getBuffer(Type.values()[bufType]); + GlVertexBuffer outBuf = mesh.getBuffer(Type.values()[bufType]); if (outBuf == null) { continue; } @@ -487,10 +487,10 @@ public static void alignBuffers(Node n, AlignOption option) { gatherGeoms(n, geoms); //gather buffer types - Map types = new EnumMap<>(VertexBuffer.Type.class); - Map typesCount = new EnumMap<>(VertexBuffer.Type.class); + Map types = new EnumMap<>(GlVertexBuffer.Type.class); + Map typesCount = new EnumMap<>(GlVertexBuffer.Type.class); for (Geometry geom : geoms) { - for (VertexBuffer buffer : geom.getMesh().getBufferList()) { + for (GlVertexBuffer buffer : geom.getMesh().getBufferList()) { if (types.get(buffer.getBufferType()) == null) { types.put(buffer.getBufferType(), buffer); if (logger.isLoggable(Level.FINE)) { @@ -510,7 +510,7 @@ public static void alignBuffers(Node n, AlignOption option) { case RemoveUnalignedBuffers: for (Geometry geom : geoms) { - for (VertexBuffer buffer : geom.getMesh().getBufferList()) { + for (GlVertexBuffer buffer : geom.getMesh().getBufferList()) { Integer count = typesCount.get(buffer.getBufferType()); if (count != null && count < geoms.size()) { geom.getMesh().clearBuffer(buffer.getBufferType()); @@ -523,9 +523,9 @@ public static void alignBuffers(Node n, AlignOption option) { break; case CreateMissingBuffers: for (Geometry geom : geoms) { - for (VertexBuffer.Type type : types.keySet()) { + for (GlVertexBuffer.Type type : types.keySet()) { if (geom.getMesh().getBuffer(type) == null) { - VertexBuffer vb = new VertexBuffer(type); + GlVertexBuffer vb = new GlVertexBuffer(type); Buffer b; switch (type) { case Index: diff --git a/jme3-core/src/tools/java/jme3tools/optimize/LodGenerator.java b/jme3-core/src/tools/java/jme3tools/optimize/LodGenerator.java index 648f85f070..2ffca26bcc 100644 --- a/jme3-core/src/tools/java/jme3tools/optimize/LodGenerator.java +++ b/jme3-core/src/tools/java/jme3tools/optimize/LodGenerator.java @@ -51,7 +51,7 @@ import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import com.jme3.util.BufferUtils; import java.nio.Buffer; import java.nio.FloatBuffer; @@ -270,7 +270,7 @@ public LodGenerator(Geometry geom) { private void build() { BoundingSphere bs = new BoundingSphere(); - bs.computeFromPoints(mesh.getFloatBuffer(VertexBuffer.Type.Position)); + bs.computeFromPoints(mesh.getFloatBuffer(GlVertexBuffer.Type.Position)); meshBoundingSphereRadius = bs.getRadius(); List vertexLookup = new ArrayList<>(); initialize(); @@ -286,9 +286,9 @@ private void gatherVertexData(Mesh mesh, List vertexLookup) { //in case the model is currently animating with software animation //attempting to retrieve the bind position instead of the position. - VertexBuffer position = mesh.getBuffer(VertexBuffer.Type.BindPosePosition); + GlVertexBuffer position = mesh.getBuffer(GlVertexBuffer.Type.BindPosePosition); if (position == null) { - position = mesh.getBuffer(VertexBuffer.Type.Position); + position = mesh.getBuffer(GlVertexBuffer.Type.Position); } FloatBuffer pos = (FloatBuffer) position.getDataReadOnly(); pos.rewind(); @@ -322,7 +322,7 @@ private Vertex findSimilar(Vertex v) { } private void gatherIndexData(Mesh mesh, List vertexLookup) { - VertexBuffer indexBuffer = mesh.getBuffer(VertexBuffer.Type.Index); + GlVertexBuffer indexBuffer = mesh.getBuffer(GlVertexBuffer.Type.Index); indexCount = indexBuffer.getNumElements() * 3; Buffer b = indexBuffer.getDataReadOnly(); b.rewind(); @@ -535,13 +535,13 @@ float computeEdgeCollapseCost(Vertex src, Edge dstEdge) { * @return an array of VertexBuffers containing the different index buffers * representing the LOD levels. */ - public VertexBuffer[] computeLods(TriangleReductionMethod reductionMethod, float... reductionValues) { + public GlVertexBuffer[] computeLods(TriangleReductionMethod reductionMethod, float... reductionValues) { int tricount = triangleList.size(); int lastBakeVertexCount = tricount; int lodCount = reductionValues.length; - VertexBuffer[] lods = new VertexBuffer[lodCount + 1]; + GlVertexBuffer[] lods = new GlVertexBuffer[lodCount + 1]; int numBakedLods = 1; - lods[0] = mesh.getBuffer(VertexBuffer.Type.Index); + lods[0] = mesh.getBuffer(GlVertexBuffer.Type.Index); for (int curLod = 0; curLod < lodCount; curLod++) { int neededTriCount = calcLodTriCount(reductionMethod, reductionValues[curLod]); while (neededTriCount < tricount) { @@ -580,11 +580,11 @@ public VertexBuffer[] computeLods(TriangleReductionMethod reductionMethod, float return cleanBuffer(lods, numBakedLods); } - private VertexBuffer[] cleanBuffer(VertexBuffer[] lods, int numBakedLods) { + private GlVertexBuffer[] cleanBuffer(GlVertexBuffer[] lods, int numBakedLods) { int index = 0; - VertexBuffer[] result = new VertexBuffer[numBakedLods]; + GlVertexBuffer[] result = new GlVertexBuffer[numBakedLods]; - for (VertexBuffer lod : lods) { + for (GlVertexBuffer lod : lods) { if (lod != null) { result[index] = lod; index++; @@ -611,18 +611,18 @@ public void bakeLods(TriangleReductionMethod reductionMethod, float... reduction mesh.setLodLevels(computeLods(reductionMethod, reductionValues)); } - private VertexBuffer makeLod(Mesh mesh) { - VertexBuffer indexBuffer = mesh.getBuffer(VertexBuffer.Type.Index); + private GlVertexBuffer makeLod(Mesh mesh) { + GlVertexBuffer indexBuffer = mesh.getBuffer(GlVertexBuffer.Type.Index); - boolean isShortBuffer = indexBuffer.getFormat() == VertexBuffer.Format.UnsignedShort; + boolean isShortBuffer = indexBuffer.getFormat() == GlVertexBuffer.Format.UnsignedShort; // Create buffers. - VertexBuffer lodBuffer = new VertexBuffer(VertexBuffer.Type.Index); + GlVertexBuffer lodBuffer = new GlVertexBuffer(GlVertexBuffer.Type.Index); int bufsize = indexCount == 0 ? 3 : indexCount; if (isShortBuffer) { - lodBuffer.setupData(VertexBuffer.Usage.Static, 3, VertexBuffer.Format.UnsignedShort, BufferUtils.createShortBuffer(bufsize)); + lodBuffer.setupData(GlVertexBuffer.Usage.Static, 3, GlVertexBuffer.Format.UnsignedShort, BufferUtils.createShortBuffer(bufsize)); } else { - lodBuffer.setupData(VertexBuffer.Usage.Static, 3, VertexBuffer.Format.UnsignedInt, BufferUtils.createIntBuffer(bufsize)); + lodBuffer.setupData(GlVertexBuffer.Usage.Static, 3, GlVertexBuffer.Format.UnsignedInt, BufferUtils.createIntBuffer(bufsize)); } diff --git a/jme3-core/src/tools/java/jme3tools/optimize/TextureAtlas.java b/jme3-core/src/tools/java/jme3tools/optimize/TextureAtlas.java index 9447b2d956..f80597f8eb 100644 --- a/jme3-core/src/tools/java/jme3tools/optimize/TextureAtlas.java +++ b/jme3-core/src/tools/java/jme3tools/optimize/TextureAtlas.java @@ -39,11 +39,11 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; import com.jme3.scene.Spatial; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Type; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; @@ -143,9 +143,9 @@ public TextureAtlas(int width, int height) { * @return false if the atlas is full. */ public boolean addGeometry(Geometry geometry) { - Texture diffuse = getMaterialTexture(geometry, "DiffuseMap"); - Texture normal = getMaterialTexture(geometry, "NormalMap"); - Texture specular = getMaterialTexture(geometry, "SpecularMap"); + GlTexture diffuse = getMaterialTexture(geometry, "DiffuseMap"); + GlTexture normal = getMaterialTexture(geometry, "NormalMap"); + GlTexture specular = getMaterialTexture(geometry, "SpecularMap"); if (diffuse == null) { diffuse = getMaterialTexture(geometry, "ColorMap"); @@ -173,7 +173,7 @@ public boolean addGeometry(Geometry geometry) { * @param mapName A freely chosen map name that can be later retrieved as a Texture. The first map name supplied will be the master map. * @return false if the atlas is full. */ - public boolean addTexture(Texture texture, String mapName) { + public boolean addTexture(GlTexture texture, String mapName) { if (texture == null) { throw new IllegalStateException("Texture cannot be null!"); } @@ -191,7 +191,7 @@ public boolean addTexture(Texture texture, String mapName) { * @param mapName A freely chosen map name that can be later retrieved as a Texture. * @param masterTexture The master texture for determining the location, it has to exist in tha master map. */ - public void addTexture(Texture texture, String mapName, Texture masterTexture) { + public void addTexture(GlTexture texture, String mapName, GlTexture masterTexture) { String sourceTextureName = textureName(masterTexture); if (sourceTextureName == null) { throw new IllegalStateException("Supplied master map texture has no asset key name!"); @@ -206,7 +206,7 @@ public void addTexture(Texture texture, String mapName, Texture masterTexture) { * @param mapName A freely chosen map name that can be later retrieved as a Texture. * @param sourceTextureName Name of the master map used for the location. */ - public void addTexture(Texture texture, String mapName, String sourceTextureName) { + public void addTexture(GlTexture texture, String mapName, String sourceTextureName) { if (texture == null) { throw new IllegalStateException("Texture cannot be null!"); } @@ -218,7 +218,7 @@ public void addTexture(Texture texture, String mapName, String sourceTextureName } } - private String textureName(Texture texture) { + private String textureName(GlTexture texture) { if (texture == null) { return null; } @@ -230,7 +230,7 @@ private String textureName(Texture texture) { } } - private boolean addImage(Image image, String name, String mapName, String sourceTextureName) { + private boolean addImage(GlImage image, String name, String mapName, String sourceTextureName) { if (rootMapName == null) { rootMapName = mapName; } @@ -270,7 +270,7 @@ private boolean addImage(Image image, String name, String mapName, String source return true; } - private void drawImage(Image source, int x, int y, String mapName) { + private void drawImage(GlImage source, int x, int y, String mapName) { if (images == null) { images = new HashMap(); } @@ -286,41 +286,41 @@ private void drawImage(Image source, int x, int y, String mapName) { ByteBuffer sourceData = source.getData(0); int height = source.getHeight(); int width = source.getWidth(); - Image newImage = null; + GlImage newImage = null; for (int yPos = 0; yPos < height; yPos++) { for (int xPos = 0; xPos < width; xPos++) { int i = ((xPos + x) + (yPos + y) * atlasWidth) * 4; - if (source.getFormat() == Format.ABGR8) { + if (source.getGlFormat() == Format.ABGR8) { int j = (xPos + yPos * width) * 4; image[i] = sourceData.get(j); //a image[i + 1] = sourceData.get(j + 1); //b image[i + 2] = sourceData.get(j + 2); //g image[i + 3] = sourceData.get(j + 3); //r - } else if (source.getFormat() == Format.BGR8) { + } else if (source.getGlFormat() == Format.BGR8) { int j = (xPos + yPos * width) * 3; image[i] = 1; //a image[i + 1] = sourceData.get(j); //b image[i + 2] = sourceData.get(j + 1); //g image[i + 3] = sourceData.get(j + 2); //r - } else if (source.getFormat() == Format.RGB8) { + } else if (source.getGlFormat() == Format.RGB8) { int j = (xPos + yPos * width) * 3; image[i] = 1; //a image[i + 1] = sourceData.get(j + 2); //b image[i + 2] = sourceData.get(j + 1); //g image[i + 3] = sourceData.get(j); //r - } else if (source.getFormat() == Format.RGBA8) { + } else if (source.getGlFormat() == Format.RGBA8) { int j = (xPos + yPos * width) * 4; image[i] = sourceData.get(j + 3); //a image[i + 1] = sourceData.get(j + 2); //b image[i + 2] = sourceData.get(j + 1); //g image[i + 3] = sourceData.get(j); //r - } else if (source.getFormat() == Format.Luminance8) { + } else if (source.getGlFormat() == Format.Luminance8) { int j = (xPos + yPos * width) * 1; image[i] = 1; //a image[i + 1] = sourceData.get(j); //b image[i + 2] = sourceData.get(j); //g image[i + 3] = sourceData.get(j); //r - } else if (source.getFormat() == Format.Luminance8Alpha8) { + } else if (source.getGlFormat() == Format.Luminance8Alpha8) { int j = (xPos + yPos * width) * 2; image[i] = sourceData.get(j + 1); //a image[i + 1] = sourceData.get(j); //b @@ -339,10 +339,10 @@ private void drawImage(Image source, int x, int y, String mapName) { image[i + 2] = sourceData.get(j + 2); //g image[i + 3] = sourceData.get(j + 3); //r }else{ - throw new UnsupportedOperationException("Cannot draw or convert textures with format " + source.getFormat()); + throw new UnsupportedOperationException("Cannot draw or convert textures with format " + source.getGlFormat()); } } else { - throw new UnsupportedOperationException("Cannot draw textures with format " + source.getFormat()); + throw new UnsupportedOperationException("Cannot draw textures with format " + source.getGlFormat()); } } } @@ -350,15 +350,15 @@ private void drawImage(Image source, int x, int y, String mapName) { } @SuppressWarnings("unchecked") - private Image convertImageToAwt(Image source) { + private GlImage convertImageToAwt(GlImage source) { //use awt dependent classes without actual dependency via reflection try { Class clazz = Class.forName("jme3tools.converters.ImageToAwt"); if (clazz == null) { return null; } - Image newImage = new Image(format, source.getWidth(), source.getHeight(), BufferUtils.createByteBuffer(source.getWidth() * source.getHeight() * 4), null, ColorSpace.Linear); - clazz.getMethod("convert", Image.class, Image.class).invoke(clazz.getDeclaredConstructor().newInstance(), source, newImage); + GlImage newImage = new GlImage(format, source.getWidth(), source.getHeight(), BufferUtils.createByteBuffer(source.getWidth() * source.getHeight() * 4), null, ColorSpace.Linear); + clazz.getMethod("convert", GlImage.class, GlImage.class).invoke(clazz.getDeclaredConstructor().newInstance(), source, newImage); return newImage; } catch (InstantiationException | IllegalAccessException @@ -376,7 +376,7 @@ private Image convertImageToAwt(Image source) { * @param texture The texture to retrieve the TextureAtlasTile for. * @return the atlas tile */ - public TextureAtlasTile getAtlasTile(Texture texture) { + public TextureAtlasTile getAtlasTile(GlTexture texture) { String sourceTextureName = textureName(texture); if (sourceTextureName != null) { return getAtlasTile(sourceTextureName); @@ -399,17 +399,17 @@ private TextureAtlasTile getAtlasTile(String assetName) { * @param mapName the desired name * @return the atlas texture */ - public Texture getAtlasTexture(String mapName) { + public GlTexture getAtlasTexture(String mapName) { if (images == null) { return null; } byte[] image = images.get(mapName); if (image != null) { //TODO check if color space shouldn't be sRGB - Texture2D tex = new Texture2D(new Image(format, atlasWidth, atlasHeight, BufferUtils.createByteBuffer(image), null, ColorSpace.Linear)); - tex.setMagFilter(Texture.MagFilter.Bilinear); - tex.setMinFilter(Texture.MinFilter.BilinearNearestMipMap); - tex.setWrap(Texture.WrapMode.EdgeClamp); + Texture2D tex = new Texture2D(new GlImage(format, atlasWidth, atlasHeight, BufferUtils.createByteBuffer(image), null, ColorSpace.Linear)); + tex.setMagFilter(GlTexture.MagFilter.Bilinear); + tex.setMinFilter(GlTexture.MinFilter.BilinearNearestMipMap); + tex.setWrap(GlTexture.WrapMode.EdgeClamp); return tex; } return null; @@ -437,14 +437,14 @@ public boolean applyCoords(Geometry geom, int offset, Mesh outMesh) { Mesh inMesh = geom.getMesh(); geom.computeWorldMatrix(); - VertexBuffer inBuf = inMesh.getBuffer(Type.TexCoord); - VertexBuffer outBuf = outMesh.getBuffer(Type.TexCoord); + GlVertexBuffer inBuf = inMesh.getBuffer(Type.TexCoord); + GlVertexBuffer outBuf = outMesh.getBuffer(Type.TexCoord); if (inBuf == null || outBuf == null) { throw new IllegalStateException("Geometry mesh has no texture coordinate buffer."); } - Texture tex = getMaterialTexture(geom, "DiffuseMap"); + GlTexture tex = getMaterialTexture(geom, "DiffuseMap"); if (tex == null) { tex = getMaterialTexture(geom, "ColorMap"); @@ -507,9 +507,9 @@ public static Geometry makeAtlasBatch(Spatial spat, AssetManager mgr, int atlasS geom.setMesh(mesh); Material mat = new Material(mgr, "Common/MatDefs/Light/Lighting.j3md"); - Texture diffuseMap = atlas.getAtlasTexture("DiffuseMap"); - Texture normalMap = atlas.getAtlasTexture("NormalMap"); - Texture specularMap = atlas.getAtlasTexture("SpecularMap"); + GlTexture diffuseMap = atlas.getAtlasTexture("DiffuseMap"); + GlTexture normalMap = atlas.getAtlasTexture("NormalMap"); + GlTexture specularMap = atlas.getAtlasTexture("SpecularMap"); if (diffuseMap != null) { mat.setTexture("DiffuseMap", diffuseMap); } @@ -534,8 +534,8 @@ private static void applyAtlasCoords(List geometries, Mesh outMesh, Te int geomVertCount = inMesh.getVertexCount(); - VertexBuffer inBuf = inMesh.getBuffer(Type.TexCoord); - VertexBuffer outBuf = outMesh.getBuffer(Type.TexCoord); + GlVertexBuffer inBuf = inMesh.getBuffer(Type.TexCoord); + GlVertexBuffer outBuf = outMesh.getBuffer(Type.TexCoord); if (inBuf == null || outBuf == null) { continue; @@ -547,13 +547,13 @@ private static void applyAtlasCoords(List geometries, Mesh outMesh, Te } } - private static Texture getMaterialTexture(Geometry geometry, String mapName) { + private static GlTexture getMaterialTexture(Geometry geometry, String mapName) { Material mat = geometry.getMaterial(); if (mat == null || mat.getParam(mapName) == null || !(mat.getParam(mapName) instanceof MatParamTexture)) { return null; } MatParamTexture param = (MatParamTexture) mat.getParam(mapName); - Texture texture = param.getTextureValue(); + GlTexture texture = param.getTextureValue(); if (texture == null) { return null; } @@ -581,7 +581,7 @@ public boolean isLeaf() { } // Algorithm from http://www.blackpawn.com/texts/lightmaps/ - public Node insert(Image image) { + public Node insert(GlImage image) { if (!isLeaf()) { Node newNode = child[0].insert(image); diff --git a/jme3-desktop/src/main/java/com/jme3/app/state/VideoRecorderAppState.java b/jme3-desktop/src/main/java/com/jme3/app/state/VideoRecorderAppState.java index 7e1d55b3e3..dc570eba8e 100644 --- a/jme3-desktop/src/main/java/com/jme3/app/state/VideoRecorderAppState.java +++ b/jme3-desktop/src/main/java/com/jme3/app/state/VideoRecorderAppState.java @@ -40,8 +40,8 @@ import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; import com.jme3.system.Timer; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage; import com.jme3.util.BufferUtils; import com.jme3.util.Screenshots; import java.awt.image.BufferedImage; @@ -223,7 +223,7 @@ private class VideoProcessor implements SceneProcessor { private LinkedBlockingQueue usedItems = new LinkedBlockingQueue<>(); private MjpegFileWriter writer; - public void addImage(Renderer renderer, FrameBuffer out) { + public void addImage(Renderer renderer, GlFrameBuffer out) { if (freeItems == null) { return; } @@ -231,7 +231,7 @@ public void addImage(Renderer renderer, FrameBuffer out) { final WorkItem item = freeItems.take(); usedItems.add(item); item.buffer.clear(); - renderer.readFrameBufferWithFormat(out, item.buffer, Image.Format.BGRA8); + renderer.readFrameBufferWithFormat(out, item.buffer, GlImage.Format.BGRA8); executor.submit(new Callable() { @Override @@ -292,7 +292,7 @@ public void postQueue(RenderQueue rq) { } @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { addImage(renderManager.getRenderer(), out); } diff --git a/jme3-desktop/src/main/java/com/jme3/system/AWTComponentRenderer.java b/jme3-desktop/src/main/java/com/jme3/system/AWTComponentRenderer.java index bfb0e85e31..f04a4c347a 100644 --- a/jme3-desktop/src/main/java/com/jme3/system/AWTComponentRenderer.java +++ b/jme3-desktop/src/main/java/com/jme3/system/AWTComponentRenderer.java @@ -50,8 +50,8 @@ import com.jme3.renderer.RenderManager; import com.jme3.renderer.Renderer; import com.jme3.system.AWTFrameProcessor.TransferMode; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage; import com.jme3.util.BufferUtils; @@ -98,7 +98,7 @@ public class AWTComponentRenderer { /** * The Frame buffer. */ - protected final FrameBuffer frameBuffer; + protected final GlFrameBuffer frameBuffer; /** * The Pixel writer. @@ -174,7 +174,7 @@ public AWTComponentRenderer(Component destination, final int width, final int he * @param width the width of the component in pixels. * @param height the height of the component in pixels. */ - public AWTComponentRenderer(Component destination, TransferMode transferMode, FrameBuffer frameBuffer, int width, int height) { + public AWTComponentRenderer(Component destination, TransferMode transferMode, GlFrameBuffer frameBuffer, int width, int height) { this.transferMode = transferMode; this.frameState = new AtomicInteger(WAITING_STATE); this.imageState = new AtomicInteger(WAITING_STATE); @@ -185,9 +185,9 @@ public AWTComponentRenderer(Component destination, TransferMode transferMode, Fr if (frameBuffer != null) { this.frameBuffer = frameBuffer; } else { - this.frameBuffer = new FrameBuffer(width, height, 1); - this.frameBuffer.setDepthBuffer(Image.Format.Depth); - this.frameBuffer.setColorBuffer(Image.Format.RGBA8); + this.frameBuffer = new GlFrameBuffer(width, height, 1); + this.frameBuffer.setDepthBuffer(GlImage.Format.Depth); + this.frameBuffer.setColorBuffer(GlImage.Format.RGBA8); this.frameBuffer.setSrgb(true); } @@ -270,7 +270,7 @@ public void copyFrameBufferToImage(RenderManager renderManager) { frameByteBuffer.clear(); final Renderer renderer = renderManager.getRenderer(); - renderer.readFrameBufferWithFormat(frameBuffer, frameByteBuffer, Image.Format.RGBA8); + renderer.readFrameBufferWithFormat(frameBuffer, frameByteBuffer, GlImage.Format.RGBA8); } finally { if (!frameState.compareAndSet(RUNNING_STATE, WAITING_STATE)) { diff --git a/jme3-desktop/src/main/java/com/jme3/system/AWTFrameProcessor.java b/jme3-desktop/src/main/java/com/jme3/system/AWTFrameProcessor.java index 111e856973..f4e9f1597b 100644 --- a/jme3-desktop/src/main/java/com/jme3/system/AWTFrameProcessor.java +++ b/jme3-desktop/src/main/java/com/jme3/system/AWTFrameProcessor.java @@ -48,8 +48,8 @@ import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage; /** * A frame processor that enables to render JMonkey frame buffer onto an AWT component. @@ -153,7 +153,7 @@ public void postQueue(RenderQueue rq) { } @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { if (!isEnabled()) { return; } @@ -559,7 +559,7 @@ protected AWTComponentRenderer reshapeInThread(final int width, final int height ViewPort viewPort = getViewPort(); RenderManager renderManager = getRenderManager(); - FrameBuffer frameBuffer = viewPort.getOutputFrameBuffer(); + GlFrameBuffer frameBuffer = viewPort.getOutputFrameBuffer(); AWTComponentRenderer frameTransfer = createFrameTransfer(frameBuffer, width, height); frameTransfer.init(renderManager.getRenderer(), isMain()); @@ -581,7 +581,7 @@ protected AWTComponentRenderer reshapeInThread(final int width, final int height * @param height the height. * @return the new frame transfer. */ - protected AWTComponentRenderer createFrameTransfer(FrameBuffer frameBuffer, int width, int height) { + protected AWTComponentRenderer createFrameTransfer(GlFrameBuffer frameBuffer, int width, int height) { return new AWTComponentRenderer(getDestination(), getTransferMode(), isMain() ? null : frameBuffer, width, height); } @@ -619,9 +619,9 @@ protected void reshapeCurrentViewPort(int width, int height) { if (found) { - FrameBuffer frameBuffer = new FrameBuffer(width, height, 1); - frameBuffer.setDepthBuffer(Image.Format.Depth); - frameBuffer.setColorBuffer(Image.Format.RGBA8); + GlFrameBuffer frameBuffer = new GlFrameBuffer(width, height, 1); + frameBuffer.setDepthBuffer(GlImage.Format.Depth); + frameBuffer.setColorBuffer(GlImage.Format.RGBA8); frameBuffer.setSrgb(true); viewPort.setOutputFrameBuffer(frameBuffer); diff --git a/jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java b/jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java index 81c197a3c5..2c28833e0e 100644 --- a/jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java +++ b/jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java @@ -37,7 +37,7 @@ import com.jme3.audio.openal.ALC; import com.jme3.audio.openal.EFX; import com.jme3.system.JmeContext.Type; -import com.jme3.texture.Image; +import com.jme3.texture.GlImage; import com.jme3.texture.image.ColorSpace; import com.jme3.util.res.Resources; @@ -102,7 +102,7 @@ private static BufferedImage ensureOpaque(BufferedImage original) { @Override public void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException { - BufferedImage awtImage = ImageToAwt.convert(new Image(Image.Format.RGBA8, width, height, imageData.duplicate(), ColorSpace.Linear), false, true, 0); + BufferedImage awtImage = ImageToAwt.convert(new GlImage(GlImage.Format.RGBA8, width, height, imageData.duplicate(), ColorSpace.Linear), false, true, 0); awtImage = verticalFlip(awtImage); ImageWriter writer = ImageIO.getImageWritersByFormatName(format).next(); diff --git a/jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanel.java b/jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanel.java index a234775bdb..2e197b5cc7 100644 --- a/jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanel.java +++ b/jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanel.java @@ -36,7 +36,8 @@ import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; -import com.jme3.texture.FrameBuffer; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage; import com.jme3.util.BufferUtils; import com.jme3.util.Screenshots; import java.awt.*; @@ -61,7 +62,7 @@ public class AwtPanel extends Canvas implements SceneProcessor { private boolean attachAsMain = false; private BufferedImage img; - private FrameBuffer fb; + private GlFrameBuffer fb; private boolean srgb = false; private ByteBuffer byteBuf; private IntBuffer intBuf; @@ -242,9 +243,9 @@ private void reshapeInThread(int width, int height) { fb = null; } - fb = new FrameBuffer(width, height, 1); - fb.setDepthTarget(FrameBuffer.FrameBufferTarget.newTarget(com.jme3.texture.Image.Format.Depth)); - fb.addColorTarget(FrameBuffer.FrameBufferTarget.newTarget(com.jme3.texture.Image.Format.RGB8)); + fb = new GlFrameBuffer(width, height, 1); + fb.setDepthTarget(GlFrameBuffer.FrameBufferTarget.newTarget(GlImage.Format.Depth)); + fb.addColorTarget(GlFrameBuffer.FrameBufferTarget.newTarget(GlImage.Format.RGB8)); fb.setSrgb(srgb); if (attachAsMain) { @@ -320,7 +321,7 @@ void onFrameEnd() { } @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { if (!attachAsMain && out != fb) { throw new IllegalStateException("Why did you change the output framebuffer?"); } diff --git a/jme3-desktop/src/main/java/com/jme3/texture/plugins/AWTLoader.java b/jme3-desktop/src/main/java/com/jme3/texture/plugins/AWTLoader.java index 926e7305af..fadd628fef 100644 --- a/jme3-desktop/src/main/java/com/jme3/texture/plugins/AWTLoader.java +++ b/jme3-desktop/src/main/java/com/jme3/texture/plugins/AWTLoader.java @@ -35,8 +35,8 @@ import com.jme3.asset.AssetLoadException; import com.jme3.asset.AssetLoader; import com.jme3.asset.TextureKey; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; import com.jme3.util.BufferUtils; import java.awt.Transparency; import java.awt.color.ColorSpace; @@ -101,7 +101,7 @@ private void flipImage(short[] img, int width, int height, int bpp){ } } - public Image load(BufferedImage img, boolean flipY){ + public GlImage load(BufferedImage img, boolean flipY){ int width = img.getWidth(); int height = img.getHeight(); @@ -113,7 +113,7 @@ public Image load(BufferedImage img, boolean flipY){ ByteBuffer data1 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*4); data1.put(dataBuf1); - return new Image(Format.ABGR8, width, height, data1, null, com.jme3.texture.image.ColorSpace.sRGB); + return new GlImage(Format.ABGR8, width, height, data1, null, com.jme3.texture.image.ColorSpace.sRGB); case BufferedImage.TYPE_3BYTE_BGR: // most common in JPEG images byte[] dataBuf2 = (byte[]) extractImageData(img); if (flipY) @@ -121,14 +121,14 @@ public Image load(BufferedImage img, boolean flipY){ ByteBuffer data2 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*3); data2.put(dataBuf2); - return new Image(Format.BGR8, width, height, data2, null, com.jme3.texture.image.ColorSpace.sRGB); + return new GlImage(Format.BGR8, width, height, data2, null, com.jme3.texture.image.ColorSpace.sRGB); case BufferedImage.TYPE_BYTE_GRAY: // grayscale fonts byte[] dataBuf3 = (byte[]) extractImageData(img); if (flipY) flipImage(dataBuf3, width, height, 8); ByteBuffer data3 = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()); data3.put(dataBuf3); - return new Image(Format.Luminance8, width, height, data3, null, com.jme3.texture.image.ColorSpace.sRGB); + return new GlImage(Format.Luminance8, width, height, data3, null, com.jme3.texture.image.ColorSpace.sRGB); default: break; } @@ -151,7 +151,7 @@ public Image load(BufferedImage img, boolean flipY){ } } data.flip(); - return new Image(Format.RGB8, width, height, data, null, com.jme3.texture.image.ColorSpace.sRGB); + return new GlImage(Format.RGB8, width, height, data, null, com.jme3.texture.image.ColorSpace.sRGB); }else{ ByteBuffer data = BufferUtils.createByteBuffer(img.getWidth()*img.getHeight()*4); // alpha @@ -171,11 +171,11 @@ public Image load(BufferedImage img, boolean flipY){ } } data.flip(); - return new Image(Format.RGBA8, width, height, data, null, com.jme3.texture.image.ColorSpace.sRGB); + return new GlImage(Format.RGBA8, width, height, data, null, com.jme3.texture.image.ColorSpace.sRGB); } } - public Image load(InputStream in, boolean flipY) throws IOException{ + public GlImage load(InputStream in, boolean flipY) throws IOException{ ImageIO.setUseCache(false); BufferedImage img = ImageIO.read(in); if (img == null){ @@ -190,7 +190,7 @@ public Object load(AssetInfo info) throws IOException { boolean flip = ((TextureKey) info.getKey()).isFlipY(); try (InputStream in = info.openStream(); BufferedInputStream bin = new BufferedInputStream(in)) { - Image img = load(bin, flip); + GlImage img = load(bin, flip); if (img == null){ throw new AssetLoadException("The given image cannot be loaded " + info.getKey()); } diff --git a/jme3-desktop/src/main/java/jme3tools/converters/ImageToAwt.java b/jme3-desktop/src/main/java/jme3tools/converters/ImageToAwt.java index 82f03a410d..ad556493f9 100644 --- a/jme3-desktop/src/main/java/jme3tools/converters/ImageToAwt.java +++ b/jme3-desktop/src/main/java/jme3tools/converters/ImageToAwt.java @@ -31,8 +31,8 @@ */ package jme3tools.converters; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; import com.jme3.texture.plugins.AWTLoader; import com.jme3.util.BufferUtils; import java.awt.Transparency; @@ -279,8 +279,8 @@ public static void convert(BufferedImage image, Format format, ByteBuffer buf) { private static final double LOG2 = Math.log(2); - public static void createData(Image image, boolean mipmaps){ - int bpp = image.getFormat().getBitsPerPixel(); + public static void createData(GlImage image, boolean mipmaps){ + int bpp = image.getGlFormat().getBitsPerPixel(); int w = image.getWidth(); int h = image.getHeight(); if (!mipmaps){ @@ -309,9 +309,9 @@ public static void createData(Image image, boolean mipmaps){ * @param input the input image (not null, unaffected) * @param output the output image (not null, modified) */ - public static void convert(Image input, Image output){ - DecodeParams inParams = params.get(input.getFormat()); - DecodeParams outParams = params.get(output.getFormat()); + public static void convert(GlImage input, GlImage output){ + DecodeParams inParams = params.get(input.getGlFormat()); + DecodeParams outParams = params.get(output.getGlFormat()); if (inParams == null || outParams == null) throw new UnsupportedOperationException(); @@ -375,9 +375,9 @@ public static void convert(Image input, Image output){ } } - public static BufferedImage convert(Image image, boolean do16bit, boolean fullAlpha, int mipLevel){ - Format format = image.getFormat(); - DecodeParams p = params.get(image.getFormat()); + public static BufferedImage convert(GlImage image, boolean do16bit, boolean fullAlpha, int mipLevel){ + Format format = image.getGlFormat(); + DecodeParams p = params.get(image.getGlFormat()); if (p == null) throw new UnsupportedOperationException(); diff --git a/jme3-desktop/src/main/java/jme3tools/converters/MipMapGenerator.java b/jme3-desktop/src/main/java/jme3tools/converters/MipMapGenerator.java index 24378647e6..1f6abe9c0f 100644 --- a/jme3-desktop/src/main/java/jme3tools/converters/MipMapGenerator.java +++ b/jme3-desktop/src/main/java/jme3tools/converters/MipMapGenerator.java @@ -32,8 +32,8 @@ package jme3tools.converters; import com.jme3.math.FastMath; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; import com.jme3.texture.plugins.AWTLoader; import com.jme3.util.BufferUtils; import java.awt.Graphics2D; @@ -64,7 +64,7 @@ private static BufferedImage scaleDown(BufferedImage sourceImage, int targetWidt return targetImage; } - public static void resizeToPowerOf2(Image image){ + public static void resizeToPowerOf2(GlImage image){ BufferedImage original = ImageToAwt.convert(image, false, true, 0); int potWidth = FastMath.nearestPowerOfTwo(image.getWidth()); int potHeight = FastMath.nearestPowerOfTwo(image.getHeight()); @@ -73,17 +73,17 @@ public static void resizeToPowerOf2(Image image){ BufferedImage scaled = scaleDown(original, potSize, potSize); AWTLoader loader = new AWTLoader(); - Image output = loader.load(scaled, false); + GlImage output = loader.load(scaled, false); image.setWidth(potSize); image.setHeight(potSize); image.setDepth(0); image.setData(output.getData(0)); - image.setFormat(output.getFormat()); + image.setFormat(output.getGlFormat()); image.setMipMapSizes(null); } - public static void generateMipMaps(Image image){ + public static void generateMipMaps(GlImage image){ BufferedImage original = ImageToAwt.convert(image, false, true, 0); int width = original.getWidth(); int height = original.getHeight(); @@ -96,8 +96,8 @@ public static void generateMipMaps(Image image){ Format format = null; while (height >= 1 || width >= 1){ - Image converted = loader.load(current, false); - format = converted.getFormat(); + GlImage converted = loader.load(current, false); + format = converted.getGlFormat(); output.add(converted.getData(0)); totalSize += converted.getData(0).capacity(); diff --git a/jme3-effects/src/main/java/com/jme3/post/filters/BloomFilter.java b/jme3-effects/src/main/java/com/jme3/post/filters/BloomFilter.java index 1ad0a967c8..5103e20817 100644 --- a/jme3-effects/src/main/java/com/jme3/post/filters/BloomFilter.java +++ b/jme3-effects/src/main/java/com/jme3/post/filters/BloomFilter.java @@ -43,7 +43,7 @@ import com.jme3.renderer.Renderer; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage.Format; import java.io.IOException; import java.util.ArrayList; diff --git a/jme3-effects/src/main/java/com/jme3/post/filters/CartoonEdgeFilter.java b/jme3-effects/src/main/java/com/jme3/post/filters/CartoonEdgeFilter.java index ab04279633..daaa8be176 100644 --- a/jme3-effects/src/main/java/com/jme3/post/filters/CartoonEdgeFilter.java +++ b/jme3-effects/src/main/java/com/jme3/post/filters/CartoonEdgeFilter.java @@ -45,7 +45,7 @@ import com.jme3.renderer.Renderer; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage.Format; /** * Applies a cartoon-style edge detection filter to all objects in the scene. diff --git a/jme3-effects/src/main/java/com/jme3/post/filters/SoftBloomFilter.java b/jme3-effects/src/main/java/com/jme3/post/filters/SoftBloomFilter.java index 3ccbf03ff9..08e1a68edb 100644 --- a/jme3-effects/src/main/java/com/jme3/post/filters/SoftBloomFilter.java +++ b/jme3-effects/src/main/java/com/jme3/post/filters/SoftBloomFilter.java @@ -43,8 +43,8 @@ import com.jme3.renderer.RenderManager; import com.jme3.renderer.Renderer; import com.jme3.renderer.ViewPort; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; import java.io.IOException; import java.util.logging.Logger; import java.util.logging.Level; @@ -77,7 +77,7 @@ public class SoftBloomFilter extends Filter { private int height; private Pass[] downsamplingPasses; private Pass[] upsamplingPasses; - private final Image.Format format = Image.Format.RGBA16F; + private final GlImage.Format format = GlImage.Format.RGBA16F; private boolean initialized = false; private int numSamplingPasses = 5; private float glowFactor = 0.05f; @@ -120,7 +120,7 @@ public void beforeRender() { downsampleMat.setVector2("TexelSize", initTexelSize); } }; - initialPass.init(renderer, w, h, format, Image.Format.Depth, 1, downsampleMat); + initialPass.init(renderer, w, h, format, GlImage.Format.Depth, 1, downsampleMat); postRenderPasses.add(initialPass); downsamplingPasses[0] = initialPass; for (int i = 1; i < downsamplingPasses.length; i++) { @@ -134,9 +134,9 @@ public void beforeRender() { downsampleMat.setVector2("TexelSize", texelSize); } }; - pass.init(renderer, w, h, format, Image.Format.Depth, 1, downsampleMat); + pass.init(renderer, w, h, format, GlImage.Format.Depth, 1, downsampleMat); if (bilinearFiltering) { - pass.getRenderedTexture().setMinFilter(Texture.MinFilter.BilinearNoMipMaps); + pass.getRenderedTexture().setMinFilter(GlTexture.MinFilter.BilinearNoMipMaps); } postRenderPasses.add(pass); downsamplingPasses[i] = pass; @@ -160,9 +160,9 @@ public void beforeRender() { upsampleMat.setVector2("TexelSize", texelSize); } }; - pass.init(renderer, w, h, format, Image.Format.Depth, 1, upsampleMat); + pass.init(renderer, w, h, format, GlImage.Format.Depth, 1, upsampleMat); if (bilinearFiltering) { - pass.getRenderedTexture().setMagFilter(Texture.MagFilter.Bilinear); + pass.getRenderedTexture().setMagFilter(GlTexture.MagFilter.Bilinear); } postRenderPasses.add(pass); upsamplingPasses[i] = pass; @@ -249,16 +249,16 @@ public void setBilinearFiltering(boolean bilinearFiltering) { if (initialized) { for (Pass p : downsamplingPasses) { if (this.bilinearFiltering) { - p.getRenderedTexture().setMinFilter(Texture.MinFilter.BilinearNoMipMaps); + p.getRenderedTexture().setMinFilter(GlTexture.MinFilter.BilinearNoMipMaps); } else { - p.getRenderedTexture().setMinFilter(Texture.MinFilter.NearestNoMipMaps); + p.getRenderedTexture().setMinFilter(GlTexture.MinFilter.NearestNoMipMaps); } } for (Pass p : upsamplingPasses) { if (this.bilinearFiltering) { - p.getRenderedTexture().setMagFilter(Texture.MagFilter.Bilinear); + p.getRenderedTexture().setMagFilter(GlTexture.MagFilter.Bilinear); } else { - p.getRenderedTexture().setMagFilter(Texture.MagFilter.Nearest); + p.getRenderedTexture().setMagFilter(GlTexture.MagFilter.Nearest); } } } diff --git a/jme3-effects/src/main/java/com/jme3/post/filters/TranslucentBucketFilter.java b/jme3-effects/src/main/java/com/jme3/post/filters/TranslucentBucketFilter.java index 1519dec4cd..adb746ee0e 100644 --- a/jme3-effects/src/main/java/com/jme3/post/filters/TranslucentBucketFilter.java +++ b/jme3-effects/src/main/java/com/jme3/post/filters/TranslucentBucketFilter.java @@ -42,8 +42,8 @@ import com.jme3.renderer.queue.RenderQueue; import com.jme3.scene.Node; import com.jme3.scene.Spatial; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Texture; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; import java.util.logging.Level; import java.util.logging.Logger; @@ -58,7 +58,7 @@ public final class TranslucentBucketFilter extends Filter { private final static Logger logger = Logger.getLogger(TranslucentBucketFilter.class.getName()); private RenderManager renderManager; private boolean enabledSoftParticles = false; - private Texture depthTexture; + private GlTexture depthTexture; private ViewPort viewPort; public TranslucentBucketFilter() { @@ -99,7 +99,7 @@ private void initSoftParticles(ViewPort vp, boolean enabledSP) { } @Override - protected void setDepthTexture(Texture depthTexture) { + protected void setDepthTexture(GlTexture depthTexture) { this.depthTexture = depthTexture; if (enabledSoftParticles && depthTexture != null) { initSoftParticles(viewPort, true); @@ -121,7 +121,7 @@ protected boolean isRequiresDepthTexture() { } @Override - protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) { + protected void postFrame(RenderManager renderManager, ViewPort viewPort, GlFrameBuffer prevFilterBuffer, GlFrameBuffer sceneBuffer) { renderManager.setCamera(viewPort.getCamera(), false); if (prevFilterBuffer != sceneBuffer) { renderManager.getRenderer().copyFrameBuffer(prevFilterBuffer, sceneBuffer, true, false); diff --git a/jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java b/jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java index 58a12570d0..4adb9ac11c 100644 --- a/jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java +++ b/jme3-effects/src/main/java/com/jme3/post/ssao/SSAOFilter.java @@ -45,8 +45,8 @@ import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; import com.jme3.shader.VarType; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture; import java.io.IOException; import java.util.ArrayList; @@ -146,8 +146,8 @@ protected void initFilter(AssetManager assetManager, RenderManager renderManager //ssao Pass ssaoMat = new Material(assetManager, "Common/MatDefs/SSAO/ssao.j3md"); ssaoMat.setTexture("Normals", normalPass.getRenderedTexture()); - Texture random = assetManager.loadTexture("Common/MatDefs/SSAO/Textures/random.png"); - random.setWrap(Texture.WrapMode.Repeat); + GlTexture random = assetManager.loadTexture("Common/MatDefs/SSAO/Textures/random.png"); + random.setWrap(GlTexture.WrapMode.Repeat); ssaoMat.setTexture("RandomMap", random); Pass ssaoPass = new Pass("SSAO pass") { diff --git a/jme3-effects/src/main/java/com/jme3/water/ReflectionProcessor.java b/jme3-effects/src/main/java/com/jme3/water/ReflectionProcessor.java index 3cc2220921..263c27aca0 100644 --- a/jme3-effects/src/main/java/com/jme3/water/ReflectionProcessor.java +++ b/jme3-effects/src/main/java/com/jme3/water/ReflectionProcessor.java @@ -38,7 +38,7 @@ import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; -import com.jme3.texture.FrameBuffer; +import com.jme3.texture.GlFrameBuffer; /** * Reflection Processor @@ -49,7 +49,7 @@ public class ReflectionProcessor implements SceneProcessor { private RenderManager rm; private ViewPort vp; private Camera reflectionCam; - private FrameBuffer reflectionBuffer; + private GlFrameBuffer reflectionBuffer; private Plane reflectionClipPlane; /** @@ -58,7 +58,7 @@ public class ReflectionProcessor implements SceneProcessor { * @param reflectionBuffer the FrameBuffer to render to * @param reflectionClipPlane the clipping plane */ - public ReflectionProcessor(Camera reflectionCam, FrameBuffer reflectionBuffer, Plane reflectionClipPlane) { + public ReflectionProcessor(Camera reflectionCam, GlFrameBuffer reflectionBuffer, Plane reflectionClipPlane) { this.reflectionCam = reflectionCam; this.reflectionBuffer = reflectionBuffer; this.reflectionClipPlane = reflectionClipPlane; @@ -101,7 +101,7 @@ public void postQueue(RenderQueue rq) { } @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { } @Override @@ -118,7 +118,7 @@ public void setProfiler(AppProfiler profiler) { * returns the frame buffer * @return the pre-existing buffer */ - public FrameBuffer getReflectionBuffer() { + public GlFrameBuffer getReflectionBuffer() { return reflectionBuffer; } @@ -128,7 +128,7 @@ public FrameBuffer getReflectionBuffer() { * * @param reflectionBuffer the FrameBuffer to use (alias created) */ - public void setReflectionBuffer(FrameBuffer reflectionBuffer) { + public void setReflectionBuffer(GlFrameBuffer reflectionBuffer) { this.reflectionBuffer = reflectionBuffer; } diff --git a/jme3-effects/src/main/java/com/jme3/water/SimpleWaterProcessor.java b/jme3-effects/src/main/java/com/jme3/water/SimpleWaterProcessor.java index 3bc5e7ca79..520833fc39 100644 --- a/jme3-effects/src/main/java/com/jme3/water/SimpleWaterProcessor.java +++ b/jme3-effects/src/main/java/com/jme3/water/SimpleWaterProcessor.java @@ -42,9 +42,9 @@ import com.jme3.scene.Spatial; import com.jme3.scene.shape.Quad; import com.jme3.texture.*; -import com.jme3.texture.FrameBuffer.FrameBufferTarget; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlFrameBuffer.FrameBufferTarget; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.ui.Picture; /** @@ -91,8 +91,8 @@ public class SimpleWaterProcessor implements SceneProcessor { protected Spatial reflectionScene; protected ViewPort reflectionView; protected ViewPort refractionView; - protected FrameBuffer reflectionBuffer; - protected FrameBuffer refractionBuffer; + protected GlFrameBuffer reflectionBuffer; + protected GlFrameBuffer refractionBuffer; protected Camera reflectionCam; protected Camera refractionCam; protected Texture2D reflectionTexture; @@ -212,7 +212,7 @@ public void postQueue(RenderQueue rq) { } @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { if (debug) { displayMap(rm.getRenderer(), dispRefraction, 64); displayMap(rm.getRenderer(), dispReflection, 256); @@ -255,11 +255,11 @@ protected void createTextures() { reflectionTexture = new Texture2D(renderWidth, renderHeight, Format.RGBA8); refractionTexture = new Texture2D(renderWidth, renderHeight, Format.RGBA8); - reflectionTexture.setMinFilter(Texture.MinFilter.Trilinear); - reflectionTexture.setMagFilter(Texture.MagFilter.Bilinear); + reflectionTexture.setMinFilter(GlTexture.MinFilter.Trilinear); + reflectionTexture.setMagFilter(GlTexture.MagFilter.Bilinear); - refractionTexture.setMinFilter(Texture.MinFilter.Trilinear); - refractionTexture.setMagFilter(Texture.MagFilter.Bilinear); + refractionTexture.setMinFilter(GlTexture.MinFilter.Trilinear); + refractionTexture.setMagFilter(GlTexture.MagFilter.Bilinear); depthTexture = new Texture2D(renderWidth, renderHeight, Format.Depth); } @@ -281,7 +281,7 @@ protected void createPreViews() { reflectionView.setClearFlags(true, true, true); reflectionView.setBackgroundColor(ColorRGBA.Black); // create offscreen framebuffer - reflectionBuffer = new FrameBuffer(renderWidth, renderHeight, 1); + reflectionBuffer = new GlFrameBuffer(renderWidth, renderHeight, 1); //setup framebuffer to use texture reflectionBuffer.setDepthTarget(FrameBufferTarget.newTarget(Format.Depth)); reflectionBuffer.addColorTarget(FrameBufferTarget.newTarget(reflectionTexture)); @@ -297,7 +297,7 @@ protected void createPreViews() { refractionView.setClearFlags(true, true, true); refractionView.setBackgroundColor(ColorRGBA.Black); // create offscreen framebuffer - refractionBuffer = new FrameBuffer(renderWidth, renderHeight, 1); + refractionBuffer = new GlFrameBuffer(renderWidth, renderHeight, 1); //setup framebuffer to use texture refractionBuffer.addColorTarget(FrameBufferTarget.newTarget(refractionTexture)); refractionBuffer.setDepthTarget(FrameBufferTarget.newTarget(depthTexture)); @@ -637,7 +637,7 @@ public void postQueue(RenderQueue rq) { } @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { } @Override diff --git a/jme3-effects/src/main/java/com/jme3/water/WaterFilter.java b/jme3-effects/src/main/java/com/jme3/water/WaterFilter.java index 0d84b23674..a264d4204e 100644 --- a/jme3-effects/src/main/java/com/jme3/water/WaterFilter.java +++ b/jme3-effects/src/main/java/com/jme3/water/WaterFilter.java @@ -49,8 +49,8 @@ import com.jme3.renderer.ViewPort; import com.jme3.scene.Node; import com.jme3.scene.Spatial; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.texture.Texture2D; import com.jme3.util.clone.Cloner; import com.jme3.util.clone.JmeCloneable; diff --git a/jme3-examples/build.gradle b/jme3-examples/build.gradle index 4f5d41b501..75d4e8c098 100644 --- a/jme3-examples/build.gradle +++ b/jme3-examples/build.gradle @@ -19,8 +19,8 @@ dependencies { implementation project(':jme3-effects') implementation project(':jme3-jbullet') implementation project(':jme3-jogg') - implementation project(':jme3-lwjgl') -// implementation project(':jme3-lwjgl3') +// implementation project(':jme3-lwjgl') + implementation project(':jme3-lwjgl3') implementation project(':jme3-networking') implementation project(':jme3-niftygui') implementation project(':jme3-plugins') diff --git a/jme3-examples/src/main/java/jme3test/animation/TestCameraMotionPath.java b/jme3-examples/src/main/java/jme3test/animation/TestCameraMotionPath.java index fcad3b0f10..3177400d44 100644 --- a/jme3-examples/src/main/java/jme3test/animation/TestCameraMotionPath.java +++ b/jme3-examples/src/main/java/jme3test/animation/TestCameraMotionPath.java @@ -31,7 +31,7 @@ */ package jme3test.animation; -import com.jme3.animation.LoopMode; +import com.jme3.anim.util.LoopMode; import com.jme3.app.SimpleApplication; import com.jme3.cinematic.MotionPath; import com.jme3.cinematic.MotionPathListener; diff --git a/jme3-examples/src/main/java/jme3test/animation/TestCinematic.java b/jme3-examples/src/main/java/jme3test/animation/TestCinematic.java index 6bce59d798..a06d218a69 100644 --- a/jme3-examples/src/main/java/jme3test/animation/TestCinematic.java +++ b/jme3-examples/src/main/java/jme3test/animation/TestCinematic.java @@ -34,7 +34,7 @@ import com.jme3.anim.AnimClip; import com.jme3.anim.AnimComposer; import com.jme3.anim.AnimFactory; -import com.jme3.animation.LoopMode; +import com.jme3.anim.util.LoopMode; import com.jme3.app.SimpleApplication; import com.jme3.cinematic.*; import com.jme3.cinematic.events.*; diff --git a/jme3-examples/src/main/java/jme3test/animation/TestIssue2076.java b/jme3-examples/src/main/java/jme3test/animation/TestIssue2076.java index 80fcac11b6..1dcc495a55 100644 --- a/jme3-examples/src/main/java/jme3test/animation/TestIssue2076.java +++ b/jme3-examples/src/main/java/jme3test/animation/TestIssue2076.java @@ -32,15 +32,13 @@ package jme3test.animation; import com.jme3.anim.SkinningControl; -import com.jme3.anim.util.AnimMigrationUtils; -import com.jme3.animation.SkeletonControl; import com.jme3.app.SimpleApplication; import com.jme3.light.AmbientLight; import com.jme3.math.ColorRGBA; import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; import com.jme3.scene.Node; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; /** * Test for JMonkeyEngine issue #2076: software skinning requires vertex @@ -102,8 +100,8 @@ private void testOldAnimationSystem(String assetPath) { // remove its vertex normals: Geometry oldGeometry = (Geometry) oldJaime.getChild(0); Mesh oldMesh = oldGeometry.getMesh(); - oldMesh.clearBuffer(VertexBuffer.Type.Normal); - oldMesh.clearBuffer(VertexBuffer.Type.BindPoseNormal); + oldMesh.clearBuffer(GlVertexBuffer.Type.Normal); + oldMesh.clearBuffer(GlVertexBuffer.Type.BindPoseNormal); } /** @@ -126,7 +124,7 @@ private void testNewAnimationSystem(String assetPath) { // remove its vertex normals: Geometry newGeometry = (Geometry) newJaime.getChild(0); Mesh newMesh = newGeometry.getMesh(); - newMesh.clearBuffer(VertexBuffer.Type.Normal); - newMesh.clearBuffer(VertexBuffer.Type.BindPoseNormal); + newMesh.clearBuffer(GlVertexBuffer.Type.Normal); + newMesh.clearBuffer(GlVertexBuffer.Type.BindPoseNormal); } } diff --git a/jme3-examples/src/main/java/jme3test/animation/TestJaime.java b/jme3-examples/src/main/java/jme3test/animation/TestJaime.java index a841b16e40..5f9432c6ff 100644 --- a/jme3-examples/src/main/java/jme3test/animation/TestJaime.java +++ b/jme3-examples/src/main/java/jme3test/animation/TestJaime.java @@ -34,8 +34,7 @@ import com.jme3.anim.AnimClip; import com.jme3.anim.AnimComposer; import com.jme3.anim.AnimFactory; -import com.jme3.anim.util.AnimMigrationUtils; -import com.jme3.animation.LoopMode; +import com.jme3.anim.util.LoopMode; import com.jme3.app.DebugKeysAppState; import com.jme3.app.FlyCamAppState; import com.jme3.app.ResetStatsState; diff --git a/jme3-examples/src/main/java/jme3test/app/TestUseAfterFree.java b/jme3-examples/src/main/java/jme3test/app/TestUseAfterFree.java index b241d78087..0406fa42c1 100644 --- a/jme3-examples/src/main/java/jme3test/app/TestUseAfterFree.java +++ b/jme3-examples/src/main/java/jme3test/app/TestUseAfterFree.java @@ -35,7 +35,7 @@ import com.jme3.material.Material; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Box; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import com.jme3.util.BufferUtils; /** @@ -49,7 +49,7 @@ public class TestUseAfterFree extends SimpleApplication { private float time = 0; private Material mat; - private Texture deletedTex; + private GlTexture deletedTex; public static void main(String[] args) { TestUseAfterFree app = new TestUseAfterFree(); diff --git a/jme3-examples/src/main/java/jme3test/asset/TestUrlLoading.java b/jme3-examples/src/main/java/jme3test/asset/TestUrlLoading.java index 563eb4c7c7..21982d5fc0 100644 --- a/jme3-examples/src/main/java/jme3test/asset/TestUrlLoading.java +++ b/jme3-examples/src/main/java/jme3test/asset/TestUrlLoading.java @@ -39,7 +39,7 @@ import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Quad; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; /** * Load an image and display it from the internet using the UrlLocator. @@ -64,7 +64,7 @@ public void simpleInitApp() { UrlLocator.class); TextureKey key = new TextureKey("mucha-window.png", false); key.setGenerateMips(true); - Texture tex = assetManager.loadTexture(key); + GlTexture tex = assetManager.loadTexture(key); Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat.setTexture("ColorMap", tex); diff --git a/jme3-examples/src/main/java/jme3test/batching/TestBatchNodeTower.java b/jme3-examples/src/main/java/jme3test/batching/TestBatchNodeTower.java index 5f0e603fb5..d823553ee6 100644 --- a/jme3-examples/src/main/java/jme3test/batching/TestBatchNodeTower.java +++ b/jme3-examples/src/main/java/jme3test/batching/TestBatchNodeTower.java @@ -57,8 +57,8 @@ import com.jme3.shadow.EdgeFilteringMode; import com.jme3.system.AppSettings; import com.jme3.system.NanoTimer; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import jme3test.bullet.BombControl; /** @@ -208,19 +208,19 @@ public void initMaterial() { mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); TextureKey key = new TextureKey("Textures/Terrain/BrickWall/BrickWall.jpg"); key.setGenerateMips(true); - Texture tex = assetManager.loadTexture(key); + GlTexture tex = assetManager.loadTexture(key); mat.setTexture("ColorMap", tex); mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG"); key2.setGenerateMips(true); - Texture tex2 = assetManager.loadTexture(key2); + GlTexture tex2 = assetManager.loadTexture(key2); mat2.setTexture("ColorMap", tex2); mat3 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); TextureKey key3 = new TextureKey("Textures/Terrain/Pond/Pond.jpg"); key3.setGenerateMips(true); - Texture tex3 = assetManager.loadTexture(key3); + GlTexture tex3 = assetManager.loadTexture(key3); tex3.setWrap(WrapMode.Repeat); mat3.setTexture("ColorMap", tex3); } diff --git a/jme3-examples/src/main/java/jme3test/bullet/PhysicsTestHelper.java b/jme3-examples/src/main/java/jme3test/bullet/PhysicsTestHelper.java index 59d02d64c8..057536a25e 100644 --- a/jme3-examples/src/main/java/jme3test/bullet/PhysicsTestHelper.java +++ b/jme3-examples/src/main/java/jme3test/bullet/PhysicsTestHelper.java @@ -51,11 +51,11 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; import com.jme3.scene.Node; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Sphere; import com.jme3.scene.shape.Sphere.TextureMode; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import com.jme3.util.BufferUtils; /** @@ -237,7 +237,7 @@ public void onAction(String name, boolean keyPressed, float tpf) { Material mat2 = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG"); key2.setGenerateMips(true); - Texture tex2 = app.getAssetManager().loadTexture(key2); + GlTexture tex2 = app.getAssetManager().loadTexture(key2); mat2.setTexture("ColorMap", tex2); if (name.equals("shoot") && !keyPressed) { Geometry bulletGeometry = new Geometry("bullet", bullet); @@ -352,8 +352,8 @@ private static Mesh createFloorMesh(int meshDetail, float floorDimensions) { } Mesh m = new Mesh(); - m.setBuffer(VertexBuffer.Type.Index, 1, BufferUtils.createIntBuffer(indexBuf)); - m.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(vertBuf)); + m.setBuffer(GlVertexBuffer.Type.Index, 1, BufferUtils.createIntBuffer(indexBuf)); + m.setBuffer(GlVertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(vertBuf)); m.updateBound(); return m; } diff --git a/jme3-examples/src/main/java/jme3test/bullet/TestAttachDriver.java b/jme3-examples/src/main/java/jme3test/bullet/TestAttachDriver.java index 8c1c712603..aa507c4d26 100644 --- a/jme3-examples/src/main/java/jme3test/bullet/TestAttachDriver.java +++ b/jme3-examples/src/main/java/jme3test/bullet/TestAttachDriver.java @@ -51,7 +51,7 @@ import com.jme3.scene.Node; import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Cylinder; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; /** * Tests attaching/detaching nodes via joints @@ -107,8 +107,8 @@ public void setupFloor() { Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); TextureKey key = new TextureKey("Interface/Logo/Monkey.jpg", true); key.setGenerateMips(true); - Texture tex = assetManager.loadTexture(key); - tex.setMinFilter(Texture.MinFilter.Trilinear); + GlTexture tex = assetManager.loadTexture(key); + tex.setMinFilter(GlTexture.MinFilter.Trilinear); mat.setTexture("ColorMap", tex); Box floor = new Box(100, 1f, 100); diff --git a/jme3-examples/src/main/java/jme3test/bullet/TestBoneRagdoll.java b/jme3-examples/src/main/java/jme3test/bullet/TestBoneRagdoll.java index 538ec8b31c..e6c120c186 100644 --- a/jme3-examples/src/main/java/jme3test/bullet/TestBoneRagdoll.java +++ b/jme3-examples/src/main/java/jme3test/bullet/TestBoneRagdoll.java @@ -61,7 +61,7 @@ import com.jme3.scene.Node; import com.jme3.scene.shape.Sphere; import com.jme3.scene.shape.Sphere.TextureMode; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; /** * @author normenhansen @@ -218,7 +218,7 @@ private void initMaterial() { "Common/MatDefs/Misc/Unshaded.j3md"); TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG"); key2.setGenerateMips(true); - Texture tex2 = assetManager.loadTexture(key2); + GlTexture tex2 = assetManager.loadTexture(key2); matBullet.setTexture("ColorMap", tex2); } diff --git a/jme3-examples/src/main/java/jme3test/bullet/TestBrickTower.java b/jme3-examples/src/main/java/jme3test/bullet/TestBrickTower.java index a607878185..3e7a31c234 100644 --- a/jme3-examples/src/main/java/jme3test/bullet/TestBrickTower.java +++ b/jme3-examples/src/main/java/jme3test/bullet/TestBrickTower.java @@ -49,8 +49,8 @@ import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Sphere; import com.jme3.scene.shape.Sphere.TextureMode; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; /** * @@ -177,19 +177,19 @@ public void initMaterial() { mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); TextureKey key = new TextureKey("Textures/Terrain/BrickWall/BrickWall.jpg"); key.setGenerateMips(true); - Texture tex = assetManager.loadTexture(key); + GlTexture tex = assetManager.loadTexture(key); mat.setTexture("ColorMap", tex); mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG"); key2.setGenerateMips(true); - Texture tex2 = assetManager.loadTexture(key2); + GlTexture tex2 = assetManager.loadTexture(key2); mat2.setTexture("ColorMap", tex2); mat3 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); TextureKey key3 = new TextureKey("Textures/Terrain/Pond/Pond.jpg"); key3.setGenerateMips(true); - Texture tex3 = assetManager.loadTexture(key3); + GlTexture tex3 = assetManager.loadTexture(key3); tex3.setWrap(WrapMode.Repeat); mat3.setTexture("ColorMap", tex3); } diff --git a/jme3-examples/src/main/java/jme3test/bullet/TestBrickWall.java b/jme3-examples/src/main/java/jme3test/bullet/TestBrickWall.java index d0cbdb2bba..eeb0fbd537 100644 --- a/jme3-examples/src/main/java/jme3test/bullet/TestBrickWall.java +++ b/jme3-examples/src/main/java/jme3test/bullet/TestBrickWall.java @@ -52,8 +52,8 @@ import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Sphere; import com.jme3.scene.shape.Sphere.TextureMode; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; /** * @@ -161,19 +161,19 @@ public void initMaterial() { mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); TextureKey key = new TextureKey("Textures/Terrain/BrickWall/BrickWall.jpg"); key.setGenerateMips(true); - Texture tex = assetManager.loadTexture(key); + GlTexture tex = assetManager.loadTexture(key); mat.setTexture("ColorMap", tex); mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG"); key2.setGenerateMips(true); - Texture tex2 = assetManager.loadTexture(key2); + GlTexture tex2 = assetManager.loadTexture(key2); mat2.setTexture("ColorMap", tex2); mat3 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); TextureKey key3 = new TextureKey("Textures/Terrain/Pond/Pond.jpg"); key3.setGenerateMips(true); - Texture tex3 = assetManager.loadTexture(key3); + GlTexture tex3 = assetManager.loadTexture(key3); tex3.setWrap(WrapMode.Repeat); mat3.setTexture("ColorMap", tex3); } diff --git a/jme3-examples/src/main/java/jme3test/bullet/TestHoveringTank.java b/jme3-examples/src/main/java/jme3test/bullet/TestHoveringTank.java index 3127ec4a68..ec712e30f9 100644 --- a/jme3-examples/src/main/java/jme3test/bullet/TestHoveringTank.java +++ b/jme3-examples/src/main/java/jme3test/bullet/TestHoveringTank.java @@ -57,8 +57,8 @@ import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.util.SkyFactory; import com.jme3.util.SkyFactory.EnvMapType; import java.util.ArrayList; @@ -255,24 +255,24 @@ private void createTerrain() { matRock.setBoolean("useTriPlanarMapping", false); matRock.setBoolean("WardIso", true); matRock.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png")); - Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); - Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap", grass); matRock.setFloat("DiffuseMap_0_scale", 64); - Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); + GlTexture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap_1", dirt); matRock.setFloat("DiffuseMap_1_scale", 16); - Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); + GlTexture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); rock.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap_2", rock); matRock.setFloat("DiffuseMap_2_scale", 128); - Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); + GlTexture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); normalMap0.setWrap(WrapMode.Repeat); - Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); + GlTexture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); normalMap1.setWrap(WrapMode.Repeat); - Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); + GlTexture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); normalMap2.setWrap(WrapMode.Repeat); matRock.setTexture("NormalMap", normalMap0); matRock.setTexture("NormalMap_1", normalMap1); diff --git a/jme3-examples/src/main/java/jme3test/bullet/TestRagdollCharacter.java b/jme3-examples/src/main/java/jme3test/bullet/TestRagdollCharacter.java index b457e3b15b..0a903c0c44 100644 --- a/jme3-examples/src/main/java/jme3test/bullet/TestRagdollCharacter.java +++ b/jme3-examples/src/main/java/jme3test/bullet/TestRagdollCharacter.java @@ -54,7 +54,7 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Node; import com.jme3.scene.shape.Box; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; /** * @author normenhansen @@ -197,7 +197,7 @@ private void initWall(float bLength, float bWidth, float bHeight) { Material mat2 = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); TextureKey key = new TextureKey("Textures/Terrain/BrickWall/BrickWall.jpg"); key.setGenerateMips(true); - Texture tex = assetManager.loadTexture(key); + GlTexture tex = assetManager.loadTexture(key); mat2.setTexture("ColorMap", tex); float startpt = bLength / 4f; diff --git a/jme3-examples/src/main/java/jme3test/bullet/TestWalkingChar.java b/jme3-examples/src/main/java/jme3test/bullet/TestWalkingChar.java index 61f08d25f2..5b2ffe6024 100644 --- a/jme3-examples/src/main/java/jme3test/bullet/TestWalkingChar.java +++ b/jme3-examples/src/main/java/jme3test/bullet/TestWalkingChar.java @@ -70,8 +70,8 @@ import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.util.SkyFactory; import java.util.ArrayList; @@ -243,24 +243,24 @@ private void createTerrain() { matRock.setBoolean("useTriPlanarMapping", false); matRock.setBoolean("WardIso", true); matRock.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png")); - Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); - Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap", grass); matRock.setFloat("DiffuseMap_0_scale", 64); - Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); + GlTexture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap_1", dirt); matRock.setFloat("DiffuseMap_1_scale", 16); - Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); + GlTexture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); rock.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap_2", rock); matRock.setFloat("DiffuseMap_2_scale", 128); - Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); + GlTexture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); normalMap0.setWrap(WrapMode.Repeat); - Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); + GlTexture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); normalMap1.setWrap(WrapMode.Repeat); - Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); + GlTexture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); normalMap2.setWrap(WrapMode.Repeat); matRock.setTexture("NormalMap", normalMap0); matRock.setTexture("NormalMap_1", normalMap1); diff --git a/jme3-examples/src/main/java/jme3test/collision/TestRayCasting.java b/jme3-examples/src/main/java/jme3test/collision/TestRayCasting.java index e3c1ba136b..6becd1cfd5 100644 --- a/jme3-examples/src/main/java/jme3test/collision/TestRayCasting.java +++ b/jme3-examples/src/main/java/jme3test/collision/TestRayCasting.java @@ -38,7 +38,7 @@ import com.jme3.math.FastMath; import com.jme3.scene.Mesh; import com.jme3.scene.Spatial; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; public class TestRayCasting extends SimpleApplication { diff --git a/jme3-examples/src/main/java/jme3test/conversion/TestMipMapGen.java b/jme3-examples/src/main/java/jme3test/conversion/TestMipMapGen.java index 6110469ee0..cc8dbf0df5 100644 --- a/jme3-examples/src/main/java/jme3test/conversion/TestMipMapGen.java +++ b/jme3-examples/src/main/java/jme3test/conversion/TestMipMapGen.java @@ -37,8 +37,8 @@ import com.jme3.material.Material; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Quad; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; import com.jme3.util.MipMapGenerator; public class TestMipMapGen extends SimpleApplication { @@ -66,11 +66,11 @@ public void simpleInitApp() { Geometry quad1 = new Geometry("Textured Quad", quadMesh); Geometry quad2 = new Geometry("Textured Quad 2", quadMesh); - Texture tex = assetManager.loadTexture("Interface/Logo/Monkey.png"); - tex.setMinFilter(Texture.MinFilter.Trilinear); + GlTexture tex = assetManager.loadTexture("Interface/Logo/Monkey.png"); + tex.setMinFilter(GlTexture.MinFilter.Trilinear); - Texture texCustomMip = tex.clone(); - Image imageCustomMip = texCustomMip.getImage().clone(); + GlTexture texCustomMip = tex.clone(); + GlImage imageCustomMip = texCustomMip.getImage().clone(); MipMapGenerator.generateMipMaps(imageCustomMip); texCustomMip.setImage(imageCustomMip); diff --git a/jme3-examples/src/main/java/jme3test/effect/TestEverything.java b/jme3-examples/src/main/java/jme3test/effect/TestEverything.java index bb5ab62a1a..98bc400680 100644 --- a/jme3-examples/src/main/java/jme3test/effect/TestEverything.java +++ b/jme3-examples/src/main/java/jme3test/effect/TestEverything.java @@ -46,7 +46,7 @@ import com.jme3.scene.Spatial.CullHint; import com.jme3.scene.shape.Box; import com.jme3.shadow.DirectionalLightShadowRenderer; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import com.jme3.util.SkyFactory; import com.jme3.util.mikktspace.MikktspaceTangentGenerator; @@ -81,7 +81,7 @@ public void setupBasicShadow(){ } public void setupSkyBox(){ - Texture envMap; + GlTexture envMap; if (renderer.getCaps().contains(Caps.FloatTexture)){ envMap = assetManager.loadTexture("Textures/Sky/St Peters/StPeters.hdr"); }else{ diff --git a/jme3-examples/src/main/java/jme3test/effect/TestIssue1773.java b/jme3-examples/src/main/java/jme3test/effect/TestIssue1773.java index b466815dfb..58bbea8833 100644 --- a/jme3-examples/src/main/java/jme3test/effect/TestIssue1773.java +++ b/jme3-examples/src/main/java/jme3test/effect/TestIssue1773.java @@ -31,7 +31,7 @@ */ package jme3test.effect; -import com.jme3.animation.LoopMode; +import com.jme3.anim.util.LoopMode; import com.jme3.app.SimpleApplication; import com.jme3.cinematic.MotionPath; import com.jme3.cinematic.events.MotionEvent; @@ -62,7 +62,7 @@ import com.jme3.scene.shape.Torus; import com.jme3.shadow.DirectionalLightShadowFilter; import com.jme3.system.AppSettings; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import java.util.Arrays; /** @@ -249,8 +249,8 @@ private void setupGround() { quad.scaleTextureCoordinates(new Vector2f(2, 2)); Geometry floor = new Geometry("Floor", quad); Material mat = new Material(assetManager, Materials.LIGHTING); - Texture tex = assetManager.loadTexture("Interface/Logo/Monkey.jpg"); - tex.setWrap(Texture.WrapMode.Repeat); + GlTexture tex = assetManager.loadTexture("Interface/Logo/Monkey.jpg"); + tex.setWrap(GlTexture.WrapMode.Repeat); mat.setTexture("DiffuseMap", tex); floor.setMaterial(mat); floor.rotate(-FastMath.HALF_PI, 0, 0); diff --git a/jme3-examples/src/main/java/jme3test/gui/TestBitmapFontLayout.java b/jme3-examples/src/main/java/jme3test/gui/TestBitmapFontLayout.java index 6d2ad6f37b..b7826e590a 100644 --- a/jme3-examples/src/main/java/jme3test/gui/TestBitmapFontLayout.java +++ b/jme3-examples/src/main/java/jme3test/gui/TestBitmapFontLayout.java @@ -61,8 +61,8 @@ import com.jme3.scene.*; import com.jme3.scene.debug.WireBox; import com.jme3.scene.shape.*; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; import com.jme3.texture.plugins.AWTLoader; @@ -109,7 +109,7 @@ public static Font loadTtf( String resource ) { } } - private Texture renderAwtFont( TestConfig test, int width, int height, BitmapFont bitmapFont ) { + private GlTexture renderAwtFont(TestConfig test, int width, int height, BitmapFont bitmapFont ) { BitmapCharacterSet charset = bitmapFont.getCharSet(); @@ -152,7 +152,7 @@ private Texture renderAwtFont( TestConfig test, int width, int height, BitmapFon g2.dispose(); - Image jmeImage = new AWTLoader().load(image, true); + GlImage jmeImage = new AWTLoader().load(image, true); return new Texture2D(jmeImage); } @@ -243,7 +243,7 @@ private Node createVisual( TestConfig test ) { int width = Math.round(x2 - Math.min(0, x1)); int height = Math.round(y2 - Math.min(0, y1)); - Texture awtText = renderAwtFont(test, width, height, bitmapFont); + GlTexture awtText = renderAwtFont(test, width, height, bitmapFont); Quad quad = new Quad(width, height); geom = new Geometry(test.name + " awt1", quad); geom.setMaterial(new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md")); diff --git a/jme3-examples/src/main/java/jme3test/gui/TestSoftwareMouse.java b/jme3-examples/src/main/java/jme3test/gui/TestSoftwareMouse.java index e1baf0b5c7..e620766e76 100644 --- a/jme3-examples/src/main/java/jme3test/gui/TestSoftwareMouse.java +++ b/jme3-examples/src/main/java/jme3test/gui/TestSoftwareMouse.java @@ -38,7 +38,7 @@ import com.jme3.math.FastMath; import com.jme3.math.Vector2f; import com.jme3.system.AppSettings; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; import com.jme3.ui.Picture; @@ -99,7 +99,7 @@ public void simpleInitApp() { flyCam.setEnabled(false); // inputManager.setCursorVisible(false); - Texture tex = assetManager.loadTexture("Interface/Logo/Cursor.png"); + GlTexture tex = assetManager.loadTexture("Interface/Logo/Cursor.png"); cursor = new Picture("cursor"); cursor.setTexture(assetManager, (Texture2D) tex, true); diff --git a/jme3-examples/src/main/java/jme3test/helloworld/HelloMaterial.java b/jme3-examples/src/main/java/jme3test/helloworld/HelloMaterial.java index a9e4b4f28a..6ad829dbb1 100644 --- a/jme3-examples/src/main/java/jme3test/helloworld/HelloMaterial.java +++ b/jme3-examples/src/main/java/jme3test/helloworld/HelloMaterial.java @@ -42,7 +42,7 @@ import com.jme3.scene.Geometry; import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Sphere; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import com.jme3.util.mikktspace.MikktspaceTangentGenerator; /** Sample 6 - how to give an object's surface a material and texture. @@ -62,7 +62,7 @@ public void simpleInitApp() { Geometry cube1Geo = new Geometry("My Textured Box", cube1Mesh); cube1Geo.setLocalTranslation(new Vector3f(-3f,1.1f,0f)); Material cube1Mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - Texture cube1Tex = assetManager.loadTexture("Interface/Logo/Monkey.jpg"); + GlTexture cube1Tex = assetManager.loadTexture("Interface/Logo/Monkey.jpg"); cube1Mat.setTexture("ColorMap", cube1Tex); cube1Geo.setMaterial(cube1Mat); rootNode.attachChild(cube1Geo); diff --git a/jme3-examples/src/main/java/jme3test/helloworld/HelloPhysics.java b/jme3-examples/src/main/java/jme3test/helloworld/HelloPhysics.java index ec566e3909..e88f9fe7c3 100644 --- a/jme3-examples/src/main/java/jme3test/helloworld/HelloPhysics.java +++ b/jme3-examples/src/main/java/jme3test/helloworld/HelloPhysics.java @@ -47,8 +47,8 @@ import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Sphere; import com.jme3.scene.shape.Sphere.TextureMode; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; /** * Example 12 - how to give objects physical properties, so they bounce and fall. @@ -132,19 +132,19 @@ public void initMaterials() { wall_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); TextureKey key = new TextureKey("Textures/Terrain/BrickWall/BrickWall.jpg"); key.setGenerateMips(true); - Texture tex = assetManager.loadTexture(key); + GlTexture tex = assetManager.loadTexture(key); wall_mat.setTexture("ColorMap", tex); stone_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); TextureKey key2 = new TextureKey("Textures/Terrain/Rock/Rock.PNG"); key2.setGenerateMips(true); - Texture tex2 = assetManager.loadTexture(key2); + GlTexture tex2 = assetManager.loadTexture(key2); stone_mat.setTexture("ColorMap", tex2); floor_mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); TextureKey key3 = new TextureKey("Textures/Terrain/Pond/Pond.jpg"); key3.setGenerateMips(true); - Texture tex3 = assetManager.loadTexture(key3); + GlTexture tex3 = assetManager.loadTexture(key3); tex3.setWrap(WrapMode.Repeat); floor_mat.setTexture("ColorMap", tex3); } diff --git a/jme3-examples/src/main/java/jme3test/helloworld/HelloTerrain.java b/jme3-examples/src/main/java/jme3test/helloworld/HelloTerrain.java index f6032faf36..c14b6e18ab 100644 --- a/jme3-examples/src/main/java/jme3test/helloworld/HelloTerrain.java +++ b/jme3-examples/src/main/java/jme3test/helloworld/HelloTerrain.java @@ -39,8 +39,8 @@ import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; public class HelloTerrain extends SimpleApplication { @@ -62,21 +62,21 @@ public void simpleInitApp() { "Textures/Terrain/splat/alphamap.png")); /* 1.2) Add GRASS texture into the red layer (Tex1). */ - Texture grass = assetManager.loadTexture( + GlTexture grass = assetManager.loadTexture( "Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); mat_terrain.setTexture("Tex1", grass); mat_terrain.setFloat("Tex1Scale", 64f); /* 1.3) Add DIRT texture into the green layer (Tex2) */ - Texture dirt = assetManager.loadTexture( + GlTexture dirt = assetManager.loadTexture( "Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); mat_terrain.setTexture("Tex2", dirt); mat_terrain.setFloat("Tex2Scale", 32f); /* 1.4) Add ROAD texture into the blue layer (Tex3) */ - Texture rock = assetManager.loadTexture( + GlTexture rock = assetManager.loadTexture( "Textures/Terrain/splat/road.jpg"); rock.setWrap(WrapMode.Repeat); mat_terrain.setTexture("Tex3", rock); @@ -84,7 +84,7 @@ public void simpleInitApp() { /* 2.a Create a custom height map from an image */ AbstractHeightMap heightmap = null; - Texture heightMapImage = assetManager.loadTexture( + GlTexture heightMapImage = assetManager.loadTexture( "Textures/Terrain/splat/mountains512.png"); heightmap = new ImageBasedHeightMap(heightMapImage.getImage()); diff --git a/jme3-examples/src/main/java/jme3test/helloworld/HelloTerrainCollision.java b/jme3-examples/src/main/java/jme3test/helloworld/HelloTerrainCollision.java index 739a93ff76..ecdffc4718 100644 --- a/jme3-examples/src/main/java/jme3test/helloworld/HelloTerrainCollision.java +++ b/jme3-examples/src/main/java/jme3test/helloworld/HelloTerrainCollision.java @@ -47,8 +47,8 @@ import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import java.util.ArrayList; import java.util.List; @@ -92,21 +92,21 @@ public void simpleInitApp() { "Textures/Terrain/splat/alphamap.png")); /* 1.2) Add GRASS texture into the red layer (Tex1). */ - Texture grass = assetManager.loadTexture( + GlTexture grass = assetManager.loadTexture( "Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); mat_terrain.setTexture("Tex1", grass); mat_terrain.setFloat("Tex1Scale", 64f); /* 1.3) Add DIRT texture into the green layer (Tex2) */ - Texture dirt = assetManager.loadTexture( + GlTexture dirt = assetManager.loadTexture( "Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); mat_terrain.setTexture("Tex2", dirt); mat_terrain.setFloat("Tex2Scale", 32f); /* 1.4) Add ROAD texture into the blue layer (Tex3) */ - Texture rock = assetManager.loadTexture( + GlTexture rock = assetManager.loadTexture( "Textures/Terrain/splat/road.jpg"); rock.setWrap(WrapMode.Repeat); mat_terrain.setTexture("Tex3", rock); @@ -114,7 +114,7 @@ public void simpleInitApp() { /* 2. Create the height map */ AbstractHeightMap heightmap = null; - Texture heightMapImage = assetManager.loadTexture( + GlTexture heightMapImage = assetManager.loadTexture( "Textures/Terrain/splat/mountains512.png"); heightmap = new ImageBasedHeightMap(heightMapImage.getImage()); heightmap.load(); diff --git a/jme3-examples/src/main/java/jme3test/light/TestConeVSFrustum.java b/jme3-examples/src/main/java/jme3test/light/TestConeVSFrustum.java index 0f2605839c..3985e23536 100644 --- a/jme3-examples/src/main/java/jme3test/light/TestConeVSFrustum.java +++ b/jme3-examples/src/main/java/jme3test/light/TestConeVSFrustum.java @@ -55,7 +55,7 @@ import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Cylinder; import com.jme3.shadow.ShadowUtil; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import com.jme3.util.TempVars; public class TestConeVSFrustum extends SimpleApplication { @@ -221,7 +221,7 @@ public void onAction(String name, boolean isPressed, float tpf) { Box boxMesh = new Box(1f, 1f, 1f); Geometry boxGeo = new Geometry("A Textured Box", boxMesh); Material boxMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - Texture monkeyTex = assetManager.loadTexture("Interface/Logo/Monkey.jpg"); + GlTexture monkeyTex = assetManager.loadTexture("Interface/Logo/Monkey.jpg"); boxMat.setTexture("ColorMap", monkeyTex); boxGeo.setMaterial(boxMat); System.err.println("light " + spotLight.getPosition()); diff --git a/jme3-examples/src/main/java/jme3test/light/TestDirectionalLightShadow.java b/jme3-examples/src/main/java/jme3test/light/TestDirectionalLightShadow.java index 0c6f437305..1e3e7acd37 100644 --- a/jme3-examples/src/main/java/jme3test/light/TestDirectionalLightShadow.java +++ b/jme3-examples/src/main/java/jme3test/light/TestDirectionalLightShadow.java @@ -54,8 +54,8 @@ import com.jme3.shadow.DirectionalLightShadowFilter; import com.jme3.shadow.DirectionalLightShadowRenderer; import com.jme3.shadow.EdgeFilteringMode; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.util.SkyFactory; import com.jme3.util.SkyFactory.EnvMapType; import com.jme3.util.mikktspace.MikktspaceTangentGenerator; @@ -134,7 +134,7 @@ public void loadScene() { matGroundL = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); - Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); matGroundL.setTexture("DiffuseMap", grass); diff --git a/jme3-examples/src/main/java/jme3test/light/TestEnvironmentMapping.java b/jme3-examples/src/main/java/jme3test/light/TestEnvironmentMapping.java index 846acfce40..85acd18e2e 100644 --- a/jme3-examples/src/main/java/jme3test/light/TestEnvironmentMapping.java +++ b/jme3-examples/src/main/java/jme3test/light/TestEnvironmentMapping.java @@ -42,7 +42,7 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Node; import com.jme3.scene.Spatial; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import com.jme3.util.SkyFactory; /** @@ -62,8 +62,8 @@ public void simpleInitApp() { TextureKey key = new TextureKey("Textures/Sky/Bright/BrightSky.dds", true); key.setGenerateMips(true); - key.setTextureTypeHint(Texture.Type.CubeMap); - final Texture tex = assetManager.loadTexture(key); + key.setTextureTypeHint(GlTexture.Type.CubeMap); + final GlTexture tex = assetManager.loadTexture(key); for (Spatial geom : buggy.getChildren()) { if (geom instanceof Geometry) { diff --git a/jme3-examples/src/main/java/jme3test/light/TestShadowBug.java b/jme3-examples/src/main/java/jme3test/light/TestShadowBug.java index acf2d6df13..1dd4c94d61 100644 --- a/jme3-examples/src/main/java/jme3test/light/TestShadowBug.java +++ b/jme3-examples/src/main/java/jme3test/light/TestShadowBug.java @@ -50,8 +50,8 @@ import com.jme3.shadow.EdgeFilteringMode; import com.jme3.shadow.PointLightShadowRenderer; import com.jme3.shadow.SpotLightShadowRenderer; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; public class TestShadowBug extends SimpleApplication { @@ -119,7 +119,7 @@ protected Geometry makeFloor() { Geometry floor = new Geometry("the Floor", box); floor.setLocalTranslation(200, -9, 200); Material matGroundL = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); - Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); matGroundL.setTexture("DiffuseMap", grass); floor.setMaterial(matGroundL); diff --git a/jme3-examples/src/main/java/jme3test/light/TestSpotLight.java b/jme3-examples/src/main/java/jme3test/light/TestSpotLight.java index ed53b0de35..d5ded001ac 100644 --- a/jme3-examples/src/main/java/jme3test/light/TestSpotLight.java +++ b/jme3-examples/src/main/java/jme3test/light/TestSpotLight.java @@ -42,7 +42,7 @@ import com.jme3.scene.Spatial; import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Sphere; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.util.mikktspace.MikktspaceTangentGenerator; public class TestSpotLight extends SimpleApplication { diff --git a/jme3-examples/src/main/java/jme3test/light/TestSpotLightShadows.java b/jme3-examples/src/main/java/jme3test/light/TestSpotLightShadows.java index 11dd519ef3..0ce70de799 100644 --- a/jme3-examples/src/main/java/jme3test/light/TestSpotLightShadows.java +++ b/jme3-examples/src/main/java/jme3test/light/TestSpotLightShadows.java @@ -48,7 +48,7 @@ import com.jme3.shadow.EdgeFilteringMode; import com.jme3.shadow.SpotLightShadowFilter; import com.jme3.shadow.SpotLightShadowRenderer; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.util.mikktspace.MikktspaceTangentGenerator; public class TestSpotLightShadows extends SimpleApplication { diff --git a/jme3-examples/src/main/java/jme3test/light/TestSpotLightTerrain.java b/jme3-examples/src/main/java/jme3test/light/TestSpotLightTerrain.java index 9fa5a0ea36..754fa38bd0 100644 --- a/jme3-examples/src/main/java/jme3test/light/TestSpotLightTerrain.java +++ b/jme3-examples/src/main/java/jme3test/light/TestSpotLightTerrain.java @@ -46,8 +46,8 @@ import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.util.SkyFactory; /** @@ -112,45 +112,45 @@ private void makeTerrain() { matTerrain.setTexture("AlphaMap_1", assetManager.loadTexture("Textures/Terrain/splat/alpha2.png")); // HEIGHTMAP image (for the terrain heightmap) - Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); + GlTexture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); // GRASS texture - Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap", grass); matTerrain.setFloat("DiffuseMap_0_scale", grassScale); // DIRT texture - Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); + GlTexture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_1", dirt); matTerrain.setFloat("DiffuseMap_1_scale", dirtScale); // ROCK texture - Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); + GlTexture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); rock.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_2", rock); matTerrain.setFloat("DiffuseMap_2_scale", rockScale); // BRICK texture - Texture brick = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg"); + GlTexture brick = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg"); brick.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_3", brick); matTerrain.setFloat("DiffuseMap_3_scale", rockScale); // RIVER ROCK texture - Texture riverRock = assetManager.loadTexture("Textures/Terrain/Pond/Pond.jpg"); + GlTexture riverRock = assetManager.loadTexture("Textures/Terrain/Pond/Pond.jpg"); riverRock.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_4", riverRock); matTerrain.setFloat("DiffuseMap_4_scale", rockScale); - Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); + GlTexture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); normalMap0.setWrap(WrapMode.Repeat); - Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); + GlTexture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); normalMap1.setWrap(WrapMode.Repeat); - Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); + GlTexture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); normalMap2.setWrap(WrapMode.Repeat); matTerrain.setTexture("NormalMap", normalMap0); matTerrain.setTexture("NormalMap_1", normalMap1); @@ -191,12 +191,12 @@ private void makeTerrain() { } private void createSky() { - Texture west = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_west.jpg"); - Texture east = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_east.jpg"); - Texture north = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_north.jpg"); - Texture south = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_south.jpg"); - Texture up = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_up.jpg"); - Texture down = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_down.jpg"); + GlTexture west = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_west.jpg"); + GlTexture east = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_east.jpg"); + GlTexture north = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_north.jpg"); + GlTexture south = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_south.jpg"); + GlTexture up = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_up.jpg"); + GlTexture down = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_down.jpg"); Spatial sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down); rootNode.attachChild(sky); diff --git a/jme3-examples/src/main/java/jme3test/light/TestTangentGen.java b/jme3-examples/src/main/java/jme3test/light/TestTangentGen.java index 2337c0bca9..17896ca47c 100644 --- a/jme3-examples/src/main/java/jme3test/light/TestTangentGen.java +++ b/jme3-examples/src/main/java/jme3test/light/TestTangentGen.java @@ -41,7 +41,7 @@ import com.jme3.scene.Mesh; import com.jme3.scene.Mesh.Mode; import com.jme3.scene.Spatial; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.scene.shape.Quad; import com.jme3.scene.shape.Sphere; import com.jme3.util.BufferUtils; diff --git a/jme3-examples/src/main/java/jme3test/material/TestGeometryShader.java b/jme3-examples/src/main/java/jme3test/material/TestGeometryShader.java index 0199d70cc2..b34c183d2b 100644 --- a/jme3-examples/src/main/java/jme3test/material/TestGeometryShader.java +++ b/jme3-examples/src/main/java/jme3test/material/TestGeometryShader.java @@ -6,7 +6,7 @@ import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import com.jme3.scene.shape.Sphere; import com.jme3.system.AppSettings; import com.jme3.util.BufferUtils; @@ -18,8 +18,8 @@ public class TestGeometryShader extends SimpleApplication { @Override public void simpleInitApp() { Mesh mesh = new Mesh(); - mesh.setBuffer(VertexBuffer.Type.Index, 1, BufferUtils.createIntBuffer(new int[]{1})); - mesh.setBuffer(VertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(new float[]{0, 0, 0})); + mesh.setBuffer(GlVertexBuffer.Type.Index, 1, BufferUtils.createIntBuffer(new int[]{1})); + mesh.setBuffer(GlVertexBuffer.Type.Position, 3, BufferUtils.createFloatBuffer(new float[]{0, 0, 0})); mesh.setMode(Mesh.Mode.Points); mesh.setBound(new BoundingBox(new Vector3f(0, 0, 0), 10, 10, 10)); mesh.updateCounts(); diff --git a/jme3-examples/src/main/java/jme3test/material/TestMaterialCompare.java b/jme3-examples/src/main/java/jme3test/material/TestMaterialCompare.java index a37855185c..895b75c123 100644 --- a/jme3-examples/src/main/java/jme3test/material/TestMaterialCompare.java +++ b/jme3-examples/src/main/java/jme3test/material/TestMaterialCompare.java @@ -37,7 +37,7 @@ import com.jme3.material.RenderState.BlendMode; import com.jme3.math.ColorRGBA; import com.jme3.system.JmeSystem; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; public class TestMaterialCompare { @@ -79,16 +79,16 @@ public static void main(String[] args) { System.out.println("TEXTURE KEYS ARE NOT EQUAL"); } - Texture tex1 = assetManager.loadTexture(tex1key); + GlTexture tex1 = assetManager.loadTexture(tex1key); mat4.setTexture("DiffuseMap", tex1); testEquality(mat4, mat5, true); // Change some stuff on the texture and compare, materials no longer equal - tex1.setWrap(Texture.WrapMode.MirroredRepeat); + tex1.setWrap(GlTexture.WrapMode.MirroredRepeat); testEquality(mat4, mat5, false); // Comparing different textures - Texture tex2 = assetManager.loadTexture("Interface/Logo/Monkey.jpg"); + GlTexture tex2 = assetManager.loadTexture("Interface/Logo/Monkey.jpg"); mat4.setTexture("DiffuseMap", tex2); testEquality(mat4, mat5, false); diff --git a/jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java b/jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java index 592663efeb..261f1c1bb5 100644 --- a/jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java +++ b/jme3-examples/src/main/java/jme3test/material/TestShaderNodes.java @@ -8,7 +8,7 @@ import com.jme3.scene.Geometry; import com.jme3.scene.shape.Box; import com.jme3.shader.Shader; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import java.util.logging.Level; import java.util.logging.Logger; @@ -25,7 +25,7 @@ public void simpleInitApp() { Logger.getLogger("com.jme3").setLevel(Level.WARNING); Box boxShape1 = new Box(1f, 1f, 1f); Geometry cube_tex = new Geometry("A Textured Box", boxShape1); - Texture tex = assetManager.loadTexture("Interface/Logo/Monkey.jpg"); + GlTexture tex = assetManager.loadTexture("Interface/Logo/Monkey.jpg"); Material mat = new Material(assetManager, "Common/MatDefs/Misc/UnshadedNodes.j3md"); mat.selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager); diff --git a/jme3-examples/src/main/java/jme3test/material/TestTessellationShader.java b/jme3-examples/src/main/java/jme3test/material/TestTessellationShader.java index a5af58bfc5..707c636153 100644 --- a/jme3-examples/src/main/java/jme3test/material/TestTessellationShader.java +++ b/jme3-examples/src/main/java/jme3test/material/TestTessellationShader.java @@ -7,7 +7,7 @@ import com.jme3.material.Material; import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import com.jme3.scene.shape.Quad; import com.jme3.system.AppSettings; import com.jme3.util.BufferUtils; @@ -26,8 +26,8 @@ public void simpleInitApp() { tessellationMaterial.setInt("TessellationFactor", tessFactor); tessellationMaterial.getAdditionalRenderState().setWireframe(true); Quad quad = new Quad(10, 10); - quad.clearBuffer(VertexBuffer.Type.Index); - quad.setBuffer(VertexBuffer.Type.Index, 4, BufferUtils.createIntBuffer(0, 1, 2, 3)); + quad.clearBuffer(GlVertexBuffer.Type.Index); + quad.setBuffer(GlVertexBuffer.Type.Index, 4, BufferUtils.createIntBuffer(0, 1, 2, 3)); quad.setMode(Mesh.Mode.Patch); quad.setPatchVertexCount(4); Geometry geometry = new Geometry("tessTest", quad); diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestAnimMigration.java b/jme3-examples/src/main/java/jme3test/model/anim/TestAnimMigration.java index 18b09edbf2..8c06940caa 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestAnimMigration.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestAnimMigration.java @@ -34,7 +34,6 @@ import com.jme3.anim.*; import com.jme3.anim.tween.Tweens; import com.jme3.anim.tween.action.*; -import com.jme3.anim.util.AnimMigrationUtils; import com.jme3.app.ChaseCameraAppState; import com.jme3.app.SimpleApplication; import com.jme3.input.KeyInput; diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestAnimSerialization.java b/jme3-examples/src/main/java/jme3test/model/anim/TestAnimSerialization.java index 512946bd01..43cefe38ca 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestAnimSerialization.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestAnimSerialization.java @@ -33,7 +33,6 @@ import com.jme3.anim.AnimComposer; import com.jme3.anim.SkinningControl; -import com.jme3.anim.util.AnimMigrationUtils; import com.jme3.app.ChaseCameraAppState; import com.jme3.app.SimpleApplication; import com.jme3.asset.plugins.FileLocator; diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java b/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java index 6c71144286..cb7dab0188 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestArmature.java @@ -170,12 +170,12 @@ public void onAction(String name, boolean isPressed, float tpf) { private Mesh createMesh() { Cylinder c = new Cylinder(30, 16, 0.1f, 1, true); - ShortBuffer jointIndex = (ShortBuffer) VertexBuffer.createBuffer(VertexBuffer.Format.UnsignedShort, 4, c.getVertexCount()); + ShortBuffer jointIndex = (ShortBuffer) GlVertexBuffer.createBuffer(GlVertexBuffer.Format.UnsignedShort, 4, c.getVertexCount()); jointIndex.rewind(); c.setMaxNumWeights(1); - FloatBuffer jointWeight = (FloatBuffer) VertexBuffer.createBuffer(VertexBuffer.Format.Float, 4, c.getVertexCount()); + FloatBuffer jointWeight = (FloatBuffer) GlVertexBuffer.createBuffer(GlVertexBuffer.Format.Float, 4, c.getVertexCount()); jointWeight.rewind(); - VertexBuffer vb = c.getBuffer(VertexBuffer.Type.Position); + GlVertexBuffer vb = c.getBuffer(GlVertexBuffer.Type.Position); FloatBuffer fvb = (FloatBuffer) vb.getData(); fvb.rewind(); for (int i = 0; i < c.getVertexCount(); i++) { @@ -194,17 +194,17 @@ private Mesh createMesh() { jointWeight.put(1f).put(0f).put(0f).put(0f); } - c.setBuffer(VertexBuffer.Type.BoneIndex, 4, jointIndex); - c.setBuffer(VertexBuffer.Type.BoneWeight, 4, jointWeight); + c.setBuffer(GlVertexBuffer.Type.BoneIndex, 4, jointIndex); + c.setBuffer(GlVertexBuffer.Type.BoneWeight, 4, jointWeight); c.updateCounts(); c.updateBound(); - VertexBuffer weightsHW = new VertexBuffer(VertexBuffer.Type.HWBoneWeight); - VertexBuffer indicesHW = new VertexBuffer(VertexBuffer.Type.HWBoneIndex); + GlVertexBuffer weightsHW = new GlVertexBuffer(GlVertexBuffer.Type.HWBoneWeight); + GlVertexBuffer indicesHW = new GlVertexBuffer(GlVertexBuffer.Type.HWBoneIndex); - indicesHW.setUsage(VertexBuffer.Usage.CpuOnly); - weightsHW.setUsage(VertexBuffer.Usage.CpuOnly); + indicesHW.setUsage(GlVertexBuffer.Usage.CpuOnly); + weightsHW.setUsage(GlVertexBuffer.Usage.CpuOnly); c.setBuffer(weightsHW); c.setBuffer(indicesHW); c.generateBindPose(); diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestAttachmentsNode.java b/jme3-examples/src/main/java/jme3test/model/anim/TestAttachmentsNode.java index 78d81f558c..8c4bfdf575 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestAttachmentsNode.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestAttachmentsNode.java @@ -36,7 +36,6 @@ import com.jme3.anim.tween.Tween; import com.jme3.anim.tween.Tweens; import com.jme3.anim.tween.action.Action; -import com.jme3.anim.util.AnimMigrationUtils; import com.jme3.app.SimpleApplication; import com.jme3.input.KeyInput; import com.jme3.input.controls.ActionListener; diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestBaseAnimSerialization.java b/jme3-examples/src/main/java/jme3test/model/anim/TestBaseAnimSerialization.java index 5a5934142c..c35104e50d 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestBaseAnimSerialization.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestBaseAnimSerialization.java @@ -194,12 +194,12 @@ public void onAction(String name, boolean isPressed, float tpf) { private Mesh createMesh() { Cylinder c = new Cylinder(30, 16, 0.1f, 1, true); - ShortBuffer jointIndex = (ShortBuffer) VertexBuffer.createBuffer(VertexBuffer.Format.UnsignedShort, 4, c.getVertexCount()); + ShortBuffer jointIndex = (ShortBuffer) GlVertexBuffer.createBuffer(GlVertexBuffer.Format.UnsignedShort, 4, c.getVertexCount()); jointIndex.rewind(); c.setMaxNumWeights(1); - FloatBuffer jointWeight = (FloatBuffer) VertexBuffer.createBuffer(VertexBuffer.Format.Float, 4, c.getVertexCount()); + FloatBuffer jointWeight = (FloatBuffer) GlVertexBuffer.createBuffer(GlVertexBuffer.Format.Float, 4, c.getVertexCount()); jointWeight.rewind(); - VertexBuffer vb = c.getBuffer(VertexBuffer.Type.Position); + GlVertexBuffer vb = c.getBuffer(GlVertexBuffer.Type.Position); FloatBuffer fvb = (FloatBuffer) vb.getData(); fvb.rewind(); for (int i = 0; i < c.getVertexCount(); i++) { @@ -218,17 +218,17 @@ private Mesh createMesh() { jointWeight.put(1f).put(0f).put(0f).put(0f); } - c.setBuffer(VertexBuffer.Type.BoneIndex, 4, jointIndex); - c.setBuffer(VertexBuffer.Type.BoneWeight, 4, jointWeight); + c.setBuffer(GlVertexBuffer.Type.BoneIndex, 4, jointIndex); + c.setBuffer(GlVertexBuffer.Type.BoneWeight, 4, jointWeight); c.updateCounts(); c.updateBound(); - VertexBuffer weightsHW = new VertexBuffer(VertexBuffer.Type.HWBoneWeight); - VertexBuffer indicesHW = new VertexBuffer(VertexBuffer.Type.HWBoneIndex); + GlVertexBuffer weightsHW = new GlVertexBuffer(GlVertexBuffer.Type.HWBoneWeight); + GlVertexBuffer indicesHW = new GlVertexBuffer(GlVertexBuffer.Type.HWBoneIndex); - indicesHW.setUsage(VertexBuffer.Usage.CpuOnly); - weightsHW.setUsage(VertexBuffer.Usage.CpuOnly); + indicesHW.setUsage(GlVertexBuffer.Usage.CpuOnly); + weightsHW.setUsage(GlVertexBuffer.Usage.CpuOnly); c.setBuffer(weightsHW); c.setBuffer(indicesHW); c.generateBindPose(); diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestCustomAnim.java b/jme3-examples/src/main/java/jme3test/model/anim/TestCustomAnim.java index 6c4b230f01..1299e3d3e8 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestCustomAnim.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestCustomAnim.java @@ -46,10 +46,10 @@ import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.Node; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Format; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.VertexBuffer.Usage; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Format; +import com.jme3.scene.GlVertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Usage; import com.jme3.scene.shape.Box; public class TestCustomAnim extends SimpleApplication { @@ -75,8 +75,8 @@ public void simpleInitApp() { Box box = new Box(1, 1, 1); - VertexBuffer weightsHW = new VertexBuffer(Type.HWBoneWeight); - VertexBuffer indicesHW = new VertexBuffer(Type.HWBoneIndex); + GlVertexBuffer weightsHW = new GlVertexBuffer(Type.HWBoneWeight); + GlVertexBuffer indicesHW = new GlVertexBuffer(Type.HWBoneIndex); indicesHW.setUsage(Usage.CpuOnly); weightsHW.setUsage(Usage.CpuOnly); box.setBuffer(weightsHW); @@ -84,13 +84,13 @@ public void simpleInitApp() { // Setup bone weight buffer FloatBuffer weights = FloatBuffer.allocate(box.getVertexCount() * 4); - VertexBuffer weightsBuf = new VertexBuffer(Type.BoneWeight); + GlVertexBuffer weightsBuf = new GlVertexBuffer(Type.BoneWeight); weightsBuf.setupData(Usage.CpuOnly, 4, Format.Float, weights); box.setBuffer(weightsBuf); // Setup bone index buffer ByteBuffer indices = ByteBuffer.allocate(box.getVertexCount() * 4); - VertexBuffer indicesBuf = new VertexBuffer(Type.BoneIndex); + GlVertexBuffer indicesBuf = new GlVertexBuffer(Type.BoneIndex); indicesBuf.setupData(Usage.CpuOnly, 4, Format.UnsignedByte, indices); box.setBuffer(indicesBuf); diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestMorph.java b/jme3-examples/src/main/java/jme3test/model/anim/TestMorph.java index f0b2161528..3fd6383105 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestMorph.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestMorph.java @@ -13,7 +13,7 @@ import com.jme3.material.Material; import com.jme3.material.RenderState; import com.jme3.scene.Geometry; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import com.jme3.scene.mesh.MorphTarget; import com.jme3.scene.shape.Box; import com.jme3.util.BufferUtils; @@ -57,7 +57,7 @@ public void simpleInitApp() { buffer.rewind(); MorphTarget target = new MorphTarget(); - target.setBuffer(VertexBuffer.Type.Position, buffer); + target.setBuffer(GlVertexBuffer.Type.Position, buffer); box.addMorphTarget(target); /* * Add a morph target that increases Y coordinates of the "right" face. @@ -77,7 +77,7 @@ public void simpleInitApp() { buffer.rewind(); final MorphTarget target2 = new MorphTarget(); - target2.setBuffer(VertexBuffer.Type.Position, buffer); + target2.setBuffer(GlVertexBuffer.Type.Position, buffer); box.addMorphTarget(target2); final Geometry g = new Geometry("box", box); @@ -141,7 +141,7 @@ public void onAnalog(String name, float value, float tpf) { @Override public void onAction(String name, boolean isPressed, float tpf) { if (name.equals("change") && isPressed) { - box.setBuffer(VertexBuffer.Type.MorphTarget0, 3, target2.getBuffer(VertexBuffer.Type.Position)); + box.setBuffer(GlVertexBuffer.Type.MorphTarget0, 3, target2.getBuffer(GlVertexBuffer.Type.Position)); } } }, "change"); diff --git a/jme3-examples/src/main/java/jme3test/model/anim/TestSingleLayerInfluenceMask.java b/jme3-examples/src/main/java/jme3test/model/anim/TestSingleLayerInfluenceMask.java index 18e38ea10f..5d21ef352f 100644 --- a/jme3-examples/src/main/java/jme3test/model/anim/TestSingleLayerInfluenceMask.java +++ b/jme3-examples/src/main/java/jme3test/model/anim/TestSingleLayerInfluenceMask.java @@ -38,7 +38,6 @@ import com.jme3.anim.SingleLayerInfluenceMask; import com.jme3.anim.SkinningControl; import com.jme3.anim.tween.action.BlendableAction; -import com.jme3.anim.util.AnimMigrationUtils; import com.jme3.app.SimpleApplication; import com.jme3.export.binary.BinaryExporter; import com.jme3.font.BitmapFont; diff --git a/jme3-examples/src/main/java/jme3test/model/shape/TestCustomMesh.java b/jme3-examples/src/main/java/jme3test/model/shape/TestCustomMesh.java index 5f205def3b..f577ad20c8 100644 --- a/jme3-examples/src/main/java/jme3test/model/shape/TestCustomMesh.java +++ b/jme3-examples/src/main/java/jme3test/model/shape/TestCustomMesh.java @@ -39,7 +39,7 @@ import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.util.BufferUtils; /** diff --git a/jme3-examples/src/main/java/jme3test/model/shape/TestCylinder.java b/jme3-examples/src/main/java/jme3test/model/shape/TestCylinder.java index bec3847faa..29b0396800 100644 --- a/jme3-examples/src/main/java/jme3test/model/shape/TestCylinder.java +++ b/jme3-examples/src/main/java/jme3test/model/shape/TestCylinder.java @@ -37,7 +37,7 @@ import com.jme3.material.Material; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Cylinder; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; public class TestCylinder extends SimpleApplication { @@ -54,8 +54,8 @@ public void simpleInitApp() { Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); TextureKey key = new TextureKey("Interface/Logo/Monkey.jpg", true); key.setGenerateMips(true); - Texture tex = assetManager.loadTexture(key); - tex.setMinFilter(Texture.MinFilter.Trilinear); + GlTexture tex = assetManager.loadTexture(key); + tex.setMinFilter(GlTexture.MinFilter.Trilinear); mat.setTexture("ColorMap", tex); geom.setMaterial(mat); diff --git a/jme3-examples/src/main/java/jme3test/niftygui/TestNiftyToMesh.java b/jme3-examples/src/main/java/jme3test/niftygui/TestNiftyToMesh.java index da428f5d5e..3846dd85aa 100644 --- a/jme3-examples/src/main/java/jme3test/niftygui/TestNiftyToMesh.java +++ b/jme3-examples/src/main/java/jme3test/niftygui/TestNiftyToMesh.java @@ -39,12 +39,12 @@ import com.jme3.renderer.ViewPort; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Box; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture.MagFilter; -import com.jme3.texture.Texture.MinFilter; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture.MagFilter; +import com.jme3.texture.GlTexture.MinFilter; import com.jme3.texture.Texture2D; -import com.jme3.texture.FrameBuffer.FrameBufferTarget; +import com.jme3.texture.GlFrameBuffer.FrameBufferTarget; import com.jme3.texture.image.ColorSpace; import de.lessvoid.nifty.Nifty; @@ -72,7 +72,7 @@ public void simpleInitApp() { niftyView.addProcessor(niftyDisplay); Texture2D depthTex = new Texture2D(1024, 768, Format.Depth); - FrameBuffer fb = new FrameBuffer(1024, 768, 1); + GlFrameBuffer fb = new GlFrameBuffer(1024, 768, 1); fb.setDepthTarget(FrameBufferTarget.newTarget(depthTex)); Texture2D tex = new Texture2D(1024, 768, Format.RGBA8); diff --git a/jme3-examples/src/main/java/jme3test/opencl/TestVertexBufferSharing.java b/jme3-examples/src/main/java/jme3test/opencl/TestVertexBufferSharing.java index 0842705b47..939fbfaf8c 100644 --- a/jme3-examples/src/main/java/jme3test/opencl/TestVertexBufferSharing.java +++ b/jme3-examples/src/main/java/jme3test/opencl/TestVertexBufferSharing.java @@ -37,7 +37,7 @@ import com.jme3.math.ColorRGBA; import com.jme3.opencl.*; import com.jme3.scene.Geometry; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import com.jme3.scene.shape.Box; import com.jme3.system.AppSettings; import com.jme3.system.JmeSystem; @@ -160,7 +160,7 @@ private void initOpenCL1() { } private void initOpenCL2() { //bind vertex buffer to OpenCL - VertexBuffer vb = geom.getMesh().getBuffer(VertexBuffer.Type.Position); + GlVertexBuffer vb = geom.getMesh().getBuffer(GlVertexBuffer.Type.Position); buffer = clContext.bindVertexBuffer(vb, MemoryAccess.READ_WRITE).register(); ws = new com.jme3.opencl.Kernel.WorkSize(geom.getMesh().getVertexCount()); } diff --git a/jme3-examples/src/main/java/jme3test/opencl/TestWriteToTexture.java b/jme3-examples/src/main/java/jme3test/opencl/TestWriteToTexture.java index aaf4cc4d06..23421f2f02 100644 --- a/jme3-examples/src/main/java/jme3test/opencl/TestWriteToTexture.java +++ b/jme3-examples/src/main/java/jme3test/opencl/TestWriteToTexture.java @@ -41,6 +41,7 @@ import com.jme3.math.Vector2f; import com.jme3.opencl.*; import com.jme3.system.AppSettings; +import com.jme3.texture.GlImage; import com.jme3.texture.Texture2D; import com.jme3.ui.Picture; import java.util.logging.Logger; @@ -80,7 +81,7 @@ public static void main(String[] args){ public void simpleInitApp() { initOpenCL1(); - tex = new Texture2D(settings.getWidth(), settings.getHeight(), 1, com.jme3.texture.Image.Format.RGBA8); + tex = new Texture2D(settings.getWidth(), settings.getHeight(), 1, GlImage.Format.RGBA8); Picture pic = new Picture("julia"); pic.setTexture(assetManager, tex, true); pic.setPosition(0, 0); diff --git a/jme3-examples/src/main/java/jme3test/post/TestCartoonEdge.java b/jme3-examples/src/main/java/jme3test/post/TestCartoonEdge.java index ff6ab497f3..b157d5f0c7 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestCartoonEdge.java +++ b/jme3-examples/src/main/java/jme3test/post/TestCartoonEdge.java @@ -46,7 +46,7 @@ import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.scene.Spatial.CullHint; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; public class TestCartoonEdge extends SimpleApplication { @@ -81,7 +81,7 @@ public void makeToonish(Spatial spatial){ Geometry g = (Geometry) spatial; Material m = g.getMaterial(); if (m.getMaterialDef().getMaterialParam("UseMaterialColors") != null) { - Texture t = assetManager.loadTexture("Textures/ColorRamp/toon.png"); + GlTexture t = assetManager.loadTexture("Textures/ColorRamp/toon.png"); // t.setMinFilter(Texture.MinFilter.NearestNoMipMaps); // t.setMagFilter(Texture.MagFilter.Nearest); m.setTexture("ColorRamp", t); diff --git a/jme3-examples/src/main/java/jme3test/post/TestContrastAdjustment.java b/jme3-examples/src/main/java/jme3test/post/TestContrastAdjustment.java index 52c125cb3e..940ef370f7 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestContrastAdjustment.java +++ b/jme3-examples/src/main/java/jme3test/post/TestContrastAdjustment.java @@ -43,7 +43,7 @@ import com.jme3.post.filters.ContrastAdjustmentFilter; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Sphere; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; /** * A {@link ContrastAdjustmentFilter} with user-controlled exponents, scales, and input range. @@ -74,7 +74,7 @@ public void simpleInitApp() { final Geometry earth = new Geometry("Earth", globe); earth.rotate(-FastMath.HALF_PI, 0f, 0f); final Material material = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - final Texture texture = assetManager.loadTexture("Textures/Sky/Earth/Earth.jpg"); + final GlTexture texture = assetManager.loadTexture("Textures/Sky/Earth/Earth.jpg"); material.setTexture("ColorMap", texture); earth.setMaterial(material); rootNode.attachChild(earth); diff --git a/jme3-examples/src/main/java/jme3test/post/TestDepthOfField.java b/jme3-examples/src/main/java/jme3test/post/TestDepthOfField.java index 3690fe9aa7..e611685cf6 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestDepthOfField.java +++ b/jme3-examples/src/main/java/jme3test/post/TestDepthOfField.java @@ -50,8 +50,8 @@ import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.util.SkyFactory; import com.jme3.util.SkyFactory.EnvMapType; import java.util.ArrayList; @@ -177,24 +177,24 @@ private void createTerrain(Node rootNode) { matRock.setBoolean("useTriPlanarMapping", false); matRock.setBoolean("WardIso", true); matRock.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png")); - Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); - Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap", grass); matRock.setFloat("DiffuseMap_0_scale", 64); - Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); + GlTexture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap_1", dirt); matRock.setFloat("DiffuseMap_1_scale", 16); - Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); + GlTexture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); rock.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap_2", rock); matRock.setFloat("DiffuseMap_2_scale", 128); - Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); + GlTexture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); normalMap0.setWrap(WrapMode.Repeat); - Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); + GlTexture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); normalMap1.setWrap(WrapMode.Repeat); - Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); + GlTexture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); normalMap2.setWrap(WrapMode.Repeat); matRock.setTexture("NormalMap", normalMap0); matRock.setTexture("NormalMap_1", normalMap1); diff --git a/jme3-examples/src/main/java/jme3test/post/TestFBOPassthrough.java b/jme3-examples/src/main/java/jme3test/post/TestFBOPassthrough.java index 72a4c875f5..3f1592a86b 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestFBOPassthrough.java +++ b/jme3-examples/src/main/java/jme3test/post/TestFBOPassthrough.java @@ -39,10 +39,10 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Node; import com.jme3.scene.shape.Sphere; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage.Format; import com.jme3.texture.Texture2D; -import com.jme3.texture.FrameBuffer.FrameBufferTarget; +import com.jme3.texture.GlFrameBuffer.FrameBufferTarget; import com.jme3.ui.Picture; /** @@ -55,7 +55,7 @@ public class TestFBOPassthrough extends SimpleApplication { final private Node fbNode = new Node("Framebuffer Node"); - private FrameBuffer fb; + private GlFrameBuffer fb; public static void main(String[] args){ TestFBOPassthrough app = new TestFBOPassthrough(); @@ -68,7 +68,7 @@ public void simpleInitApp() { int h = settings.getHeight(); //setup framebuffer - fb = new FrameBuffer(w, h, 1); + fb = new GlFrameBuffer(w, h, 1); Texture2D fbTex = new Texture2D(w, h, Format.RGBA8); fb.setDepthTarget(FrameBufferTarget.newTarget(Format.Depth)); diff --git a/jme3-examples/src/main/java/jme3test/post/TestFog.java b/jme3-examples/src/main/java/jme3test/post/TestFog.java index b7a913d79f..890fcddf81 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestFog.java +++ b/jme3-examples/src/main/java/jme3test/post/TestFog.java @@ -50,7 +50,7 @@ import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import com.jme3.util.SkyFactory; import java.util.ArrayList; import java.util.List; @@ -163,25 +163,25 @@ private void createTerrain(Node rootNode) { matRock.setBoolean("useTriPlanarMapping", false); matRock.setBoolean("WardIso", true); matRock.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png")); - Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); - Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); - grass.setWrap(Texture.WrapMode.Repeat); + GlTexture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + grass.setWrap(GlTexture.WrapMode.Repeat); matRock.setTexture("DiffuseMap", grass); matRock.setFloat("DiffuseMap_0_scale", 64); - Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); - dirt.setWrap(Texture.WrapMode.Repeat); + GlTexture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); + dirt.setWrap(GlTexture.WrapMode.Repeat); matRock.setTexture("DiffuseMap_1", dirt); matRock.setFloat("DiffuseMap_1_scale", 16); - Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); - rock.setWrap(Texture.WrapMode.Repeat); + GlTexture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); + rock.setWrap(GlTexture.WrapMode.Repeat); matRock.setTexture("DiffuseMap_2", rock); matRock.setFloat("DiffuseMap_2_scale", 128); - Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); - normalMap0.setWrap(Texture.WrapMode.Repeat); - Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); - normalMap1.setWrap(Texture.WrapMode.Repeat); - Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); - normalMap2.setWrap(Texture.WrapMode.Repeat); + GlTexture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); + normalMap0.setWrap(GlTexture.WrapMode.Repeat); + GlTexture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); + normalMap1.setWrap(GlTexture.WrapMode.Repeat); + GlTexture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); + normalMap2.setWrap(GlTexture.WrapMode.Repeat); matRock.setTexture("NormalMap", normalMap0); matRock.setTexture("NormalMap_1", normalMap1); matRock.setTexture("NormalMap_2", normalMap2); diff --git a/jme3-examples/src/main/java/jme3test/post/TestIssue1798.java b/jme3-examples/src/main/java/jme3test/post/TestIssue1798.java index 79605f29a8..a5fe510d32 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestIssue1798.java +++ b/jme3-examples/src/main/java/jme3test/post/TestIssue1798.java @@ -42,7 +42,7 @@ import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.system.AppSettings; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; /** * Test case for JME issue #1798: filtered scenes are squeezed by resizable @@ -98,7 +98,7 @@ private void makeToonish(Spatial spatial) { Geometry g = (Geometry) spatial; Material m = g.getMaterial(); if (m.getMaterialDef().getMaterialParam("UseMaterialColors") != null) { - Texture t = assetManager.loadTexture("Textures/ColorRamp/toon.png"); + GlTexture t = assetManager.loadTexture("Textures/ColorRamp/toon.png"); m.setTexture("ColorRamp", t); m.setBoolean("UseMaterialColors", true); m.setColor("Specular", ColorRGBA.Black); diff --git a/jme3-examples/src/main/java/jme3test/post/TestMultiRenderTarget.java b/jme3-examples/src/main/java/jme3test/post/TestMultiRenderTarget.java index a5422ac539..257e6175b0 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestMultiRenderTarget.java +++ b/jme3-examples/src/main/java/jme3test/post/TestMultiRenderTarget.java @@ -42,8 +42,8 @@ import com.jme3.renderer.queue.RenderQueue; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Box; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage.Format; import com.jme3.texture.Texture2D; import com.jme3.ui.Picture; @@ -54,7 +54,7 @@ * each displaying a different render of 5 colorful cubes in the main scene. */ public class TestMultiRenderTarget extends SimpleApplication implements SceneProcessor { - private FrameBuffer fb; + private GlFrameBuffer fb; /** * Displays the merged RGB (normal color) output
    *
  • from "ExtractRGB.frag" location 3,
  • @@ -174,11 +174,11 @@ public void reshape(ViewPort vp, int w, int h) { Texture2D blueTexture = new Texture2D(w, h, Format.Luminance8); // monochrome texture Texture2D rgbTexture = new Texture2D(w, h, Format.RGBA8); // color texture - fb = new FrameBuffer(w, h, 1); - fb.addColorTarget(FrameBuffer.FrameBufferTarget.newTarget(redTexture)); // location 0 - fb.addColorTarget(FrameBuffer.FrameBufferTarget.newTarget(greenTexture)); // location 1 - fb.addColorTarget(FrameBuffer.FrameBufferTarget.newTarget(blueTexture)); // location 2 - fb.addColorTarget(FrameBuffer.FrameBufferTarget.newTarget(rgbTexture)); // location 3 + fb = new GlFrameBuffer(w, h, 1); + fb.addColorTarget(GlFrameBuffer.FrameBufferTarget.newTarget(redTexture)); // location 0 + fb.addColorTarget(GlFrameBuffer.FrameBufferTarget.newTarget(greenTexture)); // location 1 + fb.addColorTarget(GlFrameBuffer.FrameBufferTarget.newTarget(blueTexture)); // location 2 + fb.addColorTarget(GlFrameBuffer.FrameBufferTarget.newTarget(rgbTexture)); // location 3 fb.setMultiTarget(true); display1.setTexture(assetManager, rgbTexture, false); @@ -220,7 +220,7 @@ public void postQueue(RenderQueue rq) { } @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { } @Override diff --git a/jme3-examples/src/main/java/jme3test/post/TestPostFilters.java b/jme3-examples/src/main/java/jme3test/post/TestPostFilters.java index a46b14655c..db49516ecc 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestPostFilters.java +++ b/jme3-examples/src/main/java/jme3test/post/TestPostFilters.java @@ -45,7 +45,7 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; import com.jme3.scene.shape.Box; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import com.jme3.util.SkyFactory; import com.jme3.util.SkyFactory.EnvMapType; import com.jme3.util.mikktspace.MikktspaceTangentGenerator; @@ -80,7 +80,7 @@ public void setupFilters() { } public void setupSkyBox() { - Texture envMap; + GlTexture envMap; if (renderer.getCaps().contains(Caps.FloatTexture)) { envMap = assetManager.loadTexture("Textures/Sky/St Peters/StPeters.hdr"); } else { diff --git a/jme3-examples/src/main/java/jme3test/post/TestPostFiltersCompositing.java b/jme3-examples/src/main/java/jme3test/post/TestPostFiltersCompositing.java index 8661ee550e..a553008231 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestPostFiltersCompositing.java +++ b/jme3-examples/src/main/java/jme3test/post/TestPostFiltersCompositing.java @@ -40,11 +40,11 @@ import com.jme3.post.filters.ColorOverlayFilter; import com.jme3.post.filters.ComposeFilter; import com.jme3.scene.Spatial; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage; import com.jme3.texture.Texture2D; -import com.jme3.texture.FrameBuffer.FrameBufferTarget; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlFrameBuffer.FrameBufferTarget; +import com.jme3.texture.GlImage.Format; import com.jme3.util.SkyFactory; /** @@ -78,8 +78,8 @@ public void simpleInitApp() { viewPort.addProcessor(fpp); //creating a frame buffer for the main viewport - FrameBuffer mainVPFrameBuffer = new FrameBuffer(cam.getWidth(), cam.getHeight(), 1); - Texture2D mainVPTexture = new Texture2D(cam.getWidth(), cam.getHeight(), Image.Format.RGBA8); + GlFrameBuffer mainVPFrameBuffer = new GlFrameBuffer(cam.getWidth(), cam.getHeight(), 1); + Texture2D mainVPTexture = new Texture2D(cam.getWidth(), cam.getHeight(), GlImage.Format.RGBA8); mainVPFrameBuffer.setDepthTarget(FrameBufferTarget.newTarget(Format.Depth)); mainVPFrameBuffer.addColorTarget(FrameBufferTarget.newTarget(mainVPTexture)); @@ -87,7 +87,7 @@ public void simpleInitApp() { // Create the post processor for the GUI viewport. final FilterPostProcessor guiFpp = new FilterPostProcessor(assetManager); - guiFpp.setFrameBufferFormat(Image.Format.RGBA8); + guiFpp.setFrameBufferFormat(GlImage.Format.RGBA8); guiFpp.addFilter(new ColorOverlayFilter(ColorRGBA.Red)); // This will compose the main viewport texture with the GUI-viewport back buffer. // Note that you can switch the order of the filters so that GUI-viewport filters are applied or not to the main viewport texture diff --git a/jme3-examples/src/main/java/jme3test/post/TestRenderToCubemap.java b/jme3-examples/src/main/java/jme3test/post/TestRenderToCubemap.java index da00ccecd5..666b51ee06 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestRenderToCubemap.java +++ b/jme3-examples/src/main/java/jme3test/post/TestRenderToCubemap.java @@ -43,11 +43,11 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; import com.jme3.scene.shape.Box; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture; import com.jme3.texture.TextureCubeMap; -import com.jme3.texture.FrameBuffer.FrameBufferTarget; +import com.jme3.texture.GlFrameBuffer.FrameBufferTarget; import com.jme3.util.SkyFactory; import com.jme3.util.SkyFactory.EnvMapType; @@ -65,7 +65,7 @@ public static void main(String[] args){ app.start(); } - public Texture setupOffscreenView(){ + public GlTexture setupOffscreenView(){ Camera offCamera = new Camera(512, 512); ViewPort offView @@ -74,7 +74,7 @@ public Texture setupOffscreenView(){ offView.setBackgroundColor(ColorRGBA.DarkGray); // create offscreen framebuffer - FrameBuffer offBuffer = new FrameBuffer(512, 512, 1); + GlFrameBuffer offBuffer = new GlFrameBuffer(512, 512, 1); //setup framebuffer's cam offCamera.setFrustumPerspective(45f, 1f, 1f, 1000f); @@ -83,8 +83,8 @@ public Texture setupOffscreenView(){ //setup framebuffer's texture TextureCubeMap offTex = new TextureCubeMap(512, 512, Format.RGBA8); - offTex.setMinFilter(Texture.MinFilter.Trilinear); - offTex.setMagFilter(Texture.MagFilter.Bilinear); + offTex.setMinFilter(GlTexture.MinFilter.Trilinear); + offTex.setMagFilter(GlTexture.MagFilter.Bilinear); //setup framebuffer to use texture offBuffer.setDepthTarget(FrameBufferTarget.newTarget(Format.Depth)); @@ -116,7 +116,7 @@ public void simpleInitApp() { cam.setLocation(new Vector3f(3, 3, 3)); cam.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y); - Texture offTex = setupOffscreenView(); + GlTexture offTex = setupOffscreenView(); Spatial sky = SkyFactory.createSky(assetManager, offTex, EnvMapType.CubeMap); rootNode.attachChild(sky); diff --git a/jme3-examples/src/main/java/jme3test/post/TestRenderToMemory.java b/jme3-examples/src/main/java/jme3test/post/TestRenderToMemory.java index 1cfa50b9a2..49c34c0a3d 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestRenderToMemory.java +++ b/jme3-examples/src/main/java/jme3test/post/TestRenderToMemory.java @@ -48,9 +48,9 @@ import com.jme3.scene.shape.Box; import com.jme3.system.AppSettings; import com.jme3.system.JmeContext.Type; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.FrameBuffer.FrameBufferTarget; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlFrameBuffer.FrameBufferTarget; +import com.jme3.texture.GlImage.Format; import com.jme3.util.BufferUtils; import com.jme3.util.Screenshots; import java.awt.Color; @@ -75,7 +75,7 @@ public class TestRenderToMemory extends SimpleApplication implements SceneProces private Geometry offBox; private float angle = 0; - private FrameBuffer offBuffer; + private GlFrameBuffer offBuffer; private ImageDisplay display; private static final int width = 800, height = 600; @@ -181,7 +181,7 @@ public void setupOffscreenView(){ offView.addProcessor(this); // create offscreen framebuffer - offBuffer = new FrameBuffer(width, height, 1); + offBuffer = new GlFrameBuffer(width, height, 1); //setup framebuffer's cam offCamera.setFrustumPerspective(45f, 1f, 1f, 1000f); @@ -253,7 +253,7 @@ public void postQueue(RenderQueue rq) { * been rendered to the framebuffer. */ @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { updateImageContents(); } diff --git a/jme3-examples/src/main/java/jme3test/post/TestRenderToTexture.java b/jme3-examples/src/main/java/jme3test/post/TestRenderToTexture.java index 88de57274d..bcf8209b41 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestRenderToTexture.java +++ b/jme3-examples/src/main/java/jme3test/post/TestRenderToTexture.java @@ -45,11 +45,11 @@ import com.jme3.renderer.ViewPort; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Box; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; -import com.jme3.texture.FrameBuffer.FrameBufferTarget; +import com.jme3.texture.GlFrameBuffer.FrameBufferTarget; /** * This test renders a scene to a texture, then displays the texture on a cube. @@ -66,7 +66,7 @@ public static void main(String[] args){ app.start(); } - public Texture setupOffscreenView(){ + public GlTexture setupOffscreenView(){ Camera offCamera = new Camera(512, 512); offView = renderManager.createPreView("Offscreen View", offCamera); @@ -74,7 +74,7 @@ public Texture setupOffscreenView(){ offView.setBackgroundColor(ColorRGBA.DarkGray); // create offscreen framebuffer - FrameBuffer offBuffer = new FrameBuffer(512, 512, 1); + GlFrameBuffer offBuffer = new GlFrameBuffer(512, 512, 1); //setup framebuffer's cam offCamera.setFrustumPerspective(45f, 1f, 1f, 1000f); @@ -83,8 +83,8 @@ public Texture setupOffscreenView(){ //setup framebuffer's texture Texture2D offTex = new Texture2D(512, 512, Format.RGBA8); - offTex.setMinFilter(Texture.MinFilter.Trilinear); - offTex.setMagFilter(Texture.MagFilter.Bilinear); + offTex.setMinFilter(GlTexture.MinFilter.Trilinear); + offTex.setMagFilter(GlTexture.MagFilter.Bilinear); //setup framebuffer to use texture offBuffer.setDepthTarget(FrameBufferTarget.newTarget(Format.Depth)); @@ -113,7 +113,7 @@ public void simpleInitApp() { //setup main scene Geometry quad = new Geometry("box", new Box(1, 1, 1)); - Texture offTex = setupOffscreenView(); + GlTexture offTex = setupOffscreenView(); Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); mat.setTexture("ColorMap", offTex); diff --git a/jme3-examples/src/main/java/jme3test/post/TestSSAO.java b/jme3-examples/src/main/java/jme3test/post/TestSSAO.java index 9275feb23d..54f23c707f 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestSSAO.java +++ b/jme3-examples/src/main/java/jme3test/post/TestSSAO.java @@ -41,7 +41,7 @@ import com.jme3.post.FilterPostProcessor; import com.jme3.post.ssao.SSAOFilter; import com.jme3.scene.Geometry; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; public class TestSSAO extends SimpleApplication { @@ -59,10 +59,10 @@ public void simpleInitApp() { flyCam.setMoveSpeed(50); Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md"); - Texture diff = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg"); - diff.setWrap(Texture.WrapMode.Repeat); - Texture norm = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall_normal.jpg"); - norm.setWrap(Texture.WrapMode.Repeat); + GlTexture diff = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg"); + diff.setWrap(GlTexture.WrapMode.Repeat); + GlTexture norm = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall_normal.jpg"); + norm.setWrap(GlTexture.WrapMode.Repeat); mat.setTexture("DiffuseMap", diff); mat.setTexture("NormalMap", norm); mat.setFloat("Shininess", 2.0f); diff --git a/jme3-examples/src/main/java/jme3test/post/TestTransparentCartoonEdge.java b/jme3-examples/src/main/java/jme3test/post/TestTransparentCartoonEdge.java index 2e2c9874f7..1fcdea9557 100644 --- a/jme3-examples/src/main/java/jme3test/post/TestTransparentCartoonEdge.java +++ b/jme3-examples/src/main/java/jme3test/post/TestTransparentCartoonEdge.java @@ -13,7 +13,7 @@ import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.scene.shape.RectangleMesh; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; public class TestTransparentCartoonEdge extends SimpleApplication { @@ -84,7 +84,7 @@ public void makeToonish(Spatial spatial){ Geometry g = (Geometry) spatial; Material m = g.getMaterial(); if (m.getMaterialDef().getName().equals("Phong Lighting")){ - Texture t = assetManager.loadTexture("Textures/ColorRamp/toon.png"); + GlTexture t = assetManager.loadTexture("Textures/ColorRamp/toon.png"); // t.setMinFilter(Texture.MinFilter.NearestNoMipMaps); // t.setMagFilter(Texture.MagFilter.Nearest); m.setTexture("ColorRamp", t); diff --git a/jme3-examples/src/main/java/jme3test/renderer/TestBackgroundImage.java b/jme3-examples/src/main/java/jme3test/renderer/TestBackgroundImage.java index c8c004a9c3..4c2987aa0b 100644 --- a/jme3-examples/src/main/java/jme3test/renderer/TestBackgroundImage.java +++ b/jme3-examples/src/main/java/jme3test/renderer/TestBackgroundImage.java @@ -31,7 +31,6 @@ */ package jme3test.renderer; -import com.jme3.anim.util.AnimMigrationUtils; import com.jme3.app.SimpleApplication; import com.jme3.light.AmbientLight; import com.jme3.light.DirectionalLight; @@ -47,7 +46,7 @@ import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.scene.shape.Quad; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import java.util.List; /** @@ -88,7 +87,7 @@ public void simpleInitApp() { * assign it to the Gui bucket, * and attach it to the background viewport. */ - Texture quadTexture + GlTexture quadTexture = assetManager.loadTexture("Interface/Logo/Monkey.png"); Material quadMaterial = new Material(assetManager, Materials.UNSHADED); quadMaterial.setTexture("ColorMap", quadTexture); diff --git a/jme3-examples/src/main/java/jme3test/renderer/TestDepthStencil.java b/jme3-examples/src/main/java/jme3test/renderer/TestDepthStencil.java index 0f333ac661..dc188f9018 100644 --- a/jme3-examples/src/main/java/jme3test/renderer/TestDepthStencil.java +++ b/jme3-examples/src/main/java/jme3test/renderer/TestDepthStencil.java @@ -45,10 +45,10 @@ import com.jme3.scene.Node; import com.jme3.scene.control.AbstractControl; import com.jme3.scene.shape.Sphere; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage.Format; import com.jme3.texture.Texture2D; -import com.jme3.texture.FrameBuffer.FrameBufferTarget; +import com.jme3.texture.GlFrameBuffer.FrameBufferTarget; import com.jme3.ui.Picture; public class TestDepthStencil extends SimpleApplication { @@ -56,7 +56,7 @@ public class TestDepthStencil extends SimpleApplication { private boolean enableStencil = false; final private Node fbNode = new Node("Framebuffer Node"); - private FrameBuffer fb; + private GlFrameBuffer fb; public static void main(String[] args){ TestDepthStencil app = new TestDepthStencil(); @@ -69,7 +69,7 @@ public void simpleInitApp() { int h = settings.getHeight(); //setup framebuffer - fb = new FrameBuffer(w, h, 1); + fb = new GlFrameBuffer(w, h, 1); Texture2D fbTex = new Texture2D(w, h, Format.RGB8); fb.setDepthTarget(FrameBufferTarget.newTarget(Format.Depth24Stencil8)); diff --git a/jme3-examples/src/main/java/jme3test/renderer/TestInconsistentCompareDetection.java b/jme3-examples/src/main/java/jme3test/renderer/TestInconsistentCompareDetection.java index dbb2eaad8d..4f44a3ec37 100644 --- a/jme3-examples/src/main/java/jme3test/renderer/TestInconsistentCompareDetection.java +++ b/jme3-examples/src/main/java/jme3test/renderer/TestInconsistentCompareDetection.java @@ -41,7 +41,7 @@ import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.scene.shape.Box; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; /** * Changes a material's texture from another thread while it is rendered. @@ -51,7 +51,7 @@ */ public class TestInconsistentCompareDetection extends SimpleApplication { - private static Texture t1, t2; + private static GlTexture t1, t2; public static void main(String[] args){ TestInconsistentCompareDetection app = new TestInconsistentCompareDetection(); @@ -104,7 +104,7 @@ public void run() { for (Spatial child : rootNode.getChildren()) { Geometry g = (Geometry) (((Node)child).getChild(0)); Material m = g.getMaterial(); - Texture curTex = m.getTextureParam("ColorMap").getTextureValue(); + GlTexture curTex = m.getTextureParam("ColorMap").getTextureValue(); if (curTex == t1) { m.setTexture("ColorMap", t2); } else { diff --git a/jme3-examples/src/main/java/jme3test/renderer/TestIssue37.java b/jme3-examples/src/main/java/jme3test/renderer/TestIssue37.java index ce276e81d9..04d8cb0d03 100644 --- a/jme3-examples/src/main/java/jme3test/renderer/TestIssue37.java +++ b/jme3-examples/src/main/java/jme3test/renderer/TestIssue37.java @@ -39,7 +39,7 @@ import com.jme3.scene.Mesh; import com.jme3.scene.shape.Box; import com.jme3.shader.VarType; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; /** * Test a Material with increasing numbers of texture parameters, to see what @@ -59,7 +59,7 @@ public class TestIssue37 extends SimpleApplication { private int numTextures; private Material manyTexturesMaterial; - private Texture testTexture; + private GlTexture testTexture; public static void main(String[] args) { Application application = new TestIssue37(); diff --git a/jme3-examples/src/main/java/jme3test/scene/instancing/TestInstanceNode.java b/jme3-examples/src/main/java/jme3test/scene/instancing/TestInstanceNode.java index 8a3da3abdd..be26b0a68a 100644 --- a/jme3-examples/src/main/java/jme3test/scene/instancing/TestInstanceNode.java +++ b/jme3-examples/src/main/java/jme3test/scene/instancing/TestInstanceNode.java @@ -66,7 +66,7 @@ public static void main(String[] args){ } private Geometry createInstance(float x, float z) { - Mesh mesh; + Mesh mesh; if (FastMath.nextRandomInt(0, 1) == 1) mesh = mesh2; else mesh = mesh1; Geometry geometry = new Geometry("randomGeom", mesh); @@ -156,7 +156,7 @@ public void simpleUpdate(float tpf) { Geometry geom = (Geometry) instance; geom.setMaterial(materials[FastMath.nextRandomInt(0, materials.length - 1)]); - Mesh mesh; + Mesh mesh; if (FastMath.nextRandomInt(0, 1) == 1) mesh = mesh2; else mesh = mesh1; geom.setMesh(mesh); diff --git a/jme3-examples/src/main/java/jme3test/stencil/TestStencilOutline.java b/jme3-examples/src/main/java/jme3test/stencil/TestStencilOutline.java index 406b15621e..3b49ff3be7 100644 --- a/jme3-examples/src/main/java/jme3test/stencil/TestStencilOutline.java +++ b/jme3-examples/src/main/java/jme3test/stencil/TestStencilOutline.java @@ -15,7 +15,7 @@ import com.jme3.scene.control.AbstractControl; import com.jme3.scene.shape.Box; import com.jme3.system.AppSettings; -import com.jme3.texture.Image; +import com.jme3.texture.GlImage; import java.util.logging.Level; import java.util.logging.Logger; @@ -131,7 +131,7 @@ public void simpleInitApp() { //This is to make sure a depth stencil format is used in the TestChooser app. FilterPostProcessor postProcessor=new FilterPostProcessor(assetManager); - postProcessor.setFrameBufferDepthFormat(Image.Format.Depth24Stencil8); + postProcessor.setFrameBufferDepthFormat(GlImage.Format.Depth24Stencil8); viewPort.addProcessor(postProcessor); postProcessor.addFilter(new BloomFilter()); } diff --git a/jme3-examples/src/main/java/jme3test/stress/TestLodGeneration.java b/jme3-examples/src/main/java/jme3test/stress/TestLodGeneration.java index 6c7e3d965d..de85acf973 100644 --- a/jme3-examples/src/main/java/jme3test/stress/TestLodGeneration.java +++ b/jme3-examples/src/main/java/jme3test/stress/TestLodGeneration.java @@ -54,7 +54,7 @@ import com.jme3.scene.Mesh; import com.jme3.scene.Node; import com.jme3.scene.Spatial; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import jme3tools.optimize.LodGenerator; @@ -217,7 +217,7 @@ private void makeLod(final LodGenerator.TriangleReductionMethod reductionMethod, public void run() { for (final Geometry geom : listGeoms) { LodGenerator lodGenerator = new LodGenerator(geom); - final VertexBuffer[] lods = lodGenerator.computeLods(reductionMethod, reductionPercentage); + final GlVertexBuffer[] lods = lodGenerator.computeLods(reductionMethod, reductionPercentage); // --- JME Thread Synchronization --- // Mesh modifications and scene graph updates must be done on the main thread. diff --git a/jme3-examples/src/main/java/jme3test/stress/TestShaderNodesStress.java b/jme3-examples/src/main/java/jme3test/stress/TestShaderNodesStress.java index 34ab16f550..10b8fea703 100644 --- a/jme3-examples/src/main/java/jme3test/stress/TestShaderNodesStress.java +++ b/jme3-examples/src/main/java/jme3test/stress/TestShaderNodesStress.java @@ -10,7 +10,7 @@ import com.jme3.renderer.queue.RenderQueue; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Quad; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import java.util.logging.Level; import java.util.logging.Logger; @@ -33,7 +33,7 @@ public void simpleInitApp() { cam.setLocation(new Vector3f(0.0f, 0.0f, 0.40647888f)); cam.setRotation(new Quaternion(0.0f, 1.0f, 0.0f, 0.0f)); - Texture tex = assetManager.loadTexture("Interface/Logo/Monkey.jpg"); + GlTexture tex = assetManager.loadTexture("Interface/Logo/Monkey.jpg"); Material mat = new Material(assetManager, "Common/MatDefs/Misc/UnshadedNodes.j3md"); //Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); diff --git a/jme3-examples/src/main/java/jme3test/terrain/PBRTerrainAdvancedTest.java b/jme3-examples/src/main/java/jme3test/terrain/PBRTerrainAdvancedTest.java index d4cd04e403..07c14715df 100644 --- a/jme3-examples/src/main/java/jme3test/terrain/PBRTerrainAdvancedTest.java +++ b/jme3-examples/src/main/java/jme3test/terrain/PBRTerrainAdvancedTest.java @@ -49,11 +49,11 @@ import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; -import com.jme3.texture.Texture.MagFilter; -import com.jme3.texture.Texture.MinFilter; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; +import com.jme3.texture.GlTexture.MagFilter; +import com.jme3.texture.GlTexture.MinFilter; import com.jme3.texture.TextureArray; import java.util.ArrayList; import java.util.List; @@ -229,31 +229,31 @@ private void setUpTerrainMaterial() { // load textures for texture arrays // These MUST all have the same dimensions and format in order to be put into a texture array. //ALBEDO MAPS - Texture dirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); - Texture darkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Color.png"); - Texture snow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Color.png"); - Texture tileRoad = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Color.png"); - Texture grass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); - Texture marble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_1K_Color.png"); - Texture gravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Color.png"); + GlTexture dirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); + GlTexture darkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Color.png"); + GlTexture snow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Color.png"); + GlTexture tileRoad = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Color.png"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); + GlTexture marble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_1K_Color.png"); + GlTexture gravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Color.png"); // NORMAL MAPS - Texture normalMapDirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground036_1K_Normal.png"); - Texture normalMapDarkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Normal.png"); - Texture normalMapSnow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Normal.png"); - Texture normalMapGravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Normal.png"); - Texture normalMapGrass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Normal.png"); - Texture normalMapMarble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_1K_Normal.png"); - Texture normalMapRoad = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Normal.png"); + GlTexture normalMapDirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground036_1K_Normal.png"); + GlTexture normalMapDarkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Normal.png"); + GlTexture normalMapSnow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Normal.png"); + GlTexture normalMapGravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Normal.png"); + GlTexture normalMapGrass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Normal.png"); + GlTexture normalMapMarble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_1K_Normal.png"); + GlTexture normalMapRoad = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Normal.png"); //PACKED METALLIC/ROUGHNESS / AMBIENT OCCLUSION / EMISSIVE INTENSITY MAPS - Texture metallicRoughnessAoEiMapDirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground036_PackedMetallicRoughnessMap.png"); - Texture metallicRoughnessAoEiMapDarkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_PackedMetallicRoughnessMap.png"); - Texture metallicRoughnessAoEiMapSnow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_PackedMetallicRoughnessMap.png"); - Texture metallicRoughnessAoEiMapGravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel_015_PackedMetallicRoughnessMap.png"); - Texture metallicRoughnessAoEiMapGrass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_PackedMetallicRoughnessMap.png"); - Texture metallicRoughnessAoEiMapMarble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_PackedMetallicRoughnessMap.png"); - Texture metallicRoughnessAoEiMapRoad = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_PackedMetallicRoughnessMap.png"); + GlTexture metallicRoughnessAoEiMapDirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground036_PackedMetallicRoughnessMap.png"); + GlTexture metallicRoughnessAoEiMapDarkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_PackedMetallicRoughnessMap.png"); + GlTexture metallicRoughnessAoEiMapSnow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_PackedMetallicRoughnessMap.png"); + GlTexture metallicRoughnessAoEiMapGravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel_015_PackedMetallicRoughnessMap.png"); + GlTexture metallicRoughnessAoEiMapGrass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_PackedMetallicRoughnessMap.png"); + GlTexture metallicRoughnessAoEiMapMarble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_PackedMetallicRoughnessMap.png"); + GlTexture metallicRoughnessAoEiMapRoad = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_PackedMetallicRoughnessMap.png"); // put all images into lists to create texture arrays. // @@ -261,9 +261,9 @@ private void setUpTerrainMaterial() { // sent to the material to tell the shader to choose that texture from // the textureArray when setting up a texture slot's mat params. // - List albedoImages = new ArrayList<>(); - List normalMapImages = new ArrayList<>(); - List metallicRoughnessAoEiMapImages = new ArrayList<>(); + List albedoImages = new ArrayList<>(); + List normalMapImages = new ArrayList<>(); + List metallicRoughnessAoEiMapImages = new ArrayList<>(); albedoImages.add(dirt.getImage()); //0 albedoImages.add(darkRock.getImage()); //1 @@ -457,7 +457,7 @@ public void simpleUpdate(float tpf) { private void setUpTerrain() { // HEIGHTMAP image (for the terrain heightmap) TextureKey hmKey = new TextureKey("Textures/Terrain/splat/mountains512.png", false); - Texture heightMapImage = assetManager.loadTexture(hmKey); + GlTexture heightMapImage = assetManager.loadTexture(hmKey); // CREATE HEIGHTMAP AbstractHeightMap heightmap = null; @@ -481,7 +481,7 @@ private void setUpTerrain() { rootNode.attachChild(terrain); } - private void setWrapAndMipMaps(Texture texture){ + private void setWrapAndMipMaps(GlTexture texture){ texture.setWrap(WrapMode.Repeat); texture.setMinFilter(MinFilter.Trilinear); texture.setMagFilter(MagFilter.Bilinear); diff --git a/jme3-examples/src/main/java/jme3test/terrain/PBRTerrainTest.java b/jme3-examples/src/main/java/jme3test/terrain/PBRTerrainTest.java index 1500257612..83dc7f633e 100644 --- a/jme3-examples/src/main/java/jme3test/terrain/PBRTerrainTest.java +++ b/jme3-examples/src/main/java/jme3test/terrain/PBRTerrainTest.java @@ -48,8 +48,8 @@ import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; /** * This test uses 'PBRTerrain.j3md' to create a terrain Material for PBR. @@ -193,7 +193,7 @@ private void setUpTerrainMaterial() { // this material also supports 'AlphaMap_2', so you can get up to 12 diffuse textures // DIRT texture, Diffuse textures 0 to 3 use the first AlphaMap - Texture dirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); + GlTexture dirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); dirt.setWrap(WrapMode.Repeat); matTerrain.setTexture("AlbedoMap_0", dirt); matTerrain.setFloat("AlbedoMap_0_scale", dirtScale); @@ -202,7 +202,7 @@ private void setUpTerrainMaterial() { //matTerrain.setInt("AfflictionMode_0", 0); // DARK ROCK texture - Texture darkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Color.png"); + GlTexture darkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Color.png"); darkRock.setWrap(WrapMode.Repeat); matTerrain.setTexture("AlbedoMap_1", darkRock); matTerrain.setFloat("AlbedoMap_1_scale", darkRockScale); @@ -211,14 +211,14 @@ private void setUpTerrainMaterial() { //matTerrain.setInt("AfflictionMode_1", 0); // SNOW texture - Texture snow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Color.png"); + GlTexture snow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Color.png"); snow.setWrap(WrapMode.Repeat); matTerrain.setTexture("AlbedoMap_2", snow); matTerrain.setFloat("AlbedoMap_2_scale", snowScale); matTerrain.setFloat("Roughness_2", 0.55f); matTerrain.setFloat("Metallic_2", 0.12f); - Texture tiles = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Color.png"); + GlTexture tiles = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Color.png"); tiles.setWrap(WrapMode.Repeat); matTerrain.setTexture("AlbedoMap_3", tiles); matTerrain.setFloat("AlbedoMap_3_scale", tileRoadScale); @@ -226,7 +226,7 @@ private void setUpTerrainMaterial() { matTerrain.setFloat("Metallic_3", 0.08f); // GRASS texture - Texture grass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); grass.setWrap(WrapMode.Repeat); matTerrain.setTexture("AlbedoMap_4", grass); matTerrain.setFloat("AlbedoMap_4_scale", grassScale); @@ -234,7 +234,7 @@ private void setUpTerrainMaterial() { matTerrain.setFloat("Metallic_4", 0); // MARBLE texture - Texture marble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_1K_Color.png"); + GlTexture marble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_1K_Color.png"); marble.setWrap(WrapMode.Repeat); matTerrain.setTexture("AlbedoMap_5", marble); matTerrain.setFloat("AlbedoMap_5_scale", marbleScale); @@ -242,32 +242,32 @@ private void setUpTerrainMaterial() { matTerrain.setFloat("Metallic_5", 0.8f); // Gravel texture - Texture gravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Color.png"); + GlTexture gravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Color.png"); gravel.setWrap(WrapMode.Repeat); matTerrain.setTexture("AlbedoMap_6", gravel); matTerrain.setFloat("AlbedoMap_6_scale", gravelScale); matTerrain.setFloat("Roughness_6", 0.9f); matTerrain.setFloat("Metallic_6", 0.07f); // NORMAL MAPS - Texture normalMapDirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground036_1K_Normal.png"); + GlTexture normalMapDirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground036_1K_Normal.png"); normalMapDirt.setWrap(WrapMode.Repeat); - Texture normalMapDarkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Normal.png"); + GlTexture normalMapDarkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Normal.png"); normalMapDarkRock.setWrap(WrapMode.Repeat); - Texture normalMapSnow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Normal.png"); + GlTexture normalMapSnow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Normal.png"); normalMapSnow.setWrap(WrapMode.Repeat); - Texture normalMapGravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Normal.png"); + GlTexture normalMapGravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Normal.png"); normalMapGravel.setWrap(WrapMode.Repeat); - Texture normalMapGrass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Normal.png"); + GlTexture normalMapGrass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Normal.png"); normalMapGrass.setWrap(WrapMode.Repeat); // Texture normalMapMarble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_1K_Normal.png"); // normalMapMarble.setWrap(WrapMode.Repeat); - Texture normalMapTiles = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Normal.png"); + GlTexture normalMapTiles = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Normal.png"); normalMapTiles.setWrap(WrapMode.Repeat); matTerrain.setTexture("NormalMap_0", normalMapDirt); @@ -365,7 +365,7 @@ public void simpleUpdate(float tpf) { private void setUpTerrain() { // HEIGHTMAP image (for the terrain heightmap) TextureKey hmKey = new TextureKey("Textures/Terrain/splat/mountains512.png", false); - Texture heightMapImage = assetManager.loadTexture(hmKey); + GlTexture heightMapImage = assetManager.loadTexture(hmKey); // CREATE HEIGHTMAP AbstractHeightMap heightmap = null; diff --git a/jme3-examples/src/main/java/jme3test/terrain/TerrainFractalGridTest.java b/jme3-examples/src/main/java/jme3test/terrain/TerrainFractalGridTest.java index 76d10a8220..d99ca8b487 100644 --- a/jme3-examples/src/main/java/jme3test/terrain/TerrainFractalGridTest.java +++ b/jme3-examples/src/main/java/jme3test/terrain/TerrainFractalGridTest.java @@ -19,8 +19,8 @@ import com.jme3.terrain.noise.filter.SmoothFilter; import com.jme3.terrain.noise.fractal.FractalSum; import com.jme3.terrain.noise.modulator.NoiseModulator; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; public class TerrainFractalGridTest extends SimpleApplication { @@ -54,19 +54,19 @@ public void simpleInitApp() { // slopeTileFactor: the texture scale for slopes // terrainSize: the total size of the terrain (used for scaling the texture) // GRASS texture - Texture grass = this.assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture grass = this.assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); mat_terrain.setTexture("region1ColorMap", grass); mat_terrain.setVector3("region1", new Vector3f(15, 200, this.grassScale)); // DIRT texture - Texture dirt = this.assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); + GlTexture dirt = this.assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); mat_terrain.setTexture("region2ColorMap", dirt); mat_terrain.setVector3("region2", new Vector3f(0, 20, this.dirtScale)); // ROCK texture - Texture rock = this.assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg"); + GlTexture rock = this.assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg"); rock.setWrap(WrapMode.Repeat); mat_terrain.setTexture("region3ColorMap", rock); mat_terrain.setVector3("region3", new Vector3f(198, 260, this.rockScale)); diff --git a/jme3-examples/src/main/java/jme3test/terrain/TerrainGridAlphaMapTest.java b/jme3-examples/src/main/java/jme3test/terrain/TerrainGridAlphaMapTest.java index a5f01976bd..cb1750f08c 100644 --- a/jme3-examples/src/main/java/jme3test/terrain/TerrainGridAlphaMapTest.java +++ b/jme3-examples/src/main/java/jme3test/terrain/TerrainGridAlphaMapTest.java @@ -38,8 +38,8 @@ import com.jme3.terrain.noise.filter.SmoothFilter; import com.jme3.terrain.noise.fractal.FractalSum; import com.jme3.terrain.noise.modulator.NoiseModulator; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import java.io.File; public class TerrainGridAlphaMapTest extends SimpleApplication { @@ -86,19 +86,19 @@ public void simpleInitApp() { material.setFloat("Shininess", 0.0f); // GRASS texture - Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); material.setTexture("DiffuseMap", grass); material.setFloat("DiffuseMap_0_scale", grassScale); // DIRT texture - Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); + GlTexture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); material.setTexture("DiffuseMap_1", dirt); material.setFloat("DiffuseMap_1_scale", dirtScale); // ROCK texture - Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); + GlTexture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); rock.setWrap(WrapMode.Repeat); material.setTexture("DiffuseMap_2", rock); material.setFloat("DiffuseMap_2_scale", rockScale); @@ -185,7 +185,7 @@ public void gridMoved(Vector3f newCenter) { @Override public void tileAttached(Vector3f cell, TerrainQuad quad) { - Texture alpha = null; + GlTexture alpha = null; try { alpha = assetManager.loadTexture("TerrainAlphaTest/alpha_" + (int)cell.x+ "_" + (int)cell.z + ".png"); } catch (Exception e) { diff --git a/jme3-examples/src/main/java/jme3test/terrain/TerrainGridTest.java b/jme3-examples/src/main/java/jme3test/terrain/TerrainGridTest.java index 762b32f2da..36b1d17120 100644 --- a/jme3-examples/src/main/java/jme3test/terrain/TerrainGridTest.java +++ b/jme3-examples/src/main/java/jme3test/terrain/TerrainGridTest.java @@ -24,8 +24,8 @@ import com.jme3.terrain.geomipmap.grid.ImageTileLoader; import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; import com.jme3.terrain.heightmap.Namer; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; public class TerrainGridTest extends SimpleApplication { @@ -64,19 +64,19 @@ public void simpleInitApp() { // slopeTileFactor: the texture scale for slopes // terrainSize: the total size of the terrain (used for scaling the texture) // GRASS texture - Texture grass = this.assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture grass = this.assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); mat_terrain.setTexture("region1ColorMap", grass); mat_terrain.setVector3("region1", new Vector3f(88, 200, this.grassScale)); // DIRT texture - Texture dirt = this.assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); + GlTexture dirt = this.assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); mat_terrain.setTexture("region2ColorMap", dirt); mat_terrain.setVector3("region2", new Vector3f(0, 90, this.dirtScale)); // ROCK texture - Texture rock = this.assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg"); + GlTexture rock = this.assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg"); rock.setWrap(WrapMode.Repeat); mat_terrain.setTexture("region3ColorMap", rock); mat_terrain.setVector3("region3", new Vector3f(198, 260, this.rockScale)); diff --git a/jme3-examples/src/main/java/jme3test/terrain/TerrainGridTileLoaderTest.java b/jme3-examples/src/main/java/jme3test/terrain/TerrainGridTileLoaderTest.java index 5f9ca109fe..3bb3e1fd0e 100644 --- a/jme3-examples/src/main/java/jme3test/terrain/TerrainGridTileLoaderTest.java +++ b/jme3-examples/src/main/java/jme3test/terrain/TerrainGridTileLoaderTest.java @@ -24,8 +24,8 @@ import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.geomipmap.grid.AssetTileLoader; import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; public class TerrainGridTileLoaderTest extends SimpleApplication { @@ -67,19 +67,19 @@ public void simpleInitApp() { // slopeTileFactor: the texture scale for slopes // terrainSize: the total size of the terrain (used for scaling the texture) // GRASS texture - Texture grass = this.assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture grass = this.assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); mat_terrain.setTexture("region1ColorMap", grass); mat_terrain.setVector3("region1", new Vector3f(88, 200, this.grassScale)); // DIRT texture - Texture dirt = this.assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); + GlTexture dirt = this.assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); mat_terrain.setTexture("region2ColorMap", dirt); mat_terrain.setVector3("region2", new Vector3f(0, 90, this.dirtScale)); // ROCK texture - Texture rock = this.assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg"); + GlTexture rock = this.assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg"); rock.setWrap(WrapMode.Repeat); mat_terrain.setTexture("region3ColorMap", rock); mat_terrain.setVector3("region3", new Vector3f(198, 260, this.rockScale)); diff --git a/jme3-examples/src/main/java/jme3test/terrain/TerrainTest.java b/jme3-examples/src/main/java/jme3test/terrain/TerrainTest.java index 4f91ebcf0e..1be4fc5b47 100644 --- a/jme3-examples/src/main/java/jme3test/terrain/TerrainTest.java +++ b/jme3-examples/src/main/java/jme3test/terrain/TerrainTest.java @@ -46,8 +46,8 @@ import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; /** * Demonstrates how to use terrain. @@ -105,22 +105,22 @@ public void simpleInitApp() { matRock.setTexture("Alpha", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png")); // HEIGHTMAP image (for the terrain heightmap) - Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); + GlTexture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); // GRASS texture - Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); matRock.setTexture("Tex1", grass); matRock.setFloat("Tex1Scale", grassScale); // DIRT texture - Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); + GlTexture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); matRock.setTexture("Tex2", dirt); matRock.setFloat("Tex2Scale", dirtScale); // ROCK texture - Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); + GlTexture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); rock.setWrap(WrapMode.Repeat); matRock.setTexture("Tex3", rock); matRock.setFloat("Tex3Scale", rockScale); diff --git a/jme3-examples/src/main/java/jme3test/terrain/TerrainTestAdvanced.java b/jme3-examples/src/main/java/jme3test/terrain/TerrainTestAdvanced.java index c69eeac3fb..3d015d9c2c 100644 --- a/jme3-examples/src/main/java/jme3test/terrain/TerrainTestAdvanced.java +++ b/jme3-examples/src/main/java/jme3test/terrain/TerrainTestAdvanced.java @@ -51,8 +51,8 @@ import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.util.SkyFactory; /** @@ -106,46 +106,46 @@ public void simpleInitApp() { // HEIGHTMAP image (for the terrain heightmap) TextureKey hmKey = new TextureKey("Textures/Terrain/splat/mountains512.png", false); - Texture heightMapImage = assetManager.loadTexture(hmKey); + GlTexture heightMapImage = assetManager.loadTexture(hmKey); // DIRT texture, Diffuse textures 0 to 3 use the first AlphaMap - Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); + GlTexture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap", dirt); matTerrain.setFloat("DiffuseMap_0_scale", dirtScale); // DARK ROCK texture - Texture darkRock = assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg"); + GlTexture darkRock = assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg"); darkRock.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_1", darkRock); matTerrain.setFloat("DiffuseMap_1_scale", darkRockScale); // PINK ROCK texture - Texture pinkRock = assetManager.loadTexture("Textures/Terrain/Rock/Rock.PNG"); + GlTexture pinkRock = assetManager.loadTexture("Textures/Terrain/Rock/Rock.PNG"); pinkRock.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_2", pinkRock); matTerrain.setFloat("DiffuseMap_2_scale", pinkRockScale); // RIVER ROCK texture, this texture will use the next alphaMap: AlphaMap_1 - Texture riverRock = assetManager.loadTexture("Textures/Terrain/Pond/Pond.jpg"); + GlTexture riverRock = assetManager.loadTexture("Textures/Terrain/Pond/Pond.jpg"); riverRock.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_3", riverRock); matTerrain.setFloat("DiffuseMap_3_scale", riverRockScale); // GRASS texture - Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_4", grass); matTerrain.setFloat("DiffuseMap_4_scale", grassScale); // BRICK texture - Texture brick = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg"); + GlTexture brick = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg"); brick.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_5", brick); matTerrain.setFloat("DiffuseMap_5_scale", brickScale); // ROAD texture - Texture road = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); + GlTexture road = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); road.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_6", road); matTerrain.setFloat("DiffuseMap_6_scale", roadScale); @@ -157,13 +157,13 @@ public void simpleInitApp() { // NORMAL MAPS - Texture normalMapDirt = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); + GlTexture normalMapDirt = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); normalMapDirt.setWrap(WrapMode.Repeat); - Texture normalMapPinkRock = assetManager.loadTexture("Textures/Terrain/Rock/Rock_normal.png"); + GlTexture normalMapPinkRock = assetManager.loadTexture("Textures/Terrain/Rock/Rock_normal.png"); normalMapPinkRock.setWrap(WrapMode.Repeat); - Texture normalMapGrass = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); + GlTexture normalMapGrass = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); normalMapGrass.setWrap(WrapMode.Repeat); - Texture normalMapRoad = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); + GlTexture normalMapRoad = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); normalMapRoad.setWrap(WrapMode.Repeat); matTerrain.setTexture("NormalMap", normalMapDirt); matTerrain.setTexture("NormalMap_1", normalMapPinkRock); @@ -296,12 +296,12 @@ public void onAction(String name, boolean pressed, float tpf) { }; private void createSky() { - Texture west = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_west.jpg"); - Texture east = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_east.jpg"); - Texture north = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_north.jpg"); - Texture south = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_south.jpg"); - Texture up = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_up.jpg"); - Texture down = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_down.jpg"); + GlTexture west = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_west.jpg"); + GlTexture east = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_east.jpg"); + GlTexture north = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_north.jpg"); + GlTexture south = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_south.jpg"); + GlTexture up = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_up.jpg"); + GlTexture down = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_down.jpg"); Spatial sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down); rootNode.attachChild(sky); diff --git a/jme3-examples/src/main/java/jme3test/terrain/TerrainTestAndroid.java b/jme3-examples/src/main/java/jme3test/terrain/TerrainTestAndroid.java index 57be5d293a..de512811bf 100644 --- a/jme3-examples/src/main/java/jme3test/terrain/TerrainTestAndroid.java +++ b/jme3-examples/src/main/java/jme3test/terrain/TerrainTestAndroid.java @@ -46,8 +46,8 @@ import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; /** * Demonstrates how to use terrain on Android. @@ -93,22 +93,22 @@ public void simpleInitApp() { matRock.setTexture("Alpha", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png")); // HEIGHTMAP image (for the terrain heightmap) - Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains128.png"); + GlTexture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains128.png"); // GRASS texture - Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); matRock.setTexture("Tex1", grass); matRock.setFloat("Tex1Scale", grassScale); // DIRT texture - Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); + GlTexture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); matRock.setTexture("Tex2", dirt); matRock.setFloat("Tex2Scale", dirtScale); // ROCK texture - Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); + GlTexture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); rock.setWrap(WrapMode.Repeat); matRock.setTexture("Tex3", rock); matRock.setFloat("Tex3Scale", rockScale); diff --git a/jme3-examples/src/main/java/jme3test/terrain/TerrainTestCollision.java b/jme3-examples/src/main/java/jme3test/terrain/TerrainTestCollision.java index 456e5cec29..78aba04165 100644 --- a/jme3-examples/src/main/java/jme3test/terrain/TerrainTestCollision.java +++ b/jme3-examples/src/main/java/jme3test/terrain/TerrainTestCollision.java @@ -59,8 +59,8 @@ import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import java.util.ArrayList; import java.util.List; @@ -104,16 +104,16 @@ public void simpleInitApp() { setupKeys(); matRock = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md"); matRock.setTexture("Alpha", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png")); - Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); - Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); matRock.setTexture("Tex1", grass); matRock.setFloat("Tex1Scale", 64f); - Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); + GlTexture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); matRock.setTexture("Tex2", dirt); matRock.setFloat("Tex2Scale", 32f); - Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); + GlTexture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); rock.setWrap(WrapMode.Repeat); matRock.setTexture("Tex3", rock); matRock.setFloat("Tex3Scale", 128f); diff --git a/jme3-examples/src/main/java/jme3test/terrain/TerrainTestModifyHeight.java b/jme3-examples/src/main/java/jme3test/terrain/TerrainTestModifyHeight.java index 4a34906fd0..105054a89a 100644 --- a/jme3-examples/src/main/java/jme3test/terrain/TerrainTestModifyHeight.java +++ b/jme3-examples/src/main/java/jme3test/terrain/TerrainTestModifyHeight.java @@ -57,8 +57,8 @@ import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import java.util.ArrayList; import java.util.List; @@ -275,25 +275,25 @@ private void createTerrain() { matTerrain.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png")); // GRASS texture - Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap", grass); matTerrain.setFloat("DiffuseMap_0_scale", grassScale); // DIRT texture - Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); + GlTexture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_1", dirt); matTerrain.setFloat("DiffuseMap_1_scale", dirtScale); // ROCK texture - Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); + GlTexture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); rock.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_2", rock); matTerrain.setFloat("DiffuseMap_2_scale", rockScale); // HEIGHTMAP image (for the terrain heightmap) - Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); + GlTexture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); AbstractHeightMap heightmap = null; try { heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.5f); diff --git a/jme3-examples/src/main/java/jme3test/terrain/TerrainTestReadWrite.java b/jme3-examples/src/main/java/jme3test/terrain/TerrainTestReadWrite.java index 06a4f95776..8ab507b7ff 100644 --- a/jme3-examples/src/main/java/jme3test/terrain/TerrainTestReadWrite.java +++ b/jme3-examples/src/main/java/jme3test/terrain/TerrainTestReadWrite.java @@ -51,8 +51,8 @@ import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import java.io.*; import java.util.logging.Level; import java.util.logging.Logger; @@ -100,33 +100,33 @@ private void createMap() { matTerrain.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png")); // HEIGHTMAP image (for the terrain heightmap) - Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); + GlTexture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); // GRASS texture - Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap", grass); matTerrain.setFloat("DiffuseMap_0_scale", grassScale); // DIRT texture - Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); + GlTexture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_1", dirt); matTerrain.setFloat("DiffuseMap_1_scale", dirtScale); // ROCK texture - Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); + GlTexture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); rock.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap_2", rock); matTerrain.setFloat("DiffuseMap_2_scale", rockScale); - Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); + GlTexture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); normalMap0.setWrap(WrapMode.Repeat); - Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); + GlTexture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); normalMap1.setWrap(WrapMode.Repeat); - Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); + GlTexture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); normalMap2.setWrap(WrapMode.Repeat); matTerrain.setTexture("NormalMap", normalMap0); matTerrain.setTexture("NormalMap_1", normalMap1); diff --git a/jme3-examples/src/main/java/jme3test/terrain/TerrainTestTile.java b/jme3-examples/src/main/java/jme3test/terrain/TerrainTestTile.java index e4d60bd915..8ba864afe8 100644 --- a/jme3-examples/src/main/java/jme3test/terrain/TerrainTestTile.java +++ b/jme3-examples/src/main/java/jme3test/terrain/TerrainTestTile.java @@ -51,8 +51,8 @@ import com.jme3.terrain.geomipmap.NeighbourFinder; import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import java.util.List; /** @@ -179,7 +179,7 @@ private class TiledTerrain extends Node implements Terrain, NeighbourFinder { matTerrain.setFloat("Shininess", 0); // GRASS texture - Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); matTerrain.setTexture("DiffuseMap", grass); matTerrain.setFloat("DiffuseMap_0_scale", grassScale); diff --git a/jme3-examples/src/main/java/jme3test/texture/TestAnisotropicFilter.java b/jme3-examples/src/main/java/jme3test/texture/TestAnisotropicFilter.java index 16ba966618..1cc8017dda 100755 --- a/jme3-examples/src/main/java/jme3test/texture/TestAnisotropicFilter.java +++ b/jme3-examples/src/main/java/jme3test/texture/TestAnisotropicFilter.java @@ -13,9 +13,9 @@ import com.jme3.renderer.Limits; import com.jme3.scene.Geometry; import com.jme3.scene.shape.RectangleMesh; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; import com.jme3.texture.image.ColorSpace; import com.jme3.texture.image.ImageRaster; @@ -52,16 +52,16 @@ public void simpleInitApp() { private static Material createCheckerBoardMaterial(AssetManager assetManager) { Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - Texture tex = createCheckerBoardTexture(); // assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.dds"); - tex.setMagFilter(Texture.MagFilter.Bilinear); - tex.setMinFilter(Texture.MinFilter.Trilinear); - tex.setWrap(Texture.WrapMode.Repeat); + GlTexture tex = createCheckerBoardTexture(); // assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.dds"); + tex.setMagFilter(GlTexture.MagFilter.Bilinear); + tex.setMinFilter(GlTexture.MinFilter.Trilinear); + tex.setWrap(GlTexture.WrapMode.Repeat); mat.setTexture("ColorMap", tex); return mat; } private static Texture2D createCheckerBoardTexture() { - Image image = new Image(Format.RGBA8, 1024, 1024, BufferUtils.createByteBuffer(1024 * 1024 * 4), ColorSpace.sRGB); + GlImage image = new GlImage(Format.RGBA8, 1024, 1024, BufferUtils.createByteBuffer(1024 * 1024 * 4), ColorSpace.sRGB); ImageRaster raster = ImageRaster.create(image); for (int y = 0; y < 1024; y++) { diff --git a/jme3-examples/src/main/java/jme3test/texture/TestImageRaster.java b/jme3-examples/src/main/java/jme3test/texture/TestImageRaster.java index 5288d4ffe6..b02b123a63 100644 --- a/jme3-examples/src/main/java/jme3test/texture/TestImageRaster.java +++ b/jme3-examples/src/main/java/jme3test/texture/TestImageRaster.java @@ -11,11 +11,11 @@ import com.jme3.renderer.queue.RenderQueue; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Quad; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.MagFilter; -import com.jme3.texture.Texture.MinFilter; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.MagFilter; +import com.jme3.texture.GlTexture.MinFilter; import com.jme3.texture.Texture2D; import com.jme3.texture.image.ImageRaster; import com.jme3.util.BufferUtils; @@ -23,11 +23,11 @@ public class TestImageRaster extends SimpleApplication { - private Image convertImage(Image image, Format newFormat) { + private GlImage convertImage(GlImage image, Format newFormat) { int width = image.getWidth(); int height = image.getHeight(); ByteBuffer data = BufferUtils.createByteBuffer( (int)Math.ceil(newFormat.getBitsPerPixel() / 8.0) * width * height); - Image convertedImage = new Image(newFormat, width, height, data,null, image.getColorSpace()); + GlImage convertedImage = new GlImage(newFormat, width, height, data,null, image.getColorSpace()); ImageRaster sourceReader = ImageRaster.create(image); ImageRaster targetWriter = ImageRaster.create(convertedImage); @@ -41,8 +41,8 @@ private Image convertImage(Image image, Format newFormat) { return convertedImage; } - private void convertAndPutImage(Image image, float posX, float posY) { - Texture tex = new Texture2D(image); + private void convertAndPutImage(GlImage image, float posX, float posY) { + GlTexture tex = new Texture2D(image); tex.setMagFilter(MagFilter.Nearest); tex.setMinFilter(MinFilter.NearestNoMipMaps); tex.setAnisotropicFilter(16); @@ -60,7 +60,7 @@ private void convertAndPutImage(Image image, float posX, float posY) { txt.setBox(new Rectangle(0, 0, 5, 5)); txt.setQueueBucket(RenderQueue.Bucket.Transparent); txt.setSize(0.5f); - txt.setText(image.getFormat().name()); + txt.setText(image.getGlFormat().name()); txt.setLocalTranslation(posX, posY, 0); rootNode.attachChild(txt); } @@ -70,11 +70,11 @@ public void simpleInitApp() { cam.setLocation(new Vector3f(16, 6, 36)); flyCam.setMoveSpeed(10); - Texture tex = assetManager.loadTexture("com/jme3/app/Monkey.png"); + GlTexture tex = assetManager.loadTexture("com/jme3/app/Monkey.png"); // Texture tex = assetManager.loadTexture("Textures/HdrTest/Memorial.hdr"); - Image originalImage = tex.getImage(); + GlImage originalImage = tex.getImage(); - Image image = convertImage(originalImage, Format.RGBA32F); + GlImage image = convertImage(originalImage, Format.RGBA32F); convertAndPutImage(image, 0, 0); image = convertImage(image, Format.RGB32F); diff --git a/jme3-examples/src/main/java/jme3test/texture/TestShaderImage.java b/jme3-examples/src/main/java/jme3test/texture/TestShaderImage.java index 52f698c99d..3b02292c2c 100644 --- a/jme3-examples/src/main/java/jme3test/texture/TestShaderImage.java +++ b/jme3-examples/src/main/java/jme3test/texture/TestShaderImage.java @@ -11,7 +11,7 @@ import com.jme3.scene.shape.Box; import com.jme3.scene.shape.Quad; import com.jme3.shader.VarType; -import com.jme3.texture.Image; +import com.jme3.texture.GlImage; import com.jme3.texture.Texture2D; import com.jme3.texture.TextureImage; @@ -37,7 +37,7 @@ public void simpleInitApp() { int width = context.getFramebufferWidth(); int height = context.getFramebufferHeight(); - Texture2D target = new Texture2D(width, height, Image.Format.RGBA8); + Texture2D target = new Texture2D(width, height, GlImage.Format.RGBA8); TextureImage targetImage = new TextureImage(target, TextureImage.Access.WriteOnly); mat.setParam("TargetImage", VarType.Image2D, targetImage); diff --git a/jme3-examples/src/main/java/jme3test/texture/TestSkyLoading.java b/jme3-examples/src/main/java/jme3test/texture/TestSkyLoading.java index 21c1b590b9..c86f790ec7 100644 --- a/jme3-examples/src/main/java/jme3test/texture/TestSkyLoading.java +++ b/jme3-examples/src/main/java/jme3test/texture/TestSkyLoading.java @@ -34,7 +34,7 @@ import com.jme3.app.SimpleApplication; import com.jme3.scene.Spatial; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import com.jme3.util.SkyFactory; public class TestSkyLoading extends SimpleApplication { @@ -46,12 +46,12 @@ public static void main(String[] args){ @Override public void simpleInitApp() { - Texture west = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_west.jpg"); - Texture east = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_east.jpg"); - Texture north = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_north.jpg"); - Texture south = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_south.jpg"); - Texture up = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_up.jpg"); - Texture down = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_down.jpg"); + GlTexture west = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_west.jpg"); + GlTexture east = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_east.jpg"); + GlTexture north = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_north.jpg"); + GlTexture south = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_south.jpg"); + GlTexture up = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_up.jpg"); + GlTexture down = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_down.jpg"); Spatial sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down); rootNode.attachChild(sky); diff --git a/jme3-examples/src/main/java/jme3test/texture/TestTexture3D.java b/jme3-examples/src/main/java/jme3test/texture/TestTexture3D.java index e3f41b6fee..03d7c5c39f 100644 --- a/jme3-examples/src/main/java/jme3test/texture/TestTexture3D.java +++ b/jme3-examples/src/main/java/jme3test/texture/TestTexture3D.java @@ -38,13 +38,13 @@ import com.jme3.math.ColorRGBA; import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.VertexBuffer.Usage; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Usage; import com.jme3.scene.shape.Sphere; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture3D; import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; @@ -73,7 +73,7 @@ public void simpleInitApp() { float[] ext = new float[]{bb.getXExtent() * 2, bb.getYExtent() * 2, bb.getZExtent() * 2}; //we need to change the UV coordinates (the sphere is assumed to be inside the 3D image box) sphere.clearBuffer(Type.TexCoord); - VertexBuffer vb = sphere.getBuffer(Type.Position); + GlVertexBuffer vb = sphere.getBuffer(Type.Position); FloatBuffer fb = (FloatBuffer) vb.getData(); float[] uvCoordinates = BufferUtils.getFloatArray(fb); //now transform the coordinates so that they are in the range of <0; 1> @@ -83,15 +83,15 @@ public void simpleInitApp() { uvCoordinates[i + 2] = (uvCoordinates[i + 2] - min.z) / ext[2]; } //apply new texture coordinates - VertexBuffer uvCoordsBuffer = new VertexBuffer(Type.TexCoord); - uvCoordsBuffer.setupData(Usage.Static, 3, com.jme3.scene.VertexBuffer.Format.Float, + GlVertexBuffer uvCoordsBuffer = new GlVertexBuffer(Type.TexCoord); + uvCoordsBuffer.setupData(Usage.Static, 3, GlVertexBuffer.Format.Float, BufferUtils.createFloatBuffer(uvCoordinates)); sphere.setBuffer(uvCoordsBuffer); //create geometry, and apply material and our 3D texture Geometry g = new Geometry("sphere", sphere); Material material = new Material(assetManager, "jme3test/texture/tex3D.j3md"); try { - Texture texture = this.getTexture(); + GlTexture texture = this.getTexture(); material.setTexture("Texture", texture); } catch (IOException e) { e.printStackTrace(); @@ -114,7 +114,7 @@ public void simpleInitApp() { /** * This method creates an RGB8 texture with the sizes of 10x10x10 pixels. */ - private Texture getTexture() throws IOException { + private GlTexture getTexture() throws IOException { ArrayList data = new ArrayList<>(1); ByteBuffer bb = BufferUtils.createByteBuffer(10 * 10 * 10 * 3);//all data must be inside one buffer for (int i = 0; i < 10; ++i) { @@ -126,6 +126,6 @@ private Texture getTexture() throws IOException { } bb.rewind(); data.add(bb); - return new Texture3D(new Image(Format.RGB8, 10, 10, 10, data, null, ColorSpace.Linear)); + return new Texture3D(new GlImage(Format.RGB8, 10, 10, 10, data, null, ColorSpace.Linear)); } } diff --git a/jme3-examples/src/main/java/jme3test/texture/TestTexture3DLoading.java b/jme3-examples/src/main/java/jme3test/texture/TestTexture3DLoading.java index ada3f1fffe..e935b12d7e 100644 --- a/jme3-examples/src/main/java/jme3test/texture/TestTexture3DLoading.java +++ b/jme3-examples/src/main/java/jme3test/texture/TestTexture3DLoading.java @@ -39,7 +39,7 @@ import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Quad; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; public class TestTexture3DLoading extends SimpleApplication { @@ -60,9 +60,9 @@ public void simpleInitApp() { Material material = new Material(assetManager, "jme3test/texture/tex3DThumb.j3md"); TextureKey key = new TextureKey("Textures/3D/flame.dds"); key.setGenerateMips(true); - key.setTextureTypeHint(Texture.Type.ThreeDimensional); + key.setTextureTypeHint(GlTexture.Type.ThreeDimensional); - Texture t = assetManager.loadTexture(key); + GlTexture t = assetManager.loadTexture(key); int rows = 4;//4 * 4 diff --git a/jme3-examples/src/main/java/jme3test/texture/TestTextureArray.java b/jme3-examples/src/main/java/jme3test/texture/TestTextureArray.java index ba56b3ff2a..21f1a36393 100644 --- a/jme3-examples/src/main/java/jme3test/texture/TestTextureArray.java +++ b/jme3-examples/src/main/java/jme3test/texture/TestTextureArray.java @@ -7,9 +7,9 @@ import com.jme3.renderer.Caps; import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; +import com.jme3.scene.GlVertexBuffer.Type; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; import com.jme3.texture.TextureArray; import com.jme3.util.BufferUtils; import java.util.ArrayList; @@ -31,13 +31,13 @@ public void simpleInitApp() } - Texture tex1 = assetManager.loadTexture( "Textures/Terrain/Pond/Pond.jpg"); - Texture tex2 = assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg"); - List images = new ArrayList<>(); + GlTexture tex1 = assetManager.loadTexture( "Textures/Terrain/Pond/Pond.jpg"); + GlTexture tex2 = assetManager.loadTexture("Textures/Terrain/Rock2/rock.jpg"); + List images = new ArrayList<>(); images.add(tex1.getImage()); images.add(tex2.getImage()); TextureArray tex3 = new TextureArray(images); - tex3.setMinFilter(Texture.MinFilter.Trilinear); + tex3.setMinFilter(GlTexture.MinFilter.Trilinear); mat.setTexture("ColorMap", tex3); Mesh m = new Mesh(); diff --git a/jme3-examples/src/main/java/jme3test/texture/TestTextureArrayCompressed.java b/jme3-examples/src/main/java/jme3test/texture/TestTextureArrayCompressed.java index 0af064f557..6cd194ae24 100644 --- a/jme3-examples/src/main/java/jme3test/texture/TestTextureArrayCompressed.java +++ b/jme3-examples/src/main/java/jme3test/texture/TestTextureArrayCompressed.java @@ -7,9 +7,9 @@ import com.jme3.renderer.Caps; import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; +import com.jme3.scene.GlVertexBuffer.Type; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; import com.jme3.texture.TextureArray; import com.jme3.util.BufferUtils; import java.util.ArrayList; @@ -31,13 +31,13 @@ public void simpleInitApp() } - Texture tex1 = assetManager.loadTexture( "Textures/Terrain/Pond/Pond_dxt5.dds"); - Texture tex2 = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall_dxt5.dds"); - List images = new ArrayList<>(); + GlTexture tex1 = assetManager.loadTexture( "Textures/Terrain/Pond/Pond_dxt5.dds"); + GlTexture tex2 = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall_dxt5.dds"); + List images = new ArrayList<>(); images.add(tex1.getImage()); images.add(tex2.getImage()); TextureArray tex3 = new TextureArray(images); - tex3.setMinFilter(Texture.MinFilter.Trilinear); + tex3.setMinFilter(GlTexture.MinFilter.Trilinear); mat.setTexture("ColorMap", tex3); Mesh m = new Mesh(); diff --git a/jme3-examples/src/main/java/jme3test/vulkan/AttributeBufferBenchmark.java b/jme3-examples/src/main/java/jme3test/vulkan/AttributeBufferBenchmark.java new file mode 100644 index 0000000000..6aa1148389 --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/vulkan/AttributeBufferBenchmark.java @@ -0,0 +1,78 @@ +package jme3test.vulkan; + +import com.jme3.vulkan.Format; +import org.lwjgl.system.MemoryUtil; + +import java.nio.ByteBuffer; + +/** + * Benchmarks interaction with ByteBuffers through {@link com.jme3.vulkan.Format.Component + * Component} compatibility, which ensure that whatever primitives are provided + * are correctly put into the buffer, and vise versa. Components will be critical + * for ensuring that Meshes don't care what MeshDescription they are using. + * + *

    From the tests, it seems that the extra overhead Components use for puts + * is minimal. Component put versus direct put pretty much take the same time.

    + */ +public class AttributeBufferBenchmark { + + public static final Format format = Format.RGBA8_SRGB; + public static final int verts = 5000; + + private static long startNanos; + + public static void main(String[] args) { + + ByteBuffer buffer = MemoryUtil.memCalloc(format.getTotalBytes() * verts); + + for (int t = 0; t < 10; t++) { + fillBufferByComponent(buffer); + fillBufferRaw(buffer); + System.out.println(); + } + + MemoryUtil.memFree(buffer); + + } + + public static void fillBufferByComponent(ByteBuffer buffer) { + float value = getTestValue(); + start(); + for (int i = 0; i < verts; i++) { + int p = i * format.getTotalBytes(); + for (Format.Component c : format) { + // The advantage of Component put is that we can provide + // any primitive type, and the Component will automatically + // convert to the correct type. + c.putFloat(buffer, p, value); + } + } + end("Fill buffer by component"); + } + + public static void fillBufferRaw(ByteBuffer buffer) { + float value = getTestValue(); + start(); + for (int i = 0; i < verts; i++) { + int p = i * format.getTotalBytes(); + for (Format.Component c : format) { + buffer.put(p + c.getOffset(), (byte)value); + } + } + end("Fill buffer raw"); + } + + public static void start() { + startNanos = System.nanoTime(); + } + + public static void end(String task) { + long end = System.nanoTime(); + System.out.println(task + ": " + ((double)Math.abs(end - startNanos) / 1_000_000) + "ms"); + } + + public static float getTestValue() { + return (float)Math.random(); + } + +} diff --git a/jme3-examples/src/main/java/jme3test/vulkan/VkGLCompatibilityTest.java b/jme3-examples/src/main/java/jme3test/vulkan/VkGLCompatibilityTest.java new file mode 100644 index 0000000000..1acc6a3bac --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/vulkan/VkGLCompatibilityTest.java @@ -0,0 +1,32 @@ +package jme3test.vulkan; + +import com.jme3.app.FlyCamAppState; +import com.jme3.app.SimpleApplication; +import com.jme3.system.AppSettings; +import com.jme3.system.vulkan.LwjglVulkanContext; + +public class VkGLCompatibilityTest extends SimpleApplication { + + public static void main(String[] args) { + VulkanHelperTest app = new VulkanHelperTest(); + AppSettings settings = new AppSettings(true); + settings.setWidth(768); + settings.setHeight(768); + settings.setRenderer("CUSTOM" + LwjglVulkanContext.class.getName()); + app.setSettings(settings); + app.setShowSettings(false); + app.start(); + } + + public VkGLCompatibilityTest() { + super(new FlyCamAppState()); + } + + @Override + public void simpleInitApp() { + + //Material material; + + } + +} diff --git a/jme3-examples/src/main/java/jme3test/vulkan/VulkanComponentsTest.java b/jme3-examples/src/main/java/jme3test/vulkan/VulkanComponentsTest.java new file mode 100644 index 0000000000..bc86e94341 --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/vulkan/VulkanComponentsTest.java @@ -0,0 +1,36 @@ +package jme3test.vulkan; + +import com.jme3.backend.Engine; +import com.jme3.backend.VulkanEngine; +import com.jme3.math.Vector3f; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.vulkan.Format; +import com.jme3.vulkan.mesh.AdaptiveMesh; +import com.jme3.vulkan.mesh.InputRate; +import com.jme3.vulkan.mesh.MeshLayout; +import com.jme3.vulkan.mesh.attribute.Position; + +public class VulkanComponentsTest { + + public static void main(String[] args) { + + Engine engine = new VulkanEngine(null, 3); + AdaptiveMesh mesh = new AdaptiveMesh(new MeshLayout(), 100, 1); + mesh.getLayout().addBinding(engine.createMeshVertexBinding(InputRate.Vertex) + .add(GlVertexBuffer.Type.Position, Format.RGB32SFloat, Position::new) + .build()); + mesh.setUsage("attr_name", GlVertexBuffer.Usage.Dynamic); + Position pos = mesh.mapAttribute(GlVertexBuffer.Type.Position); + for (int i : pos.indices()) { + pos.set(i, i, 0, 0); + } + float xExtent = 0; + for (Vector3f p : pos.iterator(new Vector3f())) { + xExtent = Math.max(xExtent, p.x); + } + pos.unmap(); + mesh.render(null, 0); + + } + +} diff --git a/jme3-examples/src/main/java/jme3test/vulkan/VulkanHelperTest.java b/jme3-examples/src/main/java/jme3test/vulkan/VulkanHelperTest.java new file mode 100644 index 0000000000..851e49d29c --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/vulkan/VulkanHelperTest.java @@ -0,0 +1,403 @@ +package jme3test.vulkan; + +import com.jme3.app.FlyCamAppState; +import com.jme3.app.SimpleApplication; +import com.jme3.material.TechniqueDef; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.Mesh; +import com.jme3.vulkan.ColorSpace; +import com.jme3.vulkan.FormatFeature; +import com.jme3.vulkan.buffers.PerFrameBuffer; +import com.jme3.vulkan.buffers.generate.MeshBufferGenerator; +import com.jme3.vulkan.pipeline.cache.PipelineCache; +import com.jme3.vulkan.pipeline.graphics.ColorBlendAttachment; +import com.jme3.vulkan.pipeline.graphics.GraphicsState; +import com.jme3.vulkan.pipeline.states.IShaderState; +import com.jme3.vulkan.render.GeometryBatch; +import com.jme3.vulkan.render.VulkanGeometryBatch; +import com.jme3.vulkan.shaderc.ShadercLoader; +import com.jme3.system.AppSettings; +import com.jme3.system.vulkan.LwjglVulkanContext; +import com.jme3.texture.ImageView; +import com.jme3.util.natives.Native; +import com.jme3.vulkan.Format; +import com.jme3.vulkan.VulkanInstance; +import com.jme3.vulkan.buffers.BufferUsage; +import com.jme3.vulkan.buffers.OldPersistentBuffer; +import com.jme3.vulkan.commands.CommandBuffer; +import com.jme3.vulkan.commands.CommandPool; +import com.jme3.vulkan.descriptors.*; +import com.jme3.vulkan.devices.DeviceFeature; +import com.jme3.vulkan.devices.DeviceFilter; +import com.jme3.vulkan.devices.GeneralPhysicalDevice; +import com.jme3.vulkan.devices.LogicalDevice; +import com.jme3.vulkan.frames.UpdateFrame; +import com.jme3.vulkan.frames.UpdateFrameManager; +import com.jme3.vulkan.images.*; +import com.jme3.vulkan.material.MatrixTransformMaterial; +import com.jme3.vulkan.material.TestMaterial; +import com.jme3.vulkan.memory.MemoryProp; +import com.jme3.vulkan.memory.MemorySize; +import com.jme3.vulkan.mesh.*; +import com.jme3.vulkan.pass.Attachment; +import com.jme3.vulkan.pass.Subpass; +import com.jme3.vulkan.pass.RenderPass; +import com.jme3.vulkan.pipeline.*; +import com.jme3.vulkan.shader.ShaderStage; +import com.jme3.vulkan.surface.Surface; +import com.jme3.vulkan.surface.Swapchain; +import com.jme3.vulkan.surface.SwapchainUpdater; +import com.jme3.vulkan.sync.Fence; +import com.jme3.vulkan.sync.Semaphore; +import com.jme3.vulkan.sync.SyncGroup; +import com.jme3.vulkan.update.BasicCommandBatch; +import com.jme3.vulkan.update.CommandBatch; +import com.jme3.vulkan.update.CommandRunner; +import com.jme3.vulkan.util.Flag; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.*; + +import java.util.Comparator; +import java.util.logging.Level; + +import static org.lwjgl.vulkan.VK13.*; + +public class VulkanHelperTest extends SimpleApplication implements SwapchainUpdater { + + private VulkanInstance instance; + private Surface surface; + private LogicalDevice device; + private Swapchain swapchain; + private RenderPass renderPass; + private CommandPool graphicsPool; + private PipelineCache pipelineCache; + private UpdateFrameManager frames; + private boolean swapchainResizeFlag = false; + private boolean applicationStopped = false; + + // framebuffer + private VulkanImageView depthView; + + // commands + private CommandBatch graphics, perFrameData, sharedData; + private Fence sharedDataFence; + + // render + private final Comparator batchSorter = (o1, o2) -> { + int compare = Long.compare(o1.getPipeline().getSortId(), o2.getPipeline().getSortId()); + if (compare != 0) return compare; + return Float.compare(o1.computeDistanceSq(), o2.computeDistanceSq()); + }; + + public static void main(String[] args) { + VulkanHelperTest app = new VulkanHelperTest(); + AppSettings settings = new AppSettings(true); + settings.setFrameRate(0); + settings.setVSync(false); + settings.setWidth(768); + settings.setHeight(768); + settings.setRenderer("CUSTOM" + LwjglVulkanContext.class.getName()); + app.setSettings(settings); + app.setShowSettings(false); + app.start(); + } + + public VulkanHelperTest() { + super(new FlyCamAppState()); + } + + @Override + public void simpleInitApp() { + + assetManager.registerLoader(ShadercLoader.class, "glsl"); + assetManager.registerLoader(VulkanImageLoader.class, "png", "jpg"); + flyCam.setMoveSpeed(5f); + flyCam.setDragToRotate(true); + + long window = ((LwjglVulkanContext)context).getWindowHandle(); + + instance = new VulkanInstance(VK_API_VERSION_1_3); + try (VulkanInstance.Builder i = instance.build()) { + i.addGlfwExtensions(); + i.addDebugExtension(); + i.addLunarGLayer(); + i.setApplicationName(VulkanHelperTest.class.getSimpleName()); + i.setApplicationVersion(1, 0, 0); + } + instance.createLogger(Level.SEVERE); + + // surface + surface = new Surface(instance, window); + + // logical device + device = new LogicalDevice<>(instance); + try (LogicalDevice.Builder d = device.build(id -> new GeneralPhysicalDevice(instance, surface, id))) { + d.addFilter(surface); + d.addFilter(DeviceFilter.swapchain(surface)); + d.addCriticalExtension(KHRSwapchain.VK_KHR_SWAPCHAIN_EXTENSION_NAME); + d.addFeature(DeviceFeature.anisotropy(1f, true)); + } + GeneralPhysicalDevice physDevice = device.getPhysicalDevice(); + + graphicsPool = device.getLongTermPool(physDevice.getGraphics()); + + // swapchain + swapchain = new Swapchain(device, surface); + swapchain.build(s -> { + s.addQueue(physDevice.getGraphics()); + s.addQueue(physDevice.getPresent()); + s.selectFormat(Swapchain.format(Format.B8G8R8A8_SRGB, ColorSpace.KhrSrgbNonlinear)); + s.selectMode(Swapchain.PresentMode.Mailbox); + s.selectExtentByWindow(); + s.selectImageCount(2); + }); + + DescriptorPool descriptorPool = new DescriptorPool(device, 10, + new PoolSize(Descriptor.UniformBuffer, 3), + new PoolSize(Descriptor.StorageBuffer, 4), + new PoolSize(Descriptor.CombinedImageSampler, 2)); + + CommandPool initPool = device.getShortTermPool(physDevice.getGraphics()); + CommandBuffer initCommands = initPool.allocateTransientCommandBuffer(); + initCommands.begin(); + + // depth texture + depthView = createDepthAttachment(initCommands); + + initCommands.endAndSubmit(SyncGroup.ASYNC); + initCommands.getPool().getQueue().waitIdle(); + + // render pass + renderPass = new RenderPass(device); + try (RenderPass.Builder p = renderPass.build()) { + Attachment color = p.createAttachment(swapchain.getFormat(), VK_SAMPLE_COUNT_1_BIT, a -> { + a.setLoad(VulkanImage.Load.Clear); + a.setStore(VulkanImage.Store.Store); + a.setStencilLoad(VulkanImage.Load.DontCare); + a.setStencilStore(VulkanImage.Store.DontCare); + a.setInitialLayout(VulkanImage.Layout.Undefined); + a.setFinalLayout(VulkanImage.Layout.PresentSrc); + a.setClearColor(ColorRGBA.Black); + }); + Attachment depth = p.createAttachment(depthView.getImage().getFormat(), VK_SAMPLE_COUNT_1_BIT, a -> { + a.setLoad(VulkanImage.Load.Clear); + a.setStore(VulkanImage.Store.DontCare); + a.setStencilLoad(VulkanImage.Load.DontCare); + a.setStencilStore(VulkanImage.Store.DontCare); + a.setInitialLayout(VulkanImage.Layout.Undefined); + a.setFinalLayout(VulkanImage.Layout.DepthStencilAttachmentOptimal); + a.setClearDepth(1f); + }); + Subpass subpass = p.createSubpass(PipelineBindPoint.Graphics, s -> { + s.addColorAttachment(color.createReference(VulkanImage.Layout.ColorAttachmentOptimal)); + s.setDepthStencilAttachment(depth.createReference(VulkanImage.Layout.DepthStencilAttachmentOptimal)); + }); + p.createDependency(null, subpass, d -> { + d.setSrcStageMask(Flag.of(PipelineStage.ColorAttachmentOutput, PipelineStage.EarlyFragmentTests)); + d.setSrcAccessMask(Flag.of(subpass.getPosition())); + d.setDstStageMask(Flag.of(PipelineStage.ColorAttachmentOutput, PipelineStage.EarlyFragmentTests)); + d.setDstAccessMask(Flag.of(Access.ColorAttachmentWrite, Access.DepthStencilAttachmentWrite)); + }); + } + + swapchain.createFrameBuffers(renderPass, depthView); + + // mesh description + MeshDescription meshDesc = new MeshDescription(); + try (MeshDescription.Builder m = meshDesc.build()) { + OldVertexBinding b = m.addBinding(InputRate.Vertex); + m.addAttribute(b, GlVertexBuffer.Type.Position.getName(), Format.RGB32SFloat, 0); + m.addAttribute(b, GlVertexBuffer.Type.TexCoord.getName(), Format.RG32SFloat, 1); + m.addAttribute(b, GlVertexBuffer.Type.Normal.getName(), Format.RGB32SFloat, 2); + } + + TestMaterial material = new TestMaterial(descriptorPool); + PipelineLayout pipelineLayout = new PipelineLayout(device); + try (PipelineLayout.Builder p = pipelineLayout.build()) { + p.addMaterial(material); + } + + GraphicsState state = new GraphicsState(); + state.setLayout(pipelineLayout); + state.setSubpass(renderPass.getSubpasses().get(0)); + state.addShader(new IShaderState("Shaders/VulkanVertTest.glsl", "main", ShaderStage.Vertex)); + state.addShader(new IShaderState("Shaders/VulkanFragTest.glsl", "main", ShaderStage.Fragment)); + state.setBlendAttachment(0, new ColorBlendAttachment()); + state.setCullMode(CullMode.None); + state.setViewPort(0); + state.setScissor(0); + state.addDynamic(DynamicState.ViewPort); + state.addDynamic(DynamicState.Scissor); + material.setTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, state); + + frames = new UpdateFrameManager<>(2, Frame::new); + + // material color texture + VulkanImage image = assetManager.loadAsset(VulkanImageLoader.key(initPool, "Common/Textures/MissingTexture.png")); + VulkanImageView imgView = new VulkanImageView(image, ImageView.Type.TwoDemensional); + try (VulkanImageView.Builder i = imgView.build()) { + i.setAspect(VulkanImage.Aspect.Color); + } + VulkanTexture texture = new VulkanTexture(device, imgView); + try (Sampler.Builder t = texture.build()) { + t.setMinMagFilters(Filter.Linear, Filter.Linear); + t.setEdgeModes(AddressMode.Repeat); + t.setMipmapMode(MipmapMode.Linear); + } + + graphics = new BasicCommandBatch(); + perFrameData = new BasicCommandBatch(); + sharedData = new BasicCommandBatch(); + sharedDataFence = new Fence(device, true); + + // set material parameters + material.getBaseColorMap().set(texture); + + // create geometry + Mesh m = new MyCustomMesh(meshDesc, new MeshBufferGenerator(device, frames, null, sharedData), + Vector3f.UNIT_Z, Vector3f.UNIT_Y, 1f, 1f, 0.5f, 0.5f); + MatrixTransformMaterial t = new MatrixTransformMaterial(descriptorPool); + PerFrameBuffer transformBuffer = new PerFrameBuffer<>(frames, MemorySize.floats(16), + s -> new OldPersistentBuffer(device, s)); + for (OldPersistentBuffer buf : transformBuffer) { + try (OldPersistentBuffer.Builder b = buf.build()) { + b.setUsage(BufferUsage.Uniform); + } + } + t.getTransforms().set(transformBuffer); + Geometry geometry = new Geometry("geometry", m, t); + geometry.setMaterial(material); + rootNode.attachChild(geometry); + + pipelineCache = new PipelineCache(device, assetManager); + + } + + @Override + public void stop() { + applicationStopped = true; + device.waitIdle(); + Native.get().clear(); // destroy all native objects + super.stop(); + } + + @Override + public boolean swapchainOutOfDate(Swapchain swapchain, int imageAcquireCode) { + if (swapchainResizeFlag || imageAcquireCode == KHRSwapchain.VK_ERROR_OUT_OF_DATE_KHR || imageAcquireCode == KHRSwapchain.VK_SUBOPTIMAL_KHR) { + swapchainResizeFlag = false; + swapchain.update(); + CommandBuffer cmd = device.getShortTermPool(device.getPhysicalDevice().getGraphics()).allocateTransientCommandBuffer(); + cmd.begin(); + depthView = createDepthAttachment(cmd); + cmd.endAndSubmit(SyncGroup.ASYNC); + cmd.getPool().getQueue().waitIdle(); + swapchain.createFrameBuffers(renderPass, depthView); + return true; + } + if (imageAcquireCode != VK_SUCCESS) { + throw new RuntimeException("Failed to acquire swapchain image."); + } + return false; + } + + @Override + public void reshape(int w, int h) { + swapchainResizeFlag = true; + } + + @Override + public void simpleUpdate(float tpf) { + frames.update(tpf); + } + + private VulkanImageView createDepthAttachment(CommandBuffer cmd) { + Format depthFormat = device.getPhysicalDevice().findSupportedFormat( + VulkanImage.Tiling.Optimal, FormatFeature.DepthStencilAttachment, + Format.Depth32SFloat, Format.Depth32SFloat_Stencil8UInt, Format.Depth24UNorm_Stencil8UInt); + BasicVulkanImage image = new BasicVulkanImage(device, VulkanImage.Type.TwoDemensional); + try (BasicVulkanImage.Builder i = image.build()) { + i.setSize(swapchain.getExtent().x, swapchain.getExtent().y); + i.setFormat(depthFormat); + i.setTiling(VulkanImage.Tiling.Optimal); + i.setUsage(ImageUsage.DepthStencilAttachment); + i.setMemoryProps(MemoryProp.DeviceLocal); + } + VulkanImageView view = new VulkanImageView(image, ImageView.Type.TwoDemensional); + try (VulkanImageView.Builder v = view.build()) { + v.setAspect(VulkanImage.Aspect.Depth); + } + image.transitionLayout(cmd, VulkanImage.Layout.DepthStencilAttachmentOptimal); + return view; + } + + private class Frame implements UpdateFrame { + + // render manager + private final CommandBuffer graphicsCommands = graphicsPool.allocateCommandBuffer(); + private final CommandRunner perFrameDataCommands, sharedDataCommands; + private final Semaphore imageAvailable = new Semaphore(device, PipelineStage.ColorAttachmentOutput); + private final Semaphore perFrameDataFinished = new Semaphore(device, PipelineStage.TopOfPipe); + private final Semaphore sharedDataFinished = new Semaphore(device, PipelineStage.TopOfPipe); + private final Semaphore renderFinished = new Semaphore(device); + private final Fence inFlight = new Fence(device, true); + + public Frame(int frame) { + perFrameDataCommands = new CommandRunner(frame, graphicsPool.allocateCommandBuffer(), perFrameData); + sharedDataCommands = new CommandRunner(frame, graphicsPool.allocateCommandBuffer(), sharedData); + } + + @Override + public void update(UpdateFrameManager frames, float tpf) { + + // block until this frame has fully completed previous rendering commands + if (applicationStopped) return; + inFlight.block(5000); + if (applicationStopped) return; + + // get swapchain image to present with + Swapchain.PresentImage image = swapchain.acquireNextImage(VulkanHelperTest.this, imageAvailable, null, 5000); + if (image == null) { + return; // no image available: skip rendering this frame + } + inFlight.reset(); + + // update viewport camera + viewPort.getCamera().update(); + viewPort.getCamera().updateViewProjection(); + + // update data + sharedDataFence.block(5000); + sharedDataCommands.run(sharedDataFence.toGroup(), c -> sharedDataFence.reset()); + perFrameDataCommands.run(perFrameDataFinished.toGroupSignal()); + + // begin rendering + graphicsCommands.resetAndBegin(); + renderPass.begin(graphicsCommands, image.getFrameBuffer()); + + // viewport and scissor + try (MemoryStack stack = MemoryStack.stackPush()) { + viewPort.getCamera().setViewPortAndScissor(stack, graphicsCommands); + } + + // run misc graphics commands via CommandBatch + graphics.run(graphicsCommands, frames.getCurrentFrame()); + + // render geometry under rootNode + GeometryBatch batch = new VulkanGeometryBatch(pipelineCache, cam, batchSorter); + batch.addAll(rootNode); + batch.render(graphicsCommands); + + // end rendering + renderPass.end(graphicsCommands); + graphicsCommands.endAndSubmit(new SyncGroup(new Semaphore[] {imageAvailable, perFrameDataFinished, + sharedDataFinished}, renderFinished, inFlight)); + swapchain.present(device.getPhysicalDevice().getPresent(), image, renderFinished.toGroupWait()); + + } + + } + +} diff --git a/jme3-examples/src/main/java/jme3test/water/TestMultiPostWater.java b/jme3-examples/src/main/java/jme3test/water/TestMultiPostWater.java index 2784d59fa1..f71cabd154 100644 --- a/jme3-examples/src/main/java/jme3test/water/TestMultiPostWater.java +++ b/jme3-examples/src/main/java/jme3test/water/TestMultiPostWater.java @@ -47,8 +47,8 @@ import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.texture.Texture2D; import com.jme3.util.SkyFactory; import com.jme3.util.SkyFactory.EnvMapType; @@ -166,24 +166,24 @@ private void createTerrain(Node rootNode) { matRock.setBoolean("useTriPlanarMapping", false); matRock.setBoolean("WardIso", true); matRock.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png")); - Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/pools.png"); - Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/pools.png"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap", grass); matRock.setFloat("DiffuseMap_0_scale", 64); - Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); + GlTexture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap_1", dirt); matRock.setFloat("DiffuseMap_1_scale", 16); - Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); + GlTexture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); rock.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap_2", rock); matRock.setFloat("DiffuseMap_2_scale", 128); - Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); + GlTexture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); normalMap0.setWrap(WrapMode.Repeat); - Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); + GlTexture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); normalMap1.setWrap(WrapMode.Repeat); - Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); + GlTexture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); normalMap2.setWrap(WrapMode.Repeat); matRock.setTexture("NormalMap", normalMap0); matRock.setTexture("NormalMap_1", normalMap1); diff --git a/jme3-examples/src/main/java/jme3test/water/TestPostWater.java b/jme3-examples/src/main/java/jme3test/water/TestPostWater.java index 7f6117bc0c..08d3d8cc7b 100644 --- a/jme3-examples/src/main/java/jme3test/water/TestPostWater.java +++ b/jme3-examples/src/main/java/jme3test/water/TestPostWater.java @@ -61,8 +61,8 @@ import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.texture.Texture2D; import com.jme3.util.SkyFactory; import com.jme3.util.SkyFactory.EnvMapType; @@ -231,7 +231,7 @@ private void createWaterFilter() { private void createTerrain(Node mainScene) { Material matRock = createTerrainMaterial(); - Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); + GlTexture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); AbstractHeightMap heightmap = null; try { heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f); @@ -276,7 +276,7 @@ private Material createTerrainMaterial() { } private void setTexture(String texture, Material mat, String param) { - Texture tex = assetManager.loadTexture(texture); + GlTexture tex = assetManager.loadTexture(texture); tex.setWrap(WrapMode.Repeat); mat.setTexture(param, tex); } diff --git a/jme3-ios/src/main/java/com/jme3/system/ios/IosImageLoader.java b/jme3-ios/src/main/java/com/jme3/system/ios/IosImageLoader.java index 07a881ee5f..0a9fa89461 100644 --- a/jme3-ios/src/main/java/com/jme3/system/ios/IosImageLoader.java +++ b/jme3-ios/src/main/java/com/jme3/system/ios/IosImageLoader.java @@ -34,8 +34,8 @@ import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetLoader; import com.jme3.asset.TextureKey; -import com.jme3.texture.Image; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; import java.io.IOException; import java.io.InputStream; @@ -48,7 +48,7 @@ public class IosImageLoader implements AssetLoader { @Override public Object load(AssetInfo info) throws IOException { boolean flip = ((TextureKey) info.getKey()).isFlipY(); - Image img = null; + GlImage img = null; InputStream in = null; try { in = info.openStream(); @@ -68,5 +68,5 @@ public Object load(AssetInfo info) throws IOException { * @param inputStream the InputStream to load the image data from * @return the loaded Image */ - private static native Image loadImageData(Format format, boolean flipY, InputStream inputStream); + private static native GlImage loadImageData(Format format, boolean flipY, InputStream inputStream); } diff --git a/jme3-jbullet/src/main/java/com/jme3/bullet/animation/RagUtils.java b/jme3-jbullet/src/main/java/com/jme3/bullet/animation/RagUtils.java index 0a51b894e8..b841b197a0 100644 --- a/jme3-jbullet/src/main/java/com/jme3/bullet/animation/RagUtils.java +++ b/jme3-jbullet/src/main/java/com/jme3/bullet/animation/RagUtils.java @@ -43,7 +43,7 @@ import com.jme3.scene.Mesh; import com.jme3.scene.Node; import com.jme3.scene.Spatial; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import com.jme3.scene.control.Control; import java.io.IOException; import java.nio.Buffer; @@ -98,7 +98,7 @@ private RagUtils() { * @return a new map from bone/torso names to sets of vertex coordinates */ static Map coordsMap(Mesh[] meshes, - String[] managerMap) { + String[] managerMap) { float[] wArray = new float[4]; int[] iArray = new int[4]; Vector3f bindPosition = new Vector3f(); @@ -113,7 +113,7 @@ static Map coordsMap(Mesh[] meshes, set = new VectorSet(1); coordsMap.put(managerName, set); } - vertexVector3f(mesh, VertexBuffer.Type.BindPosePosition, + vertexVector3f(mesh, GlVertexBuffer.Type.BindPosePosition, vertexI, bindPosition); set.add(bindPosition); } @@ -134,9 +134,9 @@ static Geometry findAnimatedGeometry(Spatial subtree) { if (subtree instanceof Geometry) { Geometry geometry = (Geometry) subtree; Mesh mesh = geometry.getMesh(); - VertexBuffer indices = mesh.getBuffer(VertexBuffer.Type.BoneIndex); + GlVertexBuffer indices = mesh.getBuffer(GlVertexBuffer.Type.BoneIndex); boolean hasIndices = indices != null; - VertexBuffer weights = mesh.getBuffer(VertexBuffer.Type.BoneWeight); + GlVertexBuffer weights = mesh.getBuffer(GlVertexBuffer.Type.BoneWeight); boolean hasWeights = weights != null; if (hasIndices && hasWeights) { result = geometry; @@ -218,7 +218,7 @@ static Joint findMainBone(Armature skeleton, Mesh[] targetMeshes) { * @return an expanded list (either storeResult or a new instance) */ static List listAnimatedMeshes(Spatial subtree, - List storeResult) { + List storeResult) { if (storeResult == null) { storeResult = new ArrayList<>(10); } @@ -226,9 +226,9 @@ static List listAnimatedMeshes(Spatial subtree, if (subtree instanceof Geometry) { Geometry geometry = (Geometry) subtree; Mesh mesh = geometry.getMesh(); - VertexBuffer indices = mesh.getBuffer(VertexBuffer.Type.BoneIndex); + GlVertexBuffer indices = mesh.getBuffer(GlVertexBuffer.Type.BoneIndex); boolean hasIndices = indices != null; - VertexBuffer weights = mesh.getBuffer(VertexBuffer.Type.BoneWeight); + GlVertexBuffer weights = mesh.getBuffer(GlVertexBuffer.Type.BoneWeight); boolean hasWeights = weights != null; if (hasIndices && hasWeights && !storeResult.contains(mesh)) { storeResult.add(mesh); @@ -404,14 +404,14 @@ private static void addWeights(Mesh mesh, float[] totalWeights) { assert maxWeightsPerVert > 0 : maxWeightsPerVert; assert maxWeightsPerVert <= 4 : maxWeightsPerVert; - VertexBuffer biBuf = mesh.getBuffer(VertexBuffer.Type.BoneIndex); + GlVertexBuffer biBuf = mesh.getBuffer(GlVertexBuffer.Type.BoneIndex); Buffer boneIndexBuffer = biBuf.getDataReadOnly(); boneIndexBuffer.rewind(); int numBoneIndices = boneIndexBuffer.remaining(); assert numBoneIndices % 4 == 0 : numBoneIndices; int numVertices = boneIndexBuffer.remaining() / 4; - VertexBuffer wBuf = mesh.getBuffer(VertexBuffer.Type.BoneWeight); + GlVertexBuffer wBuf = mesh.getBuffer(GlVertexBuffer.Type.BoneWeight); FloatBuffer weightBuffer = (FloatBuffer) wBuf.getDataReadOnly(); weightBuffer.rewind(); int numWeights = weightBuffer.remaining(); @@ -440,7 +440,7 @@ private static void addWeights(Mesh mesh, float[] totalWeights) { * @return a bone/torso name */ private static String findManager(Mesh mesh, int vertexIndex, int[] iArray, - float[] wArray, String[] managerMap) { + float[] wArray, String[] managerMap) { vertexBoneIndices(mesh, vertexIndex, iArray); vertexBoneWeights(mesh, vertexIndex, wArray); Map weightMap = weightMap(iArray, wArray, managerMap); @@ -569,7 +569,7 @@ private static float[] totalWeights(Mesh[] meshes, Armature skeleton) { * @return the data vector (either storeResult or a new instance) */ private static int[] vertexBoneIndices(Mesh mesh, - int vertexIndex, int[] storeResult) { + int vertexIndex, int[] storeResult) { if (storeResult == null) { storeResult = new int[4]; } else { @@ -581,7 +581,7 @@ private static int[] vertexBoneIndices(Mesh mesh, maxWeightsPerVert = 1; } - VertexBuffer biBuf = mesh.getBuffer(VertexBuffer.Type.BoneIndex); + GlVertexBuffer biBuf = mesh.getBuffer(GlVertexBuffer.Type.BoneIndex); Buffer boneIndexBuffer = biBuf.getDataReadOnly(); boneIndexBuffer.position(4 * vertexIndex); for (int wIndex = 0; wIndex < maxWeightsPerVert; ++wIndex) { @@ -608,7 +608,7 @@ private static int[] vertexBoneIndices(Mesh mesh, * @return the data vector (either storeResult or a new instance) */ private static float[] vertexBoneWeights(Mesh mesh, - int vertexIndex, float[] storeResult) { + int vertexIndex, float[] storeResult) { if (storeResult == null) { storeResult = new float[4]; } else { @@ -620,7 +620,7 @@ private static float[] vertexBoneWeights(Mesh mesh, maxWeightsPerVert = 1; } - VertexBuffer wBuf = mesh.getBuffer(VertexBuffer.Type.BoneWeight); + GlVertexBuffer wBuf = mesh.getBuffer(GlVertexBuffer.Type.BoneWeight); FloatBuffer weightBuffer = (FloatBuffer) wBuf.getDataReadOnly(); weightBuffer.position(4 * vertexIndex); for (int wIndex = 0; wIndex < maxWeightsPerVert; ++wIndex) { @@ -651,18 +651,18 @@ private static float[] vertexBoneWeights(Mesh mesh, * @return the data vector (either storeResult or a new instance) */ private static Vector3f vertexVector3f(Mesh mesh, - VertexBuffer.Type bufferType, int vertexIndex, - Vector3f storeResult) { - assert bufferType == VertexBuffer.Type.BindPoseNormal - || bufferType == VertexBuffer.Type.BindPosePosition - || bufferType == VertexBuffer.Type.Binormal - || bufferType == VertexBuffer.Type.Normal - || bufferType == VertexBuffer.Type.Position : bufferType; + GlVertexBuffer.Type bufferType, int vertexIndex, + Vector3f storeResult) { + assert bufferType == GlVertexBuffer.Type.BindPoseNormal + || bufferType == GlVertexBuffer.Type.BindPosePosition + || bufferType == GlVertexBuffer.Type.Binormal + || bufferType == GlVertexBuffer.Type.Normal + || bufferType == GlVertexBuffer.Type.Position : bufferType; if (storeResult == null) { storeResult = new Vector3f(); } - VertexBuffer vertexBuffer = mesh.getBuffer(bufferType); + GlVertexBuffer vertexBuffer = mesh.getBuffer(bufferType); FloatBuffer floatBuffer = (FloatBuffer) vertexBuffer.getDataReadOnly(); floatBuffer.position(3 * vertexIndex); storeResult.x = floatBuffer.get(); diff --git a/jme3-jbullet/src/main/java/com/jme3/bullet/collision/RagdollCollisionListener.java b/jme3-jbullet/src/main/java/com/jme3/bullet/collision/RagdollCollisionListener.java deleted file mode 100644 index 84e50cdaed..0000000000 --- a/jme3-jbullet/src/main/java/com/jme3/bullet/collision/RagdollCollisionListener.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2009-2018 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.bullet.collision; - -import com.jme3.animation.Bone; - -/** - * Interface to receive notifications whenever a KinematicRagdollControl - * collides with another physics object. - *

    - * This interface is shared between JBullet and Native Bullet. - * - * @author Nehon - */ -public interface RagdollCollisionListener { - - /** - * Invoked when a collision involving a KinematicRagdollControl occurs. - * - * @param bone the ragdoll bone that collided (not null) - * @param object the collision object that collided with the bone (not null) - * @param event other event details (not null) - */ - public void collide(Bone bone, PhysicsCollisionObject object, PhysicsCollisionEvent event); - -} diff --git a/jme3-jbullet/src/main/java/com/jme3/bullet/collision/shapes/HullCollisionShape.java b/jme3-jbullet/src/main/java/com/jme3/bullet/collision/shapes/HullCollisionShape.java index 24746934a5..cd03bd1c80 100644 --- a/jme3-jbullet/src/main/java/com/jme3/bullet/collision/shapes/HullCollisionShape.java +++ b/jme3-jbullet/src/main/java/com/jme3/bullet/collision/shapes/HullCollisionShape.java @@ -39,7 +39,7 @@ import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; import java.io.IOException; import java.nio.FloatBuffer; import javax.vecmath.Vector3f; diff --git a/jme3-jbullet/src/main/java/com/jme3/bullet/control/KinematicRagdollControl.java b/jme3-jbullet/src/main/java/com/jme3/bullet/control/KinematicRagdollControl.java deleted file mode 100644 index 19f054be32..0000000000 --- a/jme3-jbullet/src/main/java/com/jme3/bullet/control/KinematicRagdollControl.java +++ /dev/null @@ -1,1335 +0,0 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.bullet.control; - -import com.jme3.animation.*; -import com.jme3.bullet.PhysicsSpace; -import com.jme3.bullet.collision.*; -import com.jme3.bullet.collision.shapes.BoxCollisionShape; -import com.jme3.bullet.collision.shapes.HullCollisionShape; -import com.jme3.bullet.control.ragdoll.*; -import com.jme3.bullet.joints.SixDofJoint; -import com.jme3.bullet.objects.PhysicsRigidBody; -import com.jme3.export.*; -import com.jme3.math.*; -import com.jme3.renderer.RenderManager; -import com.jme3.renderer.ViewPort; -import com.jme3.scene.Mesh; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; -import com.jme3.util.TempVars; -import com.jme3.util.clone.JmeCloneable; -import java.io.IOException; -import java.util.*; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * This control is still a WIP, use it at your own risk
    To - * use this control you need a model with an AnimControl and a - * SkeletonControl.
    This should be the case if you imported an animated - * model from Ogre or blender.
    Note enabling/disabling the control - * add/removes it from the physics space
    - *

    - * This control creates collision shapes for all bones in the skeleton when you - * invoke spatial.addControl(ragdollControl).

    • The shape is - * HullCollision shape based on the vertices associated with each bone and based - * on a tweakable weight threshold (see setWeightThreshold)
    • If you - * don't want each bone to be a collision shape, you can specify what bone to - * use by using the addBoneName method
      By using this method, bone that are - * not used to create a shape, are "merged" to their parent to create the - * collision shape.
    • - *
    - *

    - * There are 2 modes for this control :

    • The kinematic modes - * :
      this is the default behavior, this means that the collision - * shapes of the body are able to interact with physics enabled objects. in this - * mode physics shapes follow the motion of the animated skeleton (for example - * animated by a key framed animation) this mode is enabled by calling - * setKinematicMode();
    • The ragdoll modes:
      To - * enable this behavior, you need to invoke the setRagdollMode() method. In this - * mode the character is entirely controlled by physics, so it will fall under - * the gravity and move if any force is applied to it.
    • - *
    - *

    - * This class is shared between JBullet and Native Bullet. - * - * @author Normen Hansen and Rémy Bouquet (Nehon) - */ -@Deprecated -public class KinematicRagdollControl extends AbstractPhysicsControl implements PhysicsCollisionListener, JmeCloneable { - - /** - * list of registered collision listeners - */ - protected static final Logger logger = Logger.getLogger(KinematicRagdollControl.class.getName()); - protected List listeners; - protected final Set boneList = new TreeSet<>(); - protected final Map boneLinks = new HashMap<>(); - protected final Vector3f modelPosition = new Vector3f(); - protected final Quaternion modelRotation = new Quaternion(); - protected final PhysicsRigidBody baseRigidBody; - /** - * model being controlled - */ - protected Spatial targetModel; - /** - * skeleton being controlled - */ - protected Skeleton skeleton; - protected RagdollPreset preset = new HumanoidRagdollPreset(); - protected Vector3f initScale; - protected Mode mode = Mode.Kinematic; - protected boolean debug = false; - protected boolean blendedControl = false; - protected float weightThreshold = -1.0f; - protected float blendStart = 0.0f; - /** - * blending interval for animations (in seconds, ≥0) - */ - protected float blendTime = 1.0f; - protected float eventDispatchImpulseThreshold = 10; - protected float rootMass = 15; - /** - * accumulate total mass of ragdoll when control is added to a scene - */ - protected float totalMass = 0; - private Map ikTargets = new HashMap<>(); - private Map ikChainDepth = new HashMap<>(); - /** - * rotational speed for inverse kinematics (radians per second, default=7) - */ - private float ikRotSpeed = 7f; - /** - * viscous limb-damping ratio (0→no damping, 1→critically damped, - * default=0.6) - */ - private float limbDampening = 0.6f; - /** - * distance threshold for inverse kinematics (default=0.1) - */ - private float IKThreshold = 0.1f; - /** - * Enumerate joint-control modes for this control. - */ - public static enum Mode { - /** - * collision shapes follow the movements of bones in the skeleton - */ - Kinematic, - /** - * skeleton is controlled by Bullet physics (gravity and collisions) - */ - Ragdoll, - /** - * skeleton is controlled by inverse-kinematic targets - */ - IK - } - - /** - * Link a bone to a jointed rigid body. - */ - public class PhysicsBoneLink implements Savable { - - protected PhysicsRigidBody rigidBody; - protected Bone bone; - protected SixDofJoint joint; - protected Quaternion initalWorldRotation; - protected Quaternion startBlendingRot = new Quaternion(); - protected Vector3f startBlendingPos = new Vector3f(); - - /** - * Instantiate an uninitialized link. - */ - public PhysicsBoneLink() { - } - - /** - * Access the linked bone. - * - * @return the pre-existing instance or null - */ - public Bone getBone() { - return bone; - } - - /** - * Access the linked body. - * - * @return the pre-existing instance or null - */ - public PhysicsRigidBody getRigidBody() { - return rigidBody; - } - - /** - * Serialize this bone link, for example when saving to a J3O file. - * - * @param ex exporter (not null) - * @throws IOException from exporter - */ - @Override - public void write(JmeExporter ex) throws IOException { - OutputCapsule oc = ex.getCapsule(this); - oc.write(rigidBody, "rigidBody", null); - oc.write(bone, "bone", null); - oc.write(joint, "joint", null); - oc.write(initalWorldRotation, "initalWorldRotation", null); - oc.write(startBlendingRot, "startBlendingRot", new Quaternion()); - oc.write(startBlendingPos, "startBlendingPos", new Vector3f()); - } - - /** - * De-serialize this bone link, for example when loading from a J3O - * file. - * - * @param im importer (not null) - * @throws IOException from importer - */ - @Override - public void read(JmeImporter im) throws IOException { - InputCapsule ic = im.getCapsule(this); - rigidBody = (PhysicsRigidBody) ic.readSavable("rigidBody", null); - bone = (Bone) ic.readSavable("bone", null); - joint = (SixDofJoint) ic.readSavable("joint", null); - initalWorldRotation = (Quaternion) ic.readSavable("initalWorldRotation", null); - startBlendingRot = (Quaternion) ic.readSavable("startBlendingRot", null); - startBlendingPos = (Vector3f) ic.readSavable("startBlendingPos", null); - } - } - - /** - * Instantiate an enabled control. - */ - public KinematicRagdollControl() { - baseRigidBody = new PhysicsRigidBody(new BoxCollisionShape(Vector3f.UNIT_XYZ.mult(0.1f)), 1); - baseRigidBody.setKinematic(mode == Mode.Kinematic); - } - - /** - * Instantiate an enabled control with the specified weight threshold. - * - * @param weightThreshold (>0, <1) - */ - public KinematicRagdollControl(float weightThreshold) { - this(); - this.weightThreshold = weightThreshold; - } - - /** - * Instantiate an enabled control with the specified preset and weight - * threshold. - * - * @param preset (not null) - * @param weightThreshold (>0, <1) - */ - public KinematicRagdollControl(RagdollPreset preset, float weightThreshold) { - this(); - this.preset = preset; - this.weightThreshold = weightThreshold; - } - - /** - * Instantiate an enabled control with the specified preset. - * - * @param preset (not null) - */ - public KinematicRagdollControl(RagdollPreset preset) { - this(); - this.preset = preset; - } - - /** - * Update this control. Invoked once per frame during the logical-state - * update, provided the control is added to a scene. Do not invoke directly - * from user code. - * - * @param tpf the time interval between frames (in seconds, ≥0) - */ - @Override - public void update(float tpf) { - if (!enabled) { - return; - } - if(mode == Mode.IK){ - ikUpdate(tpf); - } else if (mode == Mode.Ragdoll && targetModel.getLocalTranslation().equals(modelPosition)) { - //if the ragdoll has the control of the skeleton, we update each bone with its position in physics world space. - ragDollUpdate(tpf); - } else { - kinematicUpdate(tpf); - } - } - - /** - * Update this control in Ragdoll mode, based on Bullet physics. - * - * @param tpf the time interval between frames (in seconds, ≥0) - */ - protected void ragDollUpdate(float tpf) { - TempVars vars = TempVars.get(); - Quaternion tmpRot1 = vars.quat1; - Quaternion tmpRot2 = vars.quat2; - - for (PhysicsBoneLink link : boneLinks.values()) { - - Vector3f position = vars.vect1; - - //retrieving bone position in physics world space - Vector3f p = link.rigidBody.getMotionState().getWorldLocation(); - //transforming this position with inverse transforms of the model - targetModel.getWorldTransform().transformInverseVector(p, position); - - //retrieving bone rotation in physics world space - Quaternion q = link.rigidBody.getMotionState().getWorldRotationQuat(); - - //multiplying this rotation by the initialWorld rotation of the bone, - //then transforming it with the inverse world rotation of the model - tmpRot1.set(q).multLocal(link.initalWorldRotation); - tmpRot2.set(targetModel.getWorldRotation()).inverseLocal().mult(tmpRot1, tmpRot1); - tmpRot1.normalizeLocal(); - - //if the bone is the root bone, we apply the physic's transform to the model, so its position and rotation are correctly updated - if (link.bone.getParent() == null) { - - //offsetting the physics position/rotation by the root bone inverse model space position/rotation - modelPosition.set(p).subtractLocal(link.bone.getBindPosition()); - targetModel.getParent().getWorldTransform().transformInverseVector(modelPosition, modelPosition); - modelRotation.set(q).multLocal(tmpRot2.set(link.bone.getBindRotation()).inverseLocal()); - - - //applying transforms to the model - targetModel.setLocalTranslation(modelPosition); - - targetModel.setLocalRotation(modelRotation); - - //Applying computed transforms to the bone - link.bone.setUserTransformsInModelSpace(position, tmpRot1); - - } else { - // Some bones in the skeleton might not be associated with a collision shape, - // so we update them recursively. - RagdollUtils.setTransform(link.bone, position, tmpRot1, false, boneList); - } - } - vars.release(); - } - - /** - * Update this control in Kinematic mode, based on bone animation tracks. - * - * @param tpf the time interval between frames (in seconds, ≥0) - */ - protected void kinematicUpdate(float tpf) { - //the ragdoll does not have control, so the keyframe animation updates the physics position of the physics bones - TempVars vars = TempVars.get(); - Quaternion tmpRot1 = vars.quat1; - Quaternion tmpRot2 = vars.quat2; - Vector3f position = vars.vect1; - for (PhysicsBoneLink link : boneLinks.values()) { -// if(link.usedbyIK){ -// continue; -// } - //blended control means keyframe animation is updating the skeleton, - //but to allow smooth transition, we blend this transformation with the saved position of the ragdoll - if (blendedControl) { - Vector3f position2 = vars.vect2; - //initializing tmp vars with the start position/rotation of the ragdoll - position.set(link.startBlendingPos); - tmpRot1.set(link.startBlendingRot); - - //interpolate between ragdoll position/rotation and keyframe position/rotation - tmpRot2.set(tmpRot1).nlerp(link.bone.getModelSpaceRotation(), blendStart / blendTime); - position2.set(position).interpolateLocal(link.bone.getModelSpacePosition(), blendStart / blendTime); - tmpRot1.set(tmpRot2); - position.set(position2); - - //update bone transforms - RagdollUtils.setTransform(link.bone, position, tmpRot1, true, boneList); - } - //setting skeleton transforms to the ragdoll - matchPhysicObjectToBone(link, position, tmpRot1); - modelPosition.set(targetModel.getLocalTranslation()); - } - - //time control for blending - if (blendedControl) { - blendStart += tpf; - if (blendStart > blendTime) { - blendedControl = false; - } - } - vars.release(); - } - /** - * Update this control in IK mode, based on IK targets. - * - * @param tpf the time interval between frames (in seconds, ≥0) - */ - private void ikUpdate(float tpf){ - TempVars vars = TempVars.get(); - - Quaternion tmpRot1 = vars.quat1; - Quaternion[] tmpRot2 = new Quaternion[]{vars.quat2, new Quaternion()}; - - Iterator it = ikTargets.keySet().iterator(); - float distance; - Bone bone; - String boneName; - while (it.hasNext()) { - - boneName = it.next(); - bone = boneLinks.get(boneName).bone; - if (!bone.hasUserControl()) { - Logger.getLogger(KinematicRagdollControl.class.getSimpleName()).log(Level.FINE, "{0} doesn't have user control", boneName); - continue; - } - distance = bone.getModelSpacePosition().distance(ikTargets.get(boneName)); - if (distance < IKThreshold) { - Logger.getLogger(KinematicRagdollControl.class.getSimpleName()).log(Level.FINE, "Distance is close enough"); - continue; - } - int depth = 0; - int maxDepth = ikChainDepth.get(bone.getName()); - updateBone(boneLinks.get(bone.getName()), tpf * FastMath.sqrt(distance), vars, tmpRot1, tmpRot2, bone, ikTargets.get(boneName), depth, maxDepth); - - Vector3f position = vars.vect1; - - for (PhysicsBoneLink link : boneLinks.values()) { - matchPhysicObjectToBone(link, position, tmpRot1); - } - } - vars.release(); - } - - /** - * Update a bone and its ancestors in IK mode. Note: recursive! - * - * @param link the bone link for the affected bone (may be null) - * @param tpf the time interval between frames (in seconds, ≥0) - * @param vars unused - * @param tmpRot1 temporary storage used in calculations (not null) - * @param tmpRot2 temporary storage used in calculations (not null) - * @param tipBone (not null) - * @param target the location target in model space (not null, unaffected) - * @param depth depth of the recursion (≥0) - * @param maxDepth recursion limit (≥0) - */ - public void updateBone(PhysicsBoneLink link, float tpf, TempVars vars, Quaternion tmpRot1, Quaternion[] tmpRot2, Bone tipBone, Vector3f target, int depth, int maxDepth) { - if (link == null || link.bone.getParent() == null) { - return; - } - Quaternion preQuat = link.bone.getLocalRotation(); - Vector3f vectorAxis; - - float[] measureDist = new float[]{Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY}; - for (int dirIndex = 0; dirIndex < 3; dirIndex++) { - if (dirIndex == 0) { - vectorAxis = Vector3f.UNIT_Z; - } else if (dirIndex == 1) { - vectorAxis = Vector3f.UNIT_X; - } else { - vectorAxis = Vector3f.UNIT_Y; - } - - for (int posOrNeg = 0; posOrNeg < 2; posOrNeg++) { - float rot = ikRotSpeed * tpf / (link.rigidBody.getMass() * 2); - - rot = FastMath.clamp(rot, link.joint.getRotationalLimitMotor(dirIndex).getLoLimit(), link.joint.getRotationalLimitMotor(dirIndex).getHiLimit()); - tmpRot1.fromAngleAxis(rot, vectorAxis); -// tmpRot1.fromAngleAxis(rotSpeed * tpf / (link.rigidBody.getMass() * 2), vectorAxis); - - - tmpRot2[posOrNeg] = link.bone.getLocalRotation().mult(tmpRot1); - tmpRot2[posOrNeg].normalizeLocal(); - - ikRotSpeed = -ikRotSpeed; - - link.bone.setLocalRotation(tmpRot2[posOrNeg]); - link.bone.update(); - measureDist[posOrNeg] = tipBone.getModelSpacePosition().distance(target); - link.bone.setLocalRotation(preQuat); - } - - if (measureDist[0] < measureDist[1]) { - link.bone.setLocalRotation(tmpRot2[0]); - } else if (measureDist[0] > measureDist[1]) { - link.bone.setLocalRotation(tmpRot2[1]); - } - - } - link.bone.getLocalRotation().normalizeLocal(); - - link.bone.update(); -// link.usedbyIK = true; - if (link.bone.getParent() != null && depth < maxDepth) { - - updateBone(boneLinks.get(link.bone.getParent().getName()), tpf * limbDampening, vars, tmpRot1, tmpRot2, tipBone, target, depth + 1, maxDepth); - } - } - - /** - * Alter the transforms of a rigidBody to match the transforms of a bone. - * This is used to make the ragdoll follow animated motion in Kinematic mode - * - * @param link the bone link connecting the bone and the rigidBody - * @param position temporary storage used in calculations (not null) - * @param tmpRot1 temporary storage used in calculations (not null) - */ - protected void matchPhysicObjectToBone(PhysicsBoneLink link, Vector3f position, Quaternion tmpRot1) { - //computing position from rotation and scale - targetModel.getWorldTransform().transformVector(link.bone.getModelSpacePosition(), position); - - //computing rotation - tmpRot1.set(link.bone.getModelSpaceRotation()).multLocal(link.bone.getModelBindInverseRotation()); - targetModel.getWorldRotation().mult(tmpRot1, tmpRot1); - tmpRot1.normalizeLocal(); - - //updating physics location/rotation of the physics bone - link.rigidBody.setPhysicsLocation(position); - link.rigidBody.setPhysicsRotation(tmpRot1); - - } - - /** - * Rebuild the ragdoll. This is useful if you applied scale on the ragdoll - * after it was initialized. Same as re-attaching. - */ - public void reBuild() { - if (spatial == null) { - return; - } - removeSpatialData(spatial); - createSpatialData(spatial); - } - - /** - * Create spatial-dependent data. Invoked when this control is added to a - * scene. - * - * @param model the controlled spatial (not null) - */ - @Override - protected void createSpatialData(Spatial model) { - targetModel = model; - Node parent = model.getParent(); - - - Vector3f initPosition = model.getLocalTranslation().clone(); - Quaternion initRotation = model.getLocalRotation().clone(); - initScale = model.getLocalScale().clone(); - - model.removeFromParent(); - model.setLocalTranslation(Vector3f.ZERO); - model.setLocalRotation(Quaternion.IDENTITY); - model.setLocalScale(1); - //HACK ALERT change this - // I remove the SkeletonControl and re-add it to the Spatial to make sure it's after the ragdoll control in the stack. - //Find a proper way to order the controls. - SkeletonControl sc = model.getControl(SkeletonControl.class); - if(sc == null){ - throw new IllegalArgumentException("The root node of the model should have a SkeletonControl. Make sure the control is there and that it's not on a sub node."); - } - model.removeControl(sc); - model.addControl(sc); - - if (boneList.isEmpty()) { - // add all bones to the list - skeleton = sc.getSkeleton(); - for (int boneI = 0; boneI < skeleton.getBoneCount(); boneI++) { - String boneName = skeleton.getBone(boneI).getName(); - boneList.add(boneName); - } - } - // filter out bones without vertices - filterBoneList(sc); - - if (boneList.isEmpty()) { - throw new IllegalArgumentException( - "No suitable bones were found in the model's skeleton."); - } - - // put into bind pose and compute bone transforms in model space - // maybe don't reset to ragdoll out of animations? - scanSpatial(model); - - - if (parent != null) { - parent.attachChild(model); - - } - model.setLocalTranslation(initPosition); - model.setLocalRotation(initRotation); - model.setLocalScale(initScale); - - if (added) { - addPhysics(space); - } - logger.log(Level.FINE, "Created physics ragdoll for skeleton {0}", skeleton); - } - - /** - * Remove any bones without vertices from the boneList, so that every hull - * shape will contain at least 1 vertex. - */ - private void filterBoneList(SkeletonControl skeletonControl) { - Mesh[] targets = skeletonControl.getTargets(); - Skeleton skel = skeletonControl.getSkeleton(); - for (int boneI = 0; boneI < skel.getBoneCount(); boneI++) { - String boneName = skel.getBone(boneI).getName(); - if (boneList.contains(boneName)) { - boolean hasVertices = RagdollUtils.hasVertices(boneI, targets, - weightThreshold); - if (!hasVertices) { - boneList.remove(boneName); - } - } - } - } - - /** - * Destroy spatial-dependent data. Invoked when this control is removed from - * a spatial. - * - * @param spat the previously controlled spatial (not null) - */ - @Override - protected void removeSpatialData(Spatial spat) { - if (added) { - removePhysics(space); - } - boneLinks.clear(); - } - - /** - * Add a bone name to this control. Repeated invocations of this method can - * be used to specify which bones to use when generating collision shapes. - *

    - * Not allowed after attaching the control. - * - * @param name the name of the bone to add - */ - public void addBoneName(String name) { - boneList.add(name); - } - - /** - * Generate physics shapes and bone links for the skeleton. - * - * @param model the spatial with the model's SkeletonControl (not null) - */ - protected void scanSpatial(Spatial model) { - AnimControl animControl = model.getControl(AnimControl.class); - Map> pointsMap = null; - if (weightThreshold == -1.0f) { - pointsMap = RagdollUtils.buildPointMap(model); - } - - skeleton = animControl.getSkeleton(); - skeleton.resetAndUpdate(); - for (int i = 0; i < skeleton.getRoots().length; i++) { - Bone childBone = skeleton.getRoots()[i]; - if (childBone.getParent() == null) { - logger.log(Level.FINE, "Found root bone in skeleton {0}", skeleton); - boneRecursion(model, childBone, baseRigidBody, 1, pointsMap); - } - } - } - - /** - * Generate a physics shape and bone links for the specified bone. Note: - * recursive! - * - * @param model the spatial with the model's SkeletonControl (not null) - * @param bone the bone to be linked (not null) - * @param parent the body linked to the parent bone (not null) - * @param recursionCount depth of the recursion (≥1) - * @param pointsMap (not null) - */ - protected void boneRecursion(Spatial model, Bone bone, PhysicsRigidBody parent, int recursionCount, Map> pointsMap) { - PhysicsRigidBody parentShape = parent; - if (boneList.contains(bone.getName())) { - - PhysicsBoneLink link = new PhysicsBoneLink(); - link.bone = bone; - - //create the collision shape - HullCollisionShape shape = null; - if (pointsMap != null) { - //build a shape for the bone, using the vertices that are most influenced by this bone - shape = RagdollUtils.makeShapeFromPointMap(pointsMap, RagdollUtils.getBoneIndices(link.bone, skeleton, boneList), initScale, link.bone.getModelSpacePosition()); - } else { - //build a shape for the bone, using the vertices associated with this bone with a weight above the threshold - shape = RagdollUtils.makeShapeFromVerticeWeights(model, RagdollUtils.getBoneIndices(link.bone, skeleton, boneList), initScale, link.bone.getModelSpacePosition(), weightThreshold); - } - - PhysicsRigidBody shapeNode = new PhysicsRigidBody(shape, rootMass / recursionCount); - - shapeNode.setKinematic(mode == Mode.Kinematic); - totalMass += rootMass / recursionCount; - - link.rigidBody = shapeNode; - link.initalWorldRotation = bone.getModelSpaceRotation().clone(); - - if (parent != null) { - //get joint position for parent - Vector3f posToParent = new Vector3f(); - if (bone.getParent() != null) { - bone.getModelSpacePosition().subtract(bone.getParent().getModelSpacePosition(), posToParent).multLocal(initScale); - } - - SixDofJoint joint = new SixDofJoint(parent, shapeNode, posToParent, new Vector3f(0, 0, 0f), true); - preset.setupJointForBone(bone.getName(), joint); - - link.joint = joint; - joint.setCollisionBetweenLinkedBodys(false); - } - boneLinks.put(bone.getName(), link); - shapeNode.setUserObject(link); - parentShape = shapeNode; - } - - for (Iterator it = bone.getChildren().iterator(); it.hasNext();) { - Bone childBone = it.next(); - boneRecursion(model, childBone, parentShape, recursionCount + 1, pointsMap); - } - } - - /** - * Alter the limits of the joint connecting the specified bone to its - * parent. Can only be invoked after adding the control to a spatial. - * - * @param boneName the name of the bone - * @param maxX the maximum rotation on the X axis (in radians) - * @param minX the minimum rotation on the X axis (in radians) - * @param maxY the maximum rotation on the Y axis (in radians) - * @param minY the minimum rotation on the Y axis (in radians) - * @param maxZ the maximum rotation on the Z axis (in radians) - * @param minZ the minimum rotation on the Z axis (in radians) - */ - public void setJointLimit(String boneName, float maxX, float minX, float maxY, float minY, float maxZ, float minZ) { - PhysicsBoneLink link = boneLinks.get(boneName); - if (link != null) { - RagdollUtils.setJointLimit(link.joint, maxX, minX, maxY, minY, maxZ, minZ); - } else { - logger.log(Level.WARNING, "Not joint was found for bone {0}. make sure you call spatial.addControl(ragdoll) before setting joints limit", boneName); - } - } - - /** - * Return the joint between the specified bone and its parent. This return - * null if it's invoked before adding the control to a spatial - * - * @param boneName the name of the bone - * @return the joint between the given bone and its parent - */ - public SixDofJoint getJoint(String boneName) { - PhysicsBoneLink link = boneLinks.get(boneName); - if (link != null) { - return link.joint; - } else { - logger.log(Level.WARNING, "Not joint was found for bone {0}. make sure you call spatial.addControl(ragdoll) before setting joints limit", boneName); - return null; - } - } - - @Override - protected void setPhysicsLocation(Vector3f vec) { - if (baseRigidBody != null) { - baseRigidBody.setPhysicsLocation(vec); - } - } - - @Override - protected void setPhysicsRotation(Quaternion quat) { - if (baseRigidBody != null) { - baseRigidBody.setPhysicsRotation(quat); - } - } - - @Override - protected void addPhysics(PhysicsSpace space) { - if (baseRigidBody != null) { - space.add(baseRigidBody); - } - for (Iterator it = boneLinks.values().iterator(); it.hasNext();) { - PhysicsBoneLink physicsBoneLink = it.next(); - if (physicsBoneLink.rigidBody != null) { - space.add(physicsBoneLink.rigidBody); - if (physicsBoneLink.joint != null) { - space.add(physicsBoneLink.joint); - - } - } - } - space.addCollisionListener(this); - } - - @Override - protected void removePhysics(PhysicsSpace space) { - if (baseRigidBody != null) { - space.remove(baseRigidBody); - } - for (Iterator it = boneLinks.values().iterator(); it.hasNext();) { - PhysicsBoneLink physicsBoneLink = it.next(); - if (physicsBoneLink.joint != null) { - space.remove(physicsBoneLink.joint); - if (physicsBoneLink.rigidBody != null) { - space.remove(physicsBoneLink.rigidBody); - } - } - } - space.removeCollisionListener(this); - } - - /** - * For internal use only: callback for collision event - * - * @param event (not null) - */ - @Override - public void collision(PhysicsCollisionEvent event) { - PhysicsCollisionObject objA = event.getObjectA(); - PhysicsCollisionObject objB = event.getObjectB(); - - //excluding collisions that involve 2 parts of the ragdoll - if (event.getNodeA() == null && event.getNodeB() == null) { - return; - } - - //discarding low impulse collision - if (event.getAppliedImpulse() < eventDispatchImpulseThreshold) { - return; - } - - boolean hit = false; - Bone hitBone = null; - PhysicsCollisionObject hitObject = null; - - //Computing which bone has been hit - if (objA.getUserObject() instanceof PhysicsBoneLink) { - PhysicsBoneLink link = (PhysicsBoneLink) objA.getUserObject(); - if (link != null) { - hit = true; - hitBone = link.bone; - hitObject = objB; - } - } - - if (objB.getUserObject() instanceof PhysicsBoneLink) { - PhysicsBoneLink link = (PhysicsBoneLink) objB.getUserObject(); - if (link != null) { - hit = true; - hitBone = link.bone; - hitObject = objA; - - } - } - - //dispatching the event if the ragdoll has been hit - if (hit && listeners != null) { - for (RagdollCollisionListener listener : listeners) { - listener.collide(hitBone, hitObject, event); - } - } - - } - - /** - * Enable or disable the ragdoll behaviour. if ragdollEnabled is true, the - * character motion will only be powered by physics else, the character will - * be animated by the keyframe animation, but will be able to physically - * interact with its physics environment - * - * @param mode an enum value (not null) - */ - protected void setMode(Mode mode) { - this.mode = mode; - AnimControl animControl = targetModel.getControl(AnimControl.class); - animControl.setEnabled(mode == Mode.Kinematic); - - baseRigidBody.setKinematic(mode == Mode.Kinematic); - if (mode != Mode.IK) { - TempVars vars = TempVars.get(); - - for (PhysicsBoneLink link : boneLinks.values()) { - link.rigidBody.setKinematic(mode == Mode.Kinematic); - if (mode == Mode.Ragdoll) { - Quaternion tmpRot1 = vars.quat1; - Vector3f position = vars.vect1; - //making sure that the ragdoll is at the correct place. - matchPhysicObjectToBone(link, position, tmpRot1); - } - - } - vars.release(); - } - - if(mode != Mode.IK){ - for (Bone bone : skeleton.getRoots()) { - RagdollUtils.setUserControl(bone, mode == Mode.Ragdoll); - } - } - - } - - /** - * Smoothly blend from Ragdoll mode to Kinematic mode This is useful to - * blend ragdoll actual position to a keyframe animation for example - * - * @param blendTime the blending time between ragdoll to anim. - */ - public void blendToKinematicMode(float blendTime) { - if (mode == Mode.Kinematic) { - return; - } - blendedControl = true; - this.blendTime = blendTime; - mode = Mode.Kinematic; - AnimControl animControl = targetModel.getControl(AnimControl.class); - animControl.setEnabled(true); - - - TempVars vars = TempVars.get(); - for (PhysicsBoneLink link : boneLinks.values()) { - - Vector3f p = link.rigidBody.getMotionState().getWorldLocation(); - Vector3f position = vars.vect1; - - targetModel.getWorldTransform().transformInverseVector(p, position); - - Quaternion q = link.rigidBody.getMotionState().getWorldRotationQuat(); - Quaternion q2 = vars.quat1; - Quaternion q3 = vars.quat2; - - q2.set(q).multLocal(link.initalWorldRotation).normalizeLocal(); - q3.set(targetModel.getWorldRotation()).inverseLocal().mult(q2, q2); - q2.normalizeLocal(); - link.startBlendingPos.set(position); - link.startBlendingRot.set(q2); - link.rigidBody.setKinematic(true); - } - vars.release(); - - for (Bone bone : skeleton.getRoots()) { - RagdollUtils.setUserControl(bone, false); - } - - blendStart = 0; - } - - /** - * Put the control into Kinematic mode. In this mode, the collision shapes - * follow the movements of the skeleton while interacting with the physics - * environment. - */ - public void setKinematicMode() { - if (mode != Mode.Kinematic) { - setMode(Mode.Kinematic); - } - } - - /** - * Sets the control into Ragdoll mode The skeleton is entirely controlled by - * physics. - */ - public void setRagdollMode() { - if (mode != Mode.Ragdoll) { - setMode(Mode.Ragdoll); - } - } - - /** - * Sets the control into Inverse Kinematics mode. The affected bones are - * affected by IK. physics. - */ - public void setIKMode() { - if (mode != Mode.IK) { - setMode(Mode.IK); - } - } - - /** - * returns the mode of this control - * - * @return an enum value - */ - public Mode getMode() { - return mode; - } - - /** - * Add a collision listener to this control. - * - * @param listener (not null, alias created) - */ - public void addCollisionListener(RagdollCollisionListener listener) { - if (listeners == null) { - listeners = new ArrayList(); - } - listeners.add(listener); - } - - /** - * Alter the ragdoll's root mass. - * - * @param rootMass the desired mass (≥0) - */ - public void setRootMass(float rootMass) { - this.rootMass = rootMass; - } - - /** - * Read the ragdoll's total mass. - * - * @return mass (≥0) - */ - public float getTotalMass() { - return totalMass; - } - - /** - * Read the ragdoll's weight threshold. - * - * @return threshold - */ - public float getWeightThreshold() { - return weightThreshold; - } - - /** - * Alter the ragdoll's weight threshold. - * - * @param weightThreshold the desired threshold - */ - public void setWeightThreshold(float weightThreshold) { - this.weightThreshold = weightThreshold; - } - - /** - * Read the ragdoll's event-dispatch impulse threshold. - * - * @return threshold - */ - public float getEventDispatchImpulseThreshold() { - return eventDispatchImpulseThreshold; - } - - /** - * Alter the ragdoll's event-dispatch impulse threshold. - * - * @param eventDispatchImpulseThreshold desired threshold - */ - public void setEventDispatchImpulseThreshold(float eventDispatchImpulseThreshold) { - this.eventDispatchImpulseThreshold = eventDispatchImpulseThreshold; - } - - /** - * Alter the CcdMotionThreshold of all rigid bodies in the ragdoll. - * - * @see PhysicsRigidBody#setCcdMotionThreshold(float) - * @param value the desired threshold value (velocity, >0) or zero to - * disable CCD (default=0) - */ - public void setCcdMotionThreshold(float value) { - for (PhysicsBoneLink link : boneLinks.values()) { - link.rigidBody.setCcdMotionThreshold(value); - } - } - - /** - * Alter the CcdSweptSphereRadius of all rigid bodies in the ragdoll. - * - * @see PhysicsRigidBody#setCcdSweptSphereRadius(float) - * @param value the desired radius of the sphere used for continuous - * collision detection (≥0) - */ - public void setCcdSweptSphereRadius(float value) { - for (PhysicsBoneLink link : boneLinks.values()) { - link.rigidBody.setCcdSweptSphereRadius(value); - } - } - - /** - * Access the rigidBody associated with the named bone. - * - * @param boneName the name of the bone - * @return the associated rigidBody. - */ - public PhysicsRigidBody getBoneRigidBody(String boneName) { - PhysicsBoneLink link = boneLinks.get(boneName); - if (link != null) { - return link.rigidBody; - } - return null; - } - - /** - * Render this control. Invoked once per view port per frame, provided the - * control is added to a scene. Should be invoked only by a subclass or by - * the RenderManager. - * - * @param rm the render manager (not null) - * @param vp the view port to render (not null) - */ - @Override - public void render(RenderManager rm, ViewPort vp) { - } - - /** - * Create a shallow clone for the JME cloner. - * - * @return a new control (not null) - */ - @Override - public Object jmeClone() { - KinematicRagdollControl control = new KinematicRagdollControl(preset, weightThreshold); - control.setMode(mode); - control.setRootMass(rootMass); - control.setWeightThreshold(weightThreshold); - control.setApplyPhysicsLocal(applyLocal); - control.spatial = this.spatial; - return control; - } - - /** - * Add a target for inverse kinematics. - * - * @param bone which bone the IK applies to (not null) - * @param worldPos the world coordinates of the goal (not null) - * @param chainLength number of bones in the chain - * @return a new instance (not null, already added to ikTargets) - */ - public Vector3f setIKTarget(Bone bone, Vector3f worldPos, int chainLength) { - Vector3f target = worldPos.subtract(targetModel.getWorldTranslation()); - ikTargets.put(bone.getName(), target); - ikChainDepth.put(bone.getName(), chainLength); - int i = 0; - while (i < chainLength+2 && bone.getParent() != null) { - if (!bone.hasUserControl()) { - bone.setUserControl(true); - } - bone = bone.getParent(); - i++; - } - - -// setIKMode(); - return target; - } - - /** - * Remove the inverse-kinematics target for the specified bone. - * - * @param bone which bone has the target (not null, modified) - */ - public void removeIKTarget(Bone bone) { - int depth = ikChainDepth.remove(bone.getName()); - int i = 0; - while (i < depth+2 && bone.getParent() != null) { - if (bone.hasUserControl()) { -// matchPhysicObjectToBone(boneLinks.get(bone.getName()), position, tmpRot1); - bone.setUserControl(false); - } - bone = bone.getParent(); - i++; - } - } - - /** - * Remove all inverse-kinematics targets. - */ - public void removeAllIKTargets(){ - ikTargets.clear(); - ikChainDepth.clear(); - applyUserControl(); - } - - /** - * Ensure that user control is enabled for any bones used by inverse - * kinematics and disabled for any other bones. - */ - public void applyUserControl() { - for (Bone bone : skeleton.getRoots()) { - RagdollUtils.setUserControl(bone, false); - } - - if (ikTargets.isEmpty()) { - setKinematicMode(); - } else { - Iterator iterator = ikTargets.keySet().iterator(); - - TempVars vars = TempVars.get(); - - while (iterator.hasNext()) { - Bone bone = (Bone) iterator.next(); - while (bone.getParent() != null) { - - Quaternion tmpRot1 = vars.quat1; - Vector3f position = vars.vect1; - matchPhysicObjectToBone(boneLinks.get(bone.getName()), position, tmpRot1); - bone.setUserControl(true); - bone = bone.getParent(); - } - } - vars.release(); - } - } - - /** - * Read the rotation speed for inverse kinematics. - * - * @return speed (≥0) - */ - public float getIkRotSpeed() { - return ikRotSpeed; - } - - /** - * Alter the rotation speed for inverse kinematics. - * - * @param ikRotSpeed the desired speed (≥0, default=7) - */ - public void setIkRotSpeed(float ikRotSpeed) { - this.ikRotSpeed = ikRotSpeed; - } - - /** - * Read the distance threshold for inverse kinematics. - * - * @return distance threshold - */ - public float getIKThreshold() { - return IKThreshold; - } - - /** - * Alter the distance threshold for inverse kinematics. - * - * @param IKThreshold the desired distance threshold (default=0.1) - */ - public void setIKThreshold(float IKThreshold) { - this.IKThreshold = IKThreshold; - } - - /** - * Read the limb damping. - * - * @return the viscous damping ratio (0→no damping, 1→critically - * damped) - */ - public float getLimbDampening() { - return limbDampening; - } - - /** - * Alter the limb damping. - * - * @param limbDampening the desired viscous damping ratio (0→no - * damping, 1→critically damped, default=0.6) - */ - public void setLimbDampening(float limbDampening) { - this.limbDampening = limbDampening; - } - - /** - * Access the named bone. - * - * @param name which bone to access - * @return the pre-existing instance, or null if not found - */ - public Bone getBone(String name){ - return skeleton.getBone(name); - } - /** - * Serialize this control, for example when saving to a J3O file. - * - * @param ex exporter (not null) - * @throws IOException from exporter - */ - @Override - public void write(JmeExporter ex) throws IOException { - super.write(ex); - OutputCapsule oc = ex.getCapsule(this); - oc.write(boneList.toArray(new String[boneList.size()]), "boneList", new String[0]); - oc.write(boneLinks.values().toArray(new PhysicsBoneLink[boneLinks.size()]), "boneLinks", new PhysicsBoneLink[0]); - oc.write(modelPosition, "modelPosition", new Vector3f()); - oc.write(modelRotation, "modelRotation", new Quaternion()); - oc.write(targetModel, "targetModel", null); - oc.write(skeleton, "skeleton", null); -// oc.write(preset, "preset", null);//TODO - oc.write(initScale, "initScale", null); - oc.write(mode, "mode", null); - oc.write(blendedControl, "blendedControl", false); - oc.write(weightThreshold, "weightThreshold", -1.0f); - oc.write(blendStart, "blendStart", 0.0f); - oc.write(blendTime, "blendTime", 1.0f); - oc.write(eventDispatchImpulseThreshold, "eventDispatchImpulseThreshold", 10); - oc.write(rootMass, "rootMass", 15); - oc.write(totalMass, "totalMass", 0); - oc.write(ikRotSpeed, "rotSpeed", 7f); - oc.write(limbDampening, "limbDampening", 0.6f); - } - - /** - * De-serialize this control, for example when loading from a J3O file. - * - * @param im importer (not null) - * @throws IOException from importer - */ - @Override - public void read(JmeImporter im) throws IOException { - super.read(im); - InputCapsule ic = im.getCapsule(this); - String[] loadedBoneList = ic.readStringArray("boneList", new String[0]); - boneList.addAll(Arrays.asList(loadedBoneList)); - PhysicsBoneLink[] loadedBoneLinks = (PhysicsBoneLink[]) ic.readSavableArray("boneList", new PhysicsBoneLink[0]); - for (PhysicsBoneLink physicsBoneLink : loadedBoneLinks) { - boneLinks.put(physicsBoneLink.bone.getName(), physicsBoneLink); - } - modelPosition.set((Vector3f) ic.readSavable("modelPosition", new Vector3f())); - modelRotation.set((Quaternion) ic.readSavable("modelRotation", new Quaternion())); - targetModel = (Spatial) ic.readSavable("targetModel", null); - skeleton = (Skeleton) ic.readSavable("skeleton", null); -// preset //TODO - initScale = (Vector3f) ic.readSavable("initScale", null); - mode = ic.readEnum("mode", Mode.class, Mode.Kinematic); - blendedControl = ic.readBoolean("blendedControl", false); - weightThreshold = ic.readFloat("weightThreshold", -1.0f); - blendStart = ic.readFloat("blendStart", 0.0f); - blendTime = ic.readFloat("blendTime", 1.0f); - eventDispatchImpulseThreshold = ic.readFloat("eventDispatchImpulseThreshold", 10); - rootMass = ic.readFloat("rootMass", 15); - totalMass = ic.readFloat("totalMass", 0); - } -} diff --git a/jme3-jbullet/src/main/java/com/jme3/bullet/control/ragdoll/RagdollUtils.java b/jme3-jbullet/src/main/java/com/jme3/bullet/control/ragdoll/RagdollUtils.java deleted file mode 100644 index f24ccf5f72..0000000000 --- a/jme3-jbullet/src/main/java/com/jme3/bullet/control/ragdoll/RagdollUtils.java +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * 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 'jMonkeyEngine' 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 com.jme3.bullet.control.ragdoll; - -import com.jme3.animation.Bone; -import com.jme3.animation.Skeleton; -import com.jme3.animation.SkeletonControl; -import com.jme3.bullet.collision.shapes.HullCollisionShape; -import com.jme3.bullet.joints.SixDofJoint; -import com.jme3.math.Quaternion; -import com.jme3.math.Transform; -import com.jme3.math.Vector3f; -import com.jme3.scene.Mesh; -import com.jme3.scene.Spatial; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Type; -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.FloatBuffer; -import java.nio.ShortBuffer; -import java.util.*; - -/** - * Utility methods used by KinematicRagdollControl. - *

    - * This class is shared between JBullet and Native Bullet. - * - * @author Nehon - */ -public class RagdollUtils { - - /** - * A private constructor to inhibit instantiation of this class. - */ - private RagdollUtils() { - } - - /** - * Alter the limits of the specified 6-DOF joint. - * - * @param joint which joint to alter (not null) - * @param maxX the maximum rotation on the X axis (in radians) - * @param minX the minimum rotation on the X axis (in radians) - * @param maxY the maximum rotation on the Y axis (in radians) - * @param minY the minimum rotation on the Y axis (in radians) - * @param maxZ the maximum rotation on the Z axis (in radians) - * @param minZ the minimum rotation on the Z axis (in radians) - */ - public static void setJointLimit(SixDofJoint joint, float maxX, float minX, float maxY, float minY, float maxZ, float minZ) { - - joint.getRotationalLimitMotor(0).setHiLimit(maxX); - joint.getRotationalLimitMotor(0).setLoLimit(minX); - joint.getRotationalLimitMotor(1).setHiLimit(maxY); - joint.getRotationalLimitMotor(1).setLoLimit(minY); - joint.getRotationalLimitMotor(2).setHiLimit(maxZ); - joint.getRotationalLimitMotor(2).setLoLimit(minZ); - } - - /** - * Build a map of mesh vertices in a subtree of the scene graph. - * - * @param model the root of the subtree (may be null) - * @return a new map (not null) - */ - public static Map> buildPointMap(Spatial model) { - Map> map = new HashMap<>(); - - SkeletonControl skeletonCtrl = model.getControl(SkeletonControl.class); - Mesh[] targetMeshes = skeletonCtrl.getTargets(); - for (Mesh mesh : targetMeshes) { - buildPointMapForMesh(mesh, map); - } - - return map; - } - - private static Map> buildPointMapForMesh(Mesh mesh, Map> map) { - - FloatBuffer vertices = mesh.getFloatBuffer(Type.Position); - ByteBuffer boneIndices = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData(); - FloatBuffer boneWeight = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData(); - - vertices.rewind(); - boneIndices.rewind(); - boneWeight.rewind(); - - int vertexComponents = mesh.getVertexCount() * 3; - int k, start, index; - float maxWeight = 0; - - for (int i = 0; i < vertexComponents; i += 3) { - - - start = i / 3 * 4; - index = 0; - maxWeight = -1; - for (k = start; k < start + 4; k++) { - float weight = boneWeight.get(k); - if (weight > maxWeight) { - maxWeight = weight; - index = boneIndices.get(k); - } - } - List points = map.get(index); - if (points == null) { - points = new ArrayList(); - map.put(index, points); - } - points.add(vertices.get(i)); - points.add(vertices.get(i + 1)); - points.add(vertices.get(i + 2)); - } - return map; - } - - /** - * Create a hull collision shape from linked vertices to this bone. Vertices - * must have previously been gathered using buildPointMap(). - * - * @param pointsMap map from bone indices to coordinates (not null, - * unaffected) - * @param boneIndices (not null, unaffected) - * @param initialScale scale factors (not null, unaffected) - * @param initialPosition location (not null, unaffected) - * @return a new shape (not null) - */ - public static HullCollisionShape makeShapeFromPointMap(Map> pointsMap, List boneIndices, Vector3f initialScale, Vector3f initialPosition) { - - ArrayList points = new ArrayList<>(); - for (Integer index : boneIndices) { - List l = pointsMap.get(index); - if (l != null) { - - for (int i = 0; i < l.size(); i += 3) { - Vector3f pos = new Vector3f(); - pos.x = l.get(i); - pos.y = l.get(i + 1); - pos.z = l.get(i + 2); - pos.subtractLocal(initialPosition).multLocal(initialScale); - points.add(pos.x); - points.add(pos.y); - points.add(pos.z); - } - } - } - - assert !points.isEmpty(); - float[] p = new float[points.size()]; - for (int i = 0; i < points.size(); i++) { - p[i] = points.get(i); - } - - return new HullCollisionShape(p); - } - - /** - * Enumerate the bone indices of the specified bone and all its descendents. - * - * @param bone the input bone (not null) - * @param skeleton the skeleton containing the bone (not null) - * @param boneList a set of bone names (not null, unaffected) - * - * @return a new list (not null) - */ - public static List getBoneIndices(Bone bone, Skeleton skeleton, Set boneList) { - List list = new LinkedList<>(); - if (boneList.isEmpty()) { - list.add(skeleton.getBoneIndex(bone)); - } else { - list.add(skeleton.getBoneIndex(bone)); - for (Bone childBone : bone.getChildren()) { - if (!boneList.contains(childBone.getName())) { - list.addAll(getBoneIndices(childBone, skeleton, boneList)); - } - } - } - return list; - } - - /** - * Create a hull collision shape from linked vertices to this bone. - * - * @param model the model on which to base the shape - * @param boneIndices indices of relevant bones (not null, unaffected) - * @param initialScale scale factors - * @param initialPosition location - * @param weightThreshold minimum weight for inclusion - * @return a new shape - */ - public static HullCollisionShape makeShapeFromVerticeWeights(Spatial model, - List boneIndices, Vector3f initialScale, - Vector3f initialPosition, float weightThreshold) { - List points = new ArrayList<>(100); - - SkeletonControl skeletonCtrl = model.getControl(SkeletonControl.class); - Mesh[] targetMeshes = skeletonCtrl.getTargets(); - for (Mesh mesh : targetMeshes) { - for (Integer index : boneIndices) { - List bonePoints = getPoints(mesh, index, initialScale, - initialPosition, weightThreshold); - points.addAll(bonePoints); - } - } - - assert !points.isEmpty(); - float[] p = new float[points.size()]; - for (int i = 0; i < points.size(); i++) { - p[i] = points.get(i); - } - - return new HullCollisionShape(p); - } - - /** - * Enumerate vertices that meet the weight threshold for the indexed bone. - * - * @param mesh the mesh to analyze (not null) - * @param boneIndex the index of the bone (≥0) - * @param initialScale a scale applied to vertex positions (not null, - * unaffected) - * @param offset an offset subtracted from vertex positions (not null, - * unaffected) - * @param weightThreshold the minimum bone weight for inclusion in the - * result (≥0, ≤1) - * @return a new list of vertex coordinates (not null, length a multiple of - * 3) - */ - private static List getPoints(Mesh mesh, int boneIndex, Vector3f initialScale, Vector3f offset, float weightThreshold) { - - FloatBuffer vertices = mesh.getFloatBuffer(Type.Position); - VertexBuffer biBuf = mesh.getBuffer(VertexBuffer.Type.BoneIndex); - Buffer boneIndices = biBuf.getDataReadOnly(); - FloatBuffer boneWeight = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData(); - - vertices.rewind(); - boneIndices.rewind(); - boneWeight.rewind(); - - ArrayList results = new ArrayList<>(); - - int vertexComponents = mesh.getVertexCount() * 3; - - for (int i = 0; i < vertexComponents; i += 3) { - int k; - boolean add = false; - int start = i / 3 * 4; - for (k = start; k < start + 4; k++) { - if (readIndex(boneIndices, k) == boneIndex - && boneWeight.get(k) >= weightThreshold) { - add = true; - break; - } - } - if (add) { - - Vector3f pos = new Vector3f(); - pos.x = vertices.get(i); - pos.y = vertices.get(i + 1); - pos.z = vertices.get(i + 2); - pos.subtractLocal(offset).multLocal(initialScale); - results.add(pos.x); - results.add(pos.y); - results.add(pos.z); - - } - } - - return results; - } - - /** - * Updates a bone position and rotation. if the child bones are not in the - * bone list this means, they are not associated with a physics shape. So - * they have to be updated - * - * @param bone the bone - * @param pos the position - * @param rot the rotation - * @param restoreBoneControl true → user-control flag should be set - * @param boneList the names of all bones without collision shapes (not - * null, unaffected) - */ - public static void setTransform(Bone bone, Vector3f pos, Quaternion rot, boolean restoreBoneControl, Set boneList) { - //we ensure that we have the control - if (restoreBoneControl) { - bone.setUserControl(true); - } - //we set te user transforms of the bone - bone.setUserTransformsInModelSpace(pos, rot); - for (Bone childBone : bone.getChildren()) { - //each child bone that is not in the list is updated - if (!boneList.contains(childBone.getName())) { - Transform t = childBone.getCombinedTransform(pos, rot); - setTransform(childBone, t.getTranslation(), t.getRotation(), restoreBoneControl, boneList); - } - } - // return control to the keyframe animation - if (restoreBoneControl) { - bone.setUserControl(false); - } - } - - /** - * Alter the user-control flags of a bone and all its descendents. - * - * @param bone the ancestor bone (not null, modified) - * @param bool true to enable user control, false to disable - */ - public static void setUserControl(Bone bone, boolean bool) { - bone.setUserControl(bool); - for (Bone child : bone.getChildren()) { - setUserControl(child, bool); - } - } - - /** - * Test whether the indexed bone has at least one vertex in the specified - * meshes with a weight greater than the specified threshold. - * - * @param boneIndex the index of the bone (≥0) - * @param targets the meshes to search (not null, no null elements) - * @param weightThreshold the threshold (≥0, ≤1) - * @return true if at least 1 vertex found, otherwise false - */ - public static boolean hasVertices(int boneIndex, Mesh[] targets, - float weightThreshold) { - for (Mesh mesh : targets) { - VertexBuffer biBuf = mesh.getBuffer(VertexBuffer.Type.BoneIndex); - Buffer boneIndices = biBuf.getDataReadOnly(); - FloatBuffer boneWeight - = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData(); - - boneIndices.rewind(); - boneWeight.rewind(); - - int vertexComponents = mesh.getVertexCount() * 3; - for (int i = 0; i < vertexComponents; i += 3) { - int start = i / 3 * 4; - for (int k = start; k < start + 4; k++) { - if (readIndex(boneIndices, k) == boneIndex - && boneWeight.get(k) >= weightThreshold) { - return true; - } - } - } - } - - return false; - } - - /** - * Read an index from a buffer. - * - * @param buffer a buffer of bytes or shorts (not null) - * @param k the position from which the index will be read - * @return the index value (≥0) - */ - public static int readIndex(Buffer buffer, int k) { - int result; - if (buffer instanceof ByteBuffer) { - ByteBuffer byteBuffer = (ByteBuffer) buffer; - byte b = byteBuffer.get(k); - result = 0xff & b; - } else if (buffer instanceof ShortBuffer) { - ShortBuffer shortBuffer = (ShortBuffer) buffer; - short s = shortBuffer.get(k); - result = 0xffff & s; - } else { - throw new IllegalArgumentException(buffer.getClass().getName()); - } - - assert result >= 0 : result; - return result; - } -} diff --git a/jme3-jbullet/src/main/java/com/jme3/bullet/util/Converter.java b/jme3-jbullet/src/main/java/com/jme3/bullet/util/Converter.java index 096c926e8d..6c7527bd1c 100644 --- a/jme3-jbullet/src/main/java/com/jme3/bullet/util/Converter.java +++ b/jme3-jbullet/src/main/java/com/jme3/bullet/util/Converter.java @@ -35,7 +35,7 @@ import com.bulletphysics.dom.HeightfieldTerrainShape; import com.jme3.math.FastMath; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.scene.mesh.IndexBuffer; import com.jme3.util.BufferUtils; import java.nio.ByteBuffer; diff --git a/jme3-jbullet/src/main/java/com/jme3/bullet/util/DebugShapeFactory.java b/jme3-jbullet/src/main/java/com/jme3/bullet/util/DebugShapeFactory.java index 3e74f1ab3c..aafd5d6ba7 100644 --- a/jme3-jbullet/src/main/java/com/jme3/bullet/util/DebugShapeFactory.java +++ b/jme3-jbullet/src/main/java/com/jme3/bullet/util/DebugShapeFactory.java @@ -44,7 +44,7 @@ import com.jme3.scene.Mesh; import com.jme3.scene.Node; import com.jme3.scene.Spatial; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.util.BufferUtils; import com.jme3.util.TempVars; import java.nio.FloatBuffer; diff --git a/jme3-jbullet/src/test/java/com/jme3/jbullet/test/PreventBulletIssueRegressions.java b/jme3-jbullet/src/test/java/com/jme3/jbullet/test/PreventBulletIssueRegressions.java index bb704578e9..83cd12422d 100644 --- a/jme3-jbullet/src/test/java/com/jme3/jbullet/test/PreventBulletIssueRegressions.java +++ b/jme3-jbullet/src/test/java/com/jme3/jbullet/test/PreventBulletIssueRegressions.java @@ -41,7 +41,6 @@ import com.jme3.bullet.collision.shapes.SphereCollisionShape; import com.jme3.bullet.control.BetterCharacterControl; import com.jme3.bullet.control.GhostControl; -import com.jme3.bullet.control.KinematicRagdollControl; import com.jme3.bullet.control.RigidBodyControl; import com.jme3.bullet.objects.PhysicsRigidBody; import com.jme3.export.JmeExporter; @@ -53,7 +52,7 @@ import com.jme3.scene.Mesh; import com.jme3.scene.Node; import com.jme3.scene.Spatial; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import org.junit.Assert; import org.junit.Test; @@ -180,8 +179,8 @@ public void testIssue1004() { Geometry geometry = (Geometry) sinbad.getChild(0); Mesh mesh = geometry.getMesh(); - VertexBuffer.Type bufferType = VertexBuffer.Type.BoneIndex; - VertexBuffer vertexBuffer = mesh.getBuffer(bufferType); + GlVertexBuffer.Type bufferType = GlVertexBuffer.Type.BoneIndex; + GlVertexBuffer vertexBuffer = mesh.getBuffer(bufferType); // Remove the existing bone-index buffer. mesh.getBufferList().remove(vertexBuffer); diff --git a/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java index e95a2a56d0..d3023dada6 100644 --- a/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java +++ b/jme3-lwjgl/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java @@ -35,9 +35,10 @@ import com.jme3.opencl.Context; import com.jme3.opencl.Image.ImageDescriptor; import com.jme3.opencl.Image.ImageFormat; -import com.jme3.scene.VertexBuffer; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Texture; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.util.List; @@ -150,7 +151,7 @@ public ImageFormat[] querySupportedFormats(MemoryAccess access, Image.ImageType } @Override - public Buffer bindVertexBuffer(VertexBuffer vb, MemoryAccess access) { + public Buffer bindVertexBuffer(GlVertexBuffer vb, MemoryAccess access) { int id = vb.getId(); if (id == -1) { throw new IllegalArgumentException("vertex buffer was not yet uploaded to the GPU or is CPU only"); @@ -163,8 +164,8 @@ public Buffer bindVertexBuffer(VertexBuffer vb, MemoryAccess access) { } @Override - public Image bindImage(com.jme3.texture.Image image, Texture.Type textureType, int mipLevel, MemoryAccess access) { - int imageID = image.getId(); + public Image bindImage(GlImage image, GlTexture.Type textureType, int mipLevel, MemoryAccess access) { + int imageID = image.getNativeObject(); if (imageID == -1) { throw new IllegalArgumentException("image was not yet uploaded to the GPU"); } @@ -177,8 +178,8 @@ public Image bindImage(com.jme3.texture.Image image, Texture.Type textureType, i } @Override - protected Image bindPureRenderBuffer(FrameBuffer.RenderBuffer buffer, MemoryAccess access) { - int renderbuffer = buffer.getId(); + protected Image bindPureRenderBuffer(GlFrameBuffer.RenderBuffer buffer, MemoryAccess access) { + int renderbuffer = buffer.getRenderBufferId(); if (renderbuffer == -1) { throw new IllegalArgumentException("renderbuffer was not yet uploaded to the GPU"); } @@ -189,7 +190,7 @@ protected Image bindPureRenderBuffer(FrameBuffer.RenderBuffer buffer, MemoryAcce return new LwjglImage(mem); } - private int convertTextureType(Texture.Type textureType) { + private int convertTextureType(GlTexture.Type textureType) { switch (textureType) { case TwoDimensional: return GL11.GL_TEXTURE_2D; case TwoDimensionalArray: return GL30.GL_TEXTURE_2D_ARRAY; diff --git a/jme3-lwjgl3/build.gradle b/jme3-lwjgl3/build.gradle index 5e929e8c2f..efd0863fea 100644 --- a/jme3-lwjgl3/build.gradle +++ b/jme3-lwjgl3/build.gradle @@ -12,6 +12,8 @@ dependencies { api libs.lwjgl3.openal api libs.lwjgl3.opencl api libs.lwjgl3.opengl + api libs.lwjgl3.vulkan + api libs.lwjgl3.shaderc runtimeOnly(variantOf(libs.lwjgl3.base){ classifier('natives-windows') }) runtimeOnly(variantOf(libs.lwjgl3.base){ classifier('natives-windows-x86') }) diff --git a/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwKeyInput.java b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwKeyInput.java index 980b07527e..a0662f1ff3 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwKeyInput.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwKeyInput.java @@ -35,6 +35,7 @@ import com.jme3.input.KeyInput; import com.jme3.input.RawInputListener; import com.jme3.input.event.KeyInputEvent; +import com.jme3.system.GlfwWindow; import com.jme3.system.lwjgl.LwjglWindow; import java.util.ArrayDeque; import java.util.Queue; @@ -56,9 +57,9 @@ public class GlfwKeyInput implements KeyInput { private final Queue keyInputEvents = new ArrayDeque<>(); /** - * The LWJGL context. + * The GLFW window. */ - private final LwjglWindow context; + private final GlfwWindow context; /** * The key callback. @@ -77,7 +78,7 @@ public class GlfwKeyInput implements KeyInput { private boolean initialized; - public GlfwKeyInput(final LwjglWindow context) { + public GlfwKeyInput(final GlfwWindow context) { this.context = context; } diff --git a/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwMouseInput.java b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwMouseInput.java index 1aba44d570..94562060f1 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwMouseInput.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/input/lwjgl/GlfwMouseInput.java @@ -37,8 +37,8 @@ import com.jme3.input.event.MouseButtonEvent; import com.jme3.input.event.MouseMotionEvent; import com.jme3.math.Vector2f; -import com.jme3.system.lwjgl.LwjglWindow; -import com.jme3.system.lwjgl.WindowSizeListener; +import com.jme3.system.GlfwWindow; +import com.jme3.system.WindowSizeListener; import com.jme3.util.BufferUtils; import java.nio.ByteBuffer; import java.nio.IntBuffer; @@ -114,7 +114,7 @@ private static ByteBuffer transformCursorImage(final IntBuffer imageData, final private final Queue mouseMotionEvents = new ArrayDeque<>(); private final Queue mouseButtonEvents = new ArrayDeque<>(); - private final LwjglWindow context; + private final GlfwWindow context; private WindowSizeListener windowSizeListener; private RawInputListener listener; @@ -138,7 +138,7 @@ private static ByteBuffer transformCursorImage(final IntBuffer imageData, final private boolean initialized; private final Vector2f inputScale = new Vector2f(); - public GlfwMouseInput(final LwjglWindow context) { + public GlfwMouseInput(final GlfwWindow context) { this.context = context; this.cursorVisible = true; } diff --git a/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java index 8fef7ad1f1..45e9543401 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/opencl/lwjgl/LwjglContext.java @@ -35,9 +35,10 @@ import com.jme3.opencl.Context; import com.jme3.opencl.Image.ImageDescriptor; import com.jme3.opencl.Image.ImageFormat; -import com.jme3.scene.VertexBuffer; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Texture; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.util.List; @@ -160,7 +161,7 @@ public ImageFormat[] querySupportedFormats(MemoryAccess access, Image.ImageType } @Override - public Buffer bindVertexBuffer(VertexBuffer vb, MemoryAccess access) { + public Buffer bindVertexBuffer(GlVertexBuffer vb, MemoryAccess access) { Utils.assertSharingPossible(); int id = vb.getId(); if (id == -1) { @@ -174,9 +175,9 @@ public Buffer bindVertexBuffer(VertexBuffer vb, MemoryAccess access) { } @Override - public Image bindImage(com.jme3.texture.Image image, Texture.Type textureType, int mipLevel, MemoryAccess access) { + public Image bindImage(GlImage image, GlTexture.Type textureType, int mipLevel, MemoryAccess access) { Utils.assertSharingPossible(); - int imageID = image.getId(); + int imageID = image.getNativeObject(); if (imageID == -1) { throw new IllegalArgumentException("image was not yet uploaded to the GPU"); } @@ -189,9 +190,9 @@ public Image bindImage(com.jme3.texture.Image image, Texture.Type textureType, i } @Override - protected Image bindPureRenderBuffer(FrameBuffer.RenderBuffer buffer, MemoryAccess access) { + protected Image bindPureRenderBuffer(GlFrameBuffer.RenderBuffer buffer, MemoryAccess access) { Utils.assertSharingPossible(); - int renderbuffer = buffer.getId(); + int renderbuffer = buffer.getRenderBufferId(); if (renderbuffer == -1) { throw new IllegalArgumentException("renderbuffer was not yet uploaded to the GPU"); } @@ -202,7 +203,7 @@ protected Image bindPureRenderBuffer(FrameBuffer.RenderBuffer buffer, MemoryAcce return new LwjglImage(mem); } - private int convertTextureType(Texture.Type textureType) { + private int convertTextureType(GlTexture.Type textureType) { switch (textureType) { case TwoDimensional: return GL11.GL_TEXTURE_2D; case TwoDimensionalArray: return GL30.GL_TEXTURE_2D_ARRAY; diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/GlfwWindow.java b/jme3-lwjgl3/src/main/java/com/jme3/system/GlfwWindow.java new file mode 100644 index 0000000000..e18f38f0d8 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/GlfwWindow.java @@ -0,0 +1,17 @@ +package com.jme3.system; + +import com.jme3.math.Vector2f; + +public interface GlfwWindow { + + long getWindowHandle(); + + Vector2f getWindowContentScale(Vector2f store); + + boolean isRenderable(); + + void registerWindowSizeListener(WindowSizeListener listener); + + void removeWindowSizeListener(WindowSizeListener listener); + +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/Sync.java b/jme3-lwjgl3/src/main/java/com/jme3/system/Sync.java similarity index 99% rename from jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/Sync.java rename to jme3-lwjgl3/src/main/java/com/jme3/system/Sync.java index 3161f00a9b..6c0200c5ca 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/Sync.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/Sync.java @@ -29,7 +29,7 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.jme3.system.lwjgl; +package com.jme3.system; /** * A highly accurate sync method that continually adapts to the system @@ -38,7 +38,7 @@ * @author Riven * @author kappaOne */ -class Sync { +public class Sync { /** number of nanoseconds in a second */ private static final long NANOS_IN_SECOND = 1000L * 1000L * 1000L; diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/WindowSizeListener.java b/jme3-lwjgl3/src/main/java/com/jme3/system/WindowSizeListener.java similarity index 97% rename from jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/WindowSizeListener.java rename to jme3-lwjgl3/src/main/java/com/jme3/system/WindowSizeListener.java index 2d2105b802..b3b687f0fc 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/WindowSizeListener.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/WindowSizeListener.java @@ -29,7 +29,9 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.jme3.system.lwjgl; +package com.jme3.system; + +import com.jme3.system.lwjgl.LwjglWindow; /** * Listen to window size changes. Note, GLFW does not support registering multiple callbacks diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java index 03085401a7..6a24633944 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java @@ -43,21 +43,15 @@ import com.jme3.input.lwjgl.GlfwKeyInput; import com.jme3.input.lwjgl.GlfwMouseInput; import com.jme3.math.Vector2f; -import com.jme3.system.AppSettings; -import com.jme3.system.Displays; -import com.jme3.system.JmeContext; -import com.jme3.system.JmeSystem; -import com.jme3.system.NanoTimer; +import com.jme3.system.*; import com.jme3.util.BufferUtils; import com.jme3.util.SafeArrayList; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.nio.ByteBuffer; -import java.nio.IntBuffer; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; -import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; @@ -76,7 +70,7 @@ * * @author Daniel Johansson */ -public abstract class LwjglWindow extends LwjglContext implements Runnable { +public abstract class LwjglWindow extends LwjglContext implements Runnable, GlfwWindow { private static final Logger LOGGER = Logger.getLogger(LwjglWindow.class.getName()); @@ -212,6 +206,7 @@ public LwjglWindow(final JmeContext.Type type) { * * @param listener The WindowSizeListener to register. */ + @Override public void registerWindowSizeListener(WindowSizeListener listener) { windowSizeListeners.add(listener); } @@ -221,6 +216,7 @@ public void registerWindowSizeListener(WindowSizeListener listener) { * * @param listener The WindowSizeListener to remove. */ + @Override public void removeWindowSizeListener(WindowSizeListener listener) { windowSizeListeners.remove(listener); } @@ -863,6 +859,7 @@ public void destroy(boolean waitFor) { } } + @Override public long getWindowHandle() { return window; } @@ -880,6 +877,7 @@ public long getWindowHandle() { * @return The window content scale * @see Window content scale */ + @Override public Vector2f getWindowContentScale(Vector2f store) { if (store == null) store = new Vector2f(); diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/vulkan/LwjglVulkanContext.java b/jme3-lwjgl3/src/main/java/com/jme3/system/vulkan/LwjglVulkanContext.java new file mode 100644 index 0000000000..d72b629447 --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/vulkan/LwjglVulkanContext.java @@ -0,0 +1,457 @@ +package com.jme3.system.vulkan; + +import com.jme3.input.JoyInput; +import com.jme3.input.KeyInput; +import com.jme3.input.MouseInput; +import com.jme3.input.TouchInput; +import com.jme3.input.lwjgl.GlfwJoystickInput; +import com.jme3.input.lwjgl.GlfwKeyInput; +import com.jme3.input.lwjgl.GlfwMouseInput; +import com.jme3.math.Vector2f; +import com.jme3.opencl.Context; +import com.jme3.renderer.Renderer; +import com.jme3.renderer.vulkan.VulkanRenderer; +import com.jme3.system.*; +import com.jme3.system.Sync; +import com.jme3.system.WindowSizeListener; +import org.lwjgl.PointerBuffer; +import org.lwjgl.glfw.*; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static org.lwjgl.system.MemoryUtil.NULL; +import static org.lwjgl.glfw.GLFW.*; + +public class LwjglVulkanContext implements JmeContext, GlfwWindow, Runnable { + + private static final Logger LOGGER = Logger.getLogger(LwjglVulkanContext.class.getName()); + + private long window = NULL; + private SystemListener engine; + private VulkanRenderer renderer; + private Thread engineThread; + private Timer engineTimer; + private final AtomicBoolean created = new AtomicBoolean(false); + private final AtomicBoolean destroy = new AtomicBoolean(false); + private final AtomicBoolean restart = new AtomicBoolean(false); + private final AtomicBoolean focused = new AtomicBoolean(true); + private final AppSettings settings = new AppSettings(true); + private final Collection sizeListeners = new ArrayList<>(); + private final Vector2f windowScale = new Vector2f(1, 1); + private int frameBufferWidth, frameBufferHeight; + + private GLFWErrorCallback errorCallback; + private GLFWWindowFocusCallback focusCallback; + private GLFWWindowSizeCallback sizeCallback; + private GLFWFramebufferSizeCallback fbSizeCallback; + + private GlfwMouseInput mouseInput; + private GlfwKeyInput keyInput; + private JoyInput joyInput; + + private final int[] width = new int[1]; + private final int[] height = new int[1]; + + private boolean autoFlush = true; + + @Override + public void run() { + engineInitialize(); + engineLoop(); + engineTerminate(); + } + + @Override + public void create(boolean waitFor) { + (engineThread = new Thread(this, getClass().getSimpleName())).start(); + if (waitFor) { + waitForContextLifeEvent(true); + } + } + + protected void waitForContextLifeEvent(boolean creation) { + synchronized (created) { + while (created.get() != creation) try { + created.wait(); + } catch (InterruptedException ignored) {} + } + } + + protected void engineInitialize() { + glfwInitialize(); + rendererInitialize(); + engineTimer = new NanoTimer(); + engine.initialize(); + synchronized (created) { + created.set(true); + created.notifyAll(); + } + } + + protected void glfwInitialize() { + glfwSetErrorCallback(errorCallback = new GLFWErrorCallback() { + @Override + public void invoke(int error, long description) { + final String message = GLFWErrorCallback.getDescription(description); + engine.handleError(message, new Exception(message)); + } + }); + if (glfwPlatformSupported(GLFW_PLATFORM_WAYLAND)) { + // Disables the libdecor bar when creating a fullscreen context + // https://www.glfw.org/docs/latest/intro_guide.html#init_hints_wayland + glfwInitHint(GLFW_WAYLAND_LIBDECOR, settings.isFullscreen() ? GLFW_WAYLAND_DISABLE_LIBDECOR : GLFW_WAYLAND_PREFER_LIBDECOR); + } + glfwInit(); + if (!GLFWVulkan.glfwVulkanSupported()) { + throw new NullPointerException("Hardware does not support Vulkan."); + } + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + window = glfwCreateWindow(getSettings().getWidth(), getSettings().getHeight(), getSettings().getTitle(), NULL, NULL); + glfwSetWindowFocusCallback(window, focusCallback = new GLFWWindowFocusCallback() { + @Override + public void invoke(long window, boolean focus) { + if (focus != focused.get()) { + if (!focus) { + engine.loseFocus(); + } else { + engine.gainFocus(); + engineTimer.reset(); + } + focused.set(focus); + } + } + }); + glfwSetWindowSizeCallback(window, sizeCallback = new GLFWWindowSizeCallback() { + @Override + public void invoke(final long window, final int width, final int height) { + updateSizes(); + } + }); + glfwSetFramebufferSizeCallback(window, fbSizeCallback = new GLFWFramebufferSizeCallback() { + @Override + public void invoke(final long window, final int width, final int height) { + updateSizes(); + } + }); + } + + protected void rendererInitialize() { + renderer = new VulkanRenderer(); + } + + protected void engineLoop() { + while (true) { + if (restart.get()) { + restartContext(); + } + engine.update(); + if (renderer != null) { + renderer.postFrame(); + } + syncFrames(); + glfwPollEvents(); + if (destroy.get()) { + break; + } + if (glfwWindowShouldClose(window)) { + engine.requestClose(false); + } + } + } + + protected void syncFrames() { + if (autoFlush) { + Sync.sync(getSettings().getFrameRate()); + } else { + Sync.sync(20); + } + } + + protected void engineTerminate() { + System.out.println("terminate engine"); + engine.destroy(); + //glfwDestroyWindow(window); + glfwDestroy(); + glfwTerminate(); + System.out.println("Engine termination complete. Have a nice day."); + } + + protected void updateSizes() { + // framebuffer size (resolution) may differ from window size (e.g. HiDPI) + // resize window informants + glfwGetWindowSize(window, width, height); + int w = Math.max(width[0], 1); + int h = Math.max(height[0], 1); + if (settings.getWindowWidth() != w || settings.getWindowHeight() != h) { + settings.setWindowSize(w, h); + for (WindowSizeListener l : sizeListeners) { + l.onWindowSizeChanged(w, h); + } + } + // resize framebuffer informants + glfwGetFramebufferSize(window, width, height); + if (width[0] != frameBufferWidth || height[0] != frameBufferHeight) { + settings.setResolution(width[0], height[0]); + engine.reshape(width[0], height[0]); + frameBufferWidth = width[0]; + frameBufferHeight = height[0]; + } + // rescale engine + float xScale = (float)width[0] / w; + float yScale = (float)height[0] / h; + if (windowScale.x != xScale || windowScale.y != yScale) { + engine.rescale(xScale, yScale); + windowScale.set(xScale, yScale); + } + } + + protected void restartContext() { + try { + glfwDestroy(); + glfwInitialize(); + } catch (Exception ex) { + LOGGER.log(Level.SEVERE, "Failed to set display settings!", ex); + } + // Reinitialize context flags and such + rendererInitialize(); + + // We need to reinit the mouse and keyboard input as they are tied to a window handle + if (keyInput != null && keyInput.isInitialized()) { + keyInput.resetContext(); + } + if (mouseInput != null && mouseInput.isInitialized()) { + mouseInput.resetContext(); + } + + LOGGER.fine("Display restarted."); + } + + protected void glfwDestroy() { + try { + if (renderer != null) { + renderer.cleanup(); + } + if (errorCallback != null) { + // We need to specifically set this to null as we might set a new callback before we reinit GLFW + glfwSetErrorCallback(null); + errorCallback.close(); + errorCallback = null; + } + if (sizeCallback != null) { + sizeCallback.close(); + sizeCallback = null; + } + if (fbSizeCallback != null) { + fbSizeCallback.close(); + fbSizeCallback = null; + } + if (focusCallback != null) { + focusCallback.close(); + focusCallback = null; + } + if (window != NULL) { + glfwDestroyWindow(window); + window = NULL; + } + } catch (final Exception ex) { + engine.handleError("Failed to destroy context", ex); + } + } + + @Override + public Type getType() { + return Type.Display; + } + + @Override + public void setSettings(AppSettings settings) { + this.settings.copyFrom(settings); + } + + @Override + public SystemListener getSystemListener() { + return engine; + } + + @Override + public void setSystemListener(SystemListener listener) { + this.engine = listener; + } + + @Override + public AppSettings getSettings() { + return settings; + } + + @Override + public Renderer getRenderer() { + return renderer; + } + + @Override + public Context getOpenCLContext() { + return null; + } + + @Override + public MouseInput getMouseInput() { + if (mouseInput == null) { + mouseInput = new GlfwMouseInput(this); + } + return mouseInput; + } + + @Override + public KeyInput getKeyInput() { + if (keyInput == null) { + keyInput = new GlfwKeyInput(this); + } + return keyInput; + } + + @Override + public JoyInput getJoyInput() { + if (joyInput == null) { + joyInput = new GlfwJoystickInput(); + } + return joyInput; + } + + @Override + public TouchInput getTouchInput() { + return null; + } + + @Override + public Timer getTimer() { + return engineTimer; + } + + @Override + public void setTitle(String title) { + if (window != NULL) { + glfwSetWindowTitle(window, title); + } + } + + @Override + public boolean isCreated() { + return created.get(); + } + + @Override + public long getWindowHandle() { + return window; + } + + @Override + public Vector2f getWindowContentScale(Vector2f store) { + if (store == null) store = new Vector2f(); + glfwGetFramebufferSize(window, width, height); + store.set(width[0], height[0]); + glfwGetWindowSize(window, width, height); + store.x /= width[0]; + store.y /= height[0]; + return store; + } + + @Override + public boolean isRenderable() { + return renderer != null; + } + + @Override + public void registerWindowSizeListener(WindowSizeListener listener) { + sizeListeners.add(listener); + } + + @Override + public void removeWindowSizeListener(WindowSizeListener listener) { + sizeListeners.remove(listener); + } + + @Override + public void setAutoFlushFrames(boolean enabled) { + autoFlush = enabled; + } + + @Override + public void restart() { + if (created.get()) { + restart.set(true); + } else { + LOGGER.warning("Cannot restart context: not yet initialized."); + } + } + + @Override + public void destroy(boolean waitFor) { + destroy.set(true); + if (Thread.currentThread() != engineThread) { + waitForContextLifeEvent(false); + } + } + + @Override + public int getFramebufferHeight() { + glfwGetFramebufferSize(window, width, height); + return width[0]; + } + + @Override + public int getFramebufferWidth() { + glfwGetFramebufferSize(window, width, height); + return height[0]; + } + + @Override + public int getWindowXPosition() { + glfwGetWindowPos(window, width, height); + return width[0]; + } + + @Override + public int getWindowYPosition() { + glfwGetWindowPos(window, width, height); + return height[0]; + } + + @Override + public Displays getDisplays() { + PointerBuffer displays = glfwGetMonitors(); + long primary = glfwGetPrimaryMonitor(); + Displays displayList = new Displays(); + + for (int i = 0; i < displays.limit(); i++) { + long monitorI = displays.get(i); + int monPos = displayList.addNewMonitor(monitorI); + if (primary == monitorI) displayList.setPrimaryDisplay(monPos); + final GLFWVidMode modes = glfwGetVideoMode(monitorI); + String name = glfwGetMonitorName(monitorI); + int width = modes.width(); + int height = modes.height(); + int rate = modes.refreshRate(); + displayList.setInfo(monPos, name, width, height, rate); + LOGGER.log(Level.INFO, "Display id: " + monitorI + " Resolution: " + width + " x " + height + " @ " + rate); + } + return displayList; + } + + @Override + public int getPrimaryDisplay() { + long prim = glfwGetPrimaryMonitor(); + Displays monitors = getDisplays(); + for (int i = 0; i < monitors.size(); i++) { + long monitorI = monitors.get(i).getDisplay(); + if (monitorI == prim) return i; + } + LOGGER.log(Level.SEVERE, "Couldn't locate Primary Monitor in the list of Monitors."); + return -1; + } + +} diff --git a/jme3-niftygui/src/main/java/com/jme3/cinematic/events/GuiEvent.java b/jme3-niftygui/src/main/java/com/jme3/cinematic/events/GuiEvent.java index ee86f1bb5c..7a06770e89 100644 --- a/jme3-niftygui/src/main/java/com/jme3/cinematic/events/GuiEvent.java +++ b/jme3-niftygui/src/main/java/com/jme3/cinematic/events/GuiEvent.java @@ -31,7 +31,7 @@ */ package com.jme3.cinematic.events; -import com.jme3.animation.LoopMode; +import com.jme3.anim.util.LoopMode; import com.jme3.export.InputCapsule; import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; diff --git a/jme3-niftygui/src/main/java/com/jme3/cinematic/events/GuiTrack.java b/jme3-niftygui/src/main/java/com/jme3/cinematic/events/GuiTrack.java index 06e25271a6..f0fe3a039b 100644 --- a/jme3-niftygui/src/main/java/com/jme3/cinematic/events/GuiTrack.java +++ b/jme3-niftygui/src/main/java/com/jme3/cinematic/events/GuiTrack.java @@ -31,7 +31,7 @@ */ package com.jme3.cinematic.events; -import com.jme3.animation.LoopMode; +import com.jme3.anim.util.LoopMode; import com.jme3.export.InputCapsule; import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; diff --git a/jme3-niftygui/src/main/java/com/jme3/niftygui/JmeBatchRenderBackend.java b/jme3-niftygui/src/main/java/com/jme3/niftygui/JmeBatchRenderBackend.java index 4d08588759..7570e28d91 100644 --- a/jme3-niftygui/src/main/java/com/jme3/niftygui/JmeBatchRenderBackend.java +++ b/jme3-niftygui/src/main/java/com/jme3/niftygui/JmeBatchRenderBackend.java @@ -41,12 +41,13 @@ import com.jme3.renderer.Renderer; import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.VertexBuffer.Usage; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture.MagFilter; -import com.jme3.texture.Texture.MinFilter; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Usage; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture.MagFilter; +import com.jme3.texture.GlTexture.MinFilter; import com.jme3.texture.Texture2D; import com.jme3.texture.image.ColorSpace; import com.jme3.texture.image.ImageRaster; @@ -223,11 +224,11 @@ public Image loadImage(final String filename) { // Fix GLES format incompatibility issue with glTexSubImage Renderer renderer = display.getRenderer(); if (renderer == null || renderer.getCaps().contains(Caps.OpenGLES20)) { - if (texture.getImage().getFormat() != Format.RGBA8) { - com.jme3.texture.Image sourceImage = texture.getImage(); + if (texture.getImage().getGlFormat() != Format.RGBA8) { + GlImage sourceImage = texture.getImage(); int size = sourceImage.getWidth() * sourceImage.getHeight() * 4; ByteBuffer buffer = BufferUtils.createByteBuffer(size); - com.jme3.texture.Image rgba8Image = new com.jme3.texture.Image(Format.RGBA8, + GlImage rgba8Image = new GlImage(Format.RGBA8, sourceImage.getWidth(), sourceImage.getHeight(), buffer, @@ -250,7 +251,7 @@ public Image loadImage(final String filename) { @Override public Image loadImage(final ByteBuffer imageData, final int imageWidth, final int imageHeight) { - return new ImageImpl(new com.jme3.texture.Image(Format.RGBA8, imageWidth, imageHeight, imageData, ColorSpace.Linear)); + return new ImageImpl(new GlImage(Format.RGBA8, imageWidth, imageHeight, imageData, ColorSpace.Linear)); } @Override @@ -334,7 +335,7 @@ public void removeImageFromAtlas(final Image image, final int x, final int y, fi initialData.rewind(); modifyTexture( getTextureAtlas(atlasTextureId), - new com.jme3.texture.Image(Format.RGBA8, image.getWidth(), image.getHeight(), initialData, ColorSpace.sRGB), + new GlImage(Format.RGBA8, image.getWidth(), image.getHeight(), initialData, ColorSpace.sRGB), x, y); } @@ -376,7 +377,7 @@ private Texture2D createAtlasTextureInternal(final int width, final int height) // re-use pre-defined initial data instead of creating a new buffer initialData.rewind(); - Texture2D texture = new Texture2D(new com.jme3.texture.Image(Format.RGBA8, width, height, initialData, ColorSpace.sRGB)); + Texture2D texture = new Texture2D(new GlImage(Format.RGBA8, width, height, initialData, ColorSpace.sRGB)); texture.setMinFilter(MinFilter.NearestNoMipMaps); texture.setMagFilter(MagFilter.Nearest); return texture; @@ -384,7 +385,7 @@ private Texture2D createAtlasTextureInternal(final int width, final int height) private void modifyTexture( final Texture2D textureAtlas, - final com.jme3.texture.Image image, + final GlImage image, final int x, final int y) { Renderer renderer = display.getRenderer(); @@ -417,9 +418,9 @@ private int addTexture(final Texture2D texture) { */ private static class ImageImpl implements BatchRenderBackend.Image { - private final com.jme3.texture.Image image; + private final GlImage image; - public ImageImpl(final com.jme3.texture.Image image) { + public ImageImpl(final GlImage image) { this.image = image; } @@ -451,11 +452,11 @@ public int getHeight() { private static class ModifyTexture { private final Texture2D atlas; - private final com.jme3.texture.Image image; + private final GlImage image; private final int x; private final int y; - private ModifyTexture(final Texture2D atlas, final com.jme3.texture.Image image, final int x, final int y) { + private ModifyTexture(final Texture2D atlas, final GlImage image, final int x, final int y) { this.atlas = atlas; this.image = image; this.x = x; @@ -490,10 +491,10 @@ private class Batch { private final static int BATCH_MAX_VERTICES = BATCH_MAX_QUADS * 4; // individual buffers for all the vertex attributes - private final VertexBuffer vertexPos = new VertexBuffer(Type.Position); - private final VertexBuffer vertexTexCoord = new VertexBuffer(Type.TexCoord); - private final VertexBuffer vertexColor = new VertexBuffer(Type.Color); - private final VertexBuffer indexBuffer = new VertexBuffer(Type.Index); + private final GlVertexBuffer vertexPos = new GlVertexBuffer(Type.Position); + private final GlVertexBuffer vertexTexCoord = new GlVertexBuffer(Type.TexCoord); + private final GlVertexBuffer vertexColor = new GlVertexBuffer(Type.Color); + private final GlVertexBuffer indexBuffer = new GlVertexBuffer(Type.Index); private final Mesh mesh = new Mesh(); private final Geometry meshGeometry = new Geometry("nifty-quad", mesh); @@ -515,19 +516,19 @@ private class Batch { public Batch() { // set up mesh - vertexPos.setupData(Usage.Stream, 2, VertexBuffer.Format.Float, BufferUtils.createFloatBuffer(BATCH_MAX_VERTICES * 2)); + vertexPos.setupData(Usage.Stream, 2, GlVertexBuffer.Format.Float, BufferUtils.createFloatBuffer(BATCH_MAX_VERTICES * 2)); vertexPosBuffer = (FloatBuffer) vertexPos.getData(); mesh.setBuffer(vertexPos); - vertexTexCoord.setupData(Usage.Stream, 2, VertexBuffer.Format.Float, BufferUtils.createFloatBuffer(BATCH_MAX_VERTICES * 2)); + vertexTexCoord.setupData(Usage.Stream, 2, GlVertexBuffer.Format.Float, BufferUtils.createFloatBuffer(BATCH_MAX_VERTICES * 2)); vertexTexCoordBuffer = (FloatBuffer) vertexTexCoord.getData(); mesh.setBuffer(vertexTexCoord); - vertexColor.setupData(Usage.Stream, 4, VertexBuffer.Format.Float, BufferUtils.createFloatBuffer(BATCH_MAX_VERTICES * 4)); + vertexColor.setupData(Usage.Stream, 4, GlVertexBuffer.Format.Float, BufferUtils.createFloatBuffer(BATCH_MAX_VERTICES * 4)); vertexColorBuffer = (FloatBuffer) vertexColor.getData(); mesh.setBuffer(vertexColor); - indexBuffer.setupData(Usage.Stream, 3, VertexBuffer.Format.UnsignedShort, BufferUtils.createShortBuffer(BATCH_MAX_QUADS * 2 * 3)); + indexBuffer.setupData(Usage.Stream, 3, GlVertexBuffer.Format.UnsignedShort, BufferUtils.createShortBuffer(BATCH_MAX_QUADS * 2 * 3)); indexBufferBuffer = (ShortBuffer) indexBuffer.getData(); mesh.setBuffer(indexBuffer); diff --git a/jme3-niftygui/src/main/java/com/jme3/niftygui/NiftyJmeDisplay.java b/jme3-niftygui/src/main/java/com/jme3/niftygui/NiftyJmeDisplay.java index 10605ed08a..4269e3ac84 100644 --- a/jme3-niftygui/src/main/java/com/jme3/niftygui/NiftyJmeDisplay.java +++ b/jme3-niftygui/src/main/java/com/jme3/niftygui/NiftyJmeDisplay.java @@ -45,7 +45,7 @@ import com.jme3.renderer.Renderer; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; -import com.jme3.texture.FrameBuffer; +import com.jme3.texture.GlFrameBuffer; import com.jme3.texture.image.ColorSpace; import de.lessvoid.nifty.Nifty; import de.lessvoid.nifty.render.batch.BatchRenderConfiguration; @@ -385,7 +385,7 @@ public void postQueue(RenderQueue rq) { } @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { } @Override diff --git a/jme3-niftygui/src/main/java/com/jme3/niftygui/RenderDeviceJme.java b/jme3-niftygui/src/main/java/com/jme3/niftygui/RenderDeviceJme.java index d3bf2d5fc1..1ff249f522 100644 --- a/jme3-niftygui/src/main/java/com/jme3/niftygui/RenderDeviceJme.java +++ b/jme3-niftygui/src/main/java/com/jme3/niftygui/RenderDeviceJme.java @@ -40,10 +40,10 @@ import com.jme3.renderer.RenderManager; import com.jme3.renderer.Renderer; import com.jme3.scene.Geometry; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Format; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.VertexBuffer.Usage; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Format; +import com.jme3.scene.GlVertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Usage; import com.jme3.scene.shape.Quad; import com.jme3.texture.Texture2D; import com.jme3.texture.image.ColorSpace; @@ -70,9 +70,9 @@ public class RenderDeviceJme implements RenderDevice { private final Quad quad = new Quad(1, -1, true); private final Geometry quadGeom = new Geometry("nifty-quad", quad); private boolean clipWasSet = false; - private final VertexBuffer quadDefaultTC = quad.getBuffer(Type.TexCoord); - private final VertexBuffer quadModTC = quadDefaultTC.clone(); - private final VertexBuffer quadColor; + private final GlVertexBuffer quadDefaultTC = quad.getBuffer(Type.TexCoord); + private final GlVertexBuffer quadModTC = quadDefaultTC.clone(); + private final GlVertexBuffer quadColor; private final Matrix4f tempMat = new Matrix4f(); private final ColorRGBA tempColor = new ColorRGBA(); private final RenderState renderState = new RenderState(); @@ -141,7 +141,7 @@ public RenderDeviceJme(NiftyJmeDisplay display, ColorSpace colorSpace) { this.display = display; this.colorSpace = colorSpace; - quadColor = new VertexBuffer(Type.Color); + quadColor = new GlVertexBuffer(Type.Color); quadColor.setNormalized(true); ByteBuffer bb = BufferUtils.createByteBuffer(4 * 4); quadColor.setupData(Usage.Stream, 4, Format.UnsignedByte, bb); diff --git a/jme3-niftygui/src/main/java/com/jme3/niftygui/RenderImageJme.java b/jme3-niftygui/src/main/java/com/jme3/niftygui/RenderImageJme.java index 2354f1c596..72f156d832 100644 --- a/jme3-niftygui/src/main/java/com/jme3/niftygui/RenderImageJme.java +++ b/jme3-niftygui/src/main/java/com/jme3/niftygui/RenderImageJme.java @@ -32,16 +32,16 @@ package com.jme3.niftygui; import com.jme3.asset.TextureKey; -import com.jme3.texture.Image; -import com.jme3.texture.Texture.MagFilter; -import com.jme3.texture.Texture.MinFilter; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture.MagFilter; +import com.jme3.texture.GlTexture.MinFilter; import com.jme3.texture.Texture2D; import de.lessvoid.nifty.spi.render.RenderImage; public class RenderImageJme implements RenderImage { private Texture2D texture; - private Image image; + private GlImage image; private int width; private int height; diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/FbxLoader.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/FbxLoader.java index 02413e601f..4a033e453f 100644 --- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/FbxLoader.java +++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/FbxLoader.java @@ -31,12 +31,6 @@ */ package com.jme3.scene.plugins.fbx; -import com.jme3.animation.AnimControl; -import com.jme3.animation.Animation; -import com.jme3.animation.Bone; -import com.jme3.animation.BoneTrack; -import com.jme3.animation.Skeleton; -import com.jme3.animation.Track; import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetKey; import com.jme3.asset.AssetLoadException; diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java index e68649f276..ff5374f035 100644 --- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java +++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/SceneLoader.java @@ -11,13 +11,6 @@ import java.util.logging.Level; import java.util.logging.Logger; -import com.jme3.animation.AnimControl; -import com.jme3.animation.Animation; -import com.jme3.animation.Bone; -import com.jme3.animation.BoneTrack; -import com.jme3.animation.Skeleton; -import com.jme3.animation.SkeletonControl; -import com.jme3.animation.Track; import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetKey; import com.jme3.asset.AssetLoader; diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxLimbNode.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxLimbNode.java index 2e8f501eac..d0ea167b22 100644 --- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxLimbNode.java +++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxLimbNode.java @@ -31,8 +31,6 @@ */ package com.jme3.scene.plugins.fbx.anim; -import com.jme3.animation.Bone; -import com.jme3.animation.Skeleton; import com.jme3.asset.AssetManager; import com.jme3.scene.plugins.fbx.node.FbxNode; import java.util.ArrayList; diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxToJmeTrack.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxToJmeTrack.java index 4ae677700f..1ab102fc5d 100644 --- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxToJmeTrack.java +++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/anim/FbxToJmeTrack.java @@ -31,9 +31,6 @@ */ package com.jme3.scene.plugins.fbx.anim; -import com.jme3.animation.BoneTrack; -import com.jme3.animation.SpatialTrack; -import com.jme3.animation.Track; import com.jme3.math.Quaternion; import com.jme3.math.Transform; import com.jme3.math.Vector3f; diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/material/FbxImage.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/material/FbxImage.java index 518dd4134a..ca53746803 100644 --- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/material/FbxImage.java +++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/material/FbxImage.java @@ -37,7 +37,7 @@ import com.jme3.asset.TextureKey; import com.jme3.scene.plugins.fbx.file.FbxElement; import com.jme3.scene.plugins.fbx.obj.FbxObject; -import com.jme3.texture.Image; +import com.jme3.texture.GlImage; import com.jme3.util.PlaceholderAssets; import java.io.ByteArrayInputStream; import java.io.InputStream; @@ -86,7 +86,7 @@ public void fromElement(FbxElement element) { } } - private Image loadImageSafe(AssetManager assetManager, TextureKey texKey) { + private GlImage loadImageSafe(AssetManager assetManager, TextureKey texKey) { try { return assetManager.loadTexture(texKey).getImage(); } catch (AssetNotFoundException ex) { @@ -123,7 +123,7 @@ public TextureKey getTextureKey() { @Override protected Object toJmeObject() { - Image image = null; + GlImage image = null; String fileName = null; String relativeFilePathJme; diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/material/FbxMaterial.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/material/FbxMaterial.java index 8574bf0637..7487f5ba8a 100644 --- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/material/FbxMaterial.java +++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/material/FbxMaterial.java @@ -38,7 +38,7 @@ import com.jme3.math.ColorRGBA; import com.jme3.scene.plugins.fbx.file.FbxElement; import com.jme3.scene.plugins.fbx.obj.FbxObject; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import com.jme3.texture.image.ColorSpace; import java.util.logging.Level; import java.util.logging.Logger; @@ -108,12 +108,12 @@ protected Material toJmeObject() { float shininess = 1f; boolean separateTexCoord = false; - Texture diffuseMap = null; - Texture specularMap = null; - Texture normalMap = null; - Texture transpMap = null; - Texture emitMap = null; - Texture aoMap = null; + GlTexture diffuseMap = null; + GlTexture specularMap = null; + GlTexture normalMap = null; + GlTexture transpMap = null; + GlTexture emitMap = null; + GlTexture aoMap = null; FbxTexture fbxDiffuseMap = null; diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/material/FbxTexture.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/material/FbxTexture.java index cb1cdd0d9e..06cdb4eb56 100644 --- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/material/FbxTexture.java +++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/material/FbxTexture.java @@ -35,16 +35,16 @@ import com.jme3.asset.TextureKey; import com.jme3.scene.plugins.fbx.file.FbxElement; import com.jme3.scene.plugins.fbx.obj.FbxObject; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.MagFilter; -import com.jme3.texture.Texture.MinFilter; -import com.jme3.texture.Texture.WrapAxis; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.MagFilter; +import com.jme3.texture.GlTexture.MinFilter; +import com.jme3.texture.GlTexture.WrapAxis; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.texture.Texture2D; import com.jme3.util.PlaceholderAssets; -public class FbxTexture extends FbxObject { +public class FbxTexture extends FbxObject { private static enum AlphaSource { None, @@ -65,11 +65,11 @@ public String getUvSet() { } @Override - protected Texture toJmeObject() { - Image image = null; + protected GlTexture toJmeObject() { + GlImage image = null; TextureKey key = null; if (media != null) { - image = (Image) media.getJmeObject(); + image = (GlImage) media.getJmeObject(); key = media.getTextureKey(); } if (image == null) { diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/mesh/FbxMesh.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/mesh/FbxMesh.java index 7f48d3e201..c115eda872 100644 --- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/mesh/FbxMesh.java +++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/mesh/FbxMesh.java @@ -31,8 +31,6 @@ */ package com.jme3.scene.plugins.fbx.mesh; -import com.jme3.animation.Bone; -import com.jme3.animation.Skeleton; import com.jme3.asset.AssetManager; import com.jme3.math.ColorRGBA; import com.jme3.math.Vector2f; diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/node/FbxNode.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/node/FbxNode.java index 678e88eae9..f08b664843 100644 --- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/node/FbxNode.java +++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/node/FbxNode.java @@ -31,9 +31,6 @@ */ package com.jme3.scene.plugins.fbx.node; -import com.jme3.animation.AnimControl; -import com.jme3.animation.Skeleton; -import com.jme3.animation.SkeletonControl; import com.jme3.asset.AssetManager; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; @@ -49,7 +46,6 @@ import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.scene.Spatial.CullHint; -import com.jme3.scene.debug.SkeletonDebugger; import com.jme3.scene.plugins.fbx.anim.FbxAnimCurveNode; import com.jme3.scene.plugins.fbx.anim.FbxCluster; import com.jme3.scene.plugins.fbx.anim.FbxLimbNode; diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxImage.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxImage.java index 7ef3299339..632fab9e3d 100644 --- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxImage.java +++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxImage.java @@ -3,8 +3,8 @@ import java.io.File; import com.jme3.asset.AssetManager; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; import com.jme3.texture.image.ColorSpace; import com.jme3.util.BufferUtils; import com.jme3.scene.plugins.fbx.ContentTextureKey; @@ -19,7 +19,7 @@ public class FbxImage extends FbxObject { byte[] content; String imageType; - public Image image; + public GlImage image; public FbxImage(SceneLoader scene, FbxElement element) { super(scene, element); @@ -47,16 +47,16 @@ public FbxImage(SceneLoader scene, FbxElement element) { } - private Image createImage() { + private GlImage createImage() { AssetManager assetManager = scene.assetManager; - Image image = null; + GlImage image = null; if(filename != null) { // Try load by absolute path File file = new File(filename); if(file.exists() && file.isFile()) { File dir = new File(file.getParent()); String locatorPath = dir.getAbsolutePath(); - Texture tex = null; + GlTexture tex = null; try { assetManager.registerLocator(locatorPath, com.jme3.asset.plugins.FileLocator.class); tex = assetManager.loadTexture(file.getName()); @@ -71,7 +71,7 @@ private Image createImage() { // Try load by relative path File dir = new File(scene.sceneFolderName); String locatorPath = dir.getAbsolutePath(); - Texture tex = null; + GlTexture tex = null; try { assetManager.registerLocator(locatorPath, com.jme3.asset.plugins.FileLocator.class); tex = assetManager.loadTexture(relativeFilename); @@ -92,7 +92,7 @@ private Image createImage() { if(filename != null) { String locatorPath = scene.sceneFilename; filename = scene.sceneFilename + File.separatorChar + filename; // Unique path - Texture tex = null; + GlTexture tex = null; try { assetManager.registerLocator(locatorPath, ContentTextureLocator.class); tex = assetManager.loadTexture(new ContentTextureKey(filename, content)); @@ -108,7 +108,7 @@ private Image createImage() { if(relativeFilename != null) { String[] split = relativeFilename.split("[\\\\/]"); String filename = split[split.length - 1]; - Texture tex = null; + GlTexture tex = null; try { tex = assetManager.loadTexture(new ContentTextureKey(scene.currentAssetInfo.getKey().getFolder() + filename, content)); } catch(Exception e) {} @@ -117,7 +117,7 @@ private Image createImage() { } } if(image == null) - return new Image(Image.Format.RGB8, 1, 1, BufferUtils.createByteBuffer((int) (Image.Format.RGB8.getBitsPerPixel() / 8L)), ColorSpace.Linear); + return new GlImage(GlImage.Format.RGB8, 1, 1, BufferUtils.createByteBuffer((int) (GlImage.Format.RGB8.getBitsPerPixel() / 8L)), ColorSpace.Linear); return image; } } diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxMesh.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxMesh.java index 9cc40fe5b9..ac6147aea0 100644 --- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxMesh.java +++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxMesh.java @@ -9,7 +9,7 @@ import com.jme3.asset.AssetLoadException; import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import com.jme3.scene.plugins.fbx.SceneLoader; import com.jme3.scene.plugins.fbx.file.FbxElement; import com.jme3.scene.Mesh.Mode; @@ -342,7 +342,7 @@ private List createGeometries() throws IOException { if(vertices != null) { // Unroll vertices data array FloatBuffer positionBuffer = BufferUtils.createFloatBuffer(vCount * 3); - mesh.setBuffer(VertexBuffer.Type.Position, 3, positionBuffer); + mesh.setBuffer(GlVertexBuffer.Type.Position, 3, positionBuffer); int srcCount = vertices.length / 3; for(int i = 0; i < vCount; ++i) { int index = vertexMap.get(i); @@ -357,7 +357,7 @@ private List createGeometries() throws IOException { if(normals != null) { // Unroll normals data array FloatBuffer normalBuffer = BufferUtils.createFloatBuffer(vCount * 3); - mesh.setBuffer(VertexBuffer.Type.Normal, 3, normalBuffer); + mesh.setBuffer(GlVertexBuffer.Type.Normal, 3, normalBuffer); List mapping = null; if(normalsMapping.equals("ByVertice")) mapping = vertexMap; @@ -379,7 +379,7 @@ else if(normalsMapping.equals("ByPolygonVertex")) if(tangents != null) { // Unroll normals data array FloatBuffer tangentBuffer = BufferUtils.createFloatBuffer(vCount * 4); - mesh.setBuffer(VertexBuffer.Type.Tangent, 4, tangentBuffer); + mesh.setBuffer(GlVertexBuffer.Type.Tangent, 4, tangentBuffer); List mapping = null; if(tangentsMapping.equals("ByVertice")) mapping = vertexMap; @@ -401,7 +401,7 @@ else if(tangentsMapping.equals("ByPolygonVertex")) if(binormals != null) { // Unroll normals data array FloatBuffer binormalBuffer = BufferUtils.createFloatBuffer(vCount * 3); - mesh.setBuffer(VertexBuffer.Type.Binormal, 3, binormalBuffer); + mesh.setBuffer(GlVertexBuffer.Type.Binormal, 3, binormalBuffer); List mapping = null; if(binormalsMapping.equals("ByVertice")) mapping = vertexMap; @@ -453,28 +453,28 @@ else if(binormalsMapping.equals("ByPolygonVertex")) } // Unroll UV data array FloatBuffer uvBuffer = BufferUtils.createFloatBuffer(vCount * 2); - VertexBuffer.Type type = VertexBuffer.Type.TexCoord; + GlVertexBuffer.Type type = GlVertexBuffer.Type.TexCoord; switch(uvLayer) { case 1: - type = VertexBuffer.Type.TexCoord2; + type = GlVertexBuffer.Type.TexCoord2; break; case 2: - type = VertexBuffer.Type.TexCoord3; + type = GlVertexBuffer.Type.TexCoord3; break; case 3: - type = VertexBuffer.Type.TexCoord4; + type = GlVertexBuffer.Type.TexCoord4; break; case 4: - type = VertexBuffer.Type.TexCoord5; + type = GlVertexBuffer.Type.TexCoord5; break; case 5: - type = VertexBuffer.Type.TexCoord6; + type = GlVertexBuffer.Type.TexCoord6; break; case 6: - type = VertexBuffer.Type.TexCoord7; + type = GlVertexBuffer.Type.TexCoord7; break; case 7: - type = VertexBuffer.Type.TexCoord8; + type = GlVertexBuffer.Type.TexCoord8; break; } mesh.setBuffer(type, 2, uvBuffer); @@ -508,7 +508,7 @@ else if(binormalsMapping.equals("ByPolygonVertex")) int materialId = e.getKey(); List indexes = e.getValue(); Mesh newMesh = mesh.clone(); - newMesh.setBuffer(VertexBuffer.Type.Index, 3, toArray(indexes.toArray(new Integer[indexes.size()]))); + newMesh.setBuffer(GlVertexBuffer.Type.Index, 3, toArray(indexes.toArray(new Integer[indexes.size()]))); newMesh.setStatic(); newMesh.updateBound(); newMesh.updateCounts(); diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxNode.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxNode.java index 1554bef9b0..0b1635ec90 100644 --- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxNode.java +++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxNode.java @@ -3,8 +3,6 @@ import java.util.HashMap; import java.util.Map; -import com.jme3.animation.Bone; -import com.jme3.animation.Skeleton; import com.jme3.material.Material; import com.jme3.material.RenderState.FaceCullMode; import com.jme3.math.Matrix4f; diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxSkin.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxSkin.java index 8cb1a2e8a8..10db36749b 100644 --- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxSkin.java +++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxSkin.java @@ -7,9 +7,9 @@ import com.jme3.asset.AssetLoadException; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Type; -import com.jme3.scene.VertexBuffer.Usage; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Usage; import com.jme3.util.BufferUtils; import com.jme3.scene.plugins.fbx.SceneLoader; import com.jme3.scene.plugins.fbx.file.FbxElement; @@ -48,10 +48,10 @@ public void generateSkinning() { for(int i = 0; i < fbxMesh.geometries.size(); ++i) { Mesh mesh = fbxMesh.geometries.get(i).getMesh(); if(mesh != firstMesh) { - mesh.setBuffer(firstMesh.getBuffer(VertexBuffer.Type.BoneWeight)); - mesh.setBuffer(firstMesh.getBuffer(VertexBuffer.Type.BoneIndex)); - mesh.setBuffer(firstMesh.getBuffer(VertexBuffer.Type.HWBoneWeight)); - mesh.setBuffer(firstMesh.getBuffer(VertexBuffer.Type.HWBoneIndex)); + mesh.setBuffer(firstMesh.getBuffer(GlVertexBuffer.Type.BoneWeight)); + mesh.setBuffer(firstMesh.getBuffer(GlVertexBuffer.Type.BoneIndex)); + mesh.setBuffer(firstMesh.getBuffer(GlVertexBuffer.Type.HWBoneWeight)); + mesh.setBuffer(firstMesh.getBuffer(GlVertexBuffer.Type.HWBoneIndex)); } mesh.setMaxNumWeights(maxWeightsPerVert); mesh.generateBindPose(true); @@ -63,12 +63,12 @@ private int generateBoneData(Mesh mesh, FbxMesh fbxMesh) { // Create bone buffers FloatBuffer boneWeightData = BufferUtils.createFloatBuffer(fbxMesh.vCount * 4); ByteBuffer boneIndicesData = BufferUtils.createByteBuffer(fbxMesh.vCount * 4); - mesh.setBuffer(VertexBuffer.Type.BoneWeight, 4, boneWeightData); - mesh.setBuffer(VertexBuffer.Type.BoneIndex, 4, boneIndicesData); - mesh.getBuffer(VertexBuffer.Type.BoneWeight).setUsage(Usage.CpuOnly); - mesh.getBuffer(VertexBuffer.Type.BoneIndex).setUsage(Usage.CpuOnly); - VertexBuffer weightsHW = new VertexBuffer(Type.HWBoneWeight); - VertexBuffer indicesHW = new VertexBuffer(Type.HWBoneIndex); + mesh.setBuffer(GlVertexBuffer.Type.BoneWeight, 4, boneWeightData); + mesh.setBuffer(GlVertexBuffer.Type.BoneIndex, 4, boneIndicesData); + mesh.getBuffer(GlVertexBuffer.Type.BoneWeight).setUsage(Usage.CpuOnly); + mesh.getBuffer(GlVertexBuffer.Type.BoneIndex).setUsage(Usage.CpuOnly); + GlVertexBuffer weightsHW = new GlVertexBuffer(Type.HWBoneWeight); + GlVertexBuffer indicesHW = new GlVertexBuffer(Type.HWBoneIndex); indicesHW.setUsage(Usage.CpuOnly); // Setting usage to CpuOnly so that the buffer is not send empty to the GPU weightsHW.setUsage(Usage.CpuOnly); mesh.setBuffer(weightsHW); diff --git a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxTexture.java b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxTexture.java index 1ccc01141d..7525dac8ab 100644 --- a/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxTexture.java +++ b/jme3-plugins/src/fbx/java/com/jme3/scene/plugins/fbx/objects/FbxTexture.java @@ -1,8 +1,8 @@ package com.jme3.scene.plugins.fbx.objects; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.scene.plugins.fbx.SceneLoader; import com.jme3.scene.plugins.fbx.file.FbxElement; @@ -11,7 +11,7 @@ public class FbxTexture extends FbxObject { String bindType; String filename; - public Texture texture; + public GlTexture texture; public FbxTexture(SceneLoader scene, FbxElement element) { super(scene, element); diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java index 3002515633..6137ebc0c0 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java @@ -46,7 +46,7 @@ import com.jme3.scene.control.CameraControl; import com.jme3.scene.mesh.MorphTarget; import static com.jme3.scene.plugins.gltf.GltfUtils.*; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; import com.jme3.util.IntMap; import com.jme3.util.mikktspace.MikktspaceTangentGenerator; @@ -380,7 +380,7 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException { mesh.setMode(getMeshMode(mode)); Integer indices = getAsInteger(meshObject, "indices"); if (indices != null) { - mesh.setBuffer(readAccessorData(indices, new VertexBufferPopulator(VertexBuffer.Type.Index))); + mesh.setBuffer(readAccessorData(indices, new VertexBufferPopulator(GlVertexBuffer.Type.Index))); } JsonObject attributes = meshObject.getAsJsonObject("attributes"); assertNotNull(attributes, "No attributes defined for mesh " + mesh); @@ -404,7 +404,7 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException { SkinBuffers buffs = getSkinBuffers(bufferType); buffs.weights = readAccessorData(entry.getValue().getAsInt(), new FloatArrayPopulator()); } else { - VertexBuffer vb = readAccessorData(entry.getValue().getAsInt(), + GlVertexBuffer vb = readAccessorData(entry.getValue().getAsInt(), new VertexBufferPopulator(getVertexBufferType(bufferType))); if (vb != null) { mesh.setBuffer(vb); @@ -417,15 +417,15 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException { } handleSkinningBuffers(mesh, skinBuffers); - if (mesh.getBuffer(VertexBuffer.Type.BoneIndex) != null) { + if (mesh.getBuffer(GlVertexBuffer.Type.BoneIndex) != null) { // the mesh has some skinning, let's create needed buffers for HW skinning // creating empty buffers for HW skinning // the buffers will be set up if ever used. - VertexBuffer weightsHW = new VertexBuffer(VertexBuffer.Type.HWBoneWeight); - VertexBuffer indicesHW = new VertexBuffer(VertexBuffer.Type.HWBoneIndex); + GlVertexBuffer weightsHW = new GlVertexBuffer(GlVertexBuffer.Type.HWBoneWeight); + GlVertexBuffer indicesHW = new GlVertexBuffer(GlVertexBuffer.Type.HWBoneIndex); // setting usage to cpuOnly so that the buffer is not sent empty to the GPU - indicesHW.setUsage(VertexBuffer.Usage.CpuOnly); - weightsHW.setUsage(VertexBuffer.Usage.CpuOnly); + indicesHW.setUsage(GlVertexBuffer.Usage.CpuOnly); + weightsHW.setUsage(GlVertexBuffer.Usage.CpuOnly); mesh.setBuffer(weightsHW); mesh.setBuffer(indicesHW); mesh.generateBindPose(); @@ -450,8 +450,8 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException { } for (Map.Entry entry : target.getAsJsonObject().entrySet()) { String bufferType = entry.getKey(); - VertexBuffer.Type type = getVertexBufferType(bufferType); - VertexBuffer vb = readAccessorData(entry.getValue().getAsInt(), + GlVertexBuffer.Type type = getVertexBufferType(bufferType); + GlVertexBuffer vb = readAccessorData(entry.getValue().getAsInt(), new VertexBufferPopulator(type)); if (vb != null) { morphTarget.setBuffer(type, (FloatBuffer) vb.getData()); @@ -476,7 +476,7 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException { // Alpha blending is enabled for this material. Let's place the geom in the transparent bucket. geom.setQueueBucket(RenderQueue.Bucket.Transparent); } - if (useNormalsFlag && mesh.getBuffer(VertexBuffer.Type.Tangent) == null) { + if (useNormalsFlag && mesh.getBuffer(GlVertexBuffer.Type.Tangent) == null) { // No tangent buffer, but there is a normal map, we have to generate them using MikktSpace MikktspaceTangentGenerator.generate(geom); } @@ -539,7 +539,7 @@ private R readAccessorData(int accessorIndex, Populator populator) throws } public Object readBuffer(Integer bufferViewIndex, int byteOffset, int count, Object store, - int numComponents, VertexBuffer.Format format) throws IOException { + int numComponents, GlVertexBuffer.Format format) throws IOException { JsonObject bufferView = bufferViews.get(bufferViewIndex).getAsJsonObject(); Integer bufferIndex = getAsInteger(bufferView, "buffer"); assertNotNull(bufferIndex, "No buffer defined for bufferView " + bufferViewIndex); @@ -761,7 +761,7 @@ public Texture2D readTexture(JsonObject texture, boolean flip) throws IOExceptio if (samplerIndex != null) { texture2d = readSampler(samplerIndex, texture2d); } else { - texture2d.setWrap(Texture.WrapMode.Repeat); + texture2d.setWrap(GlTexture.WrapMode.Repeat); } texture2d = customContentManager.readExtensionAndExtras("texture", texture, texture2d); @@ -784,7 +784,7 @@ public Texture2D readImage(int sourceIndex, boolean flip) throws IOException { if (uri == null) { assertNotNull(bufferView, "Image " + sourceIndex + " should either have an uri or a bufferView"); assertNotNull(mimeType, "Image " + sourceIndex + " should have a mimeType"); - byte[] data = (byte[]) readBuffer(bufferView, 0, -1, null, 1, VertexBuffer.Format.Byte); + byte[] data = (byte[]) readBuffer(bufferView, 0, -1, null, 1, GlVertexBuffer.Format.Byte); String extension = mimeType.split("/")[1]; TextureKey key = new TextureKey("image" + sourceIndex + "." + extension, flip); result = (Texture2D) info.getManager().loadAssetFromStream(key, new ByteArrayInputStream(data)); @@ -801,7 +801,7 @@ public Texture2D readImage(int sourceIndex, boolean flip) throws IOException { // external file image String decoded = decodeUri(uri); TextureKey key = new TextureKey(info.getKey().getFolder() + decoded, flip); - Texture tex = info.getManager().loadTexture(key); + GlTexture tex = info.getManager().loadTexture(key); result = (Texture2D) tex; } return result; @@ -1016,10 +1016,10 @@ public Texture2D readSampler(int samplerIndex, Texture2D texture) throws IOExcep throw new AssetLoadException("No samplers defined"); } JsonObject sampler = samplers.get(samplerIndex).getAsJsonObject(); - Texture.MagFilter magFilter = getMagFilter(getAsInteger(sampler, "magFilter")); - Texture.MinFilter minFilter = getMinFilter(getAsInteger(sampler, "minFilter")); - Texture.WrapMode wrapS = getWrapMode(getAsInteger(sampler, "wrapS")); - Texture.WrapMode wrapT = getWrapMode(getAsInteger(sampler, "wrapT")); + GlTexture.MagFilter magFilter = getMagFilter(getAsInteger(sampler, "magFilter")); + GlTexture.MinFilter minFilter = getMinFilter(getAsInteger(sampler, "minFilter")); + GlTexture.WrapMode wrapS = getWrapMode(getAsInteger(sampler, "wrapS")); + GlTexture.WrapMode wrapT = getWrapMode(getAsInteger(sampler, "wrapT")); if (magFilter != null) { texture.setMagFilter(magFilter); @@ -1027,8 +1027,8 @@ public Texture2D readSampler(int samplerIndex, Texture2D texture) throws IOExcep if (minFilter != null) { texture.setMinFilter(minFilter); } - texture.setWrap(Texture.WrapAxis.S, wrapS); - texture.setWrap(Texture.WrapAxis.T, wrapT); + texture.setWrap(GlTexture.WrapAxis.S, wrapS); + texture.setWrap(GlTexture.WrapAxis.T, wrapT); texture = customContentManager.readExtensionAndExtras("texture.sampler", sampler, texture); @@ -1312,33 +1312,33 @@ T populate(Integer bufferViewIndex, int componentType, String type, int count, i boolean normalized) throws IOException; } - private class VertexBufferPopulator implements Populator { - VertexBuffer.Type bufferType; + private class VertexBufferPopulator implements Populator { + GlVertexBuffer.Type bufferType; - public VertexBufferPopulator(VertexBuffer.Type bufferType) { + public VertexBufferPopulator(GlVertexBuffer.Type bufferType) { this.bufferType = bufferType; } @Override - public VertexBuffer populate(Integer bufferViewIndex, int componentType, String type, int count, - int byteOffset, boolean normalized) throws IOException { + public GlVertexBuffer populate(Integer bufferViewIndex, int componentType, String type, int count, + int byteOffset, boolean normalized) throws IOException { if (bufferType == null) { logger.log(Level.WARNING, "could not assign data to any VertexBuffer type for buffer view {0}", bufferViewIndex); return null; } - VertexBuffer vb = new VertexBuffer(bufferType); - VertexBuffer.Format format = getVertexBufferFormat(componentType); - VertexBuffer.Format originalFormat = format; + GlVertexBuffer vb = new GlVertexBuffer(bufferType); + GlVertexBuffer.Format format = getVertexBufferFormat(componentType); + GlVertexBuffer.Format originalFormat = format; if (normalized) { // Some float data can be packed into short buffers, // "normalized" means they have to be unpacked. // In that case the buffer is a FloatBuffer - format = VertexBuffer.Format.Float; + format = GlVertexBuffer.Format.Float; } int numComponents = getNumberOfComponents(type); - Buffer buff = VertexBuffer.createBuffer(format, numComponents, count); + Buffer buff = GlVertexBuffer.createBuffer(format, numComponents, count); int bufferSize = numComponents * count; if (bufferViewIndex == null) { // no referenced buffer, specs says to pad the buffer with zeros. @@ -1347,10 +1347,10 @@ public VertexBuffer populate(Integer bufferViewIndex, int componentType, String readBuffer(bufferViewIndex, byteOffset, count, buff, numComponents, originalFormat); } - if (bufferType == VertexBuffer.Type.Index) { + if (bufferType == GlVertexBuffer.Type.Index) { numComponents = 3; } - vb.setupData(VertexBuffer.Usage.Dynamic, numComponents, format, buff); + vb.setupData(GlVertexBuffer.Usage.Dynamic, numComponents, format, buff); return vb; } @@ -1470,9 +1470,9 @@ public SkinBuffers populate(Integer bufferViewIndex, int componentType, String t int numComponents = getNumberOfComponents(type); // can be bytes or shorts. - VertexBuffer.Format format = VertexBuffer.Format.Byte; + GlVertexBuffer.Format format = GlVertexBuffer.Format.Byte; if (componentType == 5123) { - format = VertexBuffer.Format.Short; + format = GlVertexBuffer.Format.Short; } int dataSize = numComponents * count; diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java index 8f015f28db..04e50a3690 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java @@ -40,7 +40,7 @@ import com.jme3.plugins.json.Json; import com.jme3.plugins.json.JsonParser; import com.jme3.scene.*; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import com.jme3.util.*; import java.io.*; import java.nio.*; @@ -96,20 +96,20 @@ public static Mesh.Mode getMeshMode(Integer mode) { return Mesh.Mode.Triangles; } - public static VertexBuffer.Format getVertexBufferFormat(int componentType) { + public static GlVertexBuffer.Format getVertexBufferFormat(int componentType) { switch (componentType) { case 5120: - return VertexBuffer.Format.Byte; + return GlVertexBuffer.Format.Byte; case 5121: - return VertexBuffer.Format.UnsignedByte; + return GlVertexBuffer.Format.UnsignedByte; case 5122: - return VertexBuffer.Format.Short; + return GlVertexBuffer.Format.Short; case 5123: - return VertexBuffer.Format.UnsignedShort; + return GlVertexBuffer.Format.UnsignedShort; case 5125: - return VertexBuffer.Format.UnsignedInt; + return GlVertexBuffer.Format.UnsignedInt; case 5126: - return VertexBuffer.Format.Float; + return GlVertexBuffer.Format.Float; default: throw new AssetLoadException("Illegal component type: " + componentType); } @@ -136,36 +136,36 @@ public static int getNumberOfComponents(String type) { } } - public static VertexBuffer.Type getVertexBufferType(String attribute) { + public static GlVertexBuffer.Type getVertexBufferType(String attribute) { switch (attribute) { case "POSITION": - return VertexBuffer.Type.Position; + return GlVertexBuffer.Type.Position; case "NORMAL": - return VertexBuffer.Type.Normal; + return GlVertexBuffer.Type.Normal; case "TANGENT": - return VertexBuffer.Type.Tangent; + return GlVertexBuffer.Type.Tangent; case "TEXCOORD_0": - return VertexBuffer.Type.TexCoord; + return GlVertexBuffer.Type.TexCoord; case "TEXCOORD_1": - return VertexBuffer.Type.TexCoord2; + return GlVertexBuffer.Type.TexCoord2; case "TEXCOORD_2": - return VertexBuffer.Type.TexCoord3; + return GlVertexBuffer.Type.TexCoord3; case "TEXCOORD_3": - return VertexBuffer.Type.TexCoord4; + return GlVertexBuffer.Type.TexCoord4; case "TEXCOORD_4": - return VertexBuffer.Type.TexCoord5; + return GlVertexBuffer.Type.TexCoord5; case "TEXCOORD_5": - return VertexBuffer.Type.TexCoord6; + return GlVertexBuffer.Type.TexCoord6; case "TEXCOORD_6": - return VertexBuffer.Type.TexCoord7; + return GlVertexBuffer.Type.TexCoord7; case "TEXCOORD_7": - return VertexBuffer.Type.TexCoord8; + return GlVertexBuffer.Type.TexCoord8; case "COLOR_0": - return VertexBuffer.Type.Color; + return GlVertexBuffer.Type.Color; case "JOINTS_0": - return VertexBuffer.Type.BoneIndex; + return GlVertexBuffer.Type.BoneIndex; case "WEIGHTS_0": - return VertexBuffer.Type.BoneWeight; + return GlVertexBuffer.Type.BoneWeight; default: logger.log(Level.WARNING, "Unsupported Vertex Buffer type " + attribute); return null; @@ -178,52 +178,52 @@ public static int getIndex(String name) { return Integer.parseInt(num); } - public static Texture.MagFilter getMagFilter(Integer value) { + public static GlTexture.MagFilter getMagFilter(Integer value) { if (value == null) { return null; } switch (value) { case 9728: - return Texture.MagFilter.Nearest; + return GlTexture.MagFilter.Nearest; case 9729: - return Texture.MagFilter.Bilinear; + return GlTexture.MagFilter.Bilinear; } return null; } - public static Texture.MinFilter getMinFilter(Integer value) { + public static GlTexture.MinFilter getMinFilter(Integer value) { if (value == null) { return null; } switch (value) { case 9728: - return Texture.MinFilter.NearestNoMipMaps; + return GlTexture.MinFilter.NearestNoMipMaps; case 9729: - return Texture.MinFilter.BilinearNoMipMaps; + return GlTexture.MinFilter.BilinearNoMipMaps; case 9984: - return Texture.MinFilter.NearestNearestMipMap; + return GlTexture.MinFilter.NearestNearestMipMap; case 9985: - return Texture.MinFilter.BilinearNearestMipMap; + return GlTexture.MinFilter.BilinearNearestMipMap; case 9986: - return Texture.MinFilter.NearestLinearMipMap; + return GlTexture.MinFilter.NearestLinearMipMap; case 9987: - return Texture.MinFilter.Trilinear; + return GlTexture.MinFilter.Trilinear; } return null; } - public static Texture.WrapMode getWrapMode(Integer value) { + public static GlTexture.WrapMode getWrapMode(Integer value) { if (value == null) { - return Texture.WrapMode.Repeat; + return GlTexture.WrapMode.Repeat; } switch (value) { case 33071: - return Texture.WrapMode.EdgeClamp; + return GlTexture.WrapMode.EdgeClamp; case 33648: - return Texture.WrapMode.MirroredRepeat; + return GlTexture.WrapMode.MirroredRepeat; default: - return Texture.WrapMode.Repeat; + return GlTexture.WrapMode.Repeat; } } @@ -282,7 +282,7 @@ public static void padBuffer(Object store, int bufferSize) { } } - public static void populateBuffer(Object store, byte[] source, int count, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + public static void populateBuffer(Object store, byte[] source, int count, int byteOffset, int byteStride, int numComponents, GlVertexBuffer.Format format) throws IOException { if (store instanceof Buffer) { Buffer buffer = (Buffer) store; @@ -318,7 +318,7 @@ public static void populateBuffer(Object store, byte[] source, int count, int by } } - private static void populateByteBuffer(ByteBuffer buffer, byte[] source, int count, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) { + private static void populateByteBuffer(ByteBuffer buffer, byte[] source, int count, int byteOffset, int byteStride, int numComponents, GlVertexBuffer.Format format) { int componentSize = format.getComponentSize(); int index = byteOffset; int dataLength = componentSize * numComponents; @@ -332,7 +332,7 @@ private static void populateByteBuffer(ByteBuffer buffer, byte[] source, int cou } } - private static void populateShortBuffer(ShortBuffer buffer, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + private static void populateShortBuffer(ShortBuffer buffer, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, GlVertexBuffer.Format format) throws IOException { int componentSize = format.getComponentSize(); int index = byteOffset; int dataLength = componentSize * numComponents; @@ -352,7 +352,7 @@ private static void populateShortBuffer(ShortBuffer buffer, LittleEndien stream, } - private static void populateIntBuffer(IntBuffer buffer, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + private static void populateIntBuffer(IntBuffer buffer, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, GlVertexBuffer.Format format) throws IOException { int componentSize = format.getComponentSize(); int index = byteOffset; int dataLength = componentSize * numComponents; @@ -370,7 +370,7 @@ private static void populateIntBuffer(IntBuffer buffer, LittleEndien stream, int } } - private static void populateFloatBuffer(FloatBuffer buffer, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + private static void populateFloatBuffer(FloatBuffer buffer, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, GlVertexBuffer.Format format) throws IOException { int componentSize = format.getComponentSize(); int index = byteOffset; int dataLength = componentSize * numComponents; @@ -388,7 +388,7 @@ private static void populateFloatBuffer(FloatBuffer buffer, LittleEndien stream, } } - public static float readAsFloat(LittleEndien stream, VertexBuffer.Format format) throws IOException { + public static float readAsFloat(LittleEndien stream, GlVertexBuffer.Format format) throws IOException { //We may have packed data so depending on the format, we need to read data differently and unpack it // Implementations must use following equations to get corresponding floating-point value f from a normalized integer c and vise-versa: // accessor.componentType int-to-float float-to-int @@ -417,7 +417,7 @@ public static float readAsFloat(LittleEndien stream, VertexBuffer.Format format) } - private static void populateByteArray(byte[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + private static void populateByteArray(byte[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, GlVertexBuffer.Format format) throws IOException { int componentSize = format.getComponentSize(); int index = byteOffset; int dataLength = componentSize * numComponents; @@ -455,7 +455,7 @@ private static void read(LittleEndien stream, byte[] buffer, int length) throws } } - private static void populateShortArray(short[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + private static void populateShortArray(short[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, GlVertexBuffer.Format format) throws IOException { int componentSize = format.getComponentSize(); int index = byteOffset; int dataLength = componentSize * numComponents; @@ -548,16 +548,16 @@ public int compare(GltfLoader.WeightData o1, GltfLoader.WeightData o2) { public static void setSkinBuffers(Mesh mesh, short[] jointsArray, float[] weightsArray, int componentSize) { if (componentSize == 1) { - mesh.setBuffer(VertexBuffer.Type.BoneIndex, 4, BufferUtils.createByteBuffer(toByteArray(jointsArray))); + mesh.setBuffer(GlVertexBuffer.Type.BoneIndex, 4, BufferUtils.createByteBuffer(toByteArray(jointsArray))); } else { - mesh.setBuffer(VertexBuffer.Type.BoneIndex, 4, BufferUtils.createShortBuffer(jointsArray)); + mesh.setBuffer(GlVertexBuffer.Type.BoneIndex, 4, BufferUtils.createShortBuffer(jointsArray)); } - mesh.setBuffer(VertexBuffer.Type.BoneWeight, 4, BufferUtils.createFloatBuffer(weightsArray)); - mesh.getBuffer(VertexBuffer.Type.BoneIndex).setUsage(VertexBuffer.Usage.CpuOnly); - mesh.getBuffer(VertexBuffer.Type.BoneWeight).setUsage(VertexBuffer.Usage.CpuOnly); + mesh.setBuffer(GlVertexBuffer.Type.BoneWeight, 4, BufferUtils.createFloatBuffer(weightsArray)); + mesh.getBuffer(GlVertexBuffer.Type.BoneIndex).setUsage(GlVertexBuffer.Usage.CpuOnly); + mesh.getBuffer(GlVertexBuffer.Type.BoneWeight).setUsage(GlVertexBuffer.Usage.CpuOnly); } - private static void populateFloatArray(float[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + private static void populateFloatArray(float[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, GlVertexBuffer.Format format) throws IOException { int componentSize = format.getComponentSize(); int index = byteOffset; int dataLength = componentSize * numComponents; @@ -577,7 +577,7 @@ private static void populateFloatArray(float[] array, LittleEndien stream, int c } } - private static void populateVector3fArray(Vector3f[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + private static void populateVector3fArray(Vector3f[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, GlVertexBuffer.Format format) throws IOException { int componentSize = format.getComponentSize(); int index = byteOffset; int dataLength = componentSize * numComponents; @@ -601,7 +601,7 @@ private static void populateVector3fArray(Vector3f[] array, LittleEndien stream, } } - private static void populateQuaternionArray(Quaternion[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + private static void populateQuaternionArray(Quaternion[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, GlVertexBuffer.Format format) throws IOException { int componentSize = format.getComponentSize(); int index = byteOffset; int dataLength = componentSize * numComponents; @@ -625,7 +625,7 @@ private static void populateQuaternionArray(Quaternion[] array, LittleEndien str } } - private static void populateMatrix4fArray(Matrix4f[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, VertexBuffer.Format format) throws IOException { + private static void populateMatrix4fArray(Matrix4f[] array, LittleEndien stream, int count, int byteOffset, int byteStride, int numComponents, GlVertexBuffer.Format format) throws IOException { int componentSize = format.getComponentSize(); int index = byteOffset; int dataLength = componentSize * numComponents; @@ -859,7 +859,7 @@ public static Spatial findCommonAncestor(List spatials) { } public static void dumpMesh(Mesh m) { - for (VertexBuffer vertexBuffer : m.getBufferList().getArray()) { + for (GlVertexBuffer vertexBuffer : m.getBufferList().getArray()) { System.err.println(vertexBuffer.getBufferType()); System.err.println(vertexBuffer.getFormat()); if (vertexBuffer.getData() instanceof FloatBuffer) { diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/MaterialAdapter.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/MaterialAdapter.java index b6b10f9c83..e1cf01d08f 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/MaterialAdapter.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/MaterialAdapter.java @@ -37,7 +37,7 @@ import com.jme3.material.*; import com.jme3.math.*; import com.jme3.shader.VarType; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import java.util.HashMap; import java.util.Map; @@ -94,7 +94,7 @@ public void setParam(String gltfParamName, Object value) { return; } MatParam param; - if (value instanceof Texture) { + if (value instanceof GlTexture) { MatParam defParam = getMaterial().getMaterialDef().getMaterialParam(name); if (defParam == null) { throw new AssetLoadException("Material definition " + getMaterialDefPath() + " has not param with name" + name); @@ -102,10 +102,10 @@ public void setParam(String gltfParamName, Object value) { if (!(defParam instanceof MatParamTexture)) { throw new AssetLoadException("param with name" + name + "in material definition " + getMaterialDefPath() + " should be a texture param"); } - param = new MatParamTexture(VarType.Texture2D, name, (Texture) value, ((MatParamTexture) defParam).getColorSpace()); + param = new MatParamTexture(VarType.Texture2D, name, (GlTexture) value, ((MatParamTexture) defParam).getColorSpace()); param = adaptMatParam(param); if (param != null) { - getMaterial().setTextureParam(param.getName(), param.getVarType(), (Texture) param.getValue()); + getMaterial().setTextureParam(param.getName(), param.getVarType(), (GlTexture) param.getValue()); } } else { param = new MatParam(getVarType(value), name, value); diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/TextureTransformExtensionLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/TextureTransformExtensionLoader.java index aff6fbdcf1..781c15c343 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/TextureTransformExtensionLoader.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/TextureTransformExtensionLoader.java @@ -38,7 +38,7 @@ import com.jme3.math.Matrix3f; import com.jme3.math.Vector3f; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import static com.jme3.scene.plugins.gltf.GltfUtils.getAsInteger; import static com.jme3.scene.plugins.gltf.GltfUtils.getVertexBufferType; import com.jme3.texture.Texture2D; @@ -69,13 +69,13 @@ public class TextureTransformExtensionLoader implements ExtensionLoader { * @param transform The matrix containing the scale/rotate/translate transformations * @param verType The vertex buffer type from which to retrieve the UV coordinates */ - private void uvTransform(Mesh mesh, Matrix3f transform, VertexBuffer.Type verType) { + private void uvTransform(Mesh mesh, Matrix3f transform, GlVertexBuffer.Type verType) { if (!transform.isIdentity()) { // if transform is the identity matrix, there's nothing to do - VertexBuffer tc = mesh.getBuffer(verType); + GlVertexBuffer tc = mesh.getBuffer(verType); if (tc == null) { throw new IllegalStateException("The mesh has no texture coordinates"); } - if (tc.getFormat() != VertexBuffer.Format.Float) { + if (tc.getFormat() != GlVertexBuffer.Format.Float) { throw new UnsupportedOperationException("Only float texture coord format is supported"); } if (tc.getNumComponents() != 2) { diff --git a/jme3-plugins/src/main/java/com/jme3/material/plugin/export/material/J3MOutputCapsule.java b/jme3-plugins/src/main/java/com/jme3/material/plugin/export/material/J3MOutputCapsule.java index 950466b4ba..d701ef799c 100644 --- a/jme3-plugins/src/main/java/com/jme3/material/plugin/export/material/J3MOutputCapsule.java +++ b/jme3-plugins/src/main/java/com/jme3/material/plugin/export/material/J3MOutputCapsule.java @@ -38,8 +38,8 @@ import com.jme3.material.MatParamTexture; import com.jme3.math.*; import com.jme3.shader.VarType; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.util.IntMap; import java.io.IOException; import java.io.Writer; @@ -157,7 +157,7 @@ private String formatMatParam(MatParam param){ protected static String formatMatParamTexture(MatParamTexture param) { StringBuilder ret = new StringBuilder(); - Texture tex = (Texture) param.getValue(); + GlTexture tex = (GlTexture) param.getValue(); TextureKey key; if (tex != null) { key = (TextureKey) tex.getKey(); @@ -166,16 +166,16 @@ protected static String formatMatParamTexture(MatParamTexture param) { ret.append("Flip "); } - ret.append(formatWrapMode(tex, Texture.WrapAxis.S)); - ret.append(formatWrapMode(tex, Texture.WrapAxis.T)); - ret.append(formatWrapMode(tex, Texture.WrapAxis.R)); + ret.append(formatWrapMode(tex, GlTexture.WrapAxis.S)); + ret.append(formatWrapMode(tex, GlTexture.WrapAxis.T)); + ret.append(formatWrapMode(tex, GlTexture.WrapAxis.R)); //Min and Mag filter - if (tex.getMinFilter() != Texture.MinFilter.Trilinear) { + if (tex.getMinFilter() != GlTexture.MinFilter.Trilinear) { ret.append("Min").append(tex.getMinFilter().name()).append(" "); } - if (tex.getMagFilter() != Texture.MagFilter.Bilinear) { + if (tex.getMagFilter() != GlTexture.MagFilter.Bilinear) { ret.append("Mag").append(tex.getMagFilter().name()).append(" "); } @@ -185,7 +185,7 @@ protected static String formatMatParamTexture(MatParamTexture param) { return ret.toString(); } - protected static String formatWrapMode(Texture texVal, Texture.WrapAxis axis) { + protected static String formatWrapMode(GlTexture texVal, GlTexture.WrapAxis axis) { WrapMode mode; try { mode = texVal.getWrap(axis); diff --git a/jme3-plugins/src/main/java/com/jme3/scene/plugins/IrUtils.java b/jme3-plugins/src/main/java/com/jme3/scene/plugins/IrUtils.java index 30338d27fd..5598aad9a4 100644 --- a/jme3-plugins/src/main/java/com/jme3/scene/plugins/IrUtils.java +++ b/jme3-plugins/src/main/java/com/jme3/scene/plugins/IrUtils.java @@ -33,7 +33,7 @@ import com.jme3.math.Vector4f; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import com.jme3.scene.mesh.IndexBuffer; import com.jme3.scene.mesh.IndexIntBuffer; import com.jme3.scene.mesh.IndexShortBuffer; @@ -300,45 +300,45 @@ public static Mesh convertIrMeshToJmeMesh(IrMesh mesh) { IrVertex inspectionVertex = vertices.get(0); if (inspectionVertex.pos != null) { posBuf = BufferUtils.createVector3Buffer(vertices.size()); - jmeMesh.setBuffer(VertexBuffer.Type.Position, 3, posBuf); + jmeMesh.setBuffer(GlVertexBuffer.Type.Position, 3, posBuf); } if (inspectionVertex.norm != null) { normBuf = BufferUtils.createVector3Buffer(vertices.size()); - jmeMesh.setBuffer(VertexBuffer.Type.Normal, 3, normBuf); + jmeMesh.setBuffer(GlVertexBuffer.Type.Normal, 3, normBuf); } if (inspectionVertex.tang4d != null) { tangBuf = BufferUtils.createFloatBuffer(vertices.size() * 4); - jmeMesh.setBuffer(VertexBuffer.Type.Tangent, 4, tangBuf); + jmeMesh.setBuffer(GlVertexBuffer.Type.Tangent, 4, tangBuf); } if (inspectionVertex.tang != null || inspectionVertex.bitang != null) { throw new IllegalStateException("Mesh is using 3D tangents, must be converted to 4D tangents first."); } if (inspectionVertex.uv0 != null) { uv0Buf = BufferUtils.createVector2Buffer(vertices.size()); - jmeMesh.setBuffer(VertexBuffer.Type.TexCoord, 2, uv0Buf); + jmeMesh.setBuffer(GlVertexBuffer.Type.TexCoord, 2, uv0Buf); } if (inspectionVertex.uv1 != null) { uv1Buf = BufferUtils.createVector2Buffer(vertices.size()); - jmeMesh.setBuffer(VertexBuffer.Type.TexCoord2, 2, uv1Buf); + jmeMesh.setBuffer(GlVertexBuffer.Type.TexCoord2, 2, uv1Buf); } if (inspectionVertex.color != null) { colorBuf = BufferUtils.createByteBuffer(vertices.size() * 4); - jmeMesh.setBuffer(VertexBuffer.Type.Color, 4, colorBuf); - jmeMesh.getBuffer(VertexBuffer.Type.Color).setNormalized(true); + jmeMesh.setBuffer(GlVertexBuffer.Type.Color, 4, colorBuf); + jmeMesh.getBuffer(GlVertexBuffer.Type.Color).setNormalized(true); } if (inspectionVertex.boneWeightsIndices != null) { boneIndices = BufferUtils.createByteBuffer(vertices.size() * 4); boneWeights = BufferUtils.createFloatBuffer(vertices.size() * 4); - jmeMesh.setBuffer(VertexBuffer.Type.BoneIndex, 4, boneIndices); - jmeMesh.setBuffer(VertexBuffer.Type.BoneWeight, 4, boneWeights); + jmeMesh.setBuffer(GlVertexBuffer.Type.BoneIndex, 4, boneIndices); + jmeMesh.setBuffer(GlVertexBuffer.Type.BoneWeight, 4, boneWeights); //creating empty buffers for HW skinning //the buffers will be setup if ever used. - VertexBuffer weightsHW = new VertexBuffer(VertexBuffer.Type.HWBoneWeight); - VertexBuffer indicesHW = new VertexBuffer(VertexBuffer.Type.HWBoneIndex); + GlVertexBuffer weightsHW = new GlVertexBuffer(GlVertexBuffer.Type.HWBoneWeight); + GlVertexBuffer indicesHW = new GlVertexBuffer(GlVertexBuffer.Type.HWBoneIndex); //setting usage to cpuOnly so that the buffer is not send empty to the GPU - indicesHW.setUsage(VertexBuffer.Usage.CpuOnly); - weightsHW.setUsage(VertexBuffer.Usage.CpuOnly); + indicesHW.setUsage(GlVertexBuffer.Usage.CpuOnly); + weightsHW.setUsage(GlVertexBuffer.Usage.CpuOnly); jmeMesh.setBuffer(weightsHW); jmeMesh.setBuffer(indicesHW); @@ -346,11 +346,11 @@ public static Mesh convertIrMeshToJmeMesh(IrMesh mesh) { if (vertices.size() >= 65536) { // too many vertices: use IntBuffer instead of ShortBuffer IntBuffer ib = BufferUtils.createIntBuffer(indexes.size()); - jmeMesh.setBuffer(VertexBuffer.Type.Index, 3, ib); + jmeMesh.setBuffer(GlVertexBuffer.Type.Index, 3, ib); indexBuf = new IndexIntBuffer(ib); } else { ShortBuffer sb = BufferUtils.createShortBuffer(indexes.size()); - jmeMesh.setBuffer(VertexBuffer.Type.Index, 3, sb); + jmeMesh.setBuffer(GlVertexBuffer.Type.Index, 3, sb); indexBuf = new IndexShortBuffer(sb); } diff --git a/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/MaterialLoader.java b/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/MaterialLoader.java index 5c0b33ed63..26a25bbf04 100644 --- a/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/MaterialLoader.java +++ b/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/MaterialLoader.java @@ -39,8 +39,8 @@ import com.jme3.scene.plugins.ogre.matext.MaterialExtensionLoader; import com.jme3.scene.plugins.ogre.matext.MaterialExtensionSet; import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.texture.Texture2D; import com.jme3.util.PlaceholderAssets; import com.jme3.util.blockparser.BlockLanguageParser; @@ -60,7 +60,7 @@ public class MaterialLoader implements AssetLoader { private String folderName; private AssetManager assetManager; private ColorRGBA ambient, diffuse, specular, emissive; - private Texture[] textures = new Texture[4]; + private GlTexture[] textures = new GlTexture[4]; private String texName; private String matName; private float shininess; @@ -132,11 +132,11 @@ private void readTextureImage(String content){ TextureKey texKey = new TextureKey(folderName + path, false); texKey.setGenerateMips(genMips); if (cubic) { - texKey.setTextureTypeHint(Texture.Type.CubeMap); + texKey.setTextureTypeHint(GlTexture.Type.CubeMap); } try { - Texture loadedTexture = assetManager.loadTexture(texKey); + GlTexture loadedTexture = assetManager.loadTexture(texKey); textures[texUnit].setImage(loadedTexture.getImage()); textures[texUnit].setMinFilter(loadedTexture.getMinFilter()); diff --git a/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/MeshLoader.java b/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/MeshLoader.java index 4cb7967d94..ea14d0de2a 100644 --- a/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/MeshLoader.java +++ b/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/MeshLoader.java @@ -39,7 +39,7 @@ import com.jme3.renderer.queue.RenderQueue; import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.scene.*; -import com.jme3.scene.VertexBuffer.*; +import com.jme3.scene.GlVertexBuffer.*; import com.jme3.scene.plugins.ogre.matext.OgreMaterialKey; import com.jme3.util.*; import com.jme3.util.IntMap.Entry; @@ -83,7 +83,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { private ShortBuffer sb; private IntBuffer ib; private FloatBuffer fb; - private VertexBuffer vb; + private GlVertexBuffer vb; private Mesh mesh; private Geometry geom; private ByteBuffer indicesData; @@ -100,7 +100,7 @@ public class MeshLoader extends DefaultHandler implements AssetLoader { private String ignoreUntilEnd = null; private List geoms = new ArrayList<>(); private ArrayList usesSharedMesh = new ArrayList<>(); - private IntMap> lodLevels = new IntMap<>(); + private IntMap> lodLevels = new IntMap<>(); private AnimData animData; public MeshLoader() { @@ -189,7 +189,7 @@ private void startFaces(String count) throws SAXException { int numIndices = indicesPerFace * numFaces; - vb = new VertexBuffer(VertexBuffer.Type.Index); + vb = new GlVertexBuffer(GlVertexBuffer.Type.Index); if (!usesBigIndices) { sb = BufferUtils.createShortBuffer(numIndices); ib = null; @@ -373,8 +373,8 @@ private void startBoneAssigns() { weightsFloatData = FloatBuffer.allocate(vertCount * 4); indicesData = ByteBuffer.allocate(vertCount * 4); - VertexBuffer weights = new VertexBuffer(Type.BoneWeight); - VertexBuffer indices = new VertexBuffer(Type.BoneIndex); + GlVertexBuffer weights = new GlVertexBuffer(Type.BoneWeight); + GlVertexBuffer indices = new GlVertexBuffer(Type.BoneIndex); weights.setupData(Usage.CpuOnly, 4, Format.Float, weightsFloatData); indices.setupData(Usage.CpuOnly, 4, Format.UnsignedByte, indicesData); @@ -384,8 +384,8 @@ private void startBoneAssigns() { //creating empty buffers for HW skinning //the buffers will be setup if ever used. - VertexBuffer weightsHW = new VertexBuffer(Type.HWBoneWeight); - VertexBuffer indicesHW = new VertexBuffer(Type.HWBoneIndex); + GlVertexBuffer weightsHW = new GlVertexBuffer(Type.HWBoneWeight); + GlVertexBuffer indicesHW = new GlVertexBuffer(Type.HWBoneIndex); //setting usage to cpuOnly so that the buffer is not send empty to the GPU indicesHW.setUsage(Usage.CpuOnly); weightsHW.setUsage(Usage.CpuOnly); @@ -395,32 +395,32 @@ private void startBoneAssigns() { private void startVertexBuffer(Attributes attribs) throws SAXException { if (parseBool(attribs.getValue("positions"), false)) { - vb = new VertexBuffer(Type.Position); + vb = new GlVertexBuffer(Type.Position); fb = BufferUtils.createFloatBuffer(vertCount * 3); vb.setupData(Usage.Static, 3, Format.Float, fb); mesh.setBuffer(vb); } if (parseBool(attribs.getValue("normals"), false)) { - vb = new VertexBuffer(Type.Normal); + vb = new GlVertexBuffer(Type.Normal); fb = BufferUtils.createFloatBuffer(vertCount * 3); vb.setupData(Usage.Static, 3, Format.Float, fb); mesh.setBuffer(vb); } if (parseBool(attribs.getValue("colours_diffuse"), false)) { - vb = new VertexBuffer(Type.Color); + vb = new GlVertexBuffer(Type.Color); fb = BufferUtils.createFloatBuffer(vertCount * 4); vb.setupData(Usage.Static, 4, Format.Float, fb); mesh.setBuffer(vb); } if (parseBool(attribs.getValue("tangents"), false)) { int dimensions = parseInt(attribs.getValue("tangent_dimensions"), 3); - vb = new VertexBuffer(Type.Tangent); + vb = new GlVertexBuffer(Type.Tangent); fb = BufferUtils.createFloatBuffer(vertCount * dimensions); vb.setupData(Usage.Static, dimensions, Format.Float, fb); mesh.setBuffer(vb); } if (parseBool(attribs.getValue("binormals"), false)) { - vb = new VertexBuffer(Type.Binormal); + vb = new GlVertexBuffer(Type.Binormal); fb = BufferUtils.createFloatBuffer(vertCount * 3); vb.setupData(Usage.Static, 3, Format.Float, fb); mesh.setBuffer(vb); @@ -438,7 +438,7 @@ private void startVertexBuffer(Attributes attribs) throws SAXException { } if (i <= 7) { - vb = new VertexBuffer(TEXCOORD_TYPES[i]); + vb = new GlVertexBuffer(TEXCOORD_TYPES[i]); } else { // more than 8 texture coordinates are not supported by ogre. throw new SAXException("More than 8 texture coordinates not supported"); @@ -464,7 +464,7 @@ private void pushAttrib(Type type, Attributes attribs) throws SAXException { private void pushTangent(Attributes attribs) throws SAXException { try { - VertexBuffer tangentBuf = mesh.getBuffer(Type.Tangent); + GlVertexBuffer tangentBuf = mesh.getBuffer(Type.Tangent); FloatBuffer buf = (FloatBuffer) tangentBuf.getData(); buf.put(parseFloat(attribs.getValue("x"))).put(parseFloat(attribs.getValue("y"))).put(parseFloat(attribs.getValue("z"))); if (tangentBuf.getNumComponents() == 4) { @@ -481,7 +481,7 @@ private void pushTexCoord(Attributes attribs) throws SAXException { } Type type = TEXCOORD_TYPES[texCoordIndex]; - VertexBuffer tcvb = mesh.getBuffer(type); + GlVertexBuffer tcvb = mesh.getBuffer(type); FloatBuffer buf = (FloatBuffer) tcvb.getData(); buf.put(parseFloat(attribs.getValue("u"))); @@ -524,8 +524,8 @@ private void startLodFaceList(String submeshIndex, String numFaces) { mesh = geoms.get(index).getMesh(); int faceCount = Integer.parseInt(numFaces); - VertexBuffer originalIndexBuffer = mesh.getBuffer(Type.Index); - vb = new VertexBuffer(VertexBuffer.Type.Index); + GlVertexBuffer originalIndexBuffer = mesh.getBuffer(Type.Index); + vb = new GlVertexBuffer(GlVertexBuffer.Type.Index); if (originalIndexBuffer.getFormat() == Format.UnsignedInt) { // LOD buffer should also be integer ib = BufferUtils.createIntBuffer(faceCount * 3); @@ -537,10 +537,10 @@ private void startLodFaceList(String submeshIndex, String numFaces) { vb.setupData(Usage.Static, 3, Format.UnsignedShort, sb); } - List levels = lodLevels.get(index); + List levels = lodLevels.get(index); if (levels == null) { // Create the LOD levels list - levels = new ArrayList(); + levels = new ArrayList(); // Add the first LOD level (always the original index buffer) levels.add(originalIndexBuffer); @@ -555,10 +555,10 @@ private void startLevelOfDetail(String numLevels) { private void endLevelOfDetail() { // set the lod data for each mesh - for (Entry> entry : lodLevels) { + for (Entry> entry : lodLevels) { Mesh m = geoms.get(entry.getKey()).getMesh(); - List levels = entry.getValue(); - VertexBuffer[] levelArray = new VertexBuffer[levels.size()]; + List levels = entry.getValue(); + GlVertexBuffer[] levelArray = new GlVertexBuffer[levels.size()]; levels.toArray(levelArray); m.setLodLevels(levelArray); } @@ -736,7 +736,7 @@ public void endElement(String uri, String name, String qName) { } else if (qName.equals("geometry") || qName.equals("sharedgeometry")) { // finish writing to buffers - for (VertexBuffer buf : mesh.getBufferList().getArray()) { + for (GlVertexBuffer buf : mesh.getBufferList().getArray()) { Buffer data = buf.getData(); if (data.position() != 0) { data.flip(); diff --git a/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/SkeletonLoader.java b/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/SkeletonLoader.java index 728580523a..e0e9e435d7 100644 --- a/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/SkeletonLoader.java +++ b/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/SkeletonLoader.java @@ -32,7 +32,6 @@ package com.jme3.scene.plugins.ogre; import com.jme3.anim.*; -import com.jme3.anim.util.AnimMigrationUtils; import com.jme3.asset.AssetInfo; import com.jme3.asset.AssetLoader; import com.jme3.math.Quaternion; diff --git a/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java b/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java index 0b444b80d4..44be3d766f 100644 --- a/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java +++ b/jme3-plugins/src/ogre/java/com/jme3/scene/plugins/ogre/matext/MaterialExtensionLoader.java @@ -38,8 +38,8 @@ import com.jme3.material.Material; import com.jme3.material.MaterialList; import com.jme3.scene.plugins.ogre.MaterialLoader; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.texture.Texture2D; import com.jme3.util.PlaceholderAssets; import com.jme3.util.blockparser.Statement; @@ -74,7 +74,7 @@ private void readExtendingMaterialStatement(Statement statement) throws IOExcept TextureKey texKey = new TextureKey(texturePath, false); texKey.setGenerateMips(true); - Texture tex; + GlTexture tex; try { tex = assetManager.loadTexture(texKey); diff --git a/jme3-plugins/src/test/java/com/jme3/material/plugin/TestMaterialWrite.java b/jme3-plugins/src/test/java/com/jme3/material/plugin/TestMaterialWrite.java index e5dfa75965..400b9db850 100644 --- a/jme3-plugins/src/test/java/com/jme3/material/plugin/TestMaterialWrite.java +++ b/jme3-plugins/src/test/java/com/jme3/material/plugin/TestMaterialWrite.java @@ -41,7 +41,7 @@ import com.jme3.material.plugins.J3MLoader; import com.jme3.math.ColorRGBA; import com.jme3.system.JmeSystem; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import org.junit.Before; import org.junit.Test; @@ -77,11 +77,11 @@ public void testWriteMat() throws Exception { mat.setFloat("Shininess", 2.5f); - Texture tex = assetManager.loadTexture(new TextureKey("Common/Textures/MissingTexture.png", true)); - tex.setMagFilter(Texture.MagFilter.Nearest); - tex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); - tex.setWrap(Texture.WrapAxis.S, Texture.WrapMode.Repeat); - tex.setWrap(Texture.WrapAxis.T, Texture.WrapMode.MirroredRepeat); + GlTexture tex = assetManager.loadTexture(new TextureKey("Common/Textures/MissingTexture.png", true)); + tex.setMagFilter(GlTexture.MagFilter.Nearest); + tex.setMinFilter(GlTexture.MinFilter.BilinearNoMipMaps); + tex.setWrap(GlTexture.WrapAxis.S, GlTexture.WrapMode.Repeat); + tex.setWrap(GlTexture.WrapAxis.T, GlTexture.WrapMode.MirroredRepeat); mat.setTexture("DiffuseMap", tex); mat.getAdditionalRenderState().setDepthWrite(false); diff --git a/jme3-screenshot-tests/src/main/java/org/jmonkeyengine/screenshottests/testframework/ScreenshotNoInputAppState.java b/jme3-screenshot-tests/src/main/java/org/jmonkeyengine/screenshottests/testframework/ScreenshotNoInputAppState.java index 2817bb0e73..04376067c5 100644 --- a/jme3-screenshot-tests/src/main/java/org/jmonkeyengine/screenshottests/testframework/ScreenshotNoInputAppState.java +++ b/jme3-screenshot-tests/src/main/java/org/jmonkeyengine/screenshottests/testframework/ScreenshotNoInputAppState.java @@ -43,7 +43,7 @@ import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; import com.jme3.system.JmeSystem; -import com.jme3.texture.FrameBuffer; +import com.jme3.texture.GlFrameBuffer; import com.jme3.util.BufferUtils; import java.io.File; @@ -268,7 +268,7 @@ public void postQueue(RenderQueue rq) { } @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { if (capture) { capture = false; diff --git a/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/animation/TestIssue2076.java b/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/animation/TestIssue2076.java index 50435b1218..1c07ea1170 100644 --- a/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/animation/TestIssue2076.java +++ b/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/animation/TestIssue2076.java @@ -32,8 +32,6 @@ package org.jmonkeyengine.screenshottests.animation; import com.jme3.anim.SkinningControl; -import com.jme3.anim.util.AnimMigrationUtils; -import com.jme3.animation.SkeletonControl; import com.jme3.app.Application; import com.jme3.app.SimpleApplication; import com.jme3.app.state.BaseAppState; @@ -44,7 +42,7 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; import com.jme3.scene.Node; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import org.jmonkeyengine.screenshottests.testframework.ScreenshotTestBase; import org.junit.jupiter.api.Test; @@ -99,8 +97,8 @@ protected void initialize(Application app) { // Remove its vertex normals Geometry oldGeometry = (Geometry) oldJaime.getChild(0); Mesh oldMesh = oldGeometry.getMesh(); - oldMesh.clearBuffer(VertexBuffer.Type.Normal); - oldMesh.clearBuffer(VertexBuffer.Type.BindPoseNormal); + oldMesh.clearBuffer(GlVertexBuffer.Type.Normal); + oldMesh.clearBuffer(GlVertexBuffer.Type.BindPoseNormal); // Test new animation system Node newJaime = (Node) assetManager.loadModel(assetPath); @@ -115,8 +113,8 @@ protected void initialize(Application app) { // Remove its vertex normals Geometry newGeometry = (Geometry) newJaime.getChild(0); Mesh newMesh = newGeometry.getMesh(); - newMesh.clearBuffer(VertexBuffer.Type.Normal); - newMesh.clearBuffer(VertexBuffer.Type.BindPoseNormal); + newMesh.clearBuffer(GlVertexBuffer.Type.Normal); + newMesh.clearBuffer(GlVertexBuffer.Type.BindPoseNormal); // Position the camera to see both models simpleApplication.getCamera().setLocation(new Vector3f(0f, 0f, 5f)); diff --git a/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/effects/TestIssue1773.java b/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/effects/TestIssue1773.java index 05eb097ee7..419b19c25c 100644 --- a/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/effects/TestIssue1773.java +++ b/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/effects/TestIssue1773.java @@ -31,7 +31,7 @@ */ package org.jmonkeyengine.screenshottests.effects; -import com.jme3.animation.LoopMode; +import com.jme3.anim.util.LoopMode; import com.jme3.app.Application; import com.jme3.app.SimpleApplication; import com.jme3.app.state.BaseAppState; @@ -60,7 +60,7 @@ import com.jme3.scene.shape.CenterQuad; import com.jme3.scene.shape.Torus; import com.jme3.shadow.DirectionalLightShadowFilter; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import org.jmonkeyengine.screenshottests.testframework.ScreenshotTestBase; import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.params.ParameterizedTest; @@ -219,8 +219,8 @@ private void setupGround() { quad.scaleTextureCoordinates(new Vector2f(2, 2)); Geometry floor = new Geometry("Floor", quad); Material mat = new Material(assetManager, Materials.LIGHTING); - Texture tex = assetManager.loadTexture("Interface/Logo/Monkey.jpg"); - tex.setWrap(Texture.WrapMode.Repeat); + GlTexture tex = assetManager.loadTexture("Interface/Logo/Monkey.jpg"); + tex.setWrap(GlTexture.WrapMode.Repeat); mat.setTexture("DiffuseMap", tex); floor.setMaterial(mat); floor.rotate(-FastMath.HALF_PI, 0, 0); diff --git a/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/terrain/TestPBRTerrain.java b/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/terrain/TestPBRTerrain.java index 37f8063300..d2a71fa756 100644 --- a/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/terrain/TestPBRTerrain.java +++ b/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/terrain/TestPBRTerrain.java @@ -47,8 +47,8 @@ import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import org.jmonkeyengine.screenshottests.testframework.ScreenshotTestBase; import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.params.ParameterizedTest; @@ -143,7 +143,7 @@ private void setUpTerrainMaterial(AssetManager assetManager) { matTerrain.setTexture("AlphaMap_1", assetManager.loadTexture("Textures/Terrain/splat/alpha2.png")); // DIRT texture - Texture dirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); + GlTexture dirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); dirt.setWrap(WrapMode.Repeat); matTerrain.setTexture("AlbedoMap_0", dirt); matTerrain.setFloat("AlbedoMap_0_scale", dirtScale); @@ -151,7 +151,7 @@ private void setUpTerrainMaterial(AssetManager assetManager) { matTerrain.setFloat("Metallic_0", 0); // DARK ROCK texture - Texture darkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Color.png"); + GlTexture darkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Color.png"); darkRock.setWrap(WrapMode.Repeat); matTerrain.setTexture("AlbedoMap_1", darkRock); matTerrain.setFloat("AlbedoMap_1_scale", darkRockScale); @@ -159,7 +159,7 @@ private void setUpTerrainMaterial(AssetManager assetManager) { matTerrain.setFloat("Metallic_1", 0.02f); // SNOW texture - Texture snow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Color.png"); + GlTexture snow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Color.png"); snow.setWrap(WrapMode.Repeat); matTerrain.setTexture("AlbedoMap_2", snow); matTerrain.setFloat("AlbedoMap_2_scale", snowScale); @@ -167,7 +167,7 @@ private void setUpTerrainMaterial(AssetManager assetManager) { matTerrain.setFloat("Metallic_2", 0.12f); // TILES texture - Texture tiles = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Color.png"); + GlTexture tiles = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Color.png"); tiles.setWrap(WrapMode.Repeat); matTerrain.setTexture("AlbedoMap_3", tiles); matTerrain.setFloat("AlbedoMap_3_scale", tileRoadScale); @@ -175,7 +175,7 @@ private void setUpTerrainMaterial(AssetManager assetManager) { matTerrain.setFloat("Metallic_3", 0.08f); // GRASS texture - Texture grass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); grass.setWrap(WrapMode.Repeat); matTerrain.setTexture("AlbedoMap_4", grass); matTerrain.setFloat("AlbedoMap_4_scale", grassScale); @@ -183,7 +183,7 @@ private void setUpTerrainMaterial(AssetManager assetManager) { matTerrain.setFloat("Metallic_4", 0); // MARBLE texture - Texture marble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_1K_Color.png"); + GlTexture marble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_1K_Color.png"); marble.setWrap(WrapMode.Repeat); matTerrain.setTexture("AlbedoMap_5", marble); matTerrain.setFloat("AlbedoMap_5_scale", marbleScale); @@ -191,7 +191,7 @@ private void setUpTerrainMaterial(AssetManager assetManager) { matTerrain.setFloat("Metallic_5", 0.8f); // Gravel texture - Texture gravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Color.png"); + GlTexture gravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Color.png"); gravel.setWrap(WrapMode.Repeat); matTerrain.setTexture("AlbedoMap_6", gravel); matTerrain.setFloat("AlbedoMap_6_scale", gravelScale); @@ -199,22 +199,22 @@ private void setUpTerrainMaterial(AssetManager assetManager) { matTerrain.setFloat("Metallic_6", 0.07f); // NORMAL MAPS - Texture normalMapDirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground036_1K_Normal.png"); + GlTexture normalMapDirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground036_1K_Normal.png"); normalMapDirt.setWrap(WrapMode.Repeat); - Texture normalMapDarkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Normal.png"); + GlTexture normalMapDarkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Normal.png"); normalMapDarkRock.setWrap(WrapMode.Repeat); - Texture normalMapSnow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Normal.png"); + GlTexture normalMapSnow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Normal.png"); normalMapSnow.setWrap(WrapMode.Repeat); - Texture normalMapGravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Normal.png"); + GlTexture normalMapGravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Normal.png"); normalMapGravel.setWrap(WrapMode.Repeat); - Texture normalMapGrass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Normal.png"); + GlTexture normalMapGrass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Normal.png"); normalMapGrass.setWrap(WrapMode.Repeat); - Texture normalMapTiles = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Normal.png"); + GlTexture normalMapTiles = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Normal.png"); normalMapTiles.setWrap(WrapMode.Repeat); matTerrain.setTexture("NormalMap_0", normalMapDirt); @@ -230,7 +230,7 @@ private void setUpTerrainMaterial(AssetManager assetManager) { private void setUpTerrain(SimpleApplication simpleApp, AssetManager assetManager) { // HEIGHTMAP image (for the terrain heightmap) TextureKey hmKey = new TextureKey("Textures/Terrain/splat/mountains512.png", false); - Texture heightMapImage = assetManager.loadTexture(hmKey); + GlTexture heightMapImage = assetManager.loadTexture(hmKey); // CREATE HEIGHTMAP AbstractHeightMap heightmap; diff --git a/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/terrain/TestPBRTerrainAdvanced.java b/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/terrain/TestPBRTerrainAdvanced.java index a1f5830896..0f8b1c2d35 100644 --- a/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/terrain/TestPBRTerrainAdvanced.java +++ b/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/terrain/TestPBRTerrainAdvanced.java @@ -48,11 +48,11 @@ import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; -import com.jme3.texture.Texture.MagFilter; -import com.jme3.texture.Texture.MinFilter; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; +import com.jme3.texture.GlTexture.MagFilter; +import com.jme3.texture.GlTexture.MinFilter; import com.jme3.texture.TextureArray; import org.jmonkeyengine.screenshottests.testframework.ScreenshotTestBase; import org.junit.jupiter.api.TestInfo; @@ -167,36 +167,36 @@ private void setUpTerrainMaterial(AssetManager assetManager) { // load textures for texture arrays // These MUST all have the same dimensions and format in order to be put into a texture array. //ALBEDO MAPS - Texture dirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); - Texture darkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Color.png"); - Texture snow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Color.png"); - Texture tileRoad = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Color.png"); - Texture grass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); - Texture marble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_1K_Color.png"); - Texture gravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Color.png"); + GlTexture dirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); + GlTexture darkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Color.png"); + GlTexture snow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Color.png"); + GlTexture tileRoad = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Color.png"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Color.png"); + GlTexture marble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_1K_Color.png"); + GlTexture gravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Color.png"); // NORMAL MAPS - Texture normalMapDirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground036_1K_Normal.png"); - Texture normalMapDarkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Normal.png"); - Texture normalMapSnow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Normal.png"); - Texture normalMapGravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Normal.png"); - Texture normalMapGrass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Normal.png"); - Texture normalMapMarble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_1K_Normal.png"); - Texture normalMapRoad = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Normal.png"); + GlTexture normalMapDirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground036_1K_Normal.png"); + GlTexture normalMapDarkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_1K_Normal.png"); + GlTexture normalMapSnow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_1K_Normal.png"); + GlTexture normalMapGravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel015_1K_Normal.png"); + GlTexture normalMapGrass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_1K_Normal.png"); + GlTexture normalMapMarble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_1K_Normal.png"); + GlTexture normalMapRoad = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_1K_Normal.png"); //PACKED METALLIC/ROUGHNESS / AMBIENT OCCLUSION / EMISSIVE INTENSITY MAPS - Texture metallicRoughnessAoEiMapDirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground036_PackedMetallicRoughnessMap.png"); - Texture metallicRoughnessAoEiMapDarkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_PackedMetallicRoughnessMap.png"); - Texture metallicRoughnessAoEiMapSnow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_PackedMetallicRoughnessMap.png"); - Texture metallicRoughnessAoEiMapGravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel_015_PackedMetallicRoughnessMap.png"); - Texture metallicRoughnessAoEiMapGrass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_PackedMetallicRoughnessMap.png"); - Texture metallicRoughnessAoEiMapMarble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_PackedMetallicRoughnessMap.png"); - Texture metallicRoughnessAoEiMapRoad = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_PackedMetallicRoughnessMap.png"); + GlTexture metallicRoughnessAoEiMapDirt = assetManager.loadTexture("Textures/Terrain/PBR/Ground036_PackedMetallicRoughnessMap.png"); + GlTexture metallicRoughnessAoEiMapDarkRock = assetManager.loadTexture("Textures/Terrain/PBR/Rock035_PackedMetallicRoughnessMap.png"); + GlTexture metallicRoughnessAoEiMapSnow = assetManager.loadTexture("Textures/Terrain/PBR/Snow006_PackedMetallicRoughnessMap.png"); + GlTexture metallicRoughnessAoEiMapGravel = assetManager.loadTexture("Textures/Terrain/PBR/Gravel_015_PackedMetallicRoughnessMap.png"); + GlTexture metallicRoughnessAoEiMapGrass = assetManager.loadTexture("Textures/Terrain/PBR/Ground037_PackedMetallicRoughnessMap.png"); + GlTexture metallicRoughnessAoEiMapMarble = assetManager.loadTexture("Textures/Terrain/PBR/Marble013_PackedMetallicRoughnessMap.png"); + GlTexture metallicRoughnessAoEiMapRoad = assetManager.loadTexture("Textures/Terrain/PBR/Tiles083_PackedMetallicRoughnessMap.png"); // put all images into lists to create texture arrays. - List albedoImages = new ArrayList<>(); - List normalMapImages = new ArrayList<>(); - List metallicRoughnessAoEiMapImages = new ArrayList<>(); + List albedoImages = new ArrayList<>(); + List normalMapImages = new ArrayList<>(); + List metallicRoughnessAoEiMapImages = new ArrayList<>(); albedoImages.add(dirt.getImage()); //0 albedoImages.add(darkRock.getImage()); //1 @@ -298,7 +298,7 @@ private void setUpTerrainMaterial(AssetManager assetManager) { terrain.setMaterial(matTerrain); } - private void setWrapAndMipMaps(Texture texture) { + private void setWrapAndMipMaps(GlTexture texture) { texture.setWrap(WrapMode.Repeat); texture.setMinFilter(MinFilter.Trilinear); texture.setMagFilter(MagFilter.Bilinear); @@ -307,7 +307,7 @@ private void setWrapAndMipMaps(Texture texture) { private void setUpTerrain(SimpleApplication simpleApp, AssetManager assetManager) { // HEIGHTMAP image (for the terrain heightmap) TextureKey hmKey = new TextureKey("Textures/Terrain/splat/mountains512.png", false); - Texture heightMapImage = assetManager.loadTexture(hmKey); + GlTexture heightMapImage = assetManager.loadTexture(hmKey); // CREATE HEIGHTMAP AbstractHeightMap heightmap; diff --git a/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/water/TestPostWater.java b/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/water/TestPostWater.java index 7868ecd71e..270497c549 100644 --- a/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/water/TestPostWater.java +++ b/jme3-screenshot-tests/src/test/java/org/jmonkeyengine/screenshottests/water/TestPostWater.java @@ -52,8 +52,8 @@ import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; -import com.jme3.texture.Texture.WrapMode; +import com.jme3.texture.GlTexture; +import com.jme3.texture.GlTexture.WrapMode; import com.jme3.texture.Texture2D; import com.jme3.util.SkyFactory; import com.jme3.util.SkyFactory.EnvMapType; @@ -170,24 +170,24 @@ private void createTerrain(Node rootNode, AssetManager assetManager) { matRock.setBoolean("useTriPlanarMapping", false); matRock.setBoolean("WardIso", true); matRock.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png")); - Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); - Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); + GlTexture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); + GlTexture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap", grass); matRock.setFloat("DiffuseMap_0_scale", 64); - Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); + GlTexture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap_1", dirt); matRock.setFloat("DiffuseMap_1_scale", 16); - Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); + GlTexture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); rock.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap_2", rock); matRock.setFloat("DiffuseMap_2_scale", 128); - Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); + GlTexture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); normalMap0.setWrap(WrapMode.Repeat); - Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); + GlTexture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); normalMap1.setWrap(WrapMode.Repeat); - Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); + GlTexture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); normalMap2.setWrap(WrapMode.Repeat); matRock.setTexture("NormalMap", normalMap0); matRock.setTexture("NormalMap_1", normalMap1); diff --git a/jme3-terrain/src/main/java/com/jme3/terrain/GeoMap.java b/jme3-terrain/src/main/java/com/jme3/terrain/GeoMap.java index 4a68a9cc18..8fce3dfefa 100644 --- a/jme3-terrain/src/main/java/com/jme3/terrain/GeoMap.java +++ b/jme3-terrain/src/main/java/com/jme3/terrain/GeoMap.java @@ -35,7 +35,7 @@ import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.util.BufferUtils; import java.io.IOException; import java.nio.BufferUnderflowException; diff --git a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/LODGeomap.java b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/LODGeomap.java index 2dae19b458..e9edd01ace 100644 --- a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/LODGeomap.java +++ b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/LODGeomap.java @@ -39,7 +39,7 @@ import com.jme3.math.Vector3f; import com.jme3.scene.Mesh; import com.jme3.scene.Mesh.Mode; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.scene.mesh.IndexBuffer; import com.jme3.terrain.GeoMap; import com.jme3.util.BufferUtils; diff --git a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainPatch.java b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainPatch.java index b4ffc5a12d..229216208f 100644 --- a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainPatch.java +++ b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/TerrainPatch.java @@ -44,8 +44,8 @@ import com.jme3.math.*; import com.jme3.scene.Geometry; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.scene.mesh.IndexBuffer; import com.jme3.terrain.geomipmap.TerrainQuad.LocationHeight; import com.jme3.terrain.geomipmap.lodcalc.util.EntropyComputeUtil; @@ -298,7 +298,7 @@ public Triangle[] getGridTriangles(float x, float z) { protected void setHeight(List locationHeights, boolean overrideHeight) { final float[] heightArray = geomap.getHeightArray(); - final VertexBuffer vertexBuffer = mesh.getBuffer(Type.Position); + final GlVertexBuffer vertexBuffer = mesh.getBuffer(Type.Position); final FloatBuffer floatBuffer = mesh.getFloatBuffer(Type.Position); for (LocationHeight lh : locationHeights) { @@ -338,9 +338,9 @@ protected void updateNormals() { } private void setInBuffer(Mesh mesh, int index, Vector3f normal, Vector3f tangent, Vector3f binormal) { - VertexBuffer NB = mesh.getBuffer(Type.Normal); - VertexBuffer TB = mesh.getBuffer(Type.Tangent); - VertexBuffer BB = mesh.getBuffer(Type.Binormal); + GlVertexBuffer NB = mesh.getBuffer(Type.Normal); + GlVertexBuffer TB = mesh.getBuffer(Type.Tangent); + GlVertexBuffer BB = mesh.getBuffer(Type.Binormal); BufferUtils.setInBuffer(normal, (FloatBuffer)NB.getData(), index); BufferUtils.setInBuffer(tangent, (FloatBuffer)TB.getData(), index); BufferUtils.setInBuffer(binormal, (FloatBuffer)BB.getData(), index); diff --git a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java index 697e81b2d5..163ac0af15 100644 --- a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java +++ b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/UpdatedTerrainPatch.java @@ -31,7 +31,7 @@ */ package com.jme3.terrain.geomipmap; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.IntBuffer; diff --git a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/grid/ImageTileLoader.java b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/grid/ImageTileLoader.java index d6692c25ca..6435e2b378 100644 --- a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/grid/ImageTileLoader.java +++ b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/grid/ImageTileLoader.java @@ -40,7 +40,7 @@ import com.jme3.terrain.geomipmap.TerrainGridTileLoader; import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.heightmap.*; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; @@ -112,7 +112,7 @@ private HeightMap getHeightMapAt(Vector3f location) { try { name = namer.getName(x, z); logger.log(Level.FINE, "Loading heightmap from file: {0}", name); - final Texture texture = assetManager.loadTexture(new TextureKey(name)); + final GlTexture texture = assetManager.loadTexture(new TextureKey(name)); heightmap = new ImageBasedHeightMap(texture.getImage()); /*if (assetInfo != null){ InputStream in = assetInfo.openStream(); diff --git a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/lodcalc/util/EntropyComputeUtil.java b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/lodcalc/util/EntropyComputeUtil.java index 9057664d12..88398226ca 100644 --- a/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/lodcalc/util/EntropyComputeUtil.java +++ b/jme3-terrain/src/main/java/com/jme3/terrain/geomipmap/lodcalc/util/EntropyComputeUtil.java @@ -37,8 +37,8 @@ import com.jme3.math.Ray; import com.jme3.math.Vector3f; import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer; +import com.jme3.scene.GlVertexBuffer.Type; import com.jme3.util.BufferUtils; import java.nio.Buffer; import java.nio.ByteBuffer; @@ -77,7 +77,7 @@ public static float computeLodEntropy(Mesh terrainBlock, Buffer lodIndices){ CollisionResults results = new CollisionResults(); // Set the LOD indices on the block - VertexBuffer originalIndices = terrainBlock.getBuffer(Type.Index); + GlVertexBuffer originalIndices = terrainBlock.getBuffer(Type.Index); terrainBlock.clearBuffer(Type.Index); if (lodIndices instanceof IntBuffer) diff --git a/jme3-terrain/src/main/java/com/jme3/terrain/heightmap/ImageBasedHeightMap.java b/jme3-terrain/src/main/java/com/jme3/terrain/heightmap/ImageBasedHeightMap.java index 459beffb63..0958f27ced 100644 --- a/jme3-terrain/src/main/java/com/jme3/terrain/heightmap/ImageBasedHeightMap.java +++ b/jme3-terrain/src/main/java/com/jme3/terrain/heightmap/ImageBasedHeightMap.java @@ -32,7 +32,7 @@ package com.jme3.terrain.heightmap; import com.jme3.math.ColorRGBA; -import com.jme3.texture.Image; +import com.jme3.texture.GlImage; import com.jme3.texture.image.ImageRaster; /** @@ -47,11 +47,11 @@ public class ImageBasedHeightMap extends AbstractHeightMap { - protected Image colorImage; + protected GlImage colorImage; private float backwardsCompScale = 255f; - public void setImage(Image image) { + public void setImage(GlImage image) { this.colorImage = image; } @@ -66,11 +66,11 @@ public void setImage(Image image) { * @param colorImage * Image to map to the height map. */ - public ImageBasedHeightMap(Image colorImage) { + public ImageBasedHeightMap(GlImage colorImage) { this.colorImage = colorImage; } - public ImageBasedHeightMap(Image colorImage, float heightScale) { + public ImageBasedHeightMap(GlImage colorImage, float heightScale) { this.colorImage = colorImage; this.heightScale = heightScale; } diff --git a/jme3-terrain/src/test/java/com/jme3/terrain/TestTerrainExporting.java b/jme3-terrain/src/test/java/com/jme3/terrain/TestTerrainExporting.java index 7dceb63b0e..613fb77b42 100644 --- a/jme3-terrain/src/test/java/com/jme3/terrain/TestTerrainExporting.java +++ b/jme3-terrain/src/test/java/com/jme3/terrain/TestTerrainExporting.java @@ -38,7 +38,7 @@ import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import org.junit.Assert; import org.junit.Test; @@ -55,7 +55,7 @@ public class TestTerrainExporting extends BaseAWTTest { @Test public void testTerrainExporting() { - Texture heightMapImage = getAssetManager().loadTexture("Textures/Terrain/splat/mountains512.png"); + GlTexture heightMapImage = getAssetManager().loadTexture("Textures/Terrain/splat/mountains512.png"); AbstractHeightMap map = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f); map.load(); diff --git a/jme3-terrain/src/test/java/com/jme3/terrain/collision/TerrainCollisionTest.java b/jme3-terrain/src/test/java/com/jme3/terrain/collision/TerrainCollisionTest.java index 32768f4060..868f0194fb 100644 --- a/jme3-terrain/src/test/java/com/jme3/terrain/collision/TerrainCollisionTest.java +++ b/jme3-terrain/src/test/java/com/jme3/terrain/collision/TerrainCollisionTest.java @@ -6,7 +6,7 @@ import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -16,7 +16,7 @@ public class TerrainCollisionTest extends BaseAWTTest { @Before public void initQuad() { - Texture heightMapImage = getAssetManager().loadTexture("Textures/Terrain/splat/mountains512.png"); + GlTexture heightMapImage = getAssetManager().loadTexture("Textures/Terrain/splat/mountains512.png"); AbstractHeightMap map = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f); map.load(); quad = new TerrainQuad("terrain", 65, 513, map.getHeightMap()); diff --git a/jme3-testdata/src/main/resources/Shaders/VulkanFragTest.glsl b/jme3-testdata/src/main/resources/Shaders/VulkanFragTest.glsl new file mode 100644 index 0000000000..a1a5605e9f --- /dev/null +++ b/jme3-testdata/src/main/resources/Shaders/VulkanFragTest.glsl @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0) in vec2 texCoord; + +layout(location = 0) out vec4 outColor; + +layout(set = 0, binding = 1) uniform sampler2D colorTexture; + +void main() { + outColor = texture(colorTexture, texCoord); +} \ No newline at end of file diff --git a/jme3-testdata/src/main/resources/Shaders/VulkanTest.json b/jme3-testdata/src/main/resources/Shaders/VulkanTest.json new file mode 100644 index 0000000000..7c0a0ada7e --- /dev/null +++ b/jme3-testdata/src/main/resources/Shaders/VulkanTest.json @@ -0,0 +1,11 @@ +{ + "name" : "VulkanTest", + "techniques" : { + "main" : { + "shaders" : { + "vertex" : "Shaders/VulkanFragTest.json", + "fragment" : "Shaders/VulkanVertTest.json" + } + } + } +} \ No newline at end of file diff --git a/jme3-testdata/src/main/resources/Shaders/VulkanTest.yml b/jme3-testdata/src/main/resources/Shaders/VulkanTest.yml new file mode 100644 index 0000000000..f00188bd16 --- /dev/null +++ b/jme3-testdata/src/main/resources/Shaders/VulkanTest.yml @@ -0,0 +1,74 @@ +name: "VulkanTest" +# Links type placeholders to full class names used for instantiation +# by reflection. This enables custom types to be used. Common engine +# types are supported by default. +imports: + MyUniformType: "com.username.MyCustomUniform" + MyBufferMember: "com.username.MyCustomBufferMember" + MyRasterState: "com.username.MyCustomRasterState" +parameters: + # Most valid parameter arguments are defined by managing Uniform object + # including default values. Exceptions are type, set, and define. + # Also, binding and stages are handled by AbstractUniform. + Matrices: + type: UniformBuffer + set: 0 + binding: 0 + usage: Stream + stages: [Vertex] + layout: # defines the layout of the uniform buffer + worldViewProjection: + type: Matrix4f + time: + type: Float + default: 1.0 + myval: + type: com.username.hello.world.MyBufferMember + default: "parse_this" + BaseColorMap: + type: Texture + set: 0 + binding: 1 + define: "BASE_COLOR_MAP" + default: "Common/Textures/MissingTexture.jpg" + MyValue: + type: MyUniformType + set: 0 + binding: 1 + default: "parse_this" +pipelines: + main: + type: GraphicsState + shaders: + - stage: Vertex + file: "Shaders/VulkanVertTest.yml" + entry: "main" + - stage: Fragment + file: "Shaders/VulkanFragTest.yml" + # entry is "main" by default + dynamic: [ViewPort, Scissor] + raster: + type: MyRasterState + cullMode: None + colorBlend: + attachments: + - colorWriteMask: [R, G, B, A] + blend: false + srcColorFactor: One + dstColorFactor: Zero + colorOp: Add + srcAlphaFactor: One + dstAlphaFactor: Zero + alphaOp: Add + viewPort: + viewPorts: + - x: 0 + y: 0 + w: 128 + h: 128 + scissors: + - x: 0.0 + y: 0.0 + w: 1.0 + h: 1.0 + diff --git a/jme3-testdata/src/main/resources/Shaders/VulkanVertTest.glsl b/jme3-testdata/src/main/resources/Shaders/VulkanVertTest.glsl new file mode 100644 index 0000000000..5960ce60f9 --- /dev/null +++ b/jme3-testdata/src/main/resources/Shaders/VulkanVertTest.glsl @@ -0,0 +1,16 @@ +#version 450 + +layout (location = 0) in vec3 inPosition; +layout (location = 1) in vec2 inTexCoord; +layout (location = 2) in vec3 inNormal; + +layout (location = 0) out vec2 texCoord; + +layout (set = 0, binding = 0) uniform CameraBuffer { + mat4 worldViewProjectionMatrix; +} cam; + +void main() { + gl_Position = cam.worldViewProjectionMatrix * vec4(inPosition, 1.0); + texCoord = inTexCoord; +} \ No newline at end of file diff --git a/jme3-testdata/src/main/resources/Shaders/VulkanVertTest.j4md b/jme3-testdata/src/main/resources/Shaders/VulkanVertTest.j4md new file mode 100644 index 0000000000..1d14be2c60 --- /dev/null +++ b/jme3-testdata/src/main/resources/Shaders/VulkanVertTest.j4md @@ -0,0 +1,19 @@ +MaterialDef VulkanVertTest { + + Imports { + UniformBuffer : com.jme3.vulkan.material.UniformBuffer + } + + Vulkan { + Set { + UniformBuffer Camera (vertex) + } + Set { + UniformBuffer Material (fragment) + } + Set { + UniformBuffer Geometry (vertex) + } + } + +} \ No newline at end of file diff --git a/jme3-testdata/src/main/resources/Shaders/VulkanVertTest.json b/jme3-testdata/src/main/resources/Shaders/VulkanVertTest.json new file mode 100644 index 0000000000..8bc5811cb9 --- /dev/null +++ b/jme3-testdata/src/main/resources/Shaders/VulkanVertTest.json @@ -0,0 +1,15 @@ +{ + "shader" : "Shaders/VulkanVertTest.glsl", + "type" : "vertex", + "platforms" : ["vulkan", "opengl"], + "versions" : [460, 450], + "parameters" : { + "Matrices" : { + "type" : "UniformBuffer", + "set" : 0, + "binding" : 0, + "usage" : "Stream", + "define" : "MATRICES" + } + } +} \ No newline at end of file diff --git a/jme3-testdata/src/main/resources/Shaders/VulkanVertTest.yml b/jme3-testdata/src/main/resources/Shaders/VulkanVertTest.yml new file mode 100644 index 0000000000..2d48bd103a --- /dev/null +++ b/jme3-testdata/src/main/resources/Shaders/VulkanVertTest.yml @@ -0,0 +1,6 @@ +shader: "Shaders/VulkanVertTest.glsl" +type: "vertex" +platforms: ["vulkan", "opengl", "gles"] +versions: [460, 450] +parameters: + Matrices: "MATRICES" \ No newline at end of file diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/AbstractVRMouseManager.java b/jme3-vr/src/main/java/com/jme3/input/vr/AbstractVRMouseManager.java index 6be4cae9fb..e798dab3d3 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/AbstractVRMouseManager.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/AbstractVRMouseManager.java @@ -11,7 +11,7 @@ import com.jme3.math.Vector2f; import com.jme3.system.AppSettings; import com.jme3.system.lwjgl.LwjglWindow; -import com.jme3.texture.Texture; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; import com.jme3.ui.Picture; @@ -115,7 +115,7 @@ public void setImage(String texture) { if (environment.getApplication() != null){ if( environment.isInVR() == false ){ - Texture tex = environment.getApplication().getAssetManager().loadTexture(texture); + GlTexture tex = environment.getApplication().getAssetManager().loadTexture(texture); mouseImage.setTexture(environment.getApplication().getAssetManager(), (Texture2D)tex, true); ySize = tex.getImage().getHeight(); mouseImage.setHeight(ySize); @@ -123,7 +123,7 @@ public void setImage(String texture) { mouseImage.getMaterial().getAdditionalRenderState().setBlendMode(BlendMode.Alpha); mouseImage.getMaterial().getAdditionalRenderState().setDepthWrite(false); } else { - Texture tex = environment.getApplication().getAssetManager().loadTexture(texture); + GlTexture tex = environment.getApplication().getAssetManager().loadTexture(texture); mouseImage.setTexture(environment.getApplication().getAssetManager(), (Texture2D)tex, true); ySize = tex.getImage().getHeight(); mouseImage.setHeight(ySize); diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/lwjgl_openvr/LWJGLOpenVRViewManager.java b/jme3-vr/src/main/java/com/jme3/input/vr/lwjgl_openvr/LWJGLOpenVRViewManager.java index 2b658e53f4..3b8bd3edb3 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/lwjgl_openvr/LWJGLOpenVRViewManager.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/lwjgl_openvr/LWJGLOpenVRViewManager.java @@ -11,8 +11,8 @@ import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.scene.Spatial; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage; import com.jme3.texture.Texture2D; import com.jme3.ui.Picture; import com.jme3.util.VRGUIPositioningMode; @@ -70,7 +70,7 @@ public LWJGLOpenVRViewManager(VREnvironment environment) { * @see #getFullTexId() */ protected int getLeftTexId() { - return getLeftTexture().getImage().getId(); + return getLeftTexture().getImage().getNativeObject(); } /** @@ -81,7 +81,7 @@ protected int getLeftTexId() { * @see #getFullTexId() */ protected int getRightTexId() { - return getRightTexture().getImage().getId(); + return getRightTexture().getImage().getNativeObject(); } /** @@ -92,7 +92,7 @@ protected int getRightTexId() { * @see #getRightTexId() */ private int getFullTexId() { - return dualEyeTex.getImage().getId(); + return dualEyeTex.getImage().getNativeObject(); } /** @@ -197,11 +197,11 @@ public void postRender() { logger.severe("Submit to left compositor error: " + " (" + Integer.toString(errl) + ")"); logger.severe(" Texture handle: " + leftTextureType.handle()); - logger.severe(" Left eye texture " + leftEyeTexture.getName() + " (" + leftEyeTexture.getImage().getId() + ")"); + logger.severe(" Left eye texture " + leftEyeTexture.getName() + " (" + leftEyeTexture.getImage().getNativeObject() + ")"); logger.severe(" Type: " + leftEyeTexture.getType()); logger.severe(" Size: " + leftEyeTexture.getImage().getWidth() + "x" + leftEyeTexture.getImage().getHeight()); logger.severe(" Image depth: " + leftEyeTexture.getImage().getDepth()); - logger.severe(" Image format: " + leftEyeTexture.getImage().getFormat()); + logger.severe(" Image format: " + leftEyeTexture.getImage().getGlFormat()); logger.severe(" Image color space: " + leftEyeTexture.getImage().getColorSpace()); } @@ -212,11 +212,11 @@ public void postRender() { // logger.severe(" Texture type: "+OpenVRUtil.getETextureTypeString(rightTextureType.eType)); logger.severe(" Texture handle: " + rightTextureType.handle()); - logger.severe(" Right eye texture " + rightEyeTexture.getName() + " (" + rightEyeTexture.getImage().getId() + ")"); + logger.severe(" Right eye texture " + rightEyeTexture.getName() + " (" + rightEyeTexture.getImage().getNativeObject() + ")"); logger.severe(" Type: " + rightEyeTexture.getType()); logger.severe(" Size: " + rightEyeTexture.getImage().getWidth() + "x" + rightEyeTexture.getImage().getHeight()); logger.severe(" Image depth: " + rightEyeTexture.getImage().getDepth()); - logger.severe(" Image format: " + rightEyeTexture.getImage().getFormat()); + logger.severe(" Image format: " + rightEyeTexture.getImage().getGlFormat()); logger.severe(" Image color space: " + rightEyeTexture.getImage().getColorSpace()); } } @@ -498,23 +498,23 @@ private void setupFinalFullTexture(Camera cam) { if (environment != null) { if (environment.getApplication() != null) { // create offscreen framebuffer - FrameBuffer out = new FrameBuffer(cam.getWidth(), cam.getHeight(), 1); + GlFrameBuffer out = new GlFrameBuffer(cam.getWidth(), cam.getHeight(), 1); //offBuffer.setSrgb(true); //setup framebuffer's texture - dualEyeTex = new Texture2D(cam.getWidth(), cam.getHeight(), Image.Format.RGBA8); + dualEyeTex = new Texture2D(cam.getWidth(), cam.getHeight(), GlImage.Format.RGBA8); dualEyeTex.setMinFilter(Texture2D.MinFilter.BilinearNoMipMaps); dualEyeTex.setMagFilter(Texture2D.MagFilter.Bilinear); - logger.config("Dual eye texture " + dualEyeTex.getName() + " (" + dualEyeTex.getImage().getId() + ")"); + logger.config("Dual eye texture " + dualEyeTex.getName() + " (" + dualEyeTex.getImage().getNativeObject() + ")"); logger.config(" Type: " + dualEyeTex.getType()); logger.config(" Size: " + dualEyeTex.getImage().getWidth() + "x" + dualEyeTex.getImage().getHeight()); logger.config(" Image depth: " + dualEyeTex.getImage().getDepth()); - logger.config(" Image format: " + dualEyeTex.getImage().getFormat()); + logger.config(" Image format: " + dualEyeTex.getImage().getGlFormat()); logger.config(" Image color space: " + dualEyeTex.getImage().getColorSpace()); //setup framebuffer to use texture - out.setDepthBuffer(Image.Format.Depth); + out.setDepthBuffer(GlImage.Format.Depth); out.setColorTexture(dualEyeTex); ViewPort viewPort = environment.getApplication().getViewPort(); @@ -534,16 +534,16 @@ private ViewPort setupViewBuffers(Camera cam, String viewName) { if (environment != null) { if (environment.getApplication() != null) { // create offscreen framebuffer - FrameBuffer offBufferLeft = new FrameBuffer(cam.getWidth(), cam.getHeight(), 1); + GlFrameBuffer offBufferLeft = new GlFrameBuffer(cam.getWidth(), cam.getHeight(), 1); //offBufferLeft.setSrgb(true); //setup framebuffer's texture - Texture2D offTex = new Texture2D(cam.getWidth(), cam.getHeight(), Image.Format.RGBA8); + Texture2D offTex = new Texture2D(cam.getWidth(), cam.getHeight(), GlImage.Format.RGBA8); offTex.setMinFilter(Texture2D.MinFilter.BilinearNoMipMaps); offTex.setMagFilter(Texture2D.MagFilter.Bilinear); //setup framebuffer to use texture - offBufferLeft.setDepthBuffer(Image.Format.Depth); + offBufferLeft.setDepthBuffer(GlImage.Format.Depth); offBufferLeft.setColorTexture(offTex); ViewPort viewPort = environment.getApplication().getRenderManager().createPreView(viewName, cam); diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/oculus/OculusVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/oculus/OculusVR.java index 75c14c1647..66e15d7f49 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/oculus/OculusVR.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/oculus/OculusVR.java @@ -126,7 +126,7 @@ public class OculusVR implements VRAPI { /** * Frame buffers we can draw into. */ - private FrameBuffer framebuffers[][]; + private GlFrameBuffer framebuffers[][]; public OculusVR(VREnvironment environment) { this.environment = environment; @@ -415,7 +415,7 @@ public boolean initVRCompositor(boolean set) { setupLayers(); - framebuffers = new FrameBuffer[2][]; + framebuffers = new GlFrameBuffer[2][]; for (int eye = 0; eye < 2; eye++) setupFramebuffers(eye); @@ -532,7 +532,7 @@ public void setupFramebuffers(int eye) { LOGGER.fine("HMD Eye #" + eye + " texture chain length: " + chainLength); // Create the frame buffers - framebuffers[eye] = new FrameBuffer[chainLength]; + framebuffers[eye] = new GlFrameBuffer[chainLength]; for (int i = 0; i < chainLength; i++) { // find the GL texture ID for this texture IntBuffer textureIdB = BufferUtils.createIntBuffer(1); @@ -540,16 +540,16 @@ public void setupFramebuffers(int eye) { int textureId = textureIdB.get(); // TODO less hacky way of getting our texture into JMonkeyEngine - Image img = new Image(); + GlImage img = new GlImage(); img.setId(textureId); - img.setFormat(Image.Format.RGBA8); + img.setFormat(GlImage.Format.RGBA8); img.setWidth(textureW); img.setHeight(textureH); Texture2D tex = new Texture2D(img); - FrameBuffer buffer = new FrameBuffer(textureW, textureH, 1); - buffer.setDepthBuffer(Image.Format.Depth); + GlFrameBuffer buffer = new GlFrameBuffer(textureW, textureH, 1); + buffer.setDepthBuffer(GlImage.Format.Depth); buffer.setColorTexture(tex); framebuffers[eye][i] = buffer; @@ -630,7 +630,7 @@ public long getChain(int eye) { return chains[eye]; } - public FrameBuffer[] getFramebuffers(int eye) { + public GlFrameBuffer[] getFramebuffers(int eye) { return framebuffers[eye]; } diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/openvr/OpenVRViewManager.java b/jme3-vr/src/main/java/com/jme3/input/vr/openvr/OpenVRViewManager.java index c6d0237321..8fee4ca755 100644 --- a/jme3-vr/src/main/java/com/jme3/input/vr/openvr/OpenVRViewManager.java +++ b/jme3-vr/src/main/java/com/jme3/input/vr/openvr/OpenVRViewManager.java @@ -15,16 +15,16 @@ import com.jme3.scene.Mesh; import com.jme3.scene.Node; import com.jme3.scene.Spatial; -import com.jme3.scene.VertexBuffer; +import com.jme3.scene.GlVertexBuffer; import com.jme3.system.jopenvr.DistortionCoordinates_t; import com.jme3.system.jopenvr.JOpenVRLibrary; import com.jme3.system.jopenvr.OpenVRUtil; import com.jme3.system.jopenvr.Texture_t; import com.jme3.system.jopenvr.VRTextureBounds_t; import com.jme3.system.jopenvr.VR_IVRSystem_FnTable; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image; -import com.jme3.texture.Texture; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; import com.jme3.ui.Picture; import com.jme3.util.VRGUIPositioningMode; @@ -70,7 +70,7 @@ public OpenVRViewManager(VREnvironment environment){ * @see #getFullTexId() */ protected int getLeftTexId() { - return getLeftTexture().getImage().getId(); + return getLeftTexture().getImage().getNativeObject(); } /** @@ -80,7 +80,7 @@ protected int getLeftTexId() { * @see #getFullTexId() */ protected int getRightTexId() { - return getRightTexture().getImage().getId(); + return getRightTexture().getImage().getNativeObject(); } /** @@ -90,7 +90,7 @@ protected int getRightTexId() { * @see #getRightTexId() */ private int getFullTexId() { - return dualEyeTex.getImage().getId(); + return dualEyeTex.getImage().getNativeObject(); } /** @@ -216,11 +216,11 @@ public void postRender() { logger.severe(" Texture type: "+OpenVRUtil.getETextureTypeString(leftTextureType.eType)); logger.severe(" Texture handle: "+leftTextureType.handle); - logger.severe(" Left eye texture "+leftEyeTexture.getName()+" ("+leftEyeTexture.getImage().getId()+")"); + logger.severe(" Left eye texture "+leftEyeTexture.getName()+" ("+leftEyeTexture.getImage().getNativeObject()+")"); logger.severe(" Type: "+leftEyeTexture.getType()); logger.severe(" Size: "+leftEyeTexture.getImage().getWidth()+"x"+leftEyeTexture.getImage().getHeight()); logger.severe(" Image depth: "+leftEyeTexture.getImage().getDepth()); - logger.severe(" Image format: "+leftEyeTexture.getImage().getFormat()); + logger.severe(" Image format: "+leftEyeTexture.getImage().getGlFormat()); logger.severe(" Image color space: "+leftEyeTexture.getImage().getColorSpace()); } @@ -230,11 +230,11 @@ public void postRender() { logger.severe(" Texture type: "+OpenVRUtil.getETextureTypeString(rightTextureType.eType)); logger.severe(" Texture handle: "+rightTextureType.handle); - logger.severe(" Right eye texture "+rightEyeTexture.getName()+" ("+rightEyeTexture.getImage().getId()+")"); + logger.severe(" Right eye texture "+rightEyeTexture.getName()+" ("+rightEyeTexture.getImage().getNativeObject()+")"); logger.severe(" Type: "+rightEyeTexture.getType()); logger.severe(" Size: "+rightEyeTexture.getImage().getWidth()+"x"+rightEyeTexture.getImage().getHeight()); logger.severe(" Image depth: "+rightEyeTexture.getImage().getDepth()); - logger.severe(" Image format: "+rightEyeTexture.getImage().getFormat()); + logger.severe(" Image format: "+rightEyeTexture.getImage().getGlFormat()); logger.severe(" Image color space: "+rightEyeTexture.getImage().getColorSpace()); } } @@ -536,7 +536,7 @@ private void setupCamerasAndViews() { } } - private ViewPort setupMirrorBuffers(Camera cam, Texture tex, boolean expand) { + private ViewPort setupMirrorBuffers(Camera cam, GlTexture tex, boolean expand) { if (environment != null){ if (environment.getApplication() != null){ Camera cloneCam = cam.clone(); @@ -571,23 +571,23 @@ private void setupFinalFullTexture(Camera cam) { if (environment != null){ if (environment.getApplication() != null){ // create offscreen framebuffer - FrameBuffer out = new FrameBuffer(cam.getWidth(), cam.getHeight(), 1); + GlFrameBuffer out = new GlFrameBuffer(cam.getWidth(), cam.getHeight(), 1); //offBuffer.setSrgb(true); //setup framebuffer's texture - dualEyeTex = new Texture2D(cam.getWidth(), cam.getHeight(), Image.Format.RGBA8); - dualEyeTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); - dualEyeTex.setMagFilter(Texture.MagFilter.Bilinear); + dualEyeTex = new Texture2D(cam.getWidth(), cam.getHeight(), GlImage.Format.RGBA8); + dualEyeTex.setMinFilter(GlTexture.MinFilter.BilinearNoMipMaps); + dualEyeTex.setMagFilter(GlTexture.MagFilter.Bilinear); - logger.config("Dual eye texture "+dualEyeTex.getName()+" ("+dualEyeTex.getImage().getId()+")"); + logger.config("Dual eye texture "+dualEyeTex.getName()+" ("+dualEyeTex.getImage().getNativeObject()+")"); logger.config(" Type: "+dualEyeTex.getType()); logger.config(" Size: "+dualEyeTex.getImage().getWidth()+"x"+dualEyeTex.getImage().getHeight()); logger.config(" Image depth: "+dualEyeTex.getImage().getDepth()); - logger.config(" Image format: "+dualEyeTex.getImage().getFormat()); + logger.config(" Image format: "+dualEyeTex.getImage().getGlFormat()); logger.config(" Image color space: "+dualEyeTex.getImage().getColorSpace()); //setup framebuffer to use texture - out.setDepthBuffer(Image.Format.Depth); + out.setDepthBuffer(GlImage.Format.Depth); out.setColorTexture(dualEyeTex); ViewPort viewPort = environment.getApplication().getViewPort(); @@ -606,16 +606,16 @@ private ViewPort setupViewBuffers(Camera cam, String viewName){ if (environment != null){ if (environment.getApplication() != null){ // create offscreen framebuffer - FrameBuffer offBufferLeft = new FrameBuffer(cam.getWidth(), cam.getHeight(), 1); + GlFrameBuffer offBufferLeft = new GlFrameBuffer(cam.getWidth(), cam.getHeight(), 1); //offBufferLeft.setSrgb(true); //setup framebuffer's texture - Texture2D offTex = new Texture2D(cam.getWidth(), cam.getHeight(), Image.Format.RGBA8); - offTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); - offTex.setMagFilter(Texture.MagFilter.Bilinear); + Texture2D offTex = new Texture2D(cam.getWidth(), cam.getHeight(), GlImage.Format.RGBA8); + offTex.setMinFilter(GlTexture.MinFilter.BilinearNoMipMaps); + offTex.setMagFilter(GlTexture.MagFilter.Bilinear); //setup framebuffer to use texture - offBufferLeft.setDepthBuffer(Image.Format.Depth); + offBufferLeft.setDepthBuffer(GlImage.Format.Depth); offBufferLeft.setColorTexture(offTex); ViewPort viewPort = environment.getApplication().getRenderManager().createPreView(viewName, cam); @@ -722,11 +722,11 @@ public static Mesh setupDistortionMesh(int eye, VRAPI api) { } // OK, create the mesh - distortionMesh.setBuffer(VertexBuffer.Type.Position, 3, verts); - distortionMesh.setBuffer(VertexBuffer.Type.Index, 1, indices); - distortionMesh.setBuffer(VertexBuffer.Type.TexCoord, 2, texcoordR); - distortionMesh.setBuffer(VertexBuffer.Type.TexCoord2, 2, texcoordG); - distortionMesh.setBuffer(VertexBuffer.Type.TexCoord3, 2, texcoordB); + distortionMesh.setBuffer(GlVertexBuffer.Type.Position, 3, verts); + distortionMesh.setBuffer(GlVertexBuffer.Type.Index, 1, indices); + distortionMesh.setBuffer(GlVertexBuffer.Type.TexCoord, 2, texcoordR); + distortionMesh.setBuffer(GlVertexBuffer.Type.TexCoord2, 2, texcoordG); + distortionMesh.setBuffer(GlVertexBuffer.Type.TexCoord3, 2, texcoordB); distortionMesh.setStatic(); return distortionMesh; } diff --git a/jme3-vr/src/main/java/com/jme3/post/CartoonSSAO.java b/jme3-vr/src/main/java/com/jme3/post/CartoonSSAO.java index eb1ddd5404..7f695d4ff6 100644 --- a/jme3-vr/src/main/java/com/jme3/post/CartoonSSAO.java +++ b/jme3-vr/src/main/java/com/jme3/post/CartoonSSAO.java @@ -4,11 +4,10 @@ import com.jme3.material.Material; import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; -import com.jme3.post.Filter; import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; -import com.jme3.texture.Image.Format; +import com.jme3.texture.GlImage.Format; /** * A Cartoon Screen Space Ambient Occlusion filter with instance rendering capabilities. diff --git a/jme3-vr/src/main/java/com/jme3/post/PreNormalCaching.java b/jme3-vr/src/main/java/com/jme3/post/PreNormalCaching.java index 082e0edc07..308771070a 100644 --- a/jme3-vr/src/main/java/com/jme3/post/PreNormalCaching.java +++ b/jme3-vr/src/main/java/com/jme3/post/PreNormalCaching.java @@ -5,7 +5,7 @@ import com.jme3.renderer.RenderManager; import com.jme3.renderer.Renderer; import com.jme3.renderer.ViewPort; -import com.jme3.texture.FrameBuffer; +import com.jme3.texture.GlFrameBuffer; /** * Pre normal caching class. @@ -19,7 +19,7 @@ @Deprecated public class PreNormalCaching { - private static FrameBuffer cachedPreNormals; + private static GlFrameBuffer cachedPreNormals; private static int lastNormalPassesCount, curCount; /** diff --git a/jme3-vr/src/main/java/com/jme3/scene/CenterQuad.java b/jme3-vr/src/main/java/com/jme3/scene/CenterQuad.java index e2911eba6f..1a9b926cae 100644 --- a/jme3-vr/src/main/java/com/jme3/scene/CenterQuad.java +++ b/jme3-vr/src/main/java/com/jme3/scene/CenterQuad.java @@ -35,7 +35,7 @@ import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; -import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.GlVertexBuffer.Type; import java.io.IOException; /** diff --git a/jme3-vr/src/main/java/com/jme3/shadow/AbstractShadowFilterVR.java b/jme3-vr/src/main/java/com/jme3/shadow/AbstractShadowFilterVR.java index f49f4c7d66..348faa3513 100644 --- a/jme3-vr/src/main/java/com/jme3/shadow/AbstractShadowFilterVR.java +++ b/jme3-vr/src/main/java/com/jme3/shadow/AbstractShadowFilterVR.java @@ -43,7 +43,7 @@ import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; import com.jme3.renderer.queue.RenderQueue; -import com.jme3.texture.FrameBuffer; +import com.jme3.texture.GlFrameBuffer; import java.io.IOException; @@ -132,7 +132,7 @@ protected void postQueue(RenderQueue queue) { } @Override - protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) { + protected void postFrame(RenderManager renderManager, ViewPort viewPort, GlFrameBuffer prevFilterBuffer, GlFrameBuffer sceneBuffer) { if(!shadowRenderer.skipPostPass){ shadowRenderer.setPostShadowParams(); } diff --git a/jme3-vr/src/main/java/com/jme3/shadow/AbstractShadowRendererVR.java b/jme3-vr/src/main/java/com/jme3/shadow/AbstractShadowRendererVR.java index ac2df723c4..a12a434844 100644 --- a/jme3-vr/src/main/java/com/jme3/shadow/AbstractShadowRendererVR.java +++ b/jme3-vr/src/main/java/com/jme3/shadow/AbstractShadowRendererVR.java @@ -55,13 +55,13 @@ import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; import com.jme3.scene.debug.WireFrustum; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture.MagFilter; -import com.jme3.texture.Texture.MinFilter; -import com.jme3.texture.Texture.ShadowCompareMode; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture.MagFilter; +import com.jme3.texture.GlTexture.MinFilter; +import com.jme3.texture.GlTexture.ShadowCompareMode; import com.jme3.texture.Texture2D; -import com.jme3.texture.FrameBuffer.FrameBufferTarget; +import com.jme3.texture.GlFrameBuffer.FrameBufferTarget; import com.jme3.ui.Picture; import java.io.IOException; @@ -88,7 +88,7 @@ public abstract class AbstractShadowRendererVR implements SceneProcessor, Savabl protected float shadowIntensity = 0.7f; protected RenderManager renderManager; protected ViewPort viewPort; - protected FrameBuffer[] shadowFB; + protected GlFrameBuffer[] shadowFB; protected Texture2D[] shadowMaps; protected Texture2D dummyTex; protected Material preshadowMat; @@ -159,7 +159,7 @@ protected AbstractShadowRendererVR(AssetManager assetManager, int shadowMapSize, private void init(AssetManager assetManager, int nbShadowMaps, int shadowMapSize) { this.postshadowMat = new Material(assetManager, "Common/MatDefs/Shadow/PostShadow.j3md"); - shadowFB = new FrameBuffer[nbShadowMaps]; + shadowFB = new GlFrameBuffer[nbShadowMaps]; shadowMaps = new Texture2D[nbShadowMaps]; dispPic = new Picture[nbShadowMaps]; lightViewProjectionsMatrices = new Matrix4f[nbShadowMaps]; @@ -174,7 +174,7 @@ private void init(AssetManager assetManager, int nbShadowMaps, int shadowMapSize for (int i = 0; i < nbShadowMaps; i++) { lightViewProjectionsMatrices[i] = new Matrix4f(); - shadowFB[i] = new FrameBuffer(shadowMapSize, shadowMapSize, 1); + shadowFB[i] = new GlFrameBuffer(shadowMapSize, shadowMapSize, 1); shadowMaps[i] = new Texture2D(shadowMapSize, shadowMapSize, Format.Depth); shadowFB[i].setDepthTarget(FrameBufferTarget.newTarget(shadowMaps[i])); @@ -490,7 +490,7 @@ public void displayDebug() { protected abstract void getReceivers(GeometryList lightReceivers); @Override - public void postFrame(FrameBuffer out) { + public void postFrame(GlFrameBuffer out) { if (skipPostPass) { return; } diff --git a/jme3-vr/src/main/java/com/jme3/util/VRGuiManager.java b/jme3-vr/src/main/java/com/jme3/util/VRGuiManager.java index 61033e7ab6..b4adddda9f 100644 --- a/jme3-vr/src/main/java/com/jme3/util/VRGuiManager.java +++ b/jme3-vr/src/main/java/com/jme3/util/VRGuiManager.java @@ -16,9 +16,9 @@ import com.jme3.scene.Node; import com.jme3.scene.shape.CenterQuad; import com.jme3.system.AppSettings; -import com.jme3.texture.FrameBuffer; -import com.jme3.texture.Image.Format; -import com.jme3.texture.Texture; +import com.jme3.texture.GlFrameBuffer; +import com.jme3.texture.GlImage.Format; +import com.jme3.texture.GlTexture; import com.jme3.texture.Texture2D; import java.awt.GraphicsEnvironment; import java.util.Iterator; @@ -423,12 +423,12 @@ private Spatial getGuiQuad(Camera sourceCam){ offView.setBackgroundColor(ColorRGBA.BlackNoAlpha); // create offscreen framebuffer - FrameBuffer offBuffer = new FrameBuffer((int)guiCanvasSize.x, (int)guiCanvasSize.y, 1); + GlFrameBuffer offBuffer = new GlFrameBuffer((int)guiCanvasSize.x, (int)guiCanvasSize.y, 1); //setup framebuffer's texture guiTexture = new Texture2D((int)guiCanvasSize.x, (int)guiCanvasSize.y, Format.RGBA8); - guiTexture.setMinFilter(Texture.MinFilter.BilinearNoMipMaps); - guiTexture.setMagFilter(Texture.MagFilter.Bilinear); + guiTexture.setMinFilter(GlTexture.MinFilter.BilinearNoMipMaps); + guiTexture.setMagFilter(GlTexture.MagFilter.Bilinear); //setup framebuffer to use texture offBuffer.setDepthBuffer(Format.Depth);