package net.mgsx.gltf.demo.ui; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.Camera; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g3d.Material; import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute; import com.badlogic.gdx.graphics.g3d.environment.BaseLight; import com.badlogic.gdx.graphics.g3d.model.Animation; import com.badlogic.gdx.graphics.g3d.model.Node; import com.badlogic.gdx.graphics.g3d.model.NodePart; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.ui.Image; import com.badlogic.gdx.scenes.scene2d.ui.Label; import com.badlogic.gdx.scenes.scene2d.ui.SelectBox; import com.badlogic.gdx.scenes.scene2d.ui.Skin; import com.badlogic.gdx.scenes.scene2d.ui.Slider; import com.badlogic.gdx.scenes.scene2d.ui.Table; import com.badlogic.gdx.scenes.scene2d.ui.TextButton; import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.ObjectMap.Entry; import com.badlogic.gdx.utils.Scaling; import net.mgsx.gltf.demo.GLTFDemo.ShaderMode; import net.mgsx.gltf.demo.data.ModelEntry; import net.mgsx.gltf.demo.events.FileOpenEvent; import net.mgsx.gltf.demo.events.FileSaveEvent; import net.mgsx.gltf.demo.events.IBLFolderChangeEvent; import net.mgsx.gltf.demo.model.IBLStudio; import net.mgsx.gltf.demo.model.IBLStudio.IBLPreset; import net.mgsx.gltf.scene3d.attributes.PBRColorAttribute; import net.mgsx.gltf.scene3d.attributes.PBRFloatAttribute; import net.mgsx.gltf.scene3d.attributes.PBRTextureAttribute; import net.mgsx.gltf.scene3d.model.NodePartPlus; import net.mgsx.gltf.scene3d.model.NodePlus; import net.mgsx.gltf.scene3d.scene.SceneManager; import net.mgsx.gltf.scene3d.scene.SceneModel; import net.mgsx.gltf.scene3d.shaders.PBRShaderConfig.SRGB; public class GLTFDemoUI extends Table { public static FileSelector fileSelector = null; public static boolean LIVE_STATS = true; public SelectBox<ModelEntry> entrySelector; public SelectBox<String> variantSelector; public SelectBox<String> animationSelector; public Table screenshotsTable; public Slider debugAmbiantSlider; public Slider ambiantSlider; public Slider lightSlider; public Slider debugSpecularSlider; public SelectBox<String> cameraSelector; public final Vector3UI lightDirectionControl; public SelectBox<ShaderMode> shaderSelector; private SelectBox<String> materialSelector; private Table materialTable; private final ObjectMap<String, Material> materialMap = new ObjectMap<String, Material>(); private final ObjectMap<String, Node> nodeMap = new ObjectMap<String, Node>(); private SelectBox<String> nodeSelector; private Table nodeTable; private TextButton btNodeExclusive; private Node selectedNode; protected CollapsableUI shaderOptions; public SelectBox<SRGB> shaderSRGB; private CollapsableUI lightOptions; public SelectBox<SceneModel> sceneSelector; public SelectBox<String> lightSelector; public Label shaderCount; public TextButton skeletonButton; public BooleanUI lightShadow; public FloatUI shadowBias; public TextButton btAllAnimations; public BooleanUI fogEnabled; public BooleanUI skyBoxEnabled; public Vector4UI fogColor; public Vector3UI fogEquation; public BooleanUI IBLEnabled; private Table IBLChooser; private CollapsableUI IBLOptions; public BooleanUI IBLSpecular; public BooleanUI IBLLookup; public Vector4UI skyBoxColor; public BooleanUI outlinesEnabled; public FloatUI outlinesWidth; public FloatUI outlineDepthMin; public FloatUI outlineDepthMax; public Vector4UI outlineInnerColor; public Vector4UI outlineOuterColor; private CollapsableUI outlineOptions; public FloatUI outlineDistFalloff; public BooleanUI outlineDistFalloffOption; private Label lightLabel; private SceneManager sceneManager; public final SelectBox<IBLPreset> IBLSelector; public GLTFDemoUI(SceneManager sceneManager, Skin skin) { super(skin); this.sceneManager = sceneManager; Table root = new Table(skin); root.defaults().pad(5); Table rootRight = new Table(skin); rootRight.defaults().pad(5); add(root).expandY().top(); add().expand(); add(rootRight).expandY().top(); if(fileSelector != null){ TextButton btOpenFile = new TextButton("Open file", skin); TextButton btExportFile = new TextButton("Save to file", skin); root.add("File"); Table btTable = new Table(); btTable.add(btOpenFile); btTable.add(btExportFile); root.add(btTable).row(); btOpenFile.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { fileSelector.open(new Runnable() { @Override public void run() { GLTFDemoUI.this.fire(new FileOpenEvent(fileSelector.lastFile)); } }); } }); btExportFile.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { fileSelector.open(new Runnable() { @Override public void run() { GLTFDemoUI.this.fire(new FileSaveEvent(fileSelector.lastFile)); } }); } }); } entrySelector = new SelectBox<ModelEntry>(skin); root.add("Model"); root.add(entrySelector).row(); root.add("Screenshot"); root.add(screenshotsTable = new Table(skin)).row(); variantSelector = new SelectBox<String>(skin); root.add("File"); root.add(variantSelector).row(); // scene sceneSelector = new SelectBox<SceneModel>(skin){ @Override protected String toString(SceneModel item) { return item.name == null ? "<null>" : item.name; } }; root.add("Scene"); root.add(sceneSelector).row(); // Shader options shaderSelector = new SelectBox<ShaderMode>(skin); root.add("Shader"); root.add(shaderSelector).row(); shaderSelector.setItems(ShaderMode.values()); root.add("Shader count"); root.add(shaderCount = new Label("", skin)).row(); root.add(); root.add(shaderOptions = new CollapsableUI(skin, "Shader Options", false)).row(); shaderOptions.optTable.add("SRGB"); shaderOptions.optTable.add(shaderSRGB = new SelectBox<SRGB>(skin)).row(); shaderSRGB.setItems(SRGB.values()); shaderSRGB.setSelected(SRGB.ACCURATE); // Fog shaderOptions.optTable.add("Fog"); shaderOptions.optTable.add(fogEnabled = new BooleanUI(skin, false)).row(); shaderOptions.optTable.add("Fog Color"); shaderOptions.optTable.add(fogColor = new Vector4UI(skin, new Color())).row(); shaderOptions.optTable.add("Fog Equation"); shaderOptions.optTable.add(fogEquation = new Vector3UI(skin, new Vector3(-1f, 1f, -0.8f))).row(); // Skybox shaderOptions.optTable.add("SkyBox"); shaderOptions.optTable.add(skyBoxEnabled = new BooleanUI(skin, true)).row(); shaderOptions.optTable.add("SkyBox Color"); shaderOptions.optTable.add(skyBoxColor = new Vector4UI(skin, new Color(Color.WHITE))).row(); // Outlines root.add(); root.add(outlineOptions = new CollapsableUI(skin, "Outline Options", false)).row(); outlineOptions.optTable.add("Outlines"); outlineOptions.optTable.add(outlinesEnabled = new BooleanUI(skin, false)).row(); outlineOptions.optTable.add("Thickness"); outlineOptions.optTable.add(outlinesWidth = new FloatUI(skin, 0f)).row(); outlineOptions.optTable.add("Depth min"); outlineOptions.optTable.add(outlineDepthMin = new FloatUI(skin, .35f)).row(); outlineOptions.optTable.add("Depth max"); outlineOptions.optTable.add(outlineDepthMax = new FloatUI(skin, .9f)).row(); outlineOptions.optTable.add("Inner color"); outlineOptions.optTable.add(outlineInnerColor = new Vector4UI(skin, new Color(0,0,0,.3f))).row(); outlineOptions.optTable.add("Outer color"); outlineOptions.optTable.add(outlineOuterColor = new Vector4UI(skin, new Color(0,0,0,.7f))).row(); outlineOptions.optTable.add("Distance Falloff"); outlineOptions.optTable.add(outlineDistFalloffOption = new BooleanUI(skin, false)).row(); outlineOptions.optTable.add("Distance Exponent"); outlineOptions.optTable.add(outlineDistFalloff = new FloatUI(skin, 1f)).row(); // Lighting options root.add(); root.add(lightOptions = new CollapsableUI(skin, "Light Options", false)).row(); lightSlider = new Slider(0, 100, .01f, false, skin); lightOptions.optTable.add("Light intensity"); lightOptions.optTable.add(lightSlider).row(); lightSlider.setValue(1f); lightOptions.optTable.add("Dir Light"); lightOptions.optTable.add(lightDirectionControl = new Vector3UI(skin, new Vector3())).row(); lightOptions.optTable.add("Shadows"); lightOptions.optTable.add(lightShadow = new BooleanUI(skin, false)).row(); lightOptions.optTable.add("Shadow Bias"); lightOptions.optTable.add(shadowBias = new FloatUI(skin, 0)).row(); ambiantSlider = new Slider(0, 1, .01f, false, skin); ambiantSlider.setValue(1f); lightOptions.optTable.add("Ambient Light"); lightOptions.optTable.add(ambiantSlider).row(); // IBL options root.add(); root.add(IBLOptions = new CollapsableUI(skin, "IBL Options", false)).row(); IBLOptions.optTable.add("Overall"); IBLOptions.optTable.add(IBLEnabled = new BooleanUI(skin, true)).row(); IBLOptions.optTable.add("Radiance"); IBLOptions.optTable.add(IBLSpecular = new BooleanUI(skin, true)).row(); IBLOptions.optTable.add("BSDF"); IBLOptions.optTable.add(IBLLookup = new BooleanUI(skin, true)).row(); IBLOptions.optTable.add("Presets"); IBLOptions.optTable.add(IBLChooser = new Table(skin)).row(); IBLChooser.add(IBLSelector = new SelectBox<IBLStudio.IBLPreset>(getSkin())); if(fileSelector != null){ TextButton btOpenFile = new TextButton("Load", skin); IBLChooser.add(btOpenFile); btOpenFile.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { fileSelector.selectFolder(new Runnable() { @Override public void run() { GLTFDemoUI.this.fire(new IBLFolderChangeEvent(fileSelector.lastFile)); } }); } }); } // Scene options cameraSelector = new SelectBox<String>(skin); root.add("Camera"); root.add(cameraSelector).row(); lightSelector = new SelectBox<String>(skin); root.add(lightLabel = new Label("Lights", skin)); root.add(lightSelector).row(); animationSelector = new SelectBox<String>(skin); btAllAnimations = new TextButton("All", skin, "toggle"); root.add("Animation"); root.add(animationSelector); root.add(btAllAnimations).row(); skeletonButton = new TextButton("Skeletons", getSkin(), "toggle"); root.add("Skeletons"); root.add(skeletonButton).row(); materialSelector = new SelectBox<String>(skin); rootRight.add("Materials"); rootRight.add(materialSelector).row(); materialTable = new Table(skin); rootRight.add(); rootRight.add(materialTable).row(); nodeSelector = new SelectBox<String>(skin); rootRight.add("Nodes"); rootRight.add(nodeSelector).row(); rootRight.add(); rootRight.add(btNodeExclusive = new TextButton("Hide other nodes", getSkin(), "toggle")).row(); nodeTable = new Table(skin); rootRight.add(); rootRight.add(nodeTable).row(); materialSelector.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { setMaterial(materialSelector.getSelected()); } }); nodeSelector.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { setNode(nodeSelector.getSelected()); } }); btNodeExclusive.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { showHideOtherNodes(btNodeExclusive.isChecked()); } }); btAllAnimations.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { } }); } protected TextButton toggle(String name, boolean checked) { TextButton bt = new TextButton(name, getSkin(), "toggle"); bt.setChecked(checked); return bt; } protected void showHideOtherNodes(boolean hide) { for(Entry<String, Node> entry : nodeMap){ for(NodePart part : entry.value.parts){ part.enabled = selectedNode == null || !hide || entry.value == selectedNode; } } } protected void setNode(String nodeID) { nodeTable.clearChildren(); selectedNode = null; if(nodeID == null || nodeID.isEmpty()){ showHideOtherNodes(false); return; } final Node node = nodeMap.get(nodeID); selectedNode = node; showHideOtherNodes(btNodeExclusive.isChecked()); if(node instanceof NodePlus){ final NodePlus np = (NodePlus)node; if(np.weights != null){ WeightsUI weightEditor = new WeightsUI(getSkin(), np.weights, np.morphTargetNames); nodeTable.add("Morph Targets").row(); nodeTable.add(weightEditor); weightEditor.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { for(NodePart part : np.parts){ NodePartPlus npp = (NodePartPlus)part; if(npp.morphTargets != null){ npp.morphTargets.set(np.weights); } } } }); } } } protected void setMaterial(String materialID) { materialTable.clearChildren(); if(materialID == null || materialID.isEmpty()) return; final Material material = materialMap.get(materialID); // base color materialTable.add(new ColorAttributeUI(getSkin(), material.get(ColorAttribute.class, PBRColorAttribute.BaseColorFactor))).row(); addMaterialTextureSwitch("Color Texture", material, PBRTextureAttribute.BaseColorTexture); // emissive materialTable.add(new ColorAttributeUI(getSkin(), material.get(ColorAttribute.class, PBRColorAttribute.Emissive))).row(); addMaterialTextureSwitch("Emissive Texture", material, PBRTextureAttribute.EmissiveTexture); // metallic roughness materialTable.add(new FloatAttributeUI(getSkin(), material.get(PBRFloatAttribute.class, PBRFloatAttribute.Metallic))).row(); materialTable.add(new FloatAttributeUI(getSkin(), material.get(PBRFloatAttribute.class, PBRFloatAttribute.Roughness))).row(); addMaterialTextureSwitch("MR Texture", material, PBRTextureAttribute.MetallicRoughnessTexture); // normal materialTable.add(new FloatAttributeUI(getSkin(), material.get(PBRFloatAttribute.class, PBRFloatAttribute.NormalScale))).row(); addMaterialTextureSwitch("Normal Texture", material, PBRTextureAttribute.NormalTexture); // occlusion materialTable.add(new FloatAttributeUI(getSkin(), material.get(PBRFloatAttribute.class, PBRFloatAttribute.OcclusionStrength))).row(); addMaterialTextureSwitch("Occlusion Texture", material, PBRTextureAttribute.OcclusionTexture); } private void addMaterialTextureSwitch(String name, final Material material, long type){ final PBRTextureAttribute attribute = material.get(PBRTextureAttribute.class, type); if(attribute != null){ final TextButton bt = new TextButton(name, getSkin(), "toggle"); bt.setChecked(true); materialTable.add(bt); Image pict = new Image(attribute.textureDescription.texture); pict.setScaling(Scaling.fit); materialTable.add(pict).size(64); materialTable.row(); bt.addListener(new ChangeListener() { @Override public void changed(ChangeEvent event, Actor actor) { if(bt.isChecked()){ material.set(attribute); }else{ material.remove(attribute.type); } } }); } } public void setEntry(ModelEntry entry, FileHandle rootFolder) { variantSelector.setSelected(null); Array<String> variants = entry.variants.keys().toArray(new Array<String>()); variants.insert(0, ""); variantSelector.setItems(variants); if(entry.variants.size == 1){ variantSelector.setSelectedIndex(1); }else{ variantSelector.setSelectedIndex(0); } if(screenshotsTable.getChildren().size > 0){ Image imgScreenshot = (Image)screenshotsTable.getChildren().first(); ((TextureRegionDrawable)imgScreenshot.getDrawable()).getRegion().getTexture().dispose(); } screenshotsTable.clear(); } public void setImage(Texture texture){ if(texture != null){ Image img = new Image(texture); img.setScaling(Scaling.fit); screenshotsTable.add(img).height(100); } } public void setAnimations(Array<Animation> animations) { if(animations.size > 0){ Array<String> ids = new Array<String>(); ids.add(""); for(Animation anim : animations){ ids.add(anim.id); } animationSelector.setItems(ids); }else{ animationSelector.setItems(); } } public void setCameras(ObjectMap<Node, Camera> cameras) { Array<String> cameraNames = new Array<String>(); cameraNames.add(""); for(Entry<Node, Camera> e : cameras){ cameraNames.add(e.key.id); } cameraSelector.setItems(); cameraSelector.setItems(cameraNames); } public void setMaterials(Array<Material> materials) { materialMap.clear(); Array<String> names = new Array<String>(); names.add(""); for(Material e : materials){ names.add(e.id); materialMap.put(e.id, e); } materialSelector.setItems(); materialSelector.setItems(names); } public void setNodes(Array<Node> nodes) { nodeMap.clear(); Array<String> names = new Array<String>(); for(Node e : nodes){ names.add(e.id); nodeMap.put(e.id, e); } names.sort(); names.insert(0, ""); nodeSelector.setItems(); nodeSelector.setItems(names); } public void setScenes(Array<SceneModel> scenes) { if(scenes == null){ sceneSelector.setDisabled(true); sceneSelector.setItems(new Array<SceneModel>()); }else{ sceneSelector.setDisabled(false); Array<SceneModel> items = new Array<SceneModel>(); for(int i=0 ; i<scenes.size ; i++){ items.add(scenes.get(i)); } sceneSelector.setItems(items); } } public void setLights(ObjectMap<Node, BaseLight> lights) { Array<String> names = new Array<String>(); names.add(""); for(Entry<Node, BaseLight> entry : lights){ names.add(entry.key.id); } lightSelector.setItems(names); } @Override public void act(float delta) { if(LIVE_STATS ){ lightLabel.setText("Lights (" + sceneManager.getActiveLightsCount() + "/" + sceneManager.getTotalLightsCount() + ")"); } super.act(delta); } }