This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 03badce557 feat(Material): separate material in dedicated subclasses 
instead of a Map of properties
03badce557 is described below

commit 03badce5575d267216625f6c1bed700683044ca3
Author: jsorel <[email protected]>
AuthorDate: Thu Jul 23 22:12:12 2026 +0200

    feat(Material): separate material in dedicated subclasses instead of a Map 
of properties
---
 .../org/apache/sis/geometries/scene/Material.java  | 628 ---------------------
 .../org/apache/sis/geometries/scene/Model.java     |   1 +
 .../org/apache/sis/geometries/scene/Surface.java   |   4 +-
 .../sis/geometries/scene/material/BlinnPhong.java  | 198 +++++++
 .../sis/geometries/scene/material/Material.java    | 209 +++++++
 .../apache/sis/geometries/scene/material/PBR.java  | 342 +++++++++++
 .../sis/geometries/scene/material/PBRSG.java       | 203 +++++++
 7 files changed, 956 insertions(+), 629 deletions(-)

diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/Material.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/Material.java
deleted file mode 100644
index 88826b1df1..0000000000
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/Material.java
+++ /dev/null
@@ -1,628 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.sis.geometries.scene;
-
-import java.awt.Color;
-import java.util.HashMap;
-import java.util.Objects;
-import org.apache.sis.util.ArgumentChecks;
-
-
-/**
- * A material is a set of properties defining a visual representation.
- * Materials on there own do not suffice to obtain the visual aspect
- * of a model, they must be combined with a rendering technique.
- *
- * @author Johann Sorel (Geomatys)
- */
-public class Material extends HashMap<String,Object> {
-
-    public static enum Technique {
-        BLINN_PHONG,
-        /**
-         * Physically-Based Rendering
-         */
-        PBR,
-        /**
-         * Specular-glossiness material model from Physically-Based Rendering 
(PBR)
-         */
-        PBRSG
-    }
-
-    public static final String ALPHA_MODE_OPAQUE = "OPAQUE";
-    public static final String ALPHA_MODE_MASK = "MASK";
-    public static final String ALPHA_MODE_BLEND = "BLEND";
-
-    // PBR rendering technique 
/////////////////////////////////////////////////
-
-    /** Boolean, default is false */
-    public static final String DOUBLESIDED = "doubleSided";
-    /** Double, The alpha cutoff value of the material. default is 0.5 */
-    public static final String ALPHACUTOFF = "alphaCutoff";
-    /** String alpha mode. default is opaque*/
-    public static final String ALPHAMODE = "alphaMode";
-    /** Color */
-    public static final String EMISSIVEFACTOR = "emissiveFactor";
-    /** Texture */
-    public static final String EMISSIVETEXTURE = "emissiveTexture";
-    /** Texture */
-    public static final String OCCLUSIONTEXTURE = "occlusionTexture";
-    public static final String OCCLUSIONSTRENGTH = "occlusionStrength";
-    /** Texture */
-    public static final String NORMALTEXTURE = "normalTexture";
-    public static final String NORMALSCALE = "normalScale";
-
-
-    /** Color */
-    public static final String PBR_BASECOLORFACTOR = "baseColorFactor";
-    /** Texture */
-    public static final String PBR_BASECOLORTEXTURE = "baseColorTexture";
-    /** Number */
-    public static final String PBR_METALLICFACTOR = "metallicFactor";
-    /** Number */
-    public static final String PBR_ROUGHNESSFACTOR = "roughnessFactor";
-    /** Texture */
-    public static final String PBR_METALLICROUGHNESSTEXTURE = 
"metallicRoughnessTexture";
-
-    // PBR rendering technique 
/////////////////////////////////////////////////
-
-    /** Color */
-    public static final String PBRSG_DIFFUSEFACTOR = "diffuseFactor";
-    /** Texture */
-    public static final String PBRSG_DIFFUSETEXTURE = "diffuseTexture";
-    /** Color */
-    public static final String PBRSG_SPECULARFACTOR = "specularFactor";
-    /** Scalar */
-    public static final String PBRSG_GLOSSINESSFACTOR = "glossinessFactor";
-    /** Texture */
-    public static final String PBRSG_SPECULAR_GLOSSINESS_TEXTURE = 
"specularGlossinessTexture";
-
-
-    // Blinn-Phong rendering technique 
/////////////////////////////////////////
-
-    /** Color */
-    public static final String BP_AMBIANTFACTOR = "ambiantFactor";
-    /** Texture */
-    public static final String BP_AMBIANTTEXTURE = "ambiantTexture";
-    /** Color */
-    public static final String BP_DIFFUSEFACTOR = "diffuseFactor";
-    /** Texture */
-    public static final String BP_DIFFUSETEXTURE = "diffuseTexture";
-    /** Color */
-    public static final String BP_SPECULARFACTOR = "specularFactor";
-    /** Texture */
-    public static final String BP_SPECULARTEXTURE = "specularTexture";
-
-    // Unlit rendering technique /////////////////////////////////////////
-    // See 
https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_unlit/README.md
-
-    /** Boolean */
-    public static final String LIGHTS_UNLIT = "unlit";
-
-    // See 
https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_ior/README.md
-    /** Double */
-    public static final String IOR = "ior";
-
-    // See 
https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_transmission/README.md
-    /** Double */
-    public static final String TRANSMISSIONFACTOR = "transmissionFactor";
-    /** Texture */
-    public static final String TRANSMISSIONTEXTURE = "transmissionTexture";
-
-    // See 
https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_volume/README.md
-    /** Double */
-    public static final String THICKNESSFACTOR = "thicknessFactor";
-    /** Texture */
-    public static final String THICKNESSTEXTURE = "thicknessTexture";
-    /** Double */
-    public static final String ATTENUATIONDISTANCE = "attenuationDistance";
-    /** Color */
-    public static final String ATTENUATIONCOLOR = "attenuationColor";
-
-
-    private String id;
-    private Technique technique = Technique.PBR;
-
-    public Material() {
-
-    }
-
-    /**
-     * @return material identifier
-     */
-    public String getIdentifier() {
-        return id;
-    }
-
-    /**
-     * @param id set material identifier
-     */
-    public void setIdentifier(String id) {
-        this.id = id;
-    }
-
-    /**
-     * Get material rendering technique.
-     * Blinn-Phong and PBR are the most common.
-     * @return technique, never null.
-     */
-    public Technique getTechnique() {
-        return technique;
-    }
-
-    /**
-     * @param technique rendering technique
-     */
-    public void setTechnique(Technique technique) {
-        ArgumentChecks.ensureNonNull("technique", technique);
-        this.technique = technique;
-    }
-
-    /**
-     * Default is OPAQUE
-     * @return alphaMode
-     */
-    public String getAlphaMode() {
-        String str = (String) get(ALPHAMODE);
-        return (str == null) ? ALPHA_MODE_OPAQUE : str;
-    }
-
-    /**
-     * Default is OPAQUE
-     * @param alphaMode
-     */
-    public void setAlphaMode(String alphaMode) {
-        put(ALPHAMODE, alphaMode);
-    }
-
-    /**
-     * Default value is 0.5
-     * @return alpha cutoff
-     */
-    public double getAlphaCutoff() {
-        Number ac = (Number) get(ALPHACUTOFF);
-        return (ac == null) ? 0.5 : ac.doubleValue();
-    }
-
-    /**
-     * Default value is 0.5
-     * @param cutoff
-     */
-    public void setAlphaCutoff(double cutoff) {
-        ArgumentChecks.ensureBetween("alpha cutoff", 0.0, 1.0, cutoff);
-        put(ALPHACUTOFF, cutoff);
-    }
-
-    /**
-     * Default value is false
-     * @return double sided.
-     */
-    public boolean isDoubleSided() {
-        return Boolean.TRUE.equals(get(DOUBLESIDED));
-    }
-
-    /**
-     * Default value is false
-     * @param doublesided
-     */
-    public void setDoubleSided(boolean doublesided) {
-        put(DOUBLESIDED, doublesided);
-    }
-
-    /**
-     * Default is BLACK (0,0,0)
-     * @return emmisive factor
-     */
-    public Color getEmissiveFactor() {
-        Color color = (Color) get(EMISSIVEFACTOR);
-        return (color == null) ? Color.BLACK : color;
-    }
-
-    /**
-     * Default is BLACK (0,0,0)
-     * @param color emmisive factor
-     */
-    public void setEmissiveFactor(Color color) {
-        put(EMISSIVEFACTOR, color);
-    }
-
-    public Texture getEmissiveTexture() {
-        return (Texture) get(EMISSIVETEXTURE);
-    }
-
-    public void setEmissiveTexture(Texture texture) {
-        put(EMISSIVETEXTURE, texture);
-    }
-
-    public Texture getOcclusionTexture() {
-        return (Texture) get(OCCLUSIONTEXTURE);
-    }
-
-    public void setOcclusionTexture(Texture texture) {
-        put(OCCLUSIONTEXTURE, texture);
-    }
-
-    /**
-     * Default is 1.0
-     * @return occlusion strength
-     */
-    public double getOcclusionStrength() {
-        Number strength = (Number) get(OCCLUSIONSTRENGTH);
-        return (strength == null) ? 1.0 : strength.doubleValue();
-    }
-
-    /**
-     * Default is 1.0
-     * @param strength occlusion strength between 0.0 and 1.0
-     */
-    public void setOcclusionStrength(double strength) {
-        ArgumentChecks.ensureBetween("occlusion strength", 0.0, 1.0, strength);
-        put(OCCLUSIONSTRENGTH, strength);
-    }
-
-    public Texture getNormalTexture() {
-        return (Texture) get(NORMALTEXTURE);
-    }
-
-    public void setNormalTexture(Texture texture) {
-        put(NORMALTEXTURE, texture);
-    }
-
-    /**
-     * Default is 1.0
-     * @return normal scale
-     */
-    public double getNormalScale() {
-        Number scale = (Number) get(NORMALSCALE);
-        return (scale == null) ? 1.0 : scale.doubleValue();
-    }
-
-    /**
-     * Default is 1.0
-     * @param scale normal scale
-     */
-    public void setNormalScale(double scale) {
-        ArgumentChecks.ensureBetween("normal scale", 0.0, 1.0, scale);
-        put(NORMALSCALE, scale);
-    }
-
-    /**
-     * Default is WHITE (1,1,1)
-     * @return pbr base color factor
-     */
-    public Color getPBRBaseColorFactor() {
-        Color color = (Color) get(PBR_BASECOLORFACTOR);
-        return (color == null) ? Color.WHITE : color;
-    }
-
-    /**
-     * Default is WHITE (1,1,1)
-     * @param color pbr base color factor
-     */
-    public void setPBRBaseColorFactor(Color color) {
-        put(PBR_BASECOLORFACTOR, color);
-    }
-
-    public Texture getPBRBaseColorTexture() {
-        return (Texture) get(PBR_BASECOLORTEXTURE);
-    }
-
-    public void setPBRBaseColorTexture(Texture texture) {
-        put(PBR_BASECOLORTEXTURE, texture);
-    }
-
-    /**
-     * Default is 1.0
-     * @return between 0.0 and 1.0
-     */
-    public double getPBRMetallicFactor() {
-        Number factor = (Number) get(PBR_METALLICFACTOR);
-        return (factor == null) ? 1.0 : factor.doubleValue();
-    }
-
-    /**
-     * Default is 1.0
-     * @param factor between 0.0 and 1.0
-     */
-    public void setPBRMetallicFactor(double factor) {
-        ArgumentChecks.ensureBetween("pbr metallic factor", 0.0, 1.0, factor);
-        put(PBR_METALLICFACTOR, factor);
-    }
-
-    /**
-     * Default is 1.0
-     * @return between 0.0 and 1.0
-     */
-    public double getPBRRoughnessFactor() {
-        Number roughness = (Number) get(PBR_ROUGHNESSFACTOR);
-        return (roughness == null) ? 1.0 : roughness.doubleValue();
-    }
-
-    /**
-     * Default is 1.0
-     * @param roughness  between 0.0 and 1.0
-     */
-    public void setPBRRoughnessFactor(double roughness) {
-        ArgumentChecks.ensureBetween("PBR roughness factor", 0.0, 1.0, 
roughness);
-        put(PBR_ROUGHNESSFACTOR, roughness);
-    }
-
-    public Texture getPBRMetallicRoughnessTexture() {
-        return (Texture) get(PBR_METALLICROUGHNESSTEXTURE);
-    }
-
-    public void setPBRMetallicRoughnessTexture(Texture texture) {
-        put(PBR_METALLICROUGHNESSTEXTURE, texture);
-    }
-
-    /**
-     * Default is WHITE (1,1,1,1)
-     * @return diffuse factor
-     */
-    public Color getPBRSGDiffuseFactor() {
-        Color color = (Color) get(PBRSG_DIFFUSEFACTOR);
-        return (color == null) ? Color.WHITE : color;
-    }
-
-    /**
-     * Default is WHITE (1,1,1,1)
-     * @param color diffuse factor
-     */
-    public void setPBRSGDiffuseFactor(Color color) {
-        put(PBRSG_DIFFUSEFACTOR, color);
-    }
-
-    /**
-     * Default is null
-     * @return diffuse texture
-     */
-    public Texture getPBRSGDiffuseTexture() {
-        return (Texture) get(PBRSG_DIFFUSETEXTURE);
-    }
-
-    /**
-     * Default is null
-     * @param texture diffuse
-     */
-    public void setPBRSGDiffuseTexture(Texture texture) {
-        put(PBRSG_DIFFUSETEXTURE, texture);
-    }
-
-    /**
-     * Default is 1.0
-     * @return between 0.0 and 1.0
-     */
-    public double getPBRSGGlossinessFactor() {
-        Number factor = (Number) get(PBRSG_GLOSSINESSFACTOR);
-        return (factor == null) ? 1.0 : factor.doubleValue();
-    }
-
-    /**
-     * Default is 1.0
-     * @param factor between 0.0 and 1.0
-     */
-    public void setPBRSGGlossinessFactor(double factor) {
-        ArgumentChecks.ensureBetween("pbrsg glossiness factor", 0.0, 1.0, 
factor);
-        put(PBRSG_GLOSSINESSFACTOR, factor);
-    }
-
-    /**
-     * Default is WHITE (1,1,1)
-     * @return specular factor
-     */
-    public Color getPBRSGSpecularFactor() {
-        Color color = (Color) get(PBRSG_SPECULARFACTOR);
-        return (color == null) ? Color.WHITE : color;
-    }
-
-    /**
-     * Default is WHITE (1,1,1)
-     * @param color specular factor
-     */
-    public void setPBRSGSpecularFactor(Color color) {
-        put(PBRSG_SPECULARFACTOR, color);
-    }
-
-    /**
-     * Default is null
-     * @return specular glossiness texture
-     */
-    public Texture getPBRSGSpecularGlossinessTexture() {
-        return (Texture) get(PBRSG_SPECULAR_GLOSSINESS_TEXTURE);
-    }
-
-    /**
-     * Default is null
-     * @param texture specular glossiness
-     */
-    public void setPBRSGSpecularGlossinessTexture(Texture texture) {
-        put(PBRSG_SPECULAR_GLOSSINESS_TEXTURE, texture);
-    }
-
-    /**
-     * Default value is false
-     * @return true if lights should be disabled for this material.
-     */
-    public boolean isUnlit() {
-        return Boolean.TRUE.equals(get(LIGHTS_UNLIT));
-    }
-
-    /**
-     * Default value is false
-     * @param unlit if lights should be disabled for this material.
-     */
-    public void setUnlit(boolean unlit) {
-        put(LIGHTS_UNLIT, unlit);
-    }
-
-    /**
-     * Default value is 1.5
-     * @return index of refraction
-     */
-    public double getIOR() {
-        Number ac = (Number) get(IOR);
-        return (ac == null) ? 1.5 : ac.doubleValue();
-    }
-
-    /**
-     * Default value is 1.5
-     * @param ior
-     */
-    public void setIOR(double ior) {
-        ArgumentChecks.ensureBetween("ior", 1.0, 100.0, ior);
-        put(IOR, ior);
-    }
-
-    /**
-     * Default value is 0.0
-     * @return transmission factor
-     */
-    public double getTransmissionFactor() {
-        Number ac = (Number) get(TRANSMISSIONFACTOR);
-        return (ac == null) ? 0.0 : ac.doubleValue();
-    }
-
-    /**
-     * Default value is 0.0
-     * @param tf
-     */
-    public void setTransmissionFactor(double tf) {
-        put(TRANSMISSIONFACTOR, tf);
-    }
-
-    /**
-     * Default is null
-     * @return transmission texture
-     */
-    public Texture getTransmissionTexture() {
-        return (Texture) get(TRANSMISSIONTEXTURE);
-    }
-
-    /**
-     * Default is null
-     * @param texture transmission
-     */
-    public void setTransmissionTexture(Texture texture) {
-        put(TRANSMISSIONTEXTURE, texture);
-    }
-
-    /**
-     * Default value is 0.0
-     * @return thickness factor
-     */
-    public double getThicknessFactor() {
-        Number ac = (Number) get(THICKNESSFACTOR);
-        return (ac == null) ? 0.0 : ac.doubleValue();
-    }
-
-    /**
-     * Default value is 0.0
-     * @param tf
-     */
-    public void setThicknessFactor(double tf) {
-        put(THICKNESSFACTOR, tf);
-    }
-
-    /**
-     * Default is null
-     * @return thickness texture
-     */
-    public Texture getThicknessTexture() {
-        return (Texture) get(THICKNESSTEXTURE);
-    }
-
-    /**
-     * Default is null
-     * @param texture thickness
-     */
-    public void setThicknessTexture(Texture texture) {
-        put(THICKNESSTEXTURE, texture);
-    }
-
-    /**
-     * Default value is +Infinity
-     * @return volume attenuation distance
-     */
-    public double getAttenuationDistance() {
-        Number ac = (Number) get(ATTENUATIONDISTANCE);
-        return (ac == null) ? Double.POSITIVE_INFINITY : ac.doubleValue();
-    }
-
-    /**
-     * Default value is +Infinity
-     * @param ad volume attenuation distance
-     */
-    public void setAttenuationDistance(double ad) {
-        put(ATTENUATIONDISTANCE, ad);
-    }
-
-    /**
-     * Default is WHITE (1,1,1)
-     * @return volume attenuation color
-     */
-    public Color getAttenuationColor() {
-        Color color = (Color) get(ATTENUATIONCOLOR);
-        return (color == null) ? Color.WHITE : color;
-    }
-
-    /**
-     * Default is WHITE (1,1,1)
-     * @param color volume attenuation color
-     */
-    public void setAttenuationColor(Color color) {
-        put(ATTENUATIONCOLOR, color);
-    }
-
-    public final class Matte extends Material {
-        //todo separate parameters to different type of materials
-    }
-
-    public final class BlinnPhong extends Material {
-        //todo separate parameters to different type of materials
-    }
-
-    public final class PhysicallyBased extends Material {
-        //todo separate parameters to different type of materials
-    }
-
-
-    @Override
-    public int hashCode() {
-        int hash = 5;
-        hash = 83 * hash + Objects.hashCode(this.id);
-        return hash;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        final Material other = (Material) obj;
-        if (!Objects.equals(this.id, other.id)) {
-            return false;
-        }
-        return super.equals(obj);
-    }
-
-}
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/Model.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/Model.java
index 8a39c49b27..b49c4050ef 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/Model.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/Model.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.geometries.scene;
 
