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

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

/*
 * www.javagl.de - JglTF
 *
 * Copyright 2015-2016 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.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;

import de.javagl.jgltf.model.GltfConstants;
import de.javagl.jgltf.model.NodeModel;
import de.javagl.jgltf.model.TextureModel;
import de.javagl.jgltf.model.gl.ProgramModel;
import de.javagl.jgltf.model.gl.ShaderModel;
import de.javagl.jgltf.model.gl.ShaderModel.ShaderType;
import de.javagl.jgltf.model.gl.TechniqueModel;
import de.javagl.jgltf.model.gl.TechniqueParametersModel;
import de.javagl.jgltf.model.gl.TechniqueStatesModel;
import de.javagl.jgltf.model.gl.impl.DefaultProgramModel;
import de.javagl.jgltf.model.gl.impl.DefaultShaderModel;
import de.javagl.jgltf.model.gl.impl.DefaultTechniqueModel;
import de.javagl.jgltf.model.gl.impl.DefaultTechniqueParametersModel;
import de.javagl.jgltf.model.gl.impl.TechniqueStatesModels;
import de.javagl.jgltf.model.io.Buffers;
import de.javagl.jgltf.model.io.IO;
import de.javagl.jgltf.model.v2.MaterialModelV2;

/**
 * A class for creating the {@link RenderedMaterial} instances for glTF 2.0
 * {@link MaterialModelV2} objects.
*
* It will lazily create the internal {@link TechniqueModel}, * {@link ProgramModel} and {@link ShaderModel} instances that * are required for rendering. */ class RenderedMaterialHandler { /** * The logger used in this class */ private static final Logger logger = Logger.getLogger(RenderedMaterialHandler.class.getName()); /** * The mapping from joint counts to vertex {@link ShaderModel} instances */ private final Map vertexShaderModels; /** * The fragment {@link ShaderModel} */ private ShaderModel fragmentShaderModel; /** * The mapping from joint count to {@link ProgramModel} instances */ private final Map programModels; /** * The mapping from {@link MaterialStructure} descriptions to the * matching {@link TechniqueModel} instances */ private final Map techniqueModels; /** * Default constructor * * @param textureIndexLookup The lookup for {@link TextureModel} indices */ RenderedMaterialHandler( Function textureIndexLookup) { this.vertexShaderModels = new LinkedHashMap(); this.programModels = new LinkedHashMap(); this.techniqueModels = new LinkedHashMap(); } /** * Obtain the vertex {@link ShaderModel} for the given number of joints, * creating it if necessary * * @param numJoints The number of joints * @return The {@link ShaderModel} */ private ShaderModel obtainVertexShaderModel(int numJoints) { ShaderModel shaderModel = vertexShaderModels.get(numJoints); if (shaderModel == null) { shaderModel = createVertexShaderModel(numJoints); vertexShaderModels.put(numJoints, shaderModel); } return shaderModel; } /** * Create the vertex {@link ShaderModel} for the given number of joints * * @param numJoints The number of joints * @return The {@link ShaderModel} */ private ShaderModel createVertexShaderModel(int numJoints) { String vertexShaderDefines = ""; if (numJoints > 0) { vertexShaderDefines += "#define NUM_JOINTS " + numJoints + "\n"; } ShaderModel vertexShaderModel = createDefaultShaderModel( "pbr.vert", "pbr" + numJoints + ".vert", ShaderType.VERTEX_SHADER, vertexShaderDefines); return vertexShaderModel; } /** * Obtain the fragment {@link ShaderModel}, creating it if necessary * * @return The {@link ShaderModel} */ private ShaderModel obtainFragmentShaderModel() { if (fragmentShaderModel == null) { fragmentShaderModel = createDefaultShaderModel( "pbr.frag", "pbr.frag", ShaderType.FRAGMENT_SHADER, null); } return fragmentShaderModel; } /** * Obtain the {@link ProgramModel} for the given number of joints, * creating it if necessary * * @param numJoints The number of joints * @return The {@link ProgramModel} */ private ProgramModel obtainProgramModel(int numJoints) { ProgramModel programModel = programModels.get(numJoints); if (programModel == null) { programModel = createProgramModel(numJoints); programModels.put(numJoints, programModel); } return programModel; } /** * Create the {@link ProgramModel} for the given number of joints * * @param numJoints The number of joints * @return The {@link ProgramModel} */ private ProgramModel createProgramModel(int numJoints) { ShaderModel vertexShaderModel = obtainVertexShaderModel(numJoints); ShaderModel fragmentShaderModel = obtainFragmentShaderModel(); DefaultProgramModel programModel = new DefaultProgramModel(); programModel.setVertexShaderModel(vertexShaderModel); programModel.setFragmentShaderModel(fragmentShaderModel); return programModel; } /** * Obtain the {@link TechniqueModel} for the given * {@link MaterialStructure}, creating it if necessary * * @param materialStructure The {@link MaterialStructure} * @return The {@link TechniqueModel} */ private TechniqueModel obtainTechniqueModel( MaterialStructure materialStructure) { TechniqueModel techniqueModel = techniqueModels.get(materialStructure); if (techniqueModel == null) { techniqueModel = createTechniqueModel(materialStructure); techniqueModels.put(materialStructure, techniqueModel); } return techniqueModel; } /** * Create the {@link TechniqueModel} for the given * {@link MaterialStructure} * * @param materialStructure The {@link MaterialStructure} * @return The {@link TechniqueModel} */ private TechniqueModel createTechniqueModel( MaterialStructure materialStructure) { ProgramModel programModel = obtainProgramModel(materialStructure.getNumJoints()); DefaultTechniqueModel techniqueModel = new DefaultTechniqueModel(); techniqueModel.setProgramModel(programModel); addParametersForPbrTechnique(techniqueModel, materialStructure); TechniqueStatesModel techniqueStatesModel = TechniqueStatesModels.createDefault(); techniqueModel.setTechniqueStatesModel(techniqueStatesModel); return techniqueModel; } /** * Create a {@link RenderedMaterial} instance for the given * {@link MaterialModelV2} * * @param material The {@link MaterialModelV2} * @param numJoints The number of joints * @return The {@link RenderedMaterial} */ RenderedMaterial createRenderedMaterial( MaterialModelV2 material, int numJoints) { MaterialStructure materialStructure = new MaterialStructure(material, numJoints); TechniqueModel techniqueModel = obtainTechniqueModel(materialStructure); Map values = new LinkedHashMap(); if (Boolean.TRUE.equals(material.isDoubleSided())) { values.put("isDoubleSided", 1); } else { values.put("isDoubleSided", 0); } TextureModel baseColorTexture = material.getBaseColorTexture(); if (baseColorTexture != null) { values.put("hasBaseColorTexture", 1); values.put("baseColorTexCoord", materialStructure.getBaseColorTexCoordSemantic()); values.put("baseColorTexture", baseColorTexture); } else { values.put("hasBaseColorTexture", 0); } float[] baseColorFactor = material.getBaseColorFactor(); values.put("baseColorFactor", baseColorFactor); TextureModel metallicRoughnessTexture = material.getMetallicRoughnessTexture(); if (metallicRoughnessTexture != null) { values.put("hasMetallicRoughnessTexture", 1); values.put("metallicRoughnessTexCoord", materialStructure.getMetallicRoughnessTexCoordSemantic()); values.put("metallicRoughnessTexture", metallicRoughnessTexture); } else { values.put("hasMetallicRoughnessTexture", 0); } float metallicFactor = material.getMetallicFactor(); values.put("metallicFactor", metallicFactor); float roughnessFactor = material.getRoughnessFactor(); values.put("roughnessFactor", roughnessFactor); TextureModel normalTexture = material.getNormalTexture(); if (normalTexture != null) { values.put("hasNormalTexture", 1); values.put("normalTexCoord", materialStructure.getNormalTexCoordSemantic()); values.put("normalTexture", normalTexture); float normalScale = material.getNormalScale(); values.put("normalScale", normalScale); } else { values.put("hasNormalTexture", 0); values.put("normalScale", 1.0); } TextureModel occlusionTexture = material.getOcclusionTexture(); if (occlusionTexture != null) { values.put("hasOcclusionTexture", 1); values.put("occlusionTexCoord", materialStructure.getOcclusionTexCoordSemantic()); values.put("occlusionTexture", occlusionTexture); float occlusionStrength = material.getOcclusionStrength(); values.put("occlusionStrength", occlusionStrength); } else { values.put("hasOcclusionTexture", 0); // TODO Should this really be 1.0? values.put("occlusionStrength", 0.0); } TextureModel emissiveTexture = material.getEmissiveTexture(); if (emissiveTexture != null) { values.put("hasEmissiveTexture", 1); values.put("emissiveTexCoord", materialStructure.getEmissiveTexCoordSemantic()); values.put("emissiveTexture", emissiveTexture); } else { values.put("hasEmissiveTexture", 0); } float[] emissiveFactor = material.getEmissiveFactor(); values.put("emissiveFactor", emissiveFactor); float lightPosition[] = { -800,500,500 }; values.put("lightPosition", lightPosition); DefaultRenderedMaterial renderedMaterial = new DefaultRenderedMaterial(techniqueModel, values); return renderedMaterial; } /** * Create a default {@link ShaderModel} instance with the given URI * string and type, by reading the resource that is identified with * the given name. If the specified resource cannot be read, then * an error message will be printed and the returned shader model * will not contain any data. This method is only intended for * internal use! * * @param resourceName The name of the resource to read the source code from * @param uriString The URI string * @param shaderType The shader type * @param defines An optional string containing lines of code that * will be prefixed to the shader code, and which will usually * contain preprocessor definitions * @return The {@link ShaderModel} */ private static DefaultShaderModel createDefaultShaderModel( String resourceName, String uriString, ShaderType shaderType, String defines) { DefaultShaderModel shaderModel = new DefaultShaderModel( uriString, shaderType); try (InputStream inputStream = RenderedMaterialHandler.class.getResourceAsStream("/" + resourceName)) { byte[] data = IO.readStream(inputStream); String basicShaderString = new String(data); String fullShaderString = basicShaderString; if (defines != null) { fullShaderString = defines + "\n" + basicShaderString; } ByteBuffer shaderData = Buffers.create(fullShaderString.getBytes()); shaderModel.setShaderData(shaderData); } catch (IOException e) { logger.log(Level.SEVERE, "Could not read shader source code", e); } return shaderModel; } /** * Add all {@link TechniqueParametersModel} instances for PBR techniques * to the given {@link TechniqueModel} * * @param techniqueModel The {@link TechniqueModel} * @param materialStructure The {@link MaterialStructure} of the material * for which the {@link TechniqueModel} is intended */ private static void addParametersForPbrTechnique( DefaultTechniqueModel techniqueModel, MaterialStructure materialStructure) { addAttributeParameters(techniqueModel, "a_position", "position", GltfConstants.GL_FLOAT_VEC4, 1, "POSITION"); addAttributeParameters(techniqueModel, "a_normal", "normal", GltfConstants.GL_FLOAT_VEC4, 1, "NORMAL"); addAttributeParameters(techniqueModel, "a_tangent", "tangent", GltfConstants.GL_FLOAT_VEC4, 1, "TANGENT"); addAttributeParameters(techniqueModel, "a_baseColorTexCoord", "baseColorTexCoord", GltfConstants.GL_FLOAT_VEC2, 1, materialStructure.getBaseColorTexCoordSemantic()); addAttributeParameters(techniqueModel, "a_metallicRoughnessTexCoord", "metallicRoughnessTexCoord", GltfConstants.GL_FLOAT_VEC2, 1, materialStructure.getMetallicRoughnessTexCoordSemantic()); addAttributeParameters(techniqueModel, "a_normalTexCoord", "normalTexCoord", GltfConstants.GL_FLOAT_VEC2, 1, materialStructure.getNormalTexCoordSemantic()); addAttributeParameters(techniqueModel, "a_occlusionTexCoord", "occlusionTexCoord", GltfConstants.GL_FLOAT_VEC2, 1, materialStructure.getOcclusionTexCoordSemantic()); addAttributeParameters(techniqueModel, "a_emissiveTexCoord", "emissiveTexCoord", GltfConstants.GL_FLOAT_VEC2, 1, materialStructure.getEmissiveTexCoordSemantic()); addUniformParameters(techniqueModel, "u_modelViewMatrix", "modelViewMatrix", GltfConstants.GL_FLOAT_MAT4, 1, "MODELVIEW"); addUniformParameters(techniqueModel, "u_projectionMatrix", "projectionMatrix", GltfConstants.GL_FLOAT_MAT4, 1, "PROJECTION"); addUniformParameters(techniqueModel, "u_normalMatrix", "normalMatrix", GltfConstants.GL_FLOAT_MAT3, 1, "MODELVIEWINVERSETRANSPOSE"); addUniformParameters(techniqueModel, "u_isDoubleSided", "isDoubleSided", GltfConstants.GL_INT, 1, null); addUniformParameters(techniqueModel, "u_baseColorTexture", "baseColorTexture", GltfConstants.GL_SAMPLER_2D, 1, null); addUniformParameters(techniqueModel, "u_metallicRoughnessTexture", "metallicRoughnessTexture", GltfConstants.GL_SAMPLER_2D, 1, null); addUniformParameters(techniqueModel, "u_normalTexture", "normalTexture", GltfConstants.GL_SAMPLER_2D, 1, null); addUniformParameters(techniqueModel, "u_occlusionTexture", "occlusionTexture", GltfConstants.GL_SAMPLER_2D, 1, null); addUniformParameters(techniqueModel, "u_emissiveTexture", "emissiveTexture", GltfConstants.GL_SAMPLER_2D, 1, null); addUniformParameters(techniqueModel, "u_hasBaseColorTexture", "hasBaseColorTexture", GltfConstants.GL_INT, 1, null); addUniformParameters(techniqueModel, "u_hasMetallicRoughnessTexture", "hasMetallicRoughnessTexture", GltfConstants.GL_INT, 1, null); addUniformParameters(techniqueModel, "u_hasNormalTexture", "hasNormalTexture", GltfConstants.GL_INT, 1, null); addUniformParameters(techniqueModel, "u_hasOcclusionTexture", "hasOcclusionTexture", GltfConstants.GL_INT, 1, null); addUniformParameters(techniqueModel, "u_hasEmissiveTexture", "hasEmissiveTexture", GltfConstants.GL_INT, 1, null); addUniformParameters(techniqueModel, "u_baseColorFactor", "baseColorFactor", GltfConstants.GL_FLOAT_VEC4, 1, null); addUniformParameters(techniqueModel, "u_metallicFactor", "metallicFactor", GltfConstants.GL_FLOAT, 1, null); addUniformParameters(techniqueModel, "u_roughnessFactor", "roughnessFactor", GltfConstants.GL_FLOAT, 1, null); addUniformParameters(techniqueModel, "u_normalScale", "normalScale", GltfConstants.GL_FLOAT, 1, null); addUniformParameters(techniqueModel, "u_occlusionStrength", "occlusionStrength", GltfConstants.GL_FLOAT, 1, null); addUniformParameters(techniqueModel, "u_emissiveFactor", "emissiveFactor", GltfConstants.GL_FLOAT_VEC3, 1, null); addAttributeParameters(techniqueModel, "a_joint", "joint", GltfConstants.GL_FLOAT_VEC4, 1, "JOINTS_0"); addAttributeParameters(techniqueModel, "a_weight", "weight", GltfConstants.GL_FLOAT_VEC4, 1, "WEIGHTS_0"); if (materialStructure.getNumJoints() > 0) { addUniformParameters(techniqueModel, "u_jointMat", "jointMat", GltfConstants.GL_FLOAT_MAT4, materialStructure.getNumJoints(), "JOINTMATRIX"); } // TODO Preliminary uniform for a single point light addUniformParameters(techniqueModel, "u_lightPosition", "lightPosition", GltfConstants.GL_FLOAT_VEC3, 1, null); } /** * Add the specified attribute to the given model * * @param techniqueModel The {@link TechniqueModel} * @param attributeName The attribute name * @param parameterName The parameter name * @param type The parameter type * @param count The count * @param semantic The semantic */ private static void addAttributeParameters( DefaultTechniqueModel techniqueModel, String attributeName, String parameterName, int type, int count, String semantic) { techniqueModel.addAttribute(attributeName, parameterName); addParameters(techniqueModel, parameterName, type, count, semantic); } /** * Add the specified uniform to the given model * * @param techniqueModel The {@link TechniqueModel} * @param uniformName The uniform name * @param parameterName The parameter name * @param type The parameter type * @param count The count * @param semantic The semantic */ private static void addUniformParameters( DefaultTechniqueModel techniqueModel, String uniformName, String parameterName, int type, int count, String semantic) { techniqueModel.addUniform(uniformName, parameterName); addParameters(techniqueModel, parameterName, type, count, semantic); } /** * Add a {@link TechniqueParametersModel} with the given parameters to * the given {@link TechniqueModel} * * @param techniqueModel The {@link TechniqueModel} * @param parameterName The parameter name * @param type The parameter type * @param count The count * @param semantic The semantic */ private static void addParameters(DefaultTechniqueModel techniqueModel, String parameterName, int type, int count, String semantic) { Object value = null; NodeModel nodeModel = null; TechniqueParametersModel techniqueParametersModel = new DefaultTechniqueParametersModel( type, count, semantic, value, nodeModel); techniqueModel.addParameter( parameterName, techniqueParametersModel); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy