From 90a28b1e053edf213708bd7e0d1211120faa7bcb Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Mon, 4 Sep 2023 14:46:15 +0200 Subject: [PATCH 1/5] Never return the cached spatial directly: prevent the manipulation of the first instance of a spatial from poisoning the cache. --- .../jme3/scene/plugins/gltf/GltfLoader.java | 230 +++++++++--------- 1 file changed, 114 insertions(+), 116 deletions(-) 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 a8c5070086..976e2326ca 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 @@ -366,138 +366,136 @@ public Transform readTransforms(JsonObject nodeData) { public Geometry[] readMeshPrimitives(int meshIndex) throws IOException { Geometry[] geomArray = (Geometry[]) fetchFromCache("meshes", meshIndex, Object.class); - if (geomArray != null) { - // cloning the geoms. - Geometry[] geoms = new Geometry[geomArray.length]; - for (int i = 0; i < geoms.length; i++) { - geoms[i] = geomArray[i].clone(false); - } - return geoms; - } - JsonObject meshData = meshes.get(meshIndex).getAsJsonObject(); - JsonArray primitives = meshData.getAsJsonArray("primitives"); - assertNotNull(primitives, "Can't find any primitives in mesh " + meshIndex); - String name = getAsString(meshData, "name"); - - geomArray = new Geometry[primitives.size()]; - int index = 0; - for (JsonElement primitive : primitives) { - JsonObject meshObject = primitive.getAsJsonObject(); - Mesh mesh = new Mesh(); - addToCache("mesh", 0, mesh, 1); - Integer mode = getAsInteger(meshObject, "mode"); - mesh.setMode(getMeshMode(mode)); - Integer indices = getAsInteger(meshObject, "indices"); - if (indices != null) { - mesh.setBuffer(readAccessorData(indices, new VertexBufferPopulator(VertexBuffer.Type.Index))); - } - JsonObject attributes = meshObject.getAsJsonObject("attributes"); - assertNotNull(attributes, "No attributes defined for mesh " + mesh); - - skinBuffers.clear(); - - for (Map.Entry entry : attributes.entrySet()) { - // special case for joints and weights buffer. - // If there are more than 4 bones per vertex, there might be several of them - // we need to read them all and to keep only the 4 that have the most weight on the vertex. - String bufferType = entry.getKey(); - if (bufferType.startsWith("JOINTS")) { - SkinBuffers buffs = getSkinBuffers(bufferType); - SkinBuffers buffer - = readAccessorData(entry.getValue().getAsInt(), new JointArrayPopulator()); - buffs.joints = buffer.joints; - buffs.componentSize = buffer.componentSize; - } else if (bufferType.startsWith("WEIGHTS")) { - SkinBuffers buffs = getSkinBuffers(bufferType); - buffs.weights = readAccessorData(entry.getValue().getAsInt(), new FloatArrayPopulator()); - } else { - VertexBuffer vb = readAccessorData(entry.getValue().getAsInt(), - new VertexBufferPopulator(getVertexBufferType(bufferType))); - if (vb != null) { - mesh.setBuffer(vb); + if (geomArray == null) { + + JsonObject meshData = meshes.get(meshIndex).getAsJsonObject(); + JsonArray primitives = meshData.getAsJsonArray("primitives"); + assertNotNull(primitives, "Can't find any primitives in mesh " + meshIndex); + String name = getAsString(meshData, "name"); + + geomArray = new Geometry[primitives.size()]; + int index = 0; + for (JsonElement primitive : primitives) { + JsonObject meshObject = primitive.getAsJsonObject(); + Mesh mesh = new Mesh(); + addToCache("mesh", 0, mesh, 1); + Integer mode = getAsInteger(meshObject, "mode"); + mesh.setMode(getMeshMode(mode)); + Integer indices = getAsInteger(meshObject, "indices"); + if (indices != null) { + mesh.setBuffer(readAccessorData(indices, new VertexBufferPopulator(VertexBuffer.Type.Index))); + } + JsonObject attributes = meshObject.getAsJsonObject("attributes"); + assertNotNull(attributes, "No attributes defined for mesh " + mesh); + + skinBuffers.clear(); + + for (Map.Entry entry : attributes.entrySet()) { + // special case for joints and weights buffer. + // If there are more than 4 bones per vertex, there might be several of them + // we need to read them all and to keep only the 4 that have the most weight on the vertex. + String bufferType = entry.getKey(); + if (bufferType.startsWith("JOINTS")) { + SkinBuffers buffs = getSkinBuffers(bufferType); + SkinBuffers buffer = readAccessorData(entry.getValue().getAsInt(), new JointArrayPopulator()); + buffs.joints = buffer.joints; + buffs.componentSize = buffer.componentSize; + } else if (bufferType.startsWith("WEIGHTS")) { + SkinBuffers buffs = getSkinBuffers(bufferType); + buffs.weights = readAccessorData(entry.getValue().getAsInt(), new FloatArrayPopulator()); + } else { + VertexBuffer vb = readAccessorData(entry.getValue().getAsInt(), new VertexBufferPopulator(getVertexBufferType(bufferType))); + if (vb != null) { + mesh.setBuffer(vb); + } } } - } - handleSkinningBuffers(mesh, skinBuffers); - - if (mesh.getBuffer(VertexBuffer.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 setup if ever used. - VertexBuffer weightsHW = new VertexBuffer(VertexBuffer.Type.HWBoneWeight); - VertexBuffer indicesHW = new VertexBuffer(VertexBuffer.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); - mesh.setBuffer(weightsHW); - mesh.setBuffer(indicesHW); - mesh.generateBindPose(); - } - - // Read morph target names - LinkedList targetNames = new LinkedList<>(); - if (meshData.has("extras") && meshData.getAsJsonObject("extras").has("targetNames")) { - JsonArray targetNamesJson = meshData.getAsJsonObject("extras").getAsJsonArray("targetNames"); - for (JsonElement target : targetNamesJson) { - targetNames.add(target.getAsString()); + handleSkinningBuffers(mesh, skinBuffers); + + if (mesh.getBuffer(VertexBuffer.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 setup if ever used. + VertexBuffer weightsHW = new VertexBuffer(VertexBuffer.Type.HWBoneWeight); + VertexBuffer indicesHW = new VertexBuffer(VertexBuffer.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); + mesh.setBuffer(weightsHW); + mesh.setBuffer(indicesHW); + mesh.generateBindPose(); } - } - // Read morph targets - JsonArray targets = meshObject.getAsJsonArray("targets"); - if (targets != null) { - for (JsonElement target : targets) { - MorphTarget morphTarget = new MorphTarget(); - if (targetNames.size() > 0) { - morphTarget.setName(targetNames.pop()); + // Read morph target names + LinkedList targetNames = new LinkedList<>(); + if (meshData.has("extras") && meshData.getAsJsonObject("extras").has("targetNames")) { + JsonArray targetNamesJson = meshData.getAsJsonObject("extras").getAsJsonArray("targetNames"); + for (JsonElement target : targetNamesJson) { + targetNames.add(target.getAsString()); } - for (Map.Entry entry : target.getAsJsonObject().entrySet()) { - String bufferType = entry.getKey(); - VertexBuffer.Type type = getVertexBufferType(bufferType); - VertexBuffer vb = readAccessorData(entry.getValue().getAsInt(), - new VertexBufferPopulator(type)); - if (vb != null) { - morphTarget.setBuffer(type, (FloatBuffer) vb.getData()); + } + + // Read morph targets + JsonArray targets = meshObject.getAsJsonArray("targets"); + if (targets != null) { + for (JsonElement target : targets) { + MorphTarget morphTarget = new MorphTarget(); + if (targetNames.size() > 0) { + morphTarget.setName(targetNames.pop()); + } + for (Map.Entry entry : target.getAsJsonObject().entrySet()) { + String bufferType = entry.getKey(); + VertexBuffer.Type type = getVertexBufferType(bufferType); + VertexBuffer vb = readAccessorData(entry.getValue().getAsInt(), new VertexBufferPopulator(type)); + if (vb != null) { + morphTarget.setBuffer(type, (FloatBuffer) vb.getData()); + } } + mesh.addMorphTarget(morphTarget); } - mesh.addMorphTarget(morphTarget); } - } - // Read mesh extras - mesh = customContentManager.readExtensionAndExtras("primitive", meshObject, mesh); - Geometry geom = new Geometry(null, mesh); + // Read mesh extras + mesh = customContentManager.readExtensionAndExtras("primitive", meshObject, mesh); + Geometry geom = new Geometry(null, mesh); - Integer materialIndex = getAsInteger(meshObject, "material"); - if (materialIndex == null) { - geom.setMaterial(defaultMat); - } else { - useNormalsFlag = false; - geom.setMaterial(readMaterial(materialIndex)); - if (geom.getMaterial().getAdditionalRenderState().getBlendMode() - == RenderState.BlendMode.Alpha) { - // Alpha blending is enabled for this material. Let's place the geom in the transparent bucket. - geom.setQueueBucket(RenderQueue.Bucket.Transparent); + Integer materialIndex = getAsInteger(meshObject, "material"); + if (materialIndex == null) { + geom.setMaterial(defaultMat); + } else { + useNormalsFlag = false; + geom.setMaterial(readMaterial(materialIndex)); + if (geom.getMaterial().getAdditionalRenderState().getBlendMode() == RenderState.BlendMode.Alpha) { + // 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) { + // No tangent buffer, but there is a normal map, we have to generate them using MiiktSpace + MikktspaceTangentGenerator.generate(geom); + } } - if (useNormalsFlag && mesh.getBuffer(VertexBuffer.Type.Tangent) == null) { - // No tangent buffer, but there is a normal map, we have to generate them using MiiktSpace - MikktspaceTangentGenerator.generate(geom); + + if (name != null) { + geom.setName(name + (primitives.size() > 1 ? ("_" + index) : "")); } - } - if (name != null) { - geom.setName(name + (primitives.size() > 1 ? ("_" + index) : "")); + geom.updateModelBound(); + geomArray[index] = geom; + index++; } - geom.updateModelBound(); - geomArray[index] = geom; - index++; - } - - geomArray = customContentManager.readExtensionAndExtras("mesh", meshData, geomArray); + geomArray = customContentManager.readExtensionAndExtras("mesh", meshData, geomArray); - addToCache("meshes", meshIndex, geomArray, meshes.size()); - return geomArray; + addToCache("meshes", meshIndex, geomArray, meshes.size()); + } + + // cloning the geoms. + Geometry[] geoms = new Geometry[geomArray.length]; + for (int i = 0; i < geoms.length; i++) { + geoms[i] = geomArray[i].clone(false); + } + return geoms; + } private SkinBuffers getSkinBuffers(String bufferType) { From da8186daf34b07df9bd94865fc22977e966e3060 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Mon, 4 Sep 2023 14:47:13 +0200 Subject: [PATCH 2/5] Add extension to load speaker nodes --- .../JME_speaker/SpeakerExtensionLoader.java | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/JME_speaker/SpeakerExtensionLoader.java diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/JME_speaker/SpeakerExtensionLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/JME_speaker/SpeakerExtensionLoader.java new file mode 100644 index 0000000000..4aafef1782 --- /dev/null +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/JME_speaker/SpeakerExtensionLoader.java @@ -0,0 +1,101 @@ +/* + * 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.scene.plugins.gltf.ext.JME_speaker; + + +import com.jme3.asset.AssetManager; +import com.jme3.audio.AudioData.DataType; +import com.jme3.plugins.json.JsonElement; +import com.jme3.plugins.json.JsonObject; +import com.jme3.audio.AudioNode; +import com.jme3.scene.Node; +import com.jme3.scene.plugins.gltf.ExtensionLoader; +import com.jme3.scene.plugins.gltf.GltfLoader; + +/** + * An extension for the GLTF loader that loads speaker nodes. + * (This extension requires the jme-extras addon for blender) + */ +public class SpeakerExtensionLoader implements ExtensionLoader { + + public SpeakerExtensionLoader(){} + + @Override + public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) { + if (input instanceof Node) { + JsonObject jsonObject = extension.getAsJsonObject(); + + final float volume = jsonObject.getAsJsonPrimitive("volume").getAsFloat(); + final float pitch = jsonObject.getAsJsonPrimitive("pitch").getAsFloat(); + final float attenuation = jsonObject.getAsJsonPrimitive("attenuation").getAsFloat(); // unused + final float distanceMax = jsonObject.getAsJsonPrimitive("distance_max").getAsFloat(); + final float distanceReference = jsonObject.getAsJsonPrimitive("distance_reference").getAsFloat(); + final float volume_min = jsonObject.getAsJsonPrimitive("volume_min").getAsFloat(); // unused + final float volume_max = jsonObject.getAsJsonPrimitive("volume_max").getAsFloat(); // unused + final float angleOuterCone = jsonObject.getAsJsonPrimitive("angle_outer_cone").getAsFloat(); + final float angleInnerCone = jsonObject.getAsJsonPrimitive("angle_inner_cone").getAsFloat(); + final float outerConeVolume = jsonObject.getAsJsonPrimitive("outer_cone_volume").getAsFloat(); // unused + final String soundPath = jsonObject.getAsJsonPrimitive("sound_path").getAsString(); + + String absSoundPath = loader.getInfo().getKey().getFolder() + soundPath; + + + AssetManager am = loader.getInfo().getManager(); + AudioNode audioSource = new AudioNode(am, absSoundPath, DataType.Buffer); + audioSource.setVolume(volume); + audioSource.setPitch(pitch); + audioSource.setRefDistance(distanceReference); + audioSource.setMaxDistance(distanceMax); + audioSource.setInnerAngle(angleInnerCone); + audioSource.setOuterAngle(angleOuterCone); + audioSource.setPositional(true); + + if (angleOuterCone == 360 && angleInnerCone == 360) { + audioSource.setDirectional(false); + } else { + audioSource.setDirectional(true); + } + + audioSource.setLooping(true); + audioSource.setReverbEnabled(true); + + ((Node) input).attachChild(audioSource); + + audioSource.play(); + + } + + return input; + + } + +} From d8e6b2788e3b37528e2dbd5416930ba8a470c3fb Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Mon, 4 Sep 2023 14:49:54 +0200 Subject: [PATCH 3/5] Add userdata loader and make it the default --- .../plugins/gltf/CustomContentManager.java | 14 +- .../jme3/scene/plugins/gltf/GltfModelKey.java | 2 +- .../scene/plugins/gltf/UserDataLoader.java | 190 ++++++++++++++++++ 3 files changed, 199 insertions(+), 7 deletions(-) create mode 100644 jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UserDataLoader.java diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java index 6168132313..8eb782c321 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java @@ -130,14 +130,16 @@ private T readExtension(String name, JsonElement el, T input) throws AssetLo @SuppressWarnings("unchecked") private T readExtras(String name, JsonElement el, T input) throws AssetLoadException { - if (key == null) { - return input; - } ExtrasLoader loader; - loader = key.getExtrasLoader(); - if (loader == null) { - return input; + if (key == null) { + loader = new UserDataLoader(); + } else { + loader = key.getExtrasLoader(); + if (loader == null) { + return input; + } } + JsonElement extras = el.getAsJsonObject().getAsJsonObject("extras"); if (extras == null) { return input; diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfModelKey.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfModelKey.java index 34f43dde50..b02af8a334 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfModelKey.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfModelKey.java @@ -53,7 +53,7 @@ public class GltfModelKey extends ModelKey { private Map materialAdapters = new HashMap<>(); private static Map extensionLoaders = new HashMap<>(); - private ExtrasLoader extrasLoader; + private ExtrasLoader extrasLoader = new UserDataLoader(); private boolean keepSkeletonPose = false; public GltfModelKey(String name) { diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UserDataLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UserDataLoader.java new file mode 100644 index 0000000000..c2a43eac22 --- /dev/null +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UserDataLoader.java @@ -0,0 +1,190 @@ +package com.jme3.scene.plugins.gltf; + +import com.jme3.plugins.json.JsonArray; +import com.jme3.plugins.json.JsonElement; +import com.jme3.plugins.json.JsonObject; +import com.jme3.plugins.json.JsonPrimitive; +import com.jme3.scene.Spatial; +/* + * $Id$ + * + * Copyright (c) 2019, Simsilica, LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + +import java.lang.reflect.Array; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Attaches GLTF "extras" data to objects as appropriate. + * + * @author Paul Speed + */ +public class UserDataLoader implements ExtrasLoader { + + private static final Logger log = Logger.getLogger( + UserDataLoader.class.getName() + ); + + public UserDataLoader() {} + + @Override + public Object handleExtras( + GltfLoader loader, + String parentName, + JsonElement parent, + JsonElement extras, + Object input + ) { + log.fine( + "handleExtras(" + + loader + + ", " + + parentName + + ", " + + parent + + ", " + + extras + + ", " + + input + + ")" + ); + // Only interested in composite objects + if (!(extras instanceof JsonObject)) { + log.warning("Skipping extras:" + extras); + return input; + } + JsonObject jo = extras.getAsJsonObject(); + apply(input, jo); + return input; + } + + protected void apply(Object input, JsonObject extras) { + if (input == null) { + return; + } + if (input.getClass().isArray()) { + applyToArray(input, extras); + } else if (input instanceof Spatial) { + applyToSpatial((Spatial) input, extras); + } else { + log.warning("Unhandled input type:" + input.getClass()); + } + } + + protected void applyToArray(Object array, JsonObject extras) { + int size = Array.getLength(array); + for (int i = 0; i < size; i++) { + Object o = Array.get(array, i); + log.fine("processing array[" + i + "]:" + o); + apply(o, extras); + } + } + + protected void applyToSpatial(Spatial spatial, JsonObject extras) { + for (Map.Entry el : extras.entrySet()) { + log.fine(el.toString()); + Object val = toAttribute(el.getValue(), false); + + if (log.isLoggable(Level.FINE)) { + log.fine("setUserData(" + el.getKey() + ", " + val + ")"); + } + spatial.setUserData(el.getKey(), val); + } + } + + protected Object toAttribute(JsonElement el, boolean nested) { + if (el == null) { + return null; + } + if (el instanceof JsonObject) { + return toAttribute(el.getAsJsonObject(), nested); + } else if (el instanceof JsonArray) { + return toAttribute(el.getAsJsonArray(), nested); + } else if (el instanceof JsonPrimitive) { + return toAttribute(el.getAsJsonPrimitive(), nested); + } + log.fine("Unhandled extras element:" + el); + return null; + } + + protected Object toAttribute(JsonObject jo, boolean nested) { + Map result = new HashMap<>(); + for (Map.Entry el : jo.entrySet()) { + result.put(el.getKey(), toAttribute(el.getValue(), true)); + } + return result; + } + + protected Object toAttribute(JsonArray ja, boolean nested) { + List result = new ArrayList<>(); + for (JsonElement el : ja) { + result.add(toAttribute(el, true)); + } + return result; + } + + protected Object toAttribute(JsonPrimitive jp, boolean nested) { + if (jp.isBoolean()) { + return jp.getAsBoolean(); + } else if (jp.isNumber()) { + // JME doesn't save Maps properly and treats them as two + // separate Lists... and it doesn't like saving Doubles + // in lists so we'll just return strings in the case where + // the value would end up in a map. If users someday really + // need properly typed map values and JME map storage hasn't + // been fixed then perhaps we give the users the option of + // flattening the nested properties into dot notation, ie: + // all directly on UserData with no Map children. + if (nested) { + return jp.getAsString(); + } + Number num = jp.getAsNumber(); + // JME doesn't like to save GSON's LazilyParsedNumber so we'll + // convert it into a real number. I don't think we can reliably + // guess what type of number the user intended. It would take + // some expirimentation to determine if things like 0.0 are + // preserved + // during export or just get exported as 0. + // Rather than randomly flip-flop between number types depending + // on the inclusion (or not) of a decimal point, we'll just always + // return Double. + return num.doubleValue(); + } else if (jp.isString()) { + return jp.getAsString(); + } + log.fine("Unhandled primitive:" + jp); + return null; + } +} From 394973d8c8d0f3320d453fdaeed62b74f41f04d2 Mon Sep 17 00:00:00 2001 From: Riccardo Balbo Date: Mon, 4 Sep 2023 14:59:13 +0200 Subject: [PATCH 4/5] Make default extensions registrable statically, move all extensions into packages named after their fully qualified name --- .../plugins/gltf/CustomContentManager.java | 42 ++++++++++++++++--- .../jme3/scene/plugins/gltf/GltfLoader.java | 13 +++++- .../scene/plugins/gltf/MaterialAdapter.java | 2 +- .../LightsPunctualExtensionLoader.java | 11 ++++- .../PBRSpecGlossExtensionLoader.java | 13 ++++-- .../PBRSpecGlossMaterialAdapter.java | 3 +- .../UnlitExtensionLoader.java | 10 ++++- .../UnlitMaterialAdapter.java | 3 +- .../TextureTransformExtensionLoader.java | 15 ++++--- 9 files changed, 90 insertions(+), 22 deletions(-) rename jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/{ => ext/KHR_lights_punctual}/LightsPunctualExtensionLoader.java (97%) rename jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/{ => ext/KHR_materials_pbrSpecularGlossiness}/PBRSpecGlossExtensionLoader.java (91%) rename jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/{ => ext/KHR_materials_pbrSpecularGlossiness}/PBRSpecGlossMaterialAdapter.java (94%) rename jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/{ => ext/KHR_materials_unlit}/UnlitExtensionLoader.java (89%) rename jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/{ => ext/KHR_materials_unlit}/UnlitMaterialAdapter.java (96%) rename jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/{ => ext/KHR_texture_transform}/TextureTransformExtensionLoader.java (95%) diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java index 8eb782c321..ce641b3b36 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java @@ -34,10 +34,17 @@ import com.jme3.asset.AssetLoadException; import com.jme3.plugins.json.JsonArray; import com.jme3.plugins.json.JsonElement; +import com.jme3.scene.plugins.gltf.ext.JME_speaker.SpeakerExtensionLoader; +import com.jme3.scene.plugins.gltf.ext.KHR_lights_punctual.LightsPunctualExtensionLoader; +import com.jme3.scene.plugins.gltf.ext.KHR_materials_pbrSpecularGlossiness.PBRSpecGlossExtensionLoader; +import com.jme3.scene.plugins.gltf.ext.KHR_materials_unlit.UnlitExtensionLoader; +import com.jme3.scene.plugins.gltf.ext.KHR_texture_transform.TextureTransformExtensionLoader; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -51,13 +58,19 @@ public class CustomContentManager { private GltfModelKey key; private GltfLoader gltfLoader; - private final Map defaultExtensionLoaders = new HashMap<>(); + static final Map> defaultExtensionLoaders = new ConcurrentHashMap<>(); + static { + defaultExtensionLoaders.put("KHR_materials_pbrSpecularGlossiness", PBRSpecGlossExtensionLoader.class); + defaultExtensionLoaders.put("KHR_lights_punctual", LightsPunctualExtensionLoader.class); + defaultExtensionLoaders.put("KHR_materials_unlit", UnlitExtensionLoader.class); + defaultExtensionLoaders.put("KHR_texture_transform", TextureTransformExtensionLoader.class); + defaultExtensionLoaders.put("JME_speaker", SpeakerExtensionLoader.class); + } + + private final Map loadedExtensionLoaders = new HashMap<>(); public CustomContentManager() { - defaultExtensionLoaders.put("KHR_materials_pbrSpecularGlossiness", new PBRSpecGlossExtensionLoader()); - defaultExtensionLoaders.put("KHR_lights_punctual", new LightsPunctualExtensionLoader()); - defaultExtensionLoaders.put("KHR_materials_unlit", new UnlitExtensionLoader()); - defaultExtensionLoaders.put("KHR_texture_transform", new TextureTransformExtensionLoader()); + } void init(GltfLoader gltfLoader) { @@ -106,12 +119,29 @@ private T readExtension(String name, JsonElement el, T input) throws AssetLo for (Map.Entry ext : extensions.getAsJsonObject().entrySet()) { ExtensionLoader loader = null; + if (key != null) { loader = key.getExtensionLoader(ext.getKey()); } + if (loader == null) { - loader = defaultExtensionLoaders.get(ext.getKey()); + loader = loadedExtensionLoaders.get(ext.getKey()); + if (loader == null) { + try { + Class clz = defaultExtensionLoaders.get(ext.getKey()); + if (clz != null) { + loader = clz.getDeclaredConstructor().newInstance(); + } + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + logger.log(Level.WARNING, "Could not instantiate loader", e); + } + + if (loader != null) { + loadedExtensionLoaders.put(ext.getKey(), loader); + } + } } + if (loader == null) { logger.log(Level.WARNING, "Could not find loader for extension " + ext.getKey()); 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 976e2326ca..df06368dd8 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 @@ -1446,8 +1446,7 @@ public Matrix4f[] populate(Integer bufferViewIndex, int componentType, String ty private class JointArrayPopulator implements Populator { @Override - public SkinBuffers populate(Integer bufferViewIndex, int componentType, String type, int count, - int byteOffset, boolean normalized) throws IOException { + public SkinBuffers populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset, boolean normalized) throws IOException { int numComponents = getNumberOfComponents(type); // can be bytes or shorts. @@ -1469,4 +1468,14 @@ public SkinBuffers populate(Integer bufferViewIndex, int componentType, String t return new SkinBuffers(data, format.getComponentSize()); } } + + + public static void registerExtension(String name, Class ext) { + CustomContentManager.defaultExtensionLoaders.put(name, ext); + } + + + public static void unregisterExtension(String name) { + CustomContentManager.defaultExtensionLoaders.remove(name); + } } \ No newline at end of file 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 cb2639dc8f..4cb9078971 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 @@ -63,7 +63,7 @@ public abstract class MaterialAdapter { protected abstract MatParam adaptMatParam(MatParam param); - protected void init(AssetManager assetManager) { + public void init(AssetManager assetManager) { this.assetManager = assetManager; this.reset(); } diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/LightsPunctualExtensionLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_lights_punctual/LightsPunctualExtensionLoader.java similarity index 97% rename from jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/LightsPunctualExtensionLoader.java rename to jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_lights_punctual/LightsPunctualExtensionLoader.java index b3df7dbc88..fa2ee51465 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/LightsPunctualExtensionLoader.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_lights_punctual/LightsPunctualExtensionLoader.java @@ -29,8 +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.scene.plugins.gltf; +package com.jme3.scene.plugins.gltf.ext.KHR_lights_punctual; + import com.jme3.plugins.json.JsonArray; import com.jme3.plugins.json.JsonObject; import com.jme3.plugins.json.JsonElement; @@ -44,6 +45,10 @@ import com.jme3.math.Vector3f; import com.jme3.scene.Node; import com.jme3.scene.control.LightControl; +import com.jme3.scene.plugins.gltf.ExtensionLoader; +import com.jme3.scene.plugins.gltf.GltfLoader; +import com.jme3.scene.plugins.gltf.GltfUtils; + import java.util.HashMap; import java.util.HashSet; @@ -60,6 +65,10 @@ public class LightsPunctualExtensionLoader implements ExtensionLoader { private final HashSet pendingNodes = new HashSet<>(); private final HashMap lightDefinitions = new HashMap<>(); + public LightsPunctualExtensionLoader() { + + } + @Override public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) { if (input instanceof Node) { //We are processing a node diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossExtensionLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_pbrSpecularGlossiness/PBRSpecGlossExtensionLoader.java similarity index 91% rename from jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossExtensionLoader.java rename to jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_pbrSpecularGlossiness/PBRSpecGlossExtensionLoader.java index 2ab3862db3..2411271f6a 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossExtensionLoader.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_pbrSpecularGlossiness/PBRSpecGlossExtensionLoader.java @@ -29,15 +29,20 @@ * 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.plugins.gltf; +package com.jme3.scene.plugins.gltf.ext.KHR_materials_pbrSpecularGlossiness; import com.jme3.asset.AssetKey; -import java.io.IOException; -import com.jme3.plugins.json.JsonElement; import static com.jme3.scene.plugins.gltf.GltfUtils.getAsColor; import static com.jme3.scene.plugins.gltf.GltfUtils.getAsFloat; +import java.io.IOException; +import com.jme3.plugins.json.JsonElement; +import com.jme3.scene.plugins.gltf.ExtensionLoader; +import com.jme3.scene.plugins.gltf.GltfLoader; +import com.jme3.scene.plugins.gltf.GltfModelKey; +import com.jme3.scene.plugins.gltf.MaterialAdapter; + /** * Material adapter for PBR Specular Glossiness pipeline * Created by Nehon on 20/08/2017. @@ -46,6 +51,8 @@ public class PBRSpecGlossExtensionLoader implements ExtensionLoader { private PBRSpecGlossMaterialAdapter materialAdapter = new PBRSpecGlossMaterialAdapter(); + public PBRSpecGlossExtensionLoader(){} + @Override public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) throws IOException { MaterialAdapter adapter = materialAdapter; diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossMaterialAdapter.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_pbrSpecularGlossiness/PBRSpecGlossMaterialAdapter.java similarity index 94% rename from jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossMaterialAdapter.java rename to jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_pbrSpecularGlossiness/PBRSpecGlossMaterialAdapter.java index bd4c25c8af..d9dfc60ceb 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossMaterialAdapter.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_pbrSpecularGlossiness/PBRSpecGlossMaterialAdapter.java @@ -29,9 +29,10 @@ * 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.plugins.gltf; +package com.jme3.scene.plugins.gltf.ext.KHR_materials_pbrSpecularGlossiness; import com.jme3.material.MatParam; +import com.jme3.scene.plugins.gltf.PBRMaterialAdapter; /** * Created by Nehon on 20/08/2017. diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnlitExtensionLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_unlit/UnlitExtensionLoader.java similarity index 89% rename from jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnlitExtensionLoader.java rename to jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_unlit/UnlitExtensionLoader.java index cb45fcc533..f27317ae00 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnlitExtensionLoader.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_unlit/UnlitExtensionLoader.java @@ -29,9 +29,13 @@ * 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.plugins.gltf; -import com.jme3.plugins.json.JsonElement; +package com.jme3.scene.plugins.gltf.ext.KHR_materials_unlit; +import com.jme3.scene.plugins.gltf.ExtensionLoader; +import com.jme3.scene.plugins.gltf.GltfLoader; +import com.jme3.scene.plugins.gltf.GltfModelKey; +import com.jme3.scene.plugins.gltf.MaterialAdapter; import com.jme3.asset.AssetKey; +import com.jme3.plugins.json.JsonElement; /** * Material adapter for the Unlit pipeline @@ -41,6 +45,8 @@ public class UnlitExtensionLoader implements ExtensionLoader { private final UnlitMaterialAdapter materialAdapter = new UnlitMaterialAdapter(); + public UnlitExtensionLoader() {} + @Override public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) { MaterialAdapter adapter = materialAdapter; diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnlitMaterialAdapter.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_unlit/UnlitMaterialAdapter.java similarity index 96% rename from jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnlitMaterialAdapter.java rename to jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_unlit/UnlitMaterialAdapter.java index 53e247bb0c..0ae08698b7 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnlitMaterialAdapter.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_unlit/UnlitMaterialAdapter.java @@ -29,10 +29,11 @@ * 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.plugins.gltf; +package com.jme3.scene.plugins.gltf.ext.KHR_materials_unlit; import com.jme3.material.MatParam; import com.jme3.material.RenderState; +import com.jme3.scene.plugins.gltf.MaterialAdapter; /** * @author Markil 3 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/ext/KHR_texture_transform/TextureTransformExtensionLoader.java similarity index 95% rename from jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/TextureTransformExtensionLoader.java rename to jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_texture_transform/TextureTransformExtensionLoader.java index ea643cea0c..fdb31ea4c3 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/ext/KHR_texture_transform/TextureTransformExtensionLoader.java @@ -29,19 +29,23 @@ * 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.plugins.gltf; +package com.jme3.scene.plugins.gltf.ext.KHR_texture_transform; -import com.jme3.plugins.json.JsonArray; -import com.jme3.plugins.json.JsonObject; -import com.jme3.plugins.json.JsonElement; import com.jme3.asset.AssetLoadException; import com.jme3.math.Matrix3f; import com.jme3.math.Vector3f; +import com.jme3.plugins.json.JsonArray; +import com.jme3.plugins.json.JsonElement; +import com.jme3.plugins.json.JsonObject; import com.jme3.scene.Mesh; import com.jme3.scene.VertexBuffer; +import com.jme3.scene.plugins.gltf.ExtensionLoader; +import com.jme3.scene.plugins.gltf.GltfLoader; +import com.jme3.texture.Texture2D; + import static com.jme3.scene.plugins.gltf.GltfUtils.getAsInteger; import static com.jme3.scene.plugins.gltf.GltfUtils.getVertexBufferType; -import com.jme3.texture.Texture2D; + import java.io.IOException; import java.nio.FloatBuffer; import java.util.HashMap; @@ -62,6 +66,7 @@ public class TextureTransformExtensionLoader implements ExtensionLoader { private final static Logger logger = Logger.getLogger(TextureTransformExtensionLoader.class.getName()); + public TextureTransformExtensionLoader(){} /** * Scale/rotate/translate UV coordinates based on a transformation matrix. * Code adapted from scaleTextureCoordinates(Vector2f) in jme3-core/src/main/java/com/jme3/scene/Mesh.java From 43903735aa854d7cf542330959b63fa50b44ac51 Mon Sep 17 00:00:00 2001 From: riccardobl Date: Mon, 4 Sep 2023 13:00:56 +0000 Subject: [PATCH 5/5] auto-format --- .../plugins/gltf/CustomContentManager.java | 75 +++- .../jme3/scene/plugins/gltf/GltfLoader.java | 384 ++++++++++++------ .../scene/plugins/gltf/MaterialAdapter.java | 29 +- .../LightsPunctualExtensionLoader.java | 51 ++- .../PBRSpecGlossExtensionLoader.java | 25 +- .../UnlitExtensionLoader.java | 13 +- .../TextureTransformExtensionLoader.java | 352 ++++++++-------- 7 files changed, 581 insertions(+), 348 deletions(-) diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java index ce641b3b36..87ae344819 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java @@ -39,7 +39,6 @@ import com.jme3.scene.plugins.gltf.ext.KHR_materials_pbrSpecularGlossiness.PBRSpecGlossExtensionLoader; import com.jme3.scene.plugins.gltf.ext.KHR_materials_unlit.UnlitExtensionLoader; import com.jme3.scene.plugins.gltf.ext.KHR_texture_transform.TextureTransformExtensionLoader; - import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; @@ -53,12 +52,14 @@ */ public class CustomContentManager { - private final static Logger logger = Logger.getLogger(CustomContentManager.class.getName()); + private static final Logger logger = Logger.getLogger(CustomContentManager.class.getName()); private GltfModelKey key; private GltfLoader gltfLoader; - static final Map> defaultExtensionLoaders = new ConcurrentHashMap<>(); + static final Map> defaultExtensionLoaders = + new ConcurrentHashMap<>(); + static { defaultExtensionLoaders.put("KHR_materials_pbrSpecularGlossiness", PBRSpecGlossExtensionLoader.class); defaultExtensionLoaders.put("KHR_lights_punctual", LightsPunctualExtensionLoader.class); @@ -66,12 +67,10 @@ public class CustomContentManager { defaultExtensionLoaders.put("KHR_texture_transform", TextureTransformExtensionLoader.class); defaultExtensionLoaders.put("JME_speaker", SpeakerExtensionLoader.class); } - + private final Map loadedExtensionLoaders = new HashMap<>(); - public CustomContentManager() { - - } + public CustomContentManager() {} void init(GltfLoader gltfLoader) { this.gltfLoader = gltfLoader; @@ -85,8 +84,16 @@ void init(GltfLoader gltfLoader) { for (JsonElement extElem : extensionUsed) { String ext = extElem.getAsString(); if (ext != null) { - if (defaultExtensionLoaders.get(ext) == null && (this.key != null && this.key.getExtensionLoader(ext) == null)) { - logger.log(Level.WARNING, "Extension " + ext + " is not supported, please provide your own implementation in the GltfModelKey"); + if ( + defaultExtensionLoaders.get(ext) == null && + (this.key != null && this.key.getExtensionLoader(ext) == null) + ) { + logger.log( + Level.WARNING, + "Extension " + + ext + + " is not supported, please provide your own implementation in the GltfModelKey" + ); } } } @@ -96,15 +103,24 @@ void init(GltfLoader gltfLoader) { for (JsonElement extElem : extensionRequired) { String ext = extElem.getAsString(); if (ext != null) { - if (defaultExtensionLoaders.get(ext) == null && (this.key != null && this.key.getExtensionLoader(ext) == null)) { - logger.log(Level.SEVERE, "Extension " + ext + " is mandatory for this file, the loaded scene result will be unexpected."); + if ( + defaultExtensionLoaders.get(ext) == null && + (this.key != null && this.key.getExtensionLoader(ext) == null) + ) { + logger.log( + Level.SEVERE, + "Extension " + + ext + + " is mandatory for this file, the loaded scene result will be unexpected." + ); } } } } } - public T readExtensionAndExtras(String name, JsonElement el, T input) throws AssetLoadException, IOException { + public T readExtensionAndExtras(String name, JsonElement el, T input) + throws AssetLoadException, IOException { T output = readExtension(name, el, input); output = readExtras(name, el, output); return output; @@ -132,7 +148,14 @@ private T readExtension(String name, JsonElement el, T input) throws AssetLo if (clz != null) { loader = clz.getDeclaredConstructor().newInstance(); } - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + } catch ( + InstantiationException + | IllegalAccessException + | IllegalArgumentException + | InvocationTargetException + | NoSuchMethodException + | SecurityException e + ) { logger.log(Level.WARNING, "Could not instantiate loader", e); } @@ -141,7 +164,6 @@ private T readExtension(String name, JsonElement el, T input) throws AssetLo } } } - if (loader == null) { logger.log(Level.WARNING, "Could not find loader for extension " + ext.getKey()); @@ -151,7 +173,15 @@ private T readExtension(String name, JsonElement el, T input) throws AssetLo try { return (T) loader.handleExtension(gltfLoader, name, el, ext.getValue(), input); } catch (ClassCastException e) { - throw new AssetLoadException("Extension loader " + loader.getClass().getName() + " for extension " + ext.getKey() + " is incompatible with type " + input.getClass(), e); + throw new AssetLoadException( + "Extension loader " + + loader.getClass().getName() + + " for extension " + + ext.getKey() + + " is incompatible with type " + + input.getClass(), + e + ); } } @@ -169,7 +199,7 @@ private T readExtras(String name, JsonElement el, T input) throws AssetLoadE return input; } } - + JsonElement extras = el.getAsJsonObject().getAsJsonObject("extras"); if (extras == null) { return input; @@ -178,10 +208,15 @@ private T readExtras(String name, JsonElement el, T input) throws AssetLoadE try { return (T) loader.handleExtras(gltfLoader, name, el, extras, input); } catch (ClassCastException e) { - throw new AssetLoadException("Extra loader " + loader.getClass().getName() + " for " + name + " is incompatible with type " + input.getClass(), e); + throw new AssetLoadException( + "Extra loader " + + loader.getClass().getName() + + " for " + + name + + " is incompatible with type " + + input.getClass(), + e + ); } - } - - } 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 df06368dd8..dd68056daa 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 @@ -31,21 +31,22 @@ */ package com.jme3.scene.plugins.gltf; -import com.jme3.plugins.json.JsonArray; -import com.jme3.plugins.json.JsonObject; -import com.jme3.plugins.json.JsonPrimitive; -import com.jme3.plugins.json.JsonElement; +import static com.jme3.scene.plugins.gltf.GltfUtils.*; + import com.jme3.anim.*; import com.jme3.asset.*; import com.jme3.material.Material; import com.jme3.material.RenderState; import com.jme3.math.*; +import com.jme3.plugins.json.JsonArray; +import com.jme3.plugins.json.JsonElement; +import com.jme3.plugins.json.JsonObject; +import com.jme3.plugins.json.JsonPrimitive; import com.jme3.renderer.Camera; import com.jme3.renderer.queue.RenderQueue; import com.jme3.scene.*; 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.Texture2D; import com.jme3.util.IntMap; @@ -128,7 +129,11 @@ protected Object loadFromStream(AssetInfo assetInfo, InputStream stream) throws String version = getAsString(asset, "version"); String minVersion = getAsString(asset, "minVersion"); if (!isSupported(version, minVersion)) { - logger.log(Level.SEVERE, "Gltf Loader doesn''t support this gltf version: {0}{1}", new Object[]{version, minVersion != null ? ("/" + minVersion) : ""}); + logger.log( + Level.SEVERE, + "Gltf Loader doesn''t support this gltf version: {0}{1}", + new Object[] { version, minVersion != null ? ("/" + minVersion) : "" } + ); } scenes = docRoot.getAsJsonArray("scenes"); @@ -262,7 +267,6 @@ public Object readNode(int nodeIndex) throws IOException { spatial = node; } spatial.setName(readMeshName(meshIndex)); - } else { // no mesh, we have a node. Can be a camera node or a regular node. Integer camIndex = getAsInteger(nodeData, "camera"); @@ -341,24 +345,29 @@ public Transform readTransforms(JsonObject nodeData) { JsonArray translation = nodeData.getAsJsonArray("translation"); if (translation != null) { transform.setTranslation( - translation.get(0).getAsFloat(), - translation.get(1).getAsFloat(), - translation.get(2).getAsFloat()); + translation.get(0).getAsFloat(), + translation.get(1).getAsFloat(), + translation.get(2).getAsFloat() + ); } JsonArray rotation = nodeData.getAsJsonArray("rotation"); if (rotation != null) { - transform.setRotation(new Quaternion( + transform.setRotation( + new Quaternion( rotation.get(0).getAsFloat(), rotation.get(1).getAsFloat(), rotation.get(2).getAsFloat(), - rotation.get(3).getAsFloat())); + rotation.get(3).getAsFloat() + ) + ); } JsonArray scale = nodeData.getAsJsonArray("scale"); if (scale != null) { transform.setScale( - scale.get(0).getAsFloat(), - scale.get(1).getAsFloat(), - scale.get(2).getAsFloat()); + scale.get(0).getAsFloat(), + scale.get(1).getAsFloat(), + scale.get(2).getAsFloat() + ); } return transform; @@ -367,7 +376,6 @@ public Transform readTransforms(JsonObject nodeData) { public Geometry[] readMeshPrimitives(int meshIndex) throws IOException { Geometry[] geomArray = (Geometry[]) fetchFromCache("meshes", meshIndex, Object.class); if (geomArray == null) { - JsonObject meshData = meshes.get(meshIndex).getAsJsonObject(); JsonArray primitives = meshData.getAsJsonArray("primitives"); assertNotNull(primitives, "Can't find any primitives in mesh " + meshIndex); @@ -383,7 +391,9 @@ 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(VertexBuffer.Type.Index)) + ); } JsonObject attributes = meshObject.getAsJsonObject("attributes"); assertNotNull(attributes, "No attributes defined for mesh " + mesh); @@ -397,14 +407,21 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException { String bufferType = entry.getKey(); if (bufferType.startsWith("JOINTS")) { SkinBuffers buffs = getSkinBuffers(bufferType); - SkinBuffers buffer = readAccessorData(entry.getValue().getAsInt(), new JointArrayPopulator()); + SkinBuffers buffer = readAccessorData( + entry.getValue().getAsInt(), + new JointArrayPopulator() + ); buffs.joints = buffer.joints; buffs.componentSize = buffer.componentSize; } else if (bufferType.startsWith("WEIGHTS")) { SkinBuffers buffs = getSkinBuffers(bufferType); - buffs.weights = readAccessorData(entry.getValue().getAsInt(), new FloatArrayPopulator()); + buffs.weights = + readAccessorData(entry.getValue().getAsInt(), new FloatArrayPopulator()); } else { - VertexBuffer vb = readAccessorData(entry.getValue().getAsInt(), new VertexBufferPopulator(getVertexBufferType(bufferType))); + VertexBuffer vb = readAccessorData( + entry.getValue().getAsInt(), + new VertexBufferPopulator(getVertexBufferType(bufferType)) + ); if (vb != null) { mesh.setBuffer(vb); } @@ -429,7 +446,9 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException { // Read morph target names LinkedList targetNames = new LinkedList<>(); if (meshData.has("extras") && meshData.getAsJsonObject("extras").has("targetNames")) { - JsonArray targetNamesJson = meshData.getAsJsonObject("extras").getAsJsonArray("targetNames"); + JsonArray targetNamesJson = meshData + .getAsJsonObject("extras") + .getAsJsonArray("targetNames"); for (JsonElement target : targetNamesJson) { targetNames.add(target.getAsString()); } @@ -446,7 +465,10 @@ 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(), new VertexBufferPopulator(type)); + VertexBuffer vb = readAccessorData( + entry.getValue().getAsInt(), + new VertexBufferPopulator(type) + ); if (vb != null) { morphTarget.setBuffer(type, (FloatBuffer) vb.getData()); } @@ -465,7 +487,10 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException { } else { useNormalsFlag = false; geom.setMaterial(readMaterial(materialIndex)); - if (geom.getMaterial().getAdditionalRenderState().getBlendMode() == RenderState.BlendMode.Alpha) { + if ( + geom.getMaterial().getAdditionalRenderState().getBlendMode() == + RenderState.BlendMode.Alpha + ) { // Alpha blending is enabled for this material. Let's place the geom in the transparent bucket. geom.setQueueBucket(RenderQueue.Bucket.Transparent); } @@ -488,14 +513,13 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException { addToCache("meshes", meshIndex, geomArray, meshes.size()); } - + // cloning the geoms. Geometry[] geoms = new Geometry[geomArray.length]; for (int i = 0; i < geoms.length; i++) { geoms[i] = geomArray[i].clone(false); } return geoms; - } private SkinBuffers getSkinBuffers(String bufferType) { @@ -531,8 +555,14 @@ private R readAccessorData(int accessorIndex, Populator populator) throws return data; } - public Object readBuffer(Integer bufferViewIndex, int byteOffset, int count, Object store, - int numComponents, VertexBuffer.Format format) throws IOException { + public Object readBuffer( + Integer bufferViewIndex, + int byteOffset, + int count, + Object store, + int numComponents, + VertexBuffer.Format format + ) throws IOException { JsonObject bufferView = bufferViews.get(bufferViewIndex).getAsJsonObject(); Integer bufferIndex = getAsInteger(bufferView, "buffer"); assertNotNull(bufferIndex, "No buffer defined for bufferView " + bufferViewIndex); @@ -592,7 +622,8 @@ protected byte[] getBytes(int bufferIndex, String uri, Integer bufferLength) thr String decoded = decodeUri(uri); if (!decoded.endsWith(".bin")) { throw new AssetLoadException( - "Cannot load " + decoded + ", a .bin extension is required."); + "Cannot load " + decoded + ", a .bin extension is required." + ); } BinDataKey key = new BinDataKey(info.getKey().getFolder() + decoded); @@ -628,8 +659,10 @@ public Material readMaterial(int materialIndex) throws IOException { adapter = customContentManager.readExtensionAndExtras("material", matData, adapter); if (adapter == null) { - logger.log(Level.WARNING, - "Couldn't find any matching material definition for material " + materialIndex); + logger.log( + Level.WARNING, + "Couldn't find any matching material definition for material " + materialIndex + ); adapter = defaultMaterialAdapters.get("pbrMetallicRoughness"); adapter.init(info.getManager()); setDefaultParams(adapter.getMaterial()); @@ -641,10 +674,13 @@ public Material readMaterial(int materialIndex) throws IOException { adapter.setParam("metallicFactor", getAsFloat(pbrMat, "metallicFactor", 1f)); adapter.setParam("roughnessFactor", getAsFloat(pbrMat, "roughnessFactor", 1f)); adapter.setParam("baseColorTexture", readTexture(pbrMat.getAsJsonObject("baseColorTexture"))); - adapter.setParam("metallicRoughnessTexture", - readTexture(pbrMat.getAsJsonObject("metallicRoughnessTexture"))); + adapter.setParam( + "metallicRoughnessTexture", + readTexture(pbrMat.getAsJsonObject("metallicRoughnessTexture")) + ); JsonObject metallicRoughnessJson = pbrMat.getAsJsonObject("metallicRoughnessTexture"); - metallicRoughnessIndex = metallicRoughnessJson != null ? getAsInteger(metallicRoughnessJson, "index") : null; + metallicRoughnessIndex = + metallicRoughnessJson != null ? getAsInteger(metallicRoughnessJson, "index") : null; } adapter.getMaterial().setName(getAsString(matData, "name")); @@ -670,7 +706,7 @@ public Material readMaterial(int materialIndex) throws IOException { Integer occlusionIndex = occlusionJson != null ? getAsInteger(occlusionJson, "index") : null; if (occlusionIndex != null && occlusionIndex.equals(metallicRoughnessIndex)) { adapter.getMaterial().setBoolean("AoPackedInMRMap", true); - } else { + } else { adapter.setParam("occlusionTexture", readTexture(matData.getAsJsonObject("occlusionTexture"))); } @@ -707,7 +743,6 @@ public void readCameras() throws IOException { cam.setFrustumPerspective(yfov * FastMath.RAD_TO_DEG, aspectRatio, zNear, zFar); cam = customContentManager.readExtensionAndExtras("camera.perspective", camData, cam); - } else { Float xmag = getAsFloat(camData, "xmag"); assertNotNull(xmag, "No xmag for orthographic camera"); @@ -774,7 +809,6 @@ public Texture2D readImage(int sourceIndex, boolean flip) throws IOException { String extension = mimeType.split("/")[1]; TextureKey key = new TextureKey("image" + sourceIndex + "." + extension, flip); result = (Texture2D) info.getManager().loadAssetFromStream(key, new ByteArrayInputStream(data)); - } else if (uri.startsWith("data:")) { // base64 encoded image String[] uriInfo = uri.split(","); @@ -815,13 +849,13 @@ public void readAnimation(int animationIndex) throws IOException { continue; } assertNotNull(targetPath, "No target path for channel"); -// -// if (targetPath.equals("weights")) { -// // Morph animation, not implemented in JME, let's warn the user and skip the channel -// logger.log(Level.WARNING, -// "Morph animation is not supported by JME yet, skipping animation track"); -// continue; -// } + // + // if (targetPath.equals("weights")) { + // // Morph animation, not implemented in JME, let's warn the user and skip the channel + // logger.log(Level.WARNING, + // "Morph animation is not supported by JME yet, skipping animation track"); + // continue; + // } TrackData trackData = tracks[targetNode]; if (trackData == null) { @@ -894,21 +928,27 @@ public void readAnimation(int animationIndex) throws IOException { if (node instanceof Spatial) { Spatial s = (Spatial) node; spatials.add(s); - if (trackData.rotations != null || trackData.translations != null - || trackData.scales != null) { - TransformTrack track = new TransformTrack(s, trackData.times, - trackData.translations, trackData.rotations, trackData.scales); + if ( + trackData.rotations != null || trackData.translations != null || trackData.scales != null + ) { + TransformTrack track = new TransformTrack( + s, + trackData.times, + trackData.translations, + trackData.rotations, + trackData.scales + ); aTracks.add(track); } if (trackData.weights != null && s instanceof Geometry) { Geometry g = (Geometry) s; int nbMorph = g.getMesh().getMorphTargets().length; -// for (int k = 0; k < trackData.weights.length; k++) { -// System.err.print(trackData.weights[k] + ","); -// if(k % nbMorph == 0 && k!=0){ -// System.err.println(" "); -// } -// } + // for (int k = 0; k < trackData.weights.length; k++) { + // System.err.print(trackData.weights[k] + ","); + // if(k % nbMorph == 0 && k!=0){ + // System.err.println(" "); + // } + // } MorphTrack track = new MorphTrack(g, trackData.times, trackData.weights, nbMorph); aTracks.add(track); } @@ -922,16 +962,30 @@ public void readAnimation(int animationIndex) throws IOException { // Check if all joints affected by this animation are from the same skin, // the track will be skipped. if (skinIndex != jw.skinIndex) { - logger.log(Level.WARNING, "Animation " + animationIndex + " (" + name - + ") applies to joints that are not from the same skin: skin " - + skinIndex + ", joint " + jw.joint.getName() - + " from skin " + jw.skinIndex); + logger.log( + Level.WARNING, + "Animation " + + animationIndex + + " (" + + name + + ") applies to joints that are not from the same skin: skin " + + skinIndex + + ", joint " + + jw.joint.getName() + + " from skin " + + jw.skinIndex + ); continue; } } - TransformTrack track = new TransformTrack(jw.joint, trackData.times, - trackData.translations, trackData.rotations, trackData.scales); + TransformTrack track = new TransformTrack( + jw.joint, + trackData.times, + trackData.translations, + trackData.rotations, + trackData.scales + ); aTracks.add(track); } } @@ -945,11 +999,11 @@ public void readAnimation(int animationIndex) throws IOException { for (Joint joint : skin.joints) { if (!usedJoints.contains(joint)) { // create a track - float[] times = new float[]{0}; + float[] times = new float[] { 0 }; - Vector3f[] translations = new Vector3f[]{joint.getLocalTranslation()}; - Quaternion[] rotations = new Quaternion[]{joint.getLocalRotation()}; - Vector3f[] scales = new Vector3f[]{joint.getLocalScale()}; + Vector3f[] translations = new Vector3f[] { joint.getLocalTranslation() }; + Quaternion[] rotations = new Quaternion[] { joint.getLocalRotation() }; + Vector3f[] scales = new Vector3f[] { joint.getLocalScale() }; TransformTrack track = new TransformTrack(joint, times, translations, rotations, scales); aTracks.add(track); } @@ -1087,8 +1141,12 @@ public void readSkins() throws IOException { } } - public Joint readNodeAsBone(int nodeIndex, int jointIndex, int skinIndex, Matrix4f inverseModelBindMatrix) - throws IOException { + public Joint readNodeAsBone( + int nodeIndex, + int jointIndex, + int skinIndex, + Matrix4f inverseModelBindMatrix + ) throws IOException { JointWrapper jointWrapper = fetchFromCache("nodes", nodeIndex, JointWrapper.class); if (jointWrapper != null) { return jointWrapper.joint; @@ -1112,8 +1170,7 @@ public Joint readNodeAsBone(int nodeIndex, int jointIndex, int skinIndex, Matrix private void findChildren(int nodeIndex) throws IOException { JointWrapper jw = fetchFromCache("nodes", nodeIndex, JointWrapper.class); if (jw == null) { - logger.log(Level.WARNING, - "No JointWrapper found for nodeIndex={0}.", nodeIndex); + logger.log(Level.WARNING, "No JointWrapper found for nodeIndex={0}.", nodeIndex); return; } @@ -1157,12 +1214,12 @@ private void setupControls() { if (spatials.size() >= 1) { spatial = findCommonAncestor(spatials); } -// if (spatial != skinData.parent) { -// skinData.rootBoneTransformOffset = spatial.getWorldTransform().invert(); -// if (skinData.parent != null) { -// skinData.rootBoneTransformOffset.combineWithParent(skinData.parent.getWorldTransform()); -// } -// } + // if (spatial != skinData.parent) { + // skinData.rootBoneTransformOffset = spatial.getWorldTransform().invert(); + // if (skinData.parent != null) { + // skinData.rootBoneTransformOffset.combineWithParent(skinData.parent.getWorldTransform()); + // } + // } if (skinData.animComposer != null && skinData.animComposer.getSpatial() == null) { spatial.addControl(skinData.animComposer); } @@ -1181,9 +1238,11 @@ private void setupControls() { SkinData skinData = fetchFromCache("skins", bw.skinIndex, SkinData.class); SkinningControl skinControl = skinData.skinningControl; if (skinControl.getSpatial() == null) { - logger.log(Level.WARNING, - "No skinned Spatial for joint \"{0}\" -- will skin the model's root node!", - jointName); + logger.log( + Level.WARNING, + "No skinned Spatial for joint \"{0}\" -- will skin the model's root node!", + jointName + ); rootNode.addControl(skinControl); } skinControl.getAttachmentsNode(jointName).attachChild(bw.attachedSpatial); @@ -1238,6 +1297,7 @@ private String decodeUri(String uri) { } public static class WeightData { + float value; short index; int componentSize; @@ -1250,6 +1310,7 @@ public WeightData(float value, short index, int componentSize) { } private class JointWrapper { + Joint joint; int jointIndex; int skinIndex; @@ -1265,6 +1326,7 @@ public JointWrapper(Joint joint, int jointIndex, int skinIndex) { } private class SkinData { + SkinningControl skinningControl; MorphControl morphControl; AnimComposer animComposer; @@ -1276,6 +1338,7 @@ private class SkinData { } public static class SkinBuffers { + short[] joints; float[] weights; int componentSize; @@ -1289,11 +1352,18 @@ public SkinBuffers() {} } private interface Populator { - T populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset, - boolean normalized) throws IOException; + T populate( + Integer bufferViewIndex, + int componentType, + String type, + int count, + int byteOffset, + boolean normalized + ) throws IOException; } private class VertexBufferPopulator implements Populator { + VertexBuffer.Type bufferType; public VertexBufferPopulator(VertexBuffer.Type bufferType) { @@ -1301,10 +1371,20 @@ public VertexBufferPopulator(VertexBuffer.Type bufferType) { } @Override - public VertexBuffer populate(Integer bufferViewIndex, int componentType, String type, int count, - int byteOffset, boolean normalized) throws IOException { + public VertexBuffer 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); + logger.log( + Level.WARNING, + "could not assign data to any VertexBuffer type for buffer view {0}", + bufferViewIndex + ); return null; } @@ -1340,8 +1420,14 @@ public VertexBuffer populate(Integer bufferViewIndex, int componentType, String private class FloatArrayPopulator implements Populator { @Override - public float[] populate(Integer bufferViewIndex, int componentType, String type, int count, - int byteOffset, boolean normalized) throws IOException { + public float[] populate( + Integer bufferViewIndex, + int componentType, + String type, + int count, + int byteOffset, + boolean normalized + ) throws IOException { int numComponents = getNumberOfComponents(type); int dataSize = numComponents * count; float[] data = new float[dataSize]; @@ -1350,42 +1436,55 @@ public float[] populate(Integer bufferViewIndex, int componentType, String type, // no referenced buffer, specs says to pad the data with zeros. padBuffer(data, dataSize); } else { - readBuffer(bufferViewIndex, byteOffset, count, data, numComponents, - getVertexBufferFormat(componentType)); + readBuffer( + bufferViewIndex, + byteOffset, + count, + data, + numComponents, + getVertexBufferFormat(componentType) + ); } return data; } } -// -// private class FloatGridPopulator implements Populator { -// -// @Override -// public float[][] populate(Integer bufferViewIndex, int componentType, String type, int count, -// int byteOffset, boolean normalized) throws IOException { -// -// int numComponents = getNumberOfComponents(type); -// int dataSize = numComponents * count; -// float[] data = new float[dataSize]; -// -// if (bufferViewIndex == null) { -// // no referenced buffer, specs says to pad the data with zeros. -// padBuffer(data, dataSize); -// } else { -// readBuffer(bufferViewIndex, byteOffset, count, data, numComponents, -// getVertexBufferFormat(componentType)); -// } -// -// return data; -// } -// -// } + + // + // private class FloatGridPopulator implements Populator { + // + // @Override + // public float[][] populate(Integer bufferViewIndex, int componentType, String type, int count, + // int byteOffset, boolean normalized) throws IOException { + // + // int numComponents = getNumberOfComponents(type); + // int dataSize = numComponents * count; + // float[] data = new float[dataSize]; + // + // if (bufferViewIndex == null) { + // // no referenced buffer, specs says to pad the data with zeros. + // padBuffer(data, dataSize); + // } else { + // readBuffer(bufferViewIndex, byteOffset, count, data, numComponents, + // getVertexBufferFormat(componentType)); + // } + // + // return data; + // } + // + // } private class Vector3fArrayPopulator implements Populator { @Override - public Vector3f[] populate(Integer bufferViewIndex, int componentType, String type, int count, - int byteOffset, boolean normalized) throws IOException { + public Vector3f[] populate( + Integer bufferViewIndex, + int componentType, + String type, + int count, + int byteOffset, + boolean normalized + ) throws IOException { int numComponents = getNumberOfComponents(type); int dataSize = numComponents * count; Vector3f[] data = new Vector3f[count]; @@ -1394,8 +1493,14 @@ public Vector3f[] populate(Integer bufferViewIndex, int componentType, String ty // no referenced buffer, specs says to pad the data with zeros. padBuffer(data, dataSize); } else { - readBuffer(bufferViewIndex, byteOffset, count, data, numComponents, - getVertexBufferFormat(componentType)); + readBuffer( + bufferViewIndex, + byteOffset, + count, + data, + numComponents, + getVertexBufferFormat(componentType) + ); } return data; } @@ -1404,8 +1509,14 @@ public Vector3f[] populate(Integer bufferViewIndex, int componentType, String ty private class QuaternionArrayPopulator implements Populator { @Override - public Quaternion[] populate(Integer bufferViewIndex, int componentType, String type, int count, - int byteOffset, boolean normalized) throws IOException { + public Quaternion[] populate( + Integer bufferViewIndex, + int componentType, + String type, + int count, + int byteOffset, + boolean normalized + ) throws IOException { int numComponents = getNumberOfComponents(type); int dataSize = numComponents * count; Quaternion[] data = new Quaternion[count]; @@ -1414,8 +1525,14 @@ public Quaternion[] populate(Integer bufferViewIndex, int componentType, String // no referenced buffer, specs says to pad the data with zeros. padBuffer(data, dataSize); } else { - readBuffer(bufferViewIndex, byteOffset, count, data, numComponents, - getVertexBufferFormat(componentType)); + readBuffer( + bufferViewIndex, + byteOffset, + count, + data, + numComponents, + getVertexBufferFormat(componentType) + ); } return data; @@ -1425,8 +1542,14 @@ public Quaternion[] populate(Integer bufferViewIndex, int componentType, String private class Matrix4fArrayPopulator implements Populator { @Override - public Matrix4f[] populate(Integer bufferViewIndex, int componentType, String type, int count, - int byteOffset, boolean normalized) throws IOException { + public Matrix4f[] populate( + Integer bufferViewIndex, + int componentType, + String type, + int count, + int byteOffset, + boolean normalized + ) throws IOException { int numComponents = getNumberOfComponents(type); int dataSize = numComponents * count; Matrix4f[] data = new Matrix4f[count]; @@ -1435,8 +1558,14 @@ public Matrix4f[] populate(Integer bufferViewIndex, int componentType, String ty // no referenced buffer, specs says to pad the data with zeros. padBuffer(data, dataSize); } else { - readBuffer(bufferViewIndex, byteOffset, count, data, numComponents, - getVertexBufferFormat(componentType)); + readBuffer( + bufferViewIndex, + byteOffset, + count, + data, + numComponents, + getVertexBufferFormat(componentType) + ); } return data; @@ -1446,7 +1575,14 @@ public Matrix4f[] populate(Integer bufferViewIndex, int componentType, String ty private class JointArrayPopulator implements Populator { @Override - public SkinBuffers populate(Integer bufferViewIndex, int componentType, String type, int count, int byteOffset, boolean normalized) throws IOException { + public SkinBuffers populate( + Integer bufferViewIndex, + int componentType, + String type, + int count, + int byteOffset, + boolean normalized + ) throws IOException { int numComponents = getNumberOfComponents(type); // can be bytes or shorts. @@ -1468,14 +1604,12 @@ public SkinBuffers populate(Integer bufferViewIndex, int componentType, String t return new SkinBuffers(data, format.getComponentSize()); } } - public static void registerExtension(String name, Class ext) { - CustomContentManager.defaultExtensionLoaders.put(name, ext); + CustomContentManager.defaultExtensionLoaders.put(name, ext); } - public static void unregisterExtension(String name) { CustomContentManager.defaultExtensionLoaders.remove(name); } -} \ No newline at end of file +} 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 4cb9078971..d14f4cec43 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 @@ -31,14 +31,12 @@ */ package com.jme3.scene.plugins.gltf; - import com.jme3.asset.AssetLoadException; import com.jme3.asset.AssetManager; import com.jme3.material.*; import com.jme3.math.*; import com.jme3.shader.VarType; import com.jme3.texture.Texture; - import java.util.HashMap; import java.util.Map; @@ -89,15 +87,30 @@ public void setParam(String gltfParamName, Object value) { if (value instanceof Texture) { MatParam defParam = getMaterial().getMaterialDef().getMaterialParam(name); if (defParam == null) { - throw new AssetLoadException("Material definition " + getMaterialDefPath() + " has not param with name" + name); + throw new AssetLoadException( + "Material definition " + getMaterialDefPath() + " has not param with name" + name + ); } if (!(defParam instanceof MatParamTexture)) { - throw new AssetLoadException("param with name" + name + "in material definition " + getMaterialDefPath() + " should be a texture param"); + 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, + (Texture) 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(), (Texture) param.getValue()); } } else { param = new MatParam(getVarType(value), name, value); @@ -127,6 +140,8 @@ private VarType getVarType(Object value) { if (value instanceof Matrix3f) return VarType.Matrix3; if (value instanceof Matrix4f) return VarType.Matrix4; if (value instanceof String) return VarType.Boolean; - throw new AssetLoadException("Unsupported material parameter type : " + value.getClass().getSimpleName()); + throw new AssetLoadException( + "Unsupported material parameter type : " + value.getClass().getSimpleName() + ); } } diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_lights_punctual/LightsPunctualExtensionLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_lights_punctual/LightsPunctualExtensionLoader.java index fa2ee51465..0860bcba50 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_lights_punctual/LightsPunctualExtensionLoader.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_lights_punctual/LightsPunctualExtensionLoader.java @@ -31,10 +31,6 @@ */ package com.jme3.scene.plugins.gltf.ext.KHR_lights_punctual; - -import com.jme3.plugins.json.JsonArray; -import com.jme3.plugins.json.JsonObject; -import com.jme3.plugins.json.JsonElement; import com.jme3.asset.AssetLoadException; import com.jme3.light.DirectionalLight; import com.jme3.light.Light; @@ -43,12 +39,14 @@ import com.jme3.math.ColorRGBA; import com.jme3.math.FastMath; import com.jme3.math.Vector3f; +import com.jme3.plugins.json.JsonArray; +import com.jme3.plugins.json.JsonElement; +import com.jme3.plugins.json.JsonObject; import com.jme3.scene.Node; import com.jme3.scene.control.LightControl; import com.jme3.scene.plugins.gltf.ExtensionLoader; import com.jme3.scene.plugins.gltf.GltfLoader; import com.jme3.scene.plugins.gltf.GltfUtils; - import java.util.HashMap; import java.util.HashSet; @@ -65,12 +63,16 @@ public class LightsPunctualExtensionLoader implements ExtensionLoader { private final HashSet pendingNodes = new HashSet<>(); private final HashMap lightDefinitions = new HashMap<>(); - public LightsPunctualExtensionLoader() { - - } + public LightsPunctualExtensionLoader() {} @Override - public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) { + public Object handleExtension( + GltfLoader loader, + String parentName, + JsonElement parent, + JsonElement extension, + Object input + ) { if (input instanceof Node) { //We are processing a node JsonObject jsonObject = extension.getAsJsonObject(); if (jsonObject.has("light")) { //These will get run first when loading the gltf file @@ -103,7 +105,9 @@ public Object handleExtension(GltfLoader loader, String parentName, JsonElement lightNode = buildSpotLight(light); break; default: - throw new AssetLoadException("KHR_lights_punctual unsupported light type: " + type); + throw new AssetLoadException( + "KHR_lights_punctual unsupported light type: " + type + ); } lightDefinitions.put(i, lightNode); @@ -134,14 +138,20 @@ private SpotLight buildSpotLight(JsonObject obj) { String name = obj.has("name") ? obj.get("name").getAsString() : ""; float intensity = obj.has("intensity") ? obj.get("intensity").getAsFloat() : 1.0f; - ColorRGBA color = obj.has("color") ? GltfUtils.getAsColor(obj, "color") : new ColorRGBA(ColorRGBA.White); + ColorRGBA color = obj.has("color") + ? GltfUtils.getAsColor(obj, "color") + : new ColorRGBA(ColorRGBA.White); color = lumensToColor(color, intensity); float range = obj.has("range") ? obj.get("range").getAsFloat() : Float.POSITIVE_INFINITY; //Spot specific JsonObject spot = obj.getAsJsonObject("spot"); - float innerConeAngle = spot != null && spot.has("innerConeAngle") ? spot.get("innerConeAngle").getAsFloat() : 0f; - float outerConeAngle = spot != null && spot.has("outerConeAngle") ? spot.get("outerConeAngle").getAsFloat() : ((float) Math.PI) / 4f; + float innerConeAngle = spot != null && spot.has("innerConeAngle") + ? spot.get("innerConeAngle").getAsFloat() + : 0f; + float outerConeAngle = spot != null && spot.has("outerConeAngle") + ? spot.get("outerConeAngle").getAsFloat() + : ((float) Math.PI) / 4f; /* Correct floating point error on half PI, GLTF spec says that the outerConeAngle @@ -173,7 +183,9 @@ private DirectionalLight buildDirectionalLight(JsonObject obj) { String name = obj.has("name") ? obj.get("name").getAsString() : ""; float intensity = obj.has("intensity") ? obj.get("intensity").getAsFloat() : 1.0f; - ColorRGBA color = obj.has("color") ? GltfUtils.getAsColor(obj, "color") : new ColorRGBA(ColorRGBA.White); + ColorRGBA color = obj.has("color") + ? GltfUtils.getAsColor(obj, "color") + : new ColorRGBA(ColorRGBA.White); color = lumensToColor(color, intensity); DirectionalLight directionalLight = new DirectionalLight(); @@ -194,7 +206,9 @@ private PointLight buildPointLight(JsonObject obj) { String name = obj.has("name") ? obj.get("name").getAsString() : ""; float intensity = obj.has("intensity") ? obj.get("intensity").getAsFloat() : 1.0f; - ColorRGBA color = obj.has("color") ? GltfUtils.getAsColor(obj, "color") : new ColorRGBA(ColorRGBA.White); + ColorRGBA color = obj.has("color") + ? GltfUtils.getAsColor(obj, "color") + : new ColorRGBA(ColorRGBA.White); color = lumensToColor(color, intensity); float range = obj.has("range") ? obj.get("range").getAsFloat() : Float.POSITIVE_INFINITY; @@ -220,7 +234,9 @@ private void addLight(Node parent, Node node, int lightIndex) { LightControl control = new LightControl(light); node.addControl(control); } else { - throw new AssetLoadException("KHR_lights_punctual extension accessed undefined light at index " + lightIndex); + throw new AssetLoadException( + "KHR_lights_punctual extension accessed undefined light at index " + lightIndex + ); } } @@ -256,7 +272,7 @@ vec4 HDR_EncodeLum(in float lum){ */ float epsilon = 0.0001f; - double Le = 2f * Math.log(lumens * epsilon) / Math.log(2) + 127.0; + double Le = (2f * Math.log(lumens * epsilon)) / Math.log(2) + 127.0; ColorRGBA color = new ColorRGBA(); color.a = (float) (Le - Math.floor(Le)); //Get fractional part float val = (float) ((Le - (Math.floor(color.a * 255.0)) / 255.0) / 255.0); @@ -271,6 +287,7 @@ vec4 HDR_EncodeLum(in float lum){ * A bean to contain the relation between a node and a light index */ private static class NodeNeedingLight { + private Node node; private int lightIndex; diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_pbrSpecularGlossiness/PBRSpecGlossExtensionLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_pbrSpecularGlossiness/PBRSpecGlossExtensionLoader.java index 2411271f6a..dd053d1d45 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_pbrSpecularGlossiness/PBRSpecGlossExtensionLoader.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_pbrSpecularGlossiness/PBRSpecGlossExtensionLoader.java @@ -31,17 +31,16 @@ */ package com.jme3.scene.plugins.gltf.ext.KHR_materials_pbrSpecularGlossiness; -import com.jme3.asset.AssetKey; - import static com.jme3.scene.plugins.gltf.GltfUtils.getAsColor; import static com.jme3.scene.plugins.gltf.GltfUtils.getAsFloat; -import java.io.IOException; +import com.jme3.asset.AssetKey; import com.jme3.plugins.json.JsonElement; import com.jme3.scene.plugins.gltf.ExtensionLoader; import com.jme3.scene.plugins.gltf.GltfLoader; import com.jme3.scene.plugins.gltf.GltfModelKey; import com.jme3.scene.plugins.gltf.MaterialAdapter; +import java.io.IOException; /** * Material adapter for PBR Specular Glossiness pipeline @@ -51,10 +50,16 @@ public class PBRSpecGlossExtensionLoader implements ExtensionLoader { private PBRSpecGlossMaterialAdapter materialAdapter = new PBRSpecGlossMaterialAdapter(); - public PBRSpecGlossExtensionLoader(){} + public PBRSpecGlossExtensionLoader() {} @Override - public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) throws IOException { + public Object handleExtension( + GltfLoader loader, + String parentName, + JsonElement parent, + JsonElement extension, + Object input + ) throws IOException { MaterialAdapter adapter = materialAdapter; AssetKey key = loader.getInfo().getKey(); //check for a custom adapter for spec/gloss pipeline @@ -71,8 +76,14 @@ public Object handleExtension(GltfLoader loader, String parentName, JsonElement adapter.setParam("diffuseFactor", getAsColor(extension.getAsJsonObject(), "diffuseFactor")); adapter.setParam("specularFactor", getAsColor(extension.getAsJsonObject(), "specularFactor")); adapter.setParam("glossinessFactor", getAsFloat(extension.getAsJsonObject(), "glossinessFactor")); - adapter.setParam("diffuseTexture", loader.readTexture(extension.getAsJsonObject().getAsJsonObject("diffuseTexture"))); - adapter.setParam("specularGlossinessTexture", loader.readTexture(extension.getAsJsonObject().getAsJsonObject("specularGlossinessTexture"))); + adapter.setParam( + "diffuseTexture", + loader.readTexture(extension.getAsJsonObject().getAsJsonObject("diffuseTexture")) + ); + adapter.setParam( + "specularGlossinessTexture", + loader.readTexture(extension.getAsJsonObject().getAsJsonObject("specularGlossinessTexture")) + ); return adapter; } diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_unlit/UnlitExtensionLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_unlit/UnlitExtensionLoader.java index f27317ae00..2c0285312c 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_unlit/UnlitExtensionLoader.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_materials_unlit/UnlitExtensionLoader.java @@ -30,12 +30,13 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.jme3.scene.plugins.gltf.ext.KHR_materials_unlit; + +import com.jme3.asset.AssetKey; +import com.jme3.plugins.json.JsonElement; import com.jme3.scene.plugins.gltf.ExtensionLoader; import com.jme3.scene.plugins.gltf.GltfLoader; import com.jme3.scene.plugins.gltf.GltfModelKey; import com.jme3.scene.plugins.gltf.MaterialAdapter; -import com.jme3.asset.AssetKey; -import com.jme3.plugins.json.JsonElement; /** * Material adapter for the Unlit pipeline @@ -48,7 +49,13 @@ public class UnlitExtensionLoader implements ExtensionLoader { public UnlitExtensionLoader() {} @Override - public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) { + public Object handleExtension( + GltfLoader loader, + String parentName, + JsonElement parent, + JsonElement extension, + Object input + ) { MaterialAdapter adapter = materialAdapter; AssetKey key = loader.getInfo().getKey(); //check for a custom adapter for spec/gloss pipeline diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_texture_transform/TextureTransformExtensionLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_texture_transform/TextureTransformExtensionLoader.java index fdb31ea4c3..e316466ff9 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_texture_transform/TextureTransformExtensionLoader.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/ext/KHR_texture_transform/TextureTransformExtensionLoader.java @@ -1,169 +1,183 @@ -/* - * 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.plugins.gltf.ext.KHR_texture_transform; - -import com.jme3.asset.AssetLoadException; -import com.jme3.math.Matrix3f; -import com.jme3.math.Vector3f; -import com.jme3.plugins.json.JsonArray; -import com.jme3.plugins.json.JsonElement; -import com.jme3.plugins.json.JsonObject; -import com.jme3.scene.Mesh; -import com.jme3.scene.VertexBuffer; -import com.jme3.scene.plugins.gltf.ExtensionLoader; -import com.jme3.scene.plugins.gltf.GltfLoader; -import com.jme3.texture.Texture2D; - -import static com.jme3.scene.plugins.gltf.GltfUtils.getAsInteger; -import static com.jme3.scene.plugins.gltf.GltfUtils.getVertexBufferType; - -import java.io.IOException; -import java.nio.FloatBuffer; -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Thread-safe extension loader for KHR_texture_transform. - * It allows for UV coordinates to be scaled/rotated/translated - * based on transformation properties from textures in the glTF model. - * - * See spec at https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_texture_transform - * - * @author manuelrmo - Created on 11/20/2022 - */ -public class TextureTransformExtensionLoader implements ExtensionLoader { - - private final static Logger logger = Logger.getLogger(TextureTransformExtensionLoader.class.getName()); - - public TextureTransformExtensionLoader(){} - /** - * Scale/rotate/translate UV coordinates based on a transformation matrix. - * Code adapted from scaleTextureCoordinates(Vector2f) in jme3-core/src/main/java/com/jme3/scene/Mesh.java - * @param mesh The mesh holding the UV coordinates - * @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) { - if (!transform.isIdentity()) { // if transform is the identity matrix, there's nothing to do - VertexBuffer tc = mesh.getBuffer(verType); - 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); - Vector3f v = transform.mult(new Vector3f(x, y, 1)); - fb.put(v.getX()).put(v.getY()); - } - fb.clear(); - tc.updateData(fb); - } - } - - // The algorithm relies on the fact that the GltfLoader.class object - // loads all textures of a given mesh before doing so for the next mesh. - @Override - public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) throws IOException { - if (!(input instanceof Texture2D)) { - logger.log(Level.WARNING, "KHR_texture_transform extension added on an unsupported element, the loaded scene result will be unexpected."); - } - Mesh mesh = loader.fetchFromCache("mesh", 0, Mesh.class); - if (mesh != null) { - Matrix3f translation = new Matrix3f(); - Matrix3f rotation = new Matrix3f(); - Matrix3f scale = new Matrix3f(); - Integer texCoord = getAsInteger(parent.getAsJsonObject(), "texCoord"); - texCoord = texCoord != null ? texCoord : 0; - JsonObject jsonObject = extension.getAsJsonObject(); - if (jsonObject.has("offset")) { - JsonArray jsonArray = jsonObject.getAsJsonArray("offset"); - translation.set(0, 2, jsonArray.get(0).getAsFloat()); - translation.set(1, 2, jsonArray.get(1).getAsFloat()); - } - if (jsonObject.has("rotation")) { - float rad = jsonObject.get("rotation").getAsFloat(); - rotation.set(0, 0, (float) Math.cos(rad)); - rotation.set(0, 1, (float) Math.sin(rad)); - rotation.set(1, 0, (float) -Math.sin(rad)); - rotation.set(1, 1, (float) Math.cos(rad)); - } - if (jsonObject.has("scale")) { - JsonArray jsonArray = jsonObject.getAsJsonArray("scale"); - scale.set(0, 0, jsonArray.get(0).getAsFloat()); - scale.set(1, 1, jsonArray.get(1).getAsFloat()); - } - if (jsonObject.has("texCoord")) { - texCoord = jsonObject.get("texCoord").getAsInt(); // it overrides the parent's texCoord value - } - Matrix3f transform = translation.mult(rotation).mult(scale); - Mesh meshLast = loader.fetchFromCache("textureTransformData", 0, Mesh.class); - Map transformMap = loader.fetchFromCache("textureTransformData", 1, HashMap.class); - if (mesh != meshLast || (transformMap != null && transformMap.get(texCoord) == null)) { - // at this point, we're processing a new mesh or the same mesh as before but for a different UV set - if (mesh != meshLast) { // it's a new mesh - loader.addToCache("textureTransformData", 0, mesh, 2); - if (transformMap == null) { - transformMap = new HashMap<>(); // initialize transformMap - loader.addToCache("textureTransformData", 1, transformMap, 2); - } else { - transformMap.clear(); // reset transformMap - } - } - transformMap.put(texCoord, transform); // store the transformation matrix applied to this UV set - uvTransform(mesh, transform, getVertexBufferType("TEXCOORD_" + texCoord)); - logger.log(Level.FINE, "KHR_texture_transform extension successfully applied."); - } - else { - // at this point, we're processing the same mesh as before for an already transformed UV set - Matrix3f transformLast = transformMap.get(texCoord); - if (!transform.equals(transformLast)) { - logger.log(Level.WARNING, "KHR_texture_transform extension: use of different texture transforms for the same mesh's UVs is not supported, the loaded scene result will be unexpected."); - } - } - return input; - } - else { - throw new AssetLoadException("KHR_texture_transform extension applied to a null mesh."); - } - } -} +/* + * 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.plugins.gltf.ext.KHR_texture_transform; + +import static com.jme3.scene.plugins.gltf.GltfUtils.getAsInteger; +import static com.jme3.scene.plugins.gltf.GltfUtils.getVertexBufferType; + +import com.jme3.asset.AssetLoadException; +import com.jme3.math.Matrix3f; +import com.jme3.math.Vector3f; +import com.jme3.plugins.json.JsonArray; +import com.jme3.plugins.json.JsonElement; +import com.jme3.plugins.json.JsonObject; +import com.jme3.scene.Mesh; +import com.jme3.scene.VertexBuffer; +import com.jme3.scene.plugins.gltf.ExtensionLoader; +import com.jme3.scene.plugins.gltf.GltfLoader; +import com.jme3.texture.Texture2D; +import java.io.IOException; +import java.nio.FloatBuffer; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Thread-safe extension loader for KHR_texture_transform. + * It allows for UV coordinates to be scaled/rotated/translated + * based on transformation properties from textures in the glTF model. + * + * See spec at https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_texture_transform + * + * @author manuelrmo - Created on 11/20/2022 + */ +public class TextureTransformExtensionLoader implements ExtensionLoader { + + private static final Logger logger = Logger.getLogger(TextureTransformExtensionLoader.class.getName()); + + public TextureTransformExtensionLoader() {} + + /** + * Scale/rotate/translate UV coordinates based on a transformation matrix. + * Code adapted from scaleTextureCoordinates(Vector2f) in jme3-core/src/main/java/com/jme3/scene/Mesh.java + * @param mesh The mesh holding the UV coordinates + * @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) { + if (!transform.isIdentity()) { // if transform is the identity matrix, there's nothing to do + VertexBuffer tc = mesh.getBuffer(verType); + 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); + Vector3f v = transform.mult(new Vector3f(x, y, 1)); + fb.put(v.getX()).put(v.getY()); + } + fb.clear(); + tc.updateData(fb); + } + } + + // The algorithm relies on the fact that the GltfLoader.class object + // loads all textures of a given mesh before doing so for the next mesh. + @Override + public Object handleExtension( + GltfLoader loader, + String parentName, + JsonElement parent, + JsonElement extension, + Object input + ) throws IOException { + if (!(input instanceof Texture2D)) { + logger.log( + Level.WARNING, + "KHR_texture_transform extension added on an unsupported element, the loaded scene result will be unexpected." + ); + } + Mesh mesh = loader.fetchFromCache("mesh", 0, Mesh.class); + if (mesh != null) { + Matrix3f translation = new Matrix3f(); + Matrix3f rotation = new Matrix3f(); + Matrix3f scale = new Matrix3f(); + Integer texCoord = getAsInteger(parent.getAsJsonObject(), "texCoord"); + texCoord = texCoord != null ? texCoord : 0; + JsonObject jsonObject = extension.getAsJsonObject(); + if (jsonObject.has("offset")) { + JsonArray jsonArray = jsonObject.getAsJsonArray("offset"); + translation.set(0, 2, jsonArray.get(0).getAsFloat()); + translation.set(1, 2, jsonArray.get(1).getAsFloat()); + } + if (jsonObject.has("rotation")) { + float rad = jsonObject.get("rotation").getAsFloat(); + rotation.set(0, 0, (float) Math.cos(rad)); + rotation.set(0, 1, (float) Math.sin(rad)); + rotation.set(1, 0, (float) -Math.sin(rad)); + rotation.set(1, 1, (float) Math.cos(rad)); + } + if (jsonObject.has("scale")) { + JsonArray jsonArray = jsonObject.getAsJsonArray("scale"); + scale.set(0, 0, jsonArray.get(0).getAsFloat()); + scale.set(1, 1, jsonArray.get(1).getAsFloat()); + } + if (jsonObject.has("texCoord")) { + texCoord = jsonObject.get("texCoord").getAsInt(); // it overrides the parent's texCoord value + } + Matrix3f transform = translation.mult(rotation).mult(scale); + Mesh meshLast = loader.fetchFromCache("textureTransformData", 0, Mesh.class); + Map transformMap = loader.fetchFromCache( + "textureTransformData", + 1, + HashMap.class + ); + if (mesh != meshLast || (transformMap != null && transformMap.get(texCoord) == null)) { + // at this point, we're processing a new mesh or the same mesh as before but for a different UV set + if (mesh != meshLast) { // it's a new mesh + loader.addToCache("textureTransformData", 0, mesh, 2); + if (transformMap == null) { + transformMap = new HashMap<>(); // initialize transformMap + loader.addToCache("textureTransformData", 1, transformMap, 2); + } else { + transformMap.clear(); // reset transformMap + } + } + transformMap.put(texCoord, transform); // store the transformation matrix applied to this UV set + uvTransform(mesh, transform, getVertexBufferType("TEXCOORD_" + texCoord)); + logger.log(Level.FINE, "KHR_texture_transform extension successfully applied."); + } else { + // at this point, we're processing the same mesh as before for an already transformed UV set + Matrix3f transformLast = transformMap.get(texCoord); + if (!transform.equals(transformLast)) { + logger.log( + Level.WARNING, + "KHR_texture_transform extension: use of different texture transforms for the same mesh's UVs is not supported, the loaded scene result will be unexpected." + ); + } + } + return input; + } else { + throw new AssetLoadException("KHR_texture_transform extension applied to a null mesh."); + } + } +}