All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.javagl.jgltf.viewer.UniformGetterFactory Maven / Gradle / Ivy

/*
 * www.javagl.de - JglTF
 *
 * Copyright 2015-2017 Marco Hutter - http://www.javagl.de
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */
package de.javagl.jgltf.viewer;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;

import de.javagl.jgltf.model.NodeModel;
import de.javagl.jgltf.model.SkinModel;
import de.javagl.jgltf.model.gl.Semantic;
import de.javagl.jgltf.model.gl.TechniqueModel;
import de.javagl.jgltf.model.gl.TechniqueParametersModel;

/**
 * A class that provides a suppliers for the values of uniform variables, 
 * based on a {@link RenderedMaterial}.
 */
class UniformGetterFactory
{
    /**
     * The logger used in this class
     */
    private static final Logger logger = 
        Logger.getLogger(UniformGetterFactory.class.getName());
    
    /**
     * A supplier for the view matrix.  
     */
    private final Supplier viewMatrixSupplier;
    
    /**
     * A supplier for the projection matrix.
     */
    private final Supplier projectionMatrixSupplier;
    
    /**
     * A supplier that supplies the viewport, as 4 float elements, 
     * [x, y, width, height]
     */
    private final Supplier viewportSupplier;
    
    /**
     * The set of uniform names for which a null value 
     * was already reported. This is mainly intended for debugging.
     */
    private final Set reportedNullUniformNames;

    /**
     * The RTC center point for the CESIUM_RTC extension
     */
    private float rtcCenter[];
    
    /**
     * Creates a new instance
     * 
     * @param viewportSupplier A supplier that supplies the viewport, 
     * as 4 float elements, [x, y, width, height]
     * @param viewMatrixSupplier A supplier that supplies the view matrix,
     * which is a 4x4 matrix, given as a float array, in column-major order
     * @param projectionMatrixSupplier A supplier that supplies the projection
     * matrix, which is a 4x4 matrix, given as a float array, in 
     * column-major order
     * @param rtcCenter An optional 3D center point for the CESIUM_RTC 
     * extension
     */
    public UniformGetterFactory(
        Supplier viewportSupplier,
        Supplier viewMatrixSupplier,
        Supplier projectionMatrixSupplier,
        float rtcCenter[])
    {
        this.viewportSupplier = Objects.requireNonNull(viewportSupplier, 
            "The viewportSupplier may not be null");
        this.viewMatrixSupplier = Objects.requireNonNull(viewMatrixSupplier, 
            "The viewMatrixSupplier may not be null");
        this.projectionMatrixSupplier = 
            Objects.requireNonNull(projectionMatrixSupplier, 
                "The projectionMatrixSupplier may not be null");
        this.rtcCenter = rtcCenter == null ? new float[3] : rtcCenter.clone();
        this.reportedNullUniformNames = new LinkedHashSet();
    }
    
    /**
     * Create a supplier that supplies the value for the specified uniform.
     * If there is no {@link TechniqueParametersModel#getSemantic() semantic} 
     * defined in the {@link TechniqueModel} of the given 
     * {@link RenderedMaterial}, then this value will be obtained from the 
     * {@link TechniqueModel} of the {@link RenderedMaterial}. Otherwise, the 
     * value will be derived from the context of the currently rendered node, 
     * which is given by the local and global transform of the 
     * given {@link NodeModel}  
     * 
     * @param uniformName The name of the uniform
     * @param renderedMaterial The {@link RenderedMaterial}
     * @param nodeModel The {@link NodeModel}
     * @return The supplier for the uniform value
     */
    public Supplier createUniformValueSupplier(String uniformName, 
        RenderedMaterial renderedMaterial, NodeModel nodeModel)
    {
        TechniqueModel techniqueModel = renderedMaterial.getTechniqueModel();
        Map uniforms = techniqueModel.getUniforms();
        String parameterName = uniforms.get(uniformName);
        Map parameters = 
            techniqueModel.getParameters();
        TechniqueParametersModel techniqueParametersModel =
            parameters.get(parameterName);
        
        String semantic = techniqueParametersModel.getSemantic();
        if (semantic == null)
        {
            Supplier supplier = UniformGetters.createGenericSupplier(
                uniformName, renderedMaterial);
            return createNullLoggingSupplier(uniformName, supplier);
        }
        return createSemanticBasedSupplier(
            uniformName, techniqueModel, nodeModel);
    }
    
    /**
     * Create a supplier that wraps the given one, and prints a log message
     * once when the given supplier returns null. This is 
     * mainly intended for debugging.
     * 
     * @param uniformName The uniform name
     * @param supplier The delegate supplier
     * @return The new supplier
     */
    private Supplier createNullLoggingSupplier(
        String uniformName, Supplier supplier)
    {
        return () -> 
        {
            Object result = supplier.get();
            if (result == null)
            {
                if (!reportedNullUniformNames.contains(uniformName))
                {
                    logger.warning("Uniform value is null for " + uniformName);
                    reportedNullUniformNames.add(uniformName);
                }
            }
            return result;
        };
    }

    /**
     * Creates a supplier for the value of the uniform with the given name,
     * based on the {@link TechniqueParametersModel#getSemantic() semantic of
     * the TechniqueParametersModel} that the uniform has in the given 
     * {@link TechniqueModel}.
*
* Note: This method assumes that the semantic is not null. * If it is null, then an IllegalArgumentException * will be thrown.
*
* The currentNodeModel acts as a source for local and global * transforms of the node that is being rendered, unless a node is * referenced via the {@link TechniqueParametersModel#getNodeModel()}. * (See the section about "Semantics" in the glTF specification).
*
* The supported semantics and the types that are supplied by the * returned suppliers are *

     *  LOCAL                       float[16]
     *  MODEL                       float[16]
     *  VIEW                        float[16]
     *  PROJECTION                  float[16]
     *  MODELVIEW                   float[16]
     *  MODELVIEWPROJECTION         float[16]
     *  MODELINVERSE                float[16]
     *  VIEWINVERSE                 float[16]
     *  PROJECTIONINVERSE           float[16]
     *  MODELVIEWINVERSE            float[16]
     *  MODELVIEWPROJECTIONINVERSE  float[16]
     *  MODELINVERSETRANSPOSE       float[9]
     *  MODELVIEWINVERSETRANSPOSE   float[9]
     *  VIEWPORT                    float[4]    
     *  JOINTMATRIX                 float[16*numJoints] (see notes below)    
     * 
* If the semantic does not have any of these values, then an * IllegalArgumentException will be thrown.
*
* All matrices will be in column-major order. The returned suppliers * MAY always return the same array instance. So callers MUST NOT * store or modify the returned arrays.
*
* About the numJoints factor in the JOINTMATRIX * case: The supplier will provide numJoints 4x4 matrices * in a single array of size 16*numJoints. The number of * joints is determined as follows: *
    *
  • * The {@link NodeModel} that is given as the * currentNodeModel assumed to have a * {@link NodeModel#getSkinModel()} *
  • *
  • * The {@link SkinModel} for this skin ID is looked up. The skin model * has several {@link SkinModel#getJoints() joints}. The number of * joints is used as numJoints. *
  • *
* The actual contents of these joint matrices is computed from the * {@link SkinModel#getBindShapeMatrix(float[]) bind shape matrix}, the * {@link SkinModel#getInverseBindMatrices() inverse bind matrices} and * the global transform of the given node and the joint node. See the glTF * specification for details.
*
* * @param uniformName The uniform name * @param techniqueModel The {@link TechniqueModel} * @param currentNodeModel The current {@link NodeModel} * @return The supplier, or null if the semantic did * not have any of the valid values mentioned above. * @throws IllegalArgumentException If the semantic of the * {@link TechniqueParametersModel} for the given uniform name * in the given {@link TechniqueModel} is is null * or not a valid {@link Semantic} */ private Supplier createSemanticBasedSupplier( String uniformName, TechniqueModel techniqueModel, NodeModel currentNodeModel) { Objects.requireNonNull(uniformName, "The uniformName may not be null"); Objects.requireNonNull(techniqueModel, "The techniqueModel may not be null"); Objects.requireNonNull(currentNodeModel, "The currentNodeModel may not be null"); TechniqueParametersModel techniqueParameters = techniqueModel.getUniformParameters(uniformName); NodeModel nodeModel = currentNodeModel; NodeModel parameterNodeModel = techniqueParameters.getNodeModel(); if (parameterNodeModel != null) { nodeModel = parameterNodeModel; } String semanticString = techniqueParameters.getSemantic(); if (CesiumRtcUtils.isCesiumRtcModelViewSemantic(semanticString)) { return CesiumRtcUtils.createCesiumRtcModelViewMatrixSupplier( nodeModel, viewMatrixSupplier, rtcCenter); } if (!Semantic.contains(semanticString)) { throw new IllegalArgumentException( "Uniform " + uniformName + " has invalid semantic " + semanticString + " in technique " + techniqueModel); } Semantic semantic = Semantic.valueOf(semanticString); switch (semantic) { case LOCAL: { return nodeModel.createLocalTransformSupplier(); } case MODEL: { return nodeModel.createGlobalTransformSupplier(); } case VIEW: { return viewMatrixSupplier; } case PROJECTION: { return projectionMatrixSupplier; } case MODELVIEW: { Supplier modelMatrixSupplier = nodeModel.createGlobalTransformSupplier(); return MatrixOps .create4x4(viewMatrixSupplier) .multiply4x4(modelMatrixSupplier) .log(semanticString, Level.FINE) .build(); } case MODELVIEWPROJECTION: { Supplier modelMatrixSupplier = nodeModel.createGlobalTransformSupplier(); return MatrixOps .create4x4(projectionMatrixSupplier) .multiply4x4(viewMatrixSupplier) .multiply4x4(modelMatrixSupplier) .log(semanticString, Level.FINE) .build(); } case MODELINVERSE: { Supplier modelMatrixSupplier = nodeModel.createGlobalTransformSupplier(); return MatrixOps .create4x4(modelMatrixSupplier) .invert4x4() .log(semanticString, Level.FINE) .build(); } case VIEWINVERSE: { return MatrixOps .create4x4(viewMatrixSupplier) .invert4x4() .log(semanticString, Level.FINE) .build(); } case MODELVIEWINVERSE: { Supplier modelMatrixSupplier = nodeModel.createGlobalTransformSupplier(); return MatrixOps .create4x4(viewMatrixSupplier) .multiply4x4(modelMatrixSupplier) .invert4x4() .log(semanticString, Level.FINE) .build(); } case PROJECTIONINVERSE: { return MatrixOps .create4x4(projectionMatrixSupplier) .invert4x4() .log(semanticString, Level.FINE) .build(); } case MODELVIEWPROJECTIONINVERSE: { Supplier modelMatrixSupplier = nodeModel.createGlobalTransformSupplier(); return MatrixOps .create4x4(projectionMatrixSupplier) .multiply4x4(viewMatrixSupplier) .multiply4x4(modelMatrixSupplier) .invert4x4() .log(semanticString, Level.FINE) .build(); } case MODELINVERSETRANSPOSE: { Supplier modelMatrixSupplier = nodeModel.createGlobalTransformSupplier(); return MatrixOps .create4x4(modelMatrixSupplier) .invert4x4() .transpose4x4() .getRotationScale() .log(semanticString, Level.FINE) .build(); } case MODELVIEWINVERSETRANSPOSE: { Supplier modelMatrixSupplier = nodeModel.createGlobalTransformSupplier(); return MatrixOps .create4x4(viewMatrixSupplier) .multiply4x4(modelMatrixSupplier) .invert4x4() .transpose4x4() .getRotationScale() .log(semanticString, Level.FINE) .build(); } case VIEWPORT: { return viewportSupplier; } case JOINTMATRIX: { return createJointMatrixSupplier(currentNodeModel); } default: break; } logger.severe("Unsupported semantic: "+semantic); return null; } /** * Create a supplier for the joint matrix (actually, joint matrices) of * the {@link SkinModel} that is contained in the given {@link NodeModel}. * * @param nodeModel The {@link NodeModel} * @return The supplier */ private static Supplier createJointMatrixSupplier( NodeModel nodeModel) { SkinModel skinModel = nodeModel.getSkinModel(); // Create the supplier for the bind shape matrix (or a supplier // of the identity matrix, if the bind shape matrix is null) float bindShapeMatrix[] = skinModel.getBindShapeMatrix(null); Supplier bindShapeMatrixSupplier = MatrixOps.create4x4(() -> bindShapeMatrix) .log("bindShapeMatrix", Level.FINE) .build(); List joints = skinModel.getJoints(); int numJoints = joints.size(); // Create one supplier for each inverse bind matrix. Each of them will // extract one element of the inverse bind matrix accessor data and // provide it as a single float[16] array, representing a 4x4 matrix List> inverseBindMatrixSuppliers = new ArrayList>(); for (int i = 0; i < numJoints; i++) { final int currentJointIndex = i; float inverseBindMatrix[] = new float[16]; Supplier inverseBindMatrixSupplier = () -> { return skinModel.getInverseBindMatrix( currentJointIndex, inverseBindMatrix); }; Supplier loggingInverseBindMatrixSupplier = MatrixOps.create4x4(inverseBindMatrixSupplier) .log("inverseBindMatrix "+i, Level.FINE) .build(); inverseBindMatrixSuppliers.add(loggingInverseBindMatrixSupplier); } // Create the joint matrix suppliers. Each of them will provide a // matrix that is computed as // [jointMatrix(j)] = // [globalTransformOfNode^-1] * // [globalTransformOfJointNode] * // [inverseBindMatrix(j)] * // [bindShapeMatrix] List> jointMatrixSuppliers = new ArrayList>(); for (int j = 0; j < numJoints; j++) { NodeModel jointNodeModel = joints.get(j); Supplier inverseBindMatrixSupplier = inverseBindMatrixSuppliers.get(j); Supplier jointMatrixSupplier = MatrixOps .create4x4(nodeModel.createGlobalTransformSupplier()) .invert4x4() .multiply4x4(jointNodeModel.createGlobalTransformSupplier()) .multiply4x4(inverseBindMatrixSupplier) .multiply4x4(bindShapeMatrixSupplier) .log("jointMatrix "+j, Level.FINE) .build(); jointMatrixSuppliers.add(jointMatrixSupplier); } // Create a supplier for the joint matrices, which combines the // joint matrices of the individual joint matrix suppliers // into one array float jointMatrices[] = new float[jointMatrixSuppliers.size() * 16]; return () -> { for (int i=0; i jointMatrixSupplier = jointMatrixSuppliers.get(i); float[] jointMatrix = jointMatrixSupplier.get(); System.arraycopy(jointMatrix, 0, jointMatrices, i * 16, 16); } return jointMatrices; }; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy