Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ready: Add asset deletion within UI #2

Merged
merged 13 commits into from
Apr 20, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Disposable;
import com.mbrlabs.mundus.commons.assets.meta.Meta;
import com.mbrlabs.mundus.commons.scene3d.components.AssetUsage;

import java.util.Map;

Expand All @@ -33,7 +34,7 @@
* @author Marcus Brummer
* @version 01-10-2016
*/
public abstract class Asset implements Disposable {
public abstract class Asset implements Disposable, AssetUsage {

protected FileHandle file;
protected Meta meta;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
*/
public class MaterialAsset extends Asset {

private static final ObjectMap<String, String> MAP = new ObjectMap<String, String>();
private static final ObjectMap<String, String> MAP = new ObjectMap<>();

public static final String EXTENSION = ".mat";

Expand Down Expand Up @@ -178,4 +178,14 @@ public void dispose() {
// nothing to dispose
}

@Override
public boolean usesAsset(Asset assetToCheck) {
if (assetToCheck instanceof TextureAsset) {
boolean diffuseMatch = diffuseTexture != null && diffuseTexture.getFile().path().equals(assetToCheck.getFile().path());
boolean normalMatch = normalMap != null && normalMap.getFile().path().equals(assetToCheck.getFile().path());

return diffuseMatch || normalMatch;
}
return false;
}
}
15 changes: 15 additions & 0 deletions commons/src/main/com/mbrlabs/mundus/commons/assets/ModelAsset.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,19 @@ public void dispose() {
}
}

@Override
public boolean usesAsset(Asset assetToCheck) {
// if it's a MaterialAsset compare to the models materials
if (assetToCheck instanceof MaterialAsset) {
return defaultMaterials.containsValue(assetToCheck);
}

// check if the materials use the asset, like a texture asset
for (Map.Entry<String, MaterialAsset> stringMaterialAssetEntry : defaultMaterials.entrySet()) {
if (stringMaterialAssetEntry.getValue().usesAsset(assetToCheck)) {
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,11 @@ public void dispose() {
texture.dispose();
}

@Override
public boolean usesAsset(Asset assetToCheck) {
if (assetToCheck instanceof TextureAsset){
return texture == ((TextureAsset) assetToCheck).getTexture();
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,21 @@ public void updateUvScale(Vector2 uvScale) {
terrain.update();
meta.getTerrain().setUv(uvScale.x);
}

@Override
public boolean usesAsset(Asset assetToCheck) {
if (assetToCheck == splatmap)
return true;

// does the splatmap use the asset
if (assetToCheck instanceof TextureAsset) {
for (Map.Entry<SplatTexture.Channel, SplatTexture> texture : terrain.getTerrainTexture().getTextures().entrySet()) {
if (texture.getValue().texture.getFile().path().equals(assetToCheck.getFile().path())) {
return true;
}
}
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,9 @@ public void dispose() {
texture.dispose();
}
}

@Override
public boolean usesAsset(Asset assetToCheck) {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.mbrlabs.mundus.commons.scene3d.components;

import com.mbrlabs.mundus.commons.assets.Asset;

public interface AssetUsage {
/**
* Returns true if the implementation utilizes the given asset.
* Used for safely deleting assets by checking for usages of the asset first.
*
* Ex. should return true if material asset is passed to a model that is using that asset.
* @param assetToCheck the asset to check usages on
* @return true if the asset is used in the implementation, false if not
*/
boolean usesAsset(Asset assetToCheck);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,19 @@
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.graphics.g3d.Shader;
import com.badlogic.gdx.utils.ObjectMap;
import com.mbrlabs.mundus.commons.assets.Asset;
import com.mbrlabs.mundus.commons.assets.MaterialAsset;
import com.mbrlabs.mundus.commons.assets.ModelAsset;
import com.mbrlabs.mundus.commons.assets.TextureAsset;
import com.mbrlabs.mundus.commons.scene3d.GameObject;

import java.util.Objects;

/**
* @author Marcus Brummer
* @version 17-01-2016
*/
public class ModelComponent extends AbstractComponent {
public class ModelComponent extends AbstractComponent implements AssetUsage {

protected ModelAsset modelAsset;
protected ModelInstance modelInstance;
Expand All @@ -39,7 +43,7 @@ public class ModelComponent extends AbstractComponent {
public ModelComponent(GameObject go, Shader shader) {
super(go);
type = Type.MODEL;
materials = new ObjectMap<String, MaterialAsset>();
materials = new ObjectMap<>();
this.shader = shader;
}

Expand Down Expand Up @@ -106,4 +110,26 @@ public Component clone(GameObject go) {
return mc;
}

@Override
public boolean usesAsset(Asset assetToCheck) {
if (Objects.equals(assetToCheck.getID(), modelAsset.getID()))
return true;

if (assetToCheck instanceof MaterialAsset) {
if (materials.containsValue(assetToCheck,true)) {
return true;
}
}

if (assetToCheck instanceof TextureAsset) {
// for each texture see if there is a match
for (ObjectMap.Entry<String, MaterialAsset> next : materials) {
if (next.value.usesAsset(assetToCheck)) {
return true;
}
}
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,17 @@

import com.badlogic.gdx.graphics.g3d.Shader;
import com.badlogic.gdx.math.Vector2;
import com.mbrlabs.mundus.commons.assets.Asset;
import com.mbrlabs.mundus.commons.assets.TerrainAsset;
import com.mbrlabs.mundus.commons.scene3d.GameObject;

import java.util.Objects;

/**
* @author Marcus Brummer
* @version 18-01-2016
*/
public class TerrainComponent extends AbstractComponent {
public class TerrainComponent extends AbstractComponent implements AssetUsage {

private static final String TAG = TerrainComponent.class.getSimpleName();

Expand Down Expand Up @@ -74,4 +77,11 @@ public Component clone(GameObject go) {
return null;
}

@Override
public boolean usesAsset(Asset assetToCheck) {
if (Objects.equals(terrain.getID(), assetToCheck.getID()))
return true;

return terrain.usesAsset(assetToCheck);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ public int countTextures() {
return textures.size();
}

public Map<SplatTexture.Channel, SplatTexture> getTextures() {
return textures;
}

public SplatMap getSplatmap() {
return splatmap;
}
Expand Down
125 changes: 125 additions & 0 deletions editor/src/main/com/mbrlabs/mundus/editor/assets/EditorAssetManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,33 @@

package com.mbrlabs.mundus.editor.assets

import com.badlogic.gdx.Files
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.files.FileHandle
import com.badlogic.gdx.graphics.Pixmap
import com.badlogic.gdx.graphics.PixmapIO
import com.badlogic.gdx.utils.ObjectSet
import com.kotcrab.vis.ui.util.dialog.Dialogs
import com.mbrlabs.mundus.commons.assets.*
import com.mbrlabs.mundus.commons.assets.meta.Meta
import com.mbrlabs.mundus.commons.assets.meta.MetaTerrain
import com.mbrlabs.mundus.commons.scene3d.GameObject
import com.mbrlabs.mundus.commons.scene3d.components.AssetUsage
import com.mbrlabs.mundus.editor.Mundus
import com.mbrlabs.mundus.editor.events.LogEvent
import com.mbrlabs.mundus.editor.events.LogType
import com.mbrlabs.mundus.commons.utils.FileFormatUtils
import com.mbrlabs.mundus.editor.core.EditorScene
import com.mbrlabs.mundus.editor.core.project.ProjectManager
import com.mbrlabs.mundus.editor.ui.UI
import com.mbrlabs.mundus.editor.utils.Log
import org.apache.commons.io.FileUtils
import org.apache.commons.io.FilenameUtils
import java.io.*
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
import kotlin.collections.HashSet

/**
* @author Marcus Brummer
Expand Down Expand Up @@ -313,6 +324,120 @@ class EditorAssetManager(assetsRoot: FileHandle) : AssetManager(assetsRoot) {
metaSaver.save(asset.meta)
}

/**
* Delete the asset from the project
*/
fun deleteAsset(asset: Asset, projectManager: ProjectManager) {
val objectsUsingAsset = findAssetUsagesInScenes(projectManager, asset)
val assetsUsingAsset = findAssetUsagesInAssets(asset)

if (objectsUsingAsset.isNotEmpty() || assetsUsingAsset.isNotEmpty()) {
showUsagesFoundDialog(objectsUsingAsset, assetsUsingAsset)
return
}

// continue with deletion
assets?.removeValue(asset, true)

if (asset.file.extension().equals(FileFormatUtils.FORMAT_3D_GLTF)) {
// Delete the additional gltf binary file if found
val binPath = asset.file.pathWithoutExtension() + ".bin"
val binFile = Gdx.files.getFileHandle(binPath, Files.FileType.Absolute)
if (binFile.exists())
binFile.delete()
}

if (asset.meta.file.exists())
asset.meta.file.delete()

if (asset.file.exists())
asset.file.delete()
}

/**
* Build a dialog displaying the usages for the asset trying to be deleted.
*/
private fun showUsagesFoundDialog(objectsWithAssets: HashMap<GameObject, String>, assetsUsingAsset: ArrayList<Asset>) {
val iterator = objectsWithAssets.iterator()
var details = "Scenes using asset:"

// Create scenes section
while (iterator.hasNext()) {
val next = iterator.next()

val sceneName = next.value
val gameObject = next.key

var moreDetails = buildString {
append("\nScene: ")
append("[")
append(sceneName)
append("] Object name: ")
append("[")
append(gameObject.name)
append("]")
}

if (iterator.hasNext()) {
moreDetails += ", "
}

details += (moreDetails)
}

// add assets section
if (assetsUsingAsset.isNotEmpty()) {
details += "\n\nAssets using asset:"

for (name in assetsUsingAsset)
details += "\n" + name
}

Dialogs.showDetailsDialog(UI, "Before deleting an asset, remove usages of the asset and save. See details for usages.", "Asset deletion", details)
}

/**
* Searches all assets in the current context for any usages of the given asset
*/
private fun findAssetUsagesInAssets(asset: Asset): ArrayList<Asset> {
val assetsUsingAsset = ArrayList<Asset>()

// Check for dependent assets that are not in scenes
for (otherAsset in assets) {
if (asset != otherAsset && otherAsset.usesAsset(asset)) {
assetsUsingAsset.add(otherAsset)
}
}

return assetsUsingAsset
}

/**
* Searches all scenes in the current context for any usages of the given asset
*/
private fun findAssetUsagesInScenes(projectManager: ProjectManager, asset: Asset): HashMap<GameObject, String> {
val objectsWithAssets = HashMap<GameObject, String>()

// we check for usages in all scenes
for (sceneName in projectManager.current().scenes) {
val scene = projectManager.loadScene(projectManager.current(), sceneName)
checkSceneForAssetUsage(scene, asset, objectsWithAssets)
}

return objectsWithAssets
}

private fun checkSceneForAssetUsage(scene: EditorScene?, asset: Asset, objectsWithAssets: HashMap<GameObject, String>) {
for (gameObject in scene!!.sceneGraph.gameObjects) {
for (component in gameObject.components) {
if (component is AssetUsage) {
if (component.usesAsset(asset))
objectsWithAssets[gameObject] = scene.name
}
}
}
}

/**
* Saves an existing terrainAsset asset.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ class MetaSaver {
}
json.writeObjectEnd()

json.writer.flush()
// Close stream, otherwise file becomes locked
json.writer.close()
}

private fun addBasics(meta: Meta, json: Json) {
Expand Down
Loading