+import org.apache.sis.geometries.scene.material.Material;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/Surface.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/Surface.java
index 013b72c6cc..3e25e8e44e 100644
--- 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/Surface.java
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/Surface.java
@@ -16,6 +16,8 @@
  */
 package org.apache.sis.geometries.scene;
 
+import org.apache.sis.geometries.scene.material.PBR;
+import org.apache.sis.geometries.scene.material.Material;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -38,7 +40,7 @@ public class Surface {
     public Surface(Geometry geometry) {
         ArgumentChecks.ensureNonNull("geometry", geometry);
         this.geometry = geometry;
-        this.material = new Material();
+        this.material = new PBR();
     }
 
     public Surface(Geometry geometry, Material material) {
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/material/BlinnPhong.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/material/BlinnPhong.java
new file mode 100644
index 0000000000..1b25c2fbc5
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/material/BlinnPhong.java
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.geometries.scene.material;
+
+import java.awt.Color;
+import java.util.Objects;
+import org.apache.sis.geometries.scene.Texture;
+
+
+/**
+ * Blinn-Phong material, the classic ambient/diffuse/specular shading model.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public final class BlinnPhong extends Material {
+
+    private Color ambientFactor = Color.BLACK;
+    private Texture ambientTexture;
+    private Color diffuseFactor = Color.WHITE;
+    private Texture diffuseTexture;
+    private Color specularFactor = Color.WHITE;
+    private Texture specularTexture;
+
+    public BlinnPhong() {
+    }
+
+    /**
+     * Default is BLACK (0,0,0)
+     * @return ambient factor
+     */
+    public Color getAmbientFactor() {
+        return ambientFactor;
+    }
+
+    /**
+     * Default is BLACK (0,0,0)
+     * @param color ambient factor
+     */
+    public void setAmbientFactor(Color color) {
+        this.ambientFactor = color;
+    }
+
+    public Texture getAmbientTexture() {
+        return ambientTexture;
+    }
+
+    public void setAmbientTexture(Texture texture) {
+        this.ambientTexture = texture;
+    }
+
+    /**
+     * Default is WHITE (1,1,1)
+     * @return diffuse factor
+     */
+    public Color getDiffuseFactor() {
+        return diffuseFactor;
+    }
+
+    /**
+     * Default is WHITE (1,1,1)
+     * @param color diffuse factor
+     */
+    public void setDiffuseFactor(Color color) {
+        this.diffuseFactor = color;
+    }
+
+    public Texture getDiffuseTexture() {
+        return diffuseTexture;
+    }
+
+    public void setDiffuseTexture(Texture texture) {
+        this.diffuseTexture = texture;
+    }
+
+    /**
+     * Default is WHITE (1,1,1)
+     * @return specular factor
+     */
+    public Color getSpecularFactor() {
+        return specularFactor;
+    }
+
+    /**
+     * Default is WHITE (1,1,1)
+     * @param color specular factor
+     */
+    public void setSpecularFactor(Color color) {
+        this.specularFactor = color;
+    }
+
+    public Texture getSpecularTexture() {
+        return specularTexture;
+    }
+
+    public void setSpecularTexture(Texture texture) {
+        this.specularTexture = texture;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 5;
+        hash = 37 * hash + Objects.hashCode(getIdentifier());
+        hash = 37 * hash + Boolean.hashCode(isDoubleSided());
+        hash = 37 * hash + Objects.hashCode(getAlphaMode());
+        hash = 37 * hash + Double.hashCode(getAlphaCutoff());
+        hash = 37 * hash + Objects.hashCode(getEmissiveFactor());
+        hash = 37 * hash + Objects.hashCode(getEmissiveTexture());
+        hash = 37 * hash + Objects.hashCode(getOcclusionTexture());
+        hash = 37 * hash + Double.hashCode(getOcclusionStrength());
+        hash = 37 * hash + Objects.hashCode(getNormalTexture());
+        hash = 37 * hash + Double.hashCode(getNormalScale());
+        hash = 37 * hash + Boolean.hashCode(isUnlit());
+        hash = 37 * hash + Objects.hashCode(this.ambientFactor);
+        hash = 37 * hash + Objects.hashCode(this.ambientTexture);
+        hash = 37 * hash + Objects.hashCode(this.diffuseFactor);
+        hash = 37 * hash + Objects.hashCode(this.diffuseTexture);
+        hash = 37 * hash + Objects.hashCode(this.specularFactor);
+        hash = 37 * hash + Objects.hashCode(this.specularTexture);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final BlinnPhong other = (BlinnPhong) obj;
+        if (Double.compare(this.getAlphaCutoff(), other.getAlphaCutoff()) != 
0) {
+            return false;
+        }
+        if (Double.compare(this.getOcclusionStrength(), 
other.getOcclusionStrength()) != 0) {
+            return false;
+        }
+        if (Double.compare(this.getNormalScale(), other.getNormalScale()) != 
0) {
+            return false;
+        }
+        if (this.isDoubleSided() != other.isDoubleSided()) {
+            return false;
+        }
+        if (this.isUnlit() != other.isUnlit()) {
+            return false;
+        }
+        if (!Objects.equals(this.getIdentifier(), other.getIdentifier())) {
+            return false;
+        }
+        if (!Objects.equals(this.getAlphaMode(), other.getAlphaMode())) {
+            return false;
+        }
+        if (!Objects.equals(this.getEmissiveFactor(), 
other.getEmissiveFactor())) {
+            return false;
+        }
+        if (!Objects.equals(this.getEmissiveTexture(), 
other.getEmissiveTexture())) {
+            return false;
+        }
+        if (!Objects.equals(this.getOcclusionTexture(), 
other.getOcclusionTexture())) {
+            return false;
+        }
+        if (!Objects.equals(this.getNormalTexture(), 
other.getNormalTexture())) {
+            return false;
+        }
+        if (!Objects.equals(this.ambientFactor, other.ambientFactor)) {
+            return false;
+        }
+        if (!Objects.equals(this.ambientTexture, other.ambientTexture)) {
+            return false;
+        }
+        if (!Objects.equals(this.diffuseFactor, other.diffuseFactor)) {
+            return false;
+        }
+        if (!Objects.equals(this.diffuseTexture, other.diffuseTexture)) {
+            return false;
+        }
+        if (!Objects.equals(this.specularFactor, other.specularFactor)) {
+            return false;
+        }
+        return Objects.equals(this.specularTexture, other.specularTexture);
+    }
+}
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/material/Material.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/material/Material.java
new file mode 100644
index 0000000000..443ac5c8e0
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/material/Material.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.geometries.scene.material;
+
+import java.awt.Color;
+import org.apache.sis.geometries.scene.Texture;
+import org.apache.sis.util.ArgumentChecks;
+
+
+/**
+ * A material is a set of properties defining a visual representation.
+ * Materials on there own do not suffice to obtain the visual aspect
+ * of a model, they must be combined with a rendering technique.
+ *
+ * This base class holds the properties common to all shading techniques,
+ * regardless of the shading model used. See {@link BlinnPhong}, {@link PBR}
+ * and {@link PBRSG} for the technique-specific properties.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public abstract sealed class Material permits BlinnPhong, PBR, PBRSG {
+
+    public static final String ALPHA_MODE_OPAQUE = "OPAQUE";
+    public static final String ALPHA_MODE_MASK = "MASK";
+    public static final String ALPHA_MODE_BLEND = "BLEND";
+
+    private String id;
+    private boolean doubleSided;
+    private String alphaMode = ALPHA_MODE_OPAQUE;
+    private double alphaCutoff = 0.5;
+    private Color emissiveFactor = Color.BLACK;
+    private Texture emissiveTexture;
+    private Texture occlusionTexture;
+    private double occlusionStrength = 1.0;
+    private Texture normalTexture;
+    private double normalScale = 1.0;
+    private boolean unlit;
+
+    Material() {
+    }
+
+    /**
+     * @return material identifier
+     */
+    public String getIdentifier() {
+        return id;
+    }
+
+    /**
+     * @param id set material identifier
+     */
+    public void setIdentifier(String id) {
+        this.id = id;
+    }
+
+    /**
+     * Default is OPAQUE
+     * @return alphaMode
+     */
+    public String getAlphaMode() {
+        return alphaMode;
+    }
+
+    /**
+     * Default is OPAQUE
+     * @param alphaMode
+     */
+    public void setAlphaMode(String alphaMode) {
+        this.alphaMode = alphaMode;
+    }
+
+    /**
+     * Default value is 0.5
+     * @return alpha cutoff
+     */
+    public double getAlphaCutoff() {
+        return alphaCutoff;
+    }
+
+    /**
+     * Default value is 0.5
+     * @param cutoff
+     */
+    public void setAlphaCutoff(double cutoff) {
+        ArgumentChecks.ensureBetween("alpha cutoff", 0.0, 1.0, cutoff);
+        this.alphaCutoff = cutoff;
+    }
+
+    /**
+     * Default value is false
+     * @return double sided.
+     */
+    public boolean isDoubleSided() {
+        return doubleSided;
+    }
+
+    /**
+     * Default value is false
+     * @param doublesided
+     */
+    public void setDoubleSided(boolean doublesided) {
+        this.doubleSided = doublesided;
+    }
+
+    /**
+     * Default is BLACK (0,0,0)
+     * @return emmisive factor
+     */
+    public Color getEmissiveFactor() {
+        return emissiveFactor;
+    }
+
+    /**
+     * Default is BLACK (0,0,0)
+     * @param color emmisive factor
+     */
+    public void setEmissiveFactor(Color color) {
+        this.emissiveFactor = color;
+    }
+
+    public Texture getEmissiveTexture() {
+        return emissiveTexture;
+    }
+
+    public void setEmissiveTexture(Texture texture) {
+        this.emissiveTexture = texture;
+    }
+
+    public Texture getOcclusionTexture() {
+        return occlusionTexture;
+    }
+
+    public void setOcclusionTexture(Texture texture) {
+        this.occlusionTexture = texture;
+    }
+
+    /**
+     * Default is 1.0
+     * @return occlusion strength
+     */
+    public double getOcclusionStrength() {
+        return occlusionStrength;
+    }
+
+    /**
+     * Default is 1.0
+     * @param strength occlusion strength between 0.0 and 1.0
+     */
+    public void setOcclusionStrength(double strength) {
+        ArgumentChecks.ensureBetween("occlusion strength", 0.0, 1.0, strength);
+        this.occlusionStrength = strength;
+    }
+
+    public Texture getNormalTexture() {
+        return normalTexture;
+    }
+
+    public void setNormalTexture(Texture texture) {
+        this.normalTexture = texture;
+    }
+
+    /**
+     * Default is 1.0
+     * @return normal scale
+     */
+    public double getNormalScale() {
+        return normalScale;
+    }
+
+    /**
+     * Default is 1.0
+     * @param scale normal scale
+     */
+    public void setNormalScale(double scale) {
+        ArgumentChecks.ensureBetween("normal scale", 0.0, 1.0, scale);
+        this.normalScale = scale;
+    }
+
+    /**
+     * Default value is false
+     * @return true if lights should be disabled for this material.
+     */
+    public boolean isUnlit() {
+        return unlit;
+    }
+
+    /**
+     * Default value is false
+     * @param unlit if lights should be disabled for this material.
+     */
+    public void setUnlit(boolean unlit) {
+        this.unlit = unlit;
+    }
+
+}
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/material/PBR.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/material/PBR.java
new file mode 100644
index 0000000000..d26cde3c81
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/material/PBR.java
@@ -0,0 +1,342 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.geometries.scene.material;
+
+import java.awt.Color;
+import java.util.Objects;
+import org.apache.sis.geometries.scene.Texture;
+import org.apache.sis.util.ArgumentChecks;
+
+
+/**
+ * Physically-Based Rendering material using the metallic-roughness workflow.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public final class PBR extends Material {
+
+    private Color baseColorFactor = Color.WHITE;
+    private Texture baseColorTexture;
+    private double metallicFactor = 1.0;
+    private double roughnessFactor = 1.0;
+    private Texture metallicRoughnessTexture;
+
+    // See 
https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_ior/README.md
+    private double ior = 1.5;
+
+    // See 
https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_transmission/README.md
+    private double transmissionFactor = 0.0;
+    private Texture transmissionTexture;
+
+    // See 
https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_materials_volume/README.md
+    private double thicknessFactor = 0.0;
+    private Texture thicknessTexture;
+    private double attenuationDistance = Double.POSITIVE_INFINITY;
+    private Color attenuationColor = Color.WHITE;
+
+    public PBR() {
+    }
+
+    /**
+     * Default is WHITE (1,1,1)
+     * @return pbr base color factor
+     */
+    public Color getBaseColorFactor() {
+        return baseColorFactor;
+    }
+
+    /**
+     * Default is WHITE (1,1,1)
+     * @param color pbr base color factor
+     */
+    public void setBaseColorFactor(Color color) {
+        this.baseColorFactor = color;
+    }
+
+    public Texture getBaseColorTexture() {
+        return baseColorTexture;
+    }
+
+    public void setBaseColorTexture(Texture texture) {
+        this.baseColorTexture = texture;
+    }
+
+    /**
+     * Default is 1.0
+     * @return between 0.0 and 1.0
+     */
+    public double getMetallicFactor() {
+        return metallicFactor;
+    }
+
+    /**
+     * Default is 1.0
+     * @param factor between 0.0 and 1.0
+     */
+    public void setMetallicFactor(double factor) {
+        ArgumentChecks.ensureBetween("pbr metallic factor", 0.0, 1.0, factor);
+        this.metallicFactor = factor;
+    }
+
+    /**
+     * Default is 1.0
+     * @return between 0.0 and 1.0
+     */
+    public double getRoughnessFactor() {
+        return roughnessFactor;
+    }
+
+    /**
+     * Default is 1.0
+     * @param roughness  between 0.0 and 1.0
+     */
+    public void setRoughnessFactor(double roughness) {
+        ArgumentChecks.ensureBetween("PBR roughness factor", 0.0, 1.0, 
roughness);
+        this.roughnessFactor = roughness;
+    }
+
+    public Texture getMetallicRoughnessTexture() {
+        return metallicRoughnessTexture;
+    }
+
+    public void setMetallicRoughnessTexture(Texture texture) {
+        this.metallicRoughnessTexture = texture;
+    }
+
+    /**
+     * Default value is 1.5
+     * @return index of refraction
+     */
+    public double getIOR() {
+        return ior;
+    }
+
+    /**
+     * Default value is 1.5
+     * @param ior
+     */
+    public void setIOR(double ior) {
+        ArgumentChecks.ensureBetween("ior", 1.0, 100.0, ior);
+        this.ior = ior;
+    }
+
+    /**
+     * Default value is 0.0
+     * @return transmission factor
+     */
+    public double getTransmissionFactor() {
+        return transmissionFactor;
+    }
+
+    /**
+     * Default value is 0.0
+     * @param tf
+     */
+    public void setTransmissionFactor(double tf) {
+        this.transmissionFactor = tf;
+    }
+
+    /**
+     * Default is null
+     * @return transmission texture
+     */
+    public Texture getTransmissionTexture() {
+        return transmissionTexture;
+    }
+
+    /**
+     * Default is null
+     * @param texture transmission
+     */
+    public void setTransmissionTexture(Texture texture) {
+        this.transmissionTexture = texture;
+    }
+
+    /**
+     * Default value is 0.0
+     * @return thickness factor
+     */
+    public double getThicknessFactor() {
+        return thicknessFactor;
+    }
+
+    /**
+     * Default value is 0.0
+     * @param tf
+     */
+    public void setThicknessFactor(double tf) {
+        this.thicknessFactor = tf;
+    }
+
+    /**
+     * Default is null
+     * @return thickness texture
+     */
+    public Texture getThicknessTexture() {
+        return thicknessTexture;
+    }
+
+    /**
+     * Default is null
+     * @param texture thickness
+     */
+    public void setThicknessTexture(Texture texture) {
+        this.thicknessTexture = texture;
+    }
+
+    /**
+     * Default value is +Infinity
+     * @return volume attenuation distance
+     */
+    public double getAttenuationDistance() {
+        return attenuationDistance;
+    }
+
+    /**
+     * Default value is +Infinity
+     * @param ad volume attenuation distance
+     */
+    public void setAttenuationDistance(double ad) {
+        this.attenuationDistance = ad;
+    }
+
+    /**
+     * Default is WHITE (1,1,1)
+     * @return volume attenuation color
+     */
+    public Color getAttenuationColor() {
+        return attenuationColor;
+    }
+
+    /**
+     * Default is WHITE (1,1,1)
+     * @param color volume attenuation color
+     */
+    public void setAttenuationColor(Color color) {
+        this.attenuationColor = color;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 5;
+        hash = 41 * hash + Objects.hashCode(getIdentifier());
+        hash = 41 * hash + Boolean.hashCode(isDoubleSided());
+        hash = 41 * hash + Objects.hashCode(getAlphaMode());
+        hash = 41 * hash + Double.hashCode(getAlphaCutoff());
+        hash = 41 * hash + Objects.hashCode(getEmissiveFactor());
+        hash = 41 * hash + Objects.hashCode(getEmissiveTexture());
+        hash = 41 * hash + Objects.hashCode(getOcclusionTexture());
+        hash = 41 * hash + Double.hashCode(getOcclusionStrength());
+        hash = 41 * hash + Objects.hashCode(getNormalTexture());
+        hash = 41 * hash + Double.hashCode(getNormalScale());
+        hash = 41 * hash + Boolean.hashCode(isUnlit());
+        hash = 41 * hash + Objects.hashCode(this.baseColorFactor);
+        hash = 41 * hash + Objects.hashCode(this.baseColorTexture);
+        hash = 41 * hash + Double.hashCode(this.metallicFactor);
+        hash = 41 * hash + Double.hashCode(this.roughnessFactor);
+        hash = 41 * hash + Objects.hashCode(this.metallicRoughnessTexture);
+        hash = 41 * hash + Double.hashCode(this.ior);
+        hash = 41 * hash + Double.hashCode(this.transmissionFactor);
+        hash = 41 * hash + Objects.hashCode(this.transmissionTexture);
+        hash = 41 * hash + Double.hashCode(this.thicknessFactor);
+        hash = 41 * hash + Objects.hashCode(this.thicknessTexture);
+        hash = 41 * hash + Double.hashCode(this.attenuationDistance);
+        hash = 41 * hash + Objects.hashCode(this.attenuationColor);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final PBR other = (PBR) obj;
+        if (Double.compare(this.getAlphaCutoff(), other.getAlphaCutoff()) != 
0) {
+            return false;
+        }
+        if (Double.compare(this.getOcclusionStrength(), 
other.getOcclusionStrength()) != 0) {
+            return false;
+        }
+        if (Double.compare(this.getNormalScale(), other.getNormalScale()) != 
0) {
+            return false;
+        }
+        if (this.isDoubleSided() != other.isDoubleSided()) {
+            return false;
+        }
+        if (this.isUnlit() != other.isUnlit()) {
+            return false;
+        }
+        if (Double.compare(this.metallicFactor, other.metallicFactor) != 0) {
+            return false;
+        }
+        if (Double.compare(this.roughnessFactor, other.roughnessFactor) != 0) {
+            return false;
+        }
+        if (Double.compare(this.ior, other.ior) != 0) {
+            return false;
+        }
+        if (Double.compare(this.transmissionFactor, other.transmissionFactor) 
!= 0) {
+            return false;
+        }
+        if (Double.compare(this.thicknessFactor, other.thicknessFactor) != 0) {
+            return false;
+        }
+        if (Double.compare(this.attenuationDistance, 
other.attenuationDistance) != 0) {
+            return false;
+        }
+        if (!Objects.equals(this.getIdentifier(), other.getIdentifier())) {
+            return false;
+        }
+        if (!Objects.equals(this.getAlphaMode(), other.getAlphaMode())) {
+            return false;
+        }
+        if (!Objects.equals(this.getEmissiveFactor(), 
other.getEmissiveFactor())) {
+            return false;
+        }
+        if (!Objects.equals(this.getEmissiveTexture(), 
other.getEmissiveTexture())) {
+            return false;
+        }
+        if (!Objects.equals(this.getOcclusionTexture(), 
other.getOcclusionTexture())) {
+            return false;
+        }
+        if (!Objects.equals(this.getNormalTexture(), 
other.getNormalTexture())) {
+            return false;
+        }
+        if (!Objects.equals(this.baseColorFactor, other.baseColorFactor)) {
+            return false;
+        }
+        if (!Objects.equals(this.baseColorTexture, other.baseColorTexture)) {
+            return false;
+        }
+        if (!Objects.equals(this.metallicRoughnessTexture, 
other.metallicRoughnessTexture)) {
+            return false;
+        }
+        if (!Objects.equals(this.transmissionTexture, 
other.transmissionTexture)) {
+            return false;
+        }
+        if (!Objects.equals(this.thicknessTexture, other.thicknessTexture)) {
+            return false;
+        }
+        return Objects.equals(this.attenuationColor, other.attenuationColor);
+    }
+}
diff --git 
a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/material/PBRSG.java
 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/material/PBRSG.java
new file mode 100644
index 0000000000..6dec15207d
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/scene/material/PBRSG.java
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.geometries.scene.material;
+
+import java.awt.Color;
+import java.util.Objects;
+import org.apache.sis.geometries.scene.Texture;
+import org.apache.sis.util.ArgumentChecks;
+
+
+/**
+ * Specular-glossiness material model from Physically-Based Rendering (PBR).
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public final class PBRSG extends Material {
+
+    private Color diffuseFactor = Color.WHITE;
+    private Texture diffuseTexture;
+    private Color specularFactor = Color.WHITE;
+    private double glossinessFactor = 1.0;
+    private Texture specularGlossinessTexture;
+
+    public PBRSG() {
+    }
+
+    /**
+     * Default is WHITE (1,1,1,1)
+     * @return diffuse factor
+     */
+    public Color getDiffuseFactor() {
+        return diffuseFactor;
+    }
+
+    /**
+     * Default is WHITE (1,1,1,1)
+     * @param color diffuse factor
+     */
+    public void setDiffuseFactor(Color color) {
+        this.diffuseFactor = color;
+    }
+
+    /**
+     * Default is null
+     * @return diffuse texture
+     */
+    public Texture getDiffuseTexture() {
+        return diffuseTexture;
+    }
+
+    /**
+     * Default is null
+     * @param texture diffuse
+     */
+    public void setDiffuseTexture(Texture texture) {
+        this.diffuseTexture = texture;
+    }
+
+    /**
+     * Default is WHITE (1,1,1)
+     * @return specular factor
+     */
+    public Color getSpecularFactor() {
+        return specularFactor;
+    }
+
+    /**
+     * Default is WHITE (1,1,1)
+     * @param color specular factor
+     */
+    public void setSpecularFactor(Color color) {
+        this.specularFactor = color;
+    }
+
+    /**
+     * Default is 1.0
+     * @return between 0.0 and 1.0
+     */
+    public double getGlossinessFactor() {
+        return glossinessFactor;
+    }
+
+    /**
+     * Default is 1.0
+     * @param factor between 0.0 and 1.0
+     */
+    public void setGlossinessFactor(double factor) {
+        ArgumentChecks.ensureBetween("pbrsg glossiness factor", 0.0, 1.0, 
factor);
+        this.glossinessFactor = factor;
+    }
+
+    /**
+     * Default is null
+     * @return specular glossiness texture
+     */
+    public Texture getSpecularGlossinessTexture() {
+        return specularGlossinessTexture;
+    }
+
+    /**
+     * Default is null
+     * @param texture specular glossiness
+     */
+    public void setSpecularGlossinessTexture(Texture texture) {
+        this.specularGlossinessTexture = texture;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 5;
+        hash = 43 * hash + Objects.hashCode(getIdentifier());
+        hash = 43 * hash + Boolean.hashCode(isDoubleSided());
+        hash = 43 * hash + Objects.hashCode(getAlphaMode());
+        hash = 43 * hash + Double.hashCode(getAlphaCutoff());
+        hash = 43 * hash + Objects.hashCode(getEmissiveFactor());
+        hash = 43 * hash + Objects.hashCode(getEmissiveTexture());
+        hash = 43 * hash + Objects.hashCode(getOcclusionTexture());
+        hash = 43 * hash + Double.hashCode(getOcclusionStrength());
+        hash = 43 * hash + Objects.hashCode(getNormalTexture());
+        hash = 43 * hash + Double.hashCode(getNormalScale());
+        hash = 43 * hash + Boolean.hashCode(isUnlit());
+        hash = 43 * hash + Objects.hashCode(this.diffuseFactor);
+        hash = 43 * hash + Objects.hashCode(this.diffuseTexture);
+        hash = 43 * hash + Objects.hashCode(this.specularFactor);
+        hash = 43 * hash + Double.hashCode(this.glossinessFactor);
+        hash = 43 * hash + Objects.hashCode(this.specularGlossinessTexture);
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final PBRSG other = (PBRSG) obj;
+        if (Double.compare(this.getAlphaCutoff(), other.getAlphaCutoff()) != 
0) {
+            return false;
+        }
+        if (Double.compare(this.getOcclusionStrength(), 
other.getOcclusionStrength()) != 0) {
+            return false;
+        }
+        if (Double.compare(this.getNormalScale(), other.getNormalScale()) != 
0) {
+            return false;
+        }
+        if (this.isDoubleSided() != other.isDoubleSided()) {
+            return false;
+        }
+        if (this.isUnlit() != other.isUnlit()) {
+            return false;
+        }
+        if (Double.compare(this.glossinessFactor, other.glossinessFactor) != 
0) {
+            return false;
+        }
+        if (!Objects.equals(this.getIdentifier(), other.getIdentifier())) {
+            return false;
+        }
+        if (!Objects.equals(this.getAlphaMode(), other.getAlphaMode())) {
+            return false;
+        }
+        if (!Objects.equals(this.getEmissiveFactor(), 
other.getEmissiveFactor())) {
+            return false;
+        }
+        if (!Objects.equals(this.getEmissiveTexture(), 
other.getEmissiveTexture())) {
+            return false;
+        }
+        if (!Objects.equals(this.getOcclusionTexture(), 
other.getOcclusionTexture())) {
+            return false;
+        }
+        if (!Objects.equals(this.getNormalTexture(), 
other.getNormalTexture())) {
+            return false;
+        }
+        if (!Objects.equals(this.diffuseFactor, other.diffuseFactor)) {
+            return false;
+        }
+        if (!Objects.equals(this.diffuseTexture, other.diffuseTexture)) {
+            return false;
+        }
+        if (!Objects.equals(this.specularFactor, other.specularFactor)) {
+            return false;
+        }
+        return Objects.equals(this.specularGlossinessTexture, 
other.specularGlossinessTexture);
+    }
+}

Reply via email to