package com.uwsoft.editor.proxy;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map.Entry;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.uwsoft.editor.renderer.data.*;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import com.badlogic.gdx.Files;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.ParticleEffect;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.utils.Json;
import com.puremvc.patterns.proxy.BaseProxy;
import com.uwsoft.editor.data.SpineAnimData;
import com.uwsoft.editor.Overlap2DFacade;
import com.uwsoft.editor.renderer.resources.FontSizePair;
import com.uwsoft.editor.renderer.resources.IResourceRetriever;
import com.uwsoft.editor.renderer.utils.MySkin;
/**
* Created by azakhary on 4/26/2015.
*/
public class ResourceManager extends BaseProxy implements IResourceRetriever {
public String packResolutionName = "orig";
private static final String TAG = ResourceManager.class.getCanonicalName();
public static final String NAME = TAG;
private HashMap<String, ParticleEffect> particleEffects = new HashMap<String, ParticleEffect>(1);
private TextureAtlas currentProjectAtlas;
private HashMap<String, SpineAnimData> spineAnimAtlases = new HashMap<String, SpineAnimData>();
private HashMap<String, TextureAtlas> spriteAnimAtlases = new HashMap<String, TextureAtlas>();
private HashMap<String, FileHandle> spriterAnimFiles = new HashMap<String, FileHandle>();
private HashMap<FontSizePair, BitmapFont> bitmapFonts = new HashMap<>();
private HashMap<String, ShaderProgram> shaderPrograms = new HashMap<String, ShaderProgram>(1);
private TextureRegion defaultRegion;
private ResolutionManager resolutionManager;
public ResourceManager() {
super(NAME);
}
@Override
public void onRegister() {
super.onRegister();
facade = Overlap2DFacade.getInstance();
resolutionManager = facade.retrieveProxy(ResolutionManager.NAME);
// TODO: substitute this with "NO IMAGE" icon
Pixmap pixmap = new Pixmap(50, 50, Pixmap.Format.RGBA8888);
pixmap.setColor(new Color(1, 1, 1, 0.4f));
pixmap.fill();
defaultRegion = new TextureRegion(new Texture(pixmap));
}
@Override
public TextureRegion getTextureRegion(String name) {
TextureRegion reg = currentProjectAtlas.findRegion(name);
if(reg == null) {
reg = defaultRegion;
}
return reg;
}
public TextureAtlas getTextureAtlas() {
return currentProjectAtlas;
}
@Override
public ParticleEffect getParticleEffect(String name) {
return new ParticleEffect(particleEffects.get(name));
}
@Override
public TextureAtlas getSkeletonAtlas(String animationName) {
SpineAnimData animData = spineAnimAtlases.get(animationName);
return animData.atlas;
}
/**
* Sets working resolution, please set before doing any loading
* @param resolution String resolution name, default is "orig" later use resolution names created in editor
*/
public void setWorkingResolution(String resolution) {
ResolutionEntryVO resolutionObject = getProjectVO().getResolution("resolutionName");
if(resolutionObject != null) {
packResolutionName = resolution;
}
}
@Override
public FileHandle getSkeletonJSON(String animationName) {
SpineAnimData animData = spineAnimAtlases.get(animationName);
return animData.jsonFile;
}
@Override
public FileHandle getSCMLFile(String name) {
return spriterAnimFiles.get(name);
}
@Override
public TextureAtlas getSpriteAnimation(String animationName) {
return spriteAnimAtlases.get(animationName);
}
@Override
public BitmapFont getBitmapFont(String fontName, int fontSize) {
FontSizePair pair = new FontSizePair(fontName, fontSize);
return bitmapFonts.get(pair);
}
@Override
public MySkin getSkin() {
//return textureManager.projectSkin;
// not sure if we are going to use skins for labels
return null;
}
@Override
public ProjectInfoVO getProjectVO() {
ProjectManager projectManager = facade.retrieveProxy(ProjectManager.NAME);
return projectManager.getCurrentProjectInfoVO();
}
@Override
public SceneVO getSceneVO(String name) {
SceneDataManager sceneDataManager = facade.retrieveProxy(SceneDataManager.NAME);
// TODO: this should be cached
FileHandle file = Gdx.files.internal(sceneDataManager.getCurrProjectScenePathByName(name));
Json json = new Json();
json.setIgnoreUnknownFields(true);
return json.fromJson(SceneVO.class, file.readString());
}
public void loadCurrentProjectData(String projectPath, String curResolution) {
packResolutionName = curResolution;
loadCurrentProjectAssets(projectPath + "/assets/" + curResolution + "/pack/pack.atlas");
loadCurrentProjectSkin(projectPath + "/assets/orig/styles");
loadCurrentProjectParticles(projectPath + "/assets/orig/particles");
loadCurrentProjectSpineAnimations(projectPath + "/assets/", curResolution);
loadCurrentProjectSpriteAnimations(projectPath + "/assets/", curResolution);
loadCurrentProjectSpriterAnimations(projectPath + "/assets/", curResolution);
loadCurrentProjectBitmapFonts(projectPath, curResolution);
loadCurrentProjectShaders(projectPath + "/assets/shaders/");
}
private void loadCurrentProjectParticles(String path) {
particleEffects.clear();
FileHandle sourceDir = new FileHandle(path);
for (FileHandle entry : sourceDir.list()) {
File file = entry.file();
String filename = file.getName();
if (file.isDirectory() || filename.endsWith(".DS_Store")) continue;
ParticleEffect particleEffect = new ParticleEffect();
particleEffect.load(Gdx.files.internal(file.getAbsolutePath()), currentProjectAtlas, "");
particleEffects.put(filename, particleEffect);
}
}
private void loadCurrentProjectSpineAnimations(String path, String curResolution) {
spineAnimAtlases.clear();
FileHandle sourceDir = new FileHandle(path + "orig/spine-animations");
for (FileHandle entry : sourceDir.list()) {
if (entry.file().isDirectory()) {
String animName = FilenameUtils.removeExtension(entry.file().getName());
TextureAtlas atlas = new TextureAtlas(Gdx.files.internal(path + curResolution + "/spine-animations/" + File.separator + animName + File.separator + animName + ".atlas"));
FileHandle animJsonFile = Gdx.files.internal(entry.file().getAbsolutePath() + File.separator + animName + ".json");
SpineAnimData data = new SpineAnimData();
data.atlas = atlas;
data.jsonFile = animJsonFile;
data.animName = animName;
spineAnimAtlases.put(animName, data);
}
}
}
private void loadCurrentProjectSpriteAnimations(String path, String curResolution) {
spriteAnimAtlases.clear();
FileHandle sourceDir = new FileHandle(path + curResolution + "/sprite-animations");
for (FileHandle entry : sourceDir.list()) {
if (entry.file().isDirectory()) {
String animName = FilenameUtils.removeExtension(entry.file().getName());
TextureAtlas atlas = new TextureAtlas(Gdx.files.internal(entry.file().getAbsolutePath() + File.separator + animName + ".atlas"));
spriteAnimAtlases.put(animName, atlas);
}
}
}
private void loadCurrentProjectSpriterAnimations(String path, String curResolution) {
spriterAnimFiles.clear();
FileHandle sourceDir = new FileHandle(path + "orig" + "/spriter-animations");
for (FileHandle entry : sourceDir.list()) {
if (entry.file().isDirectory()) {
String animName = entry.file().getName();
FileHandle scmlFile = new FileHandle(path + "orig" + "/spriter-animations/" + animName + "/" + animName + ".scml");
spriterAnimFiles.put(animName, scmlFile);
}
}
}
public void loadCurrentProjectAssets(String packPath) {
try {
currentProjectAtlas = new TextureAtlas(Gdx.files.getFileHandle(packPath, Files.FileType.Internal));
} catch (Exception e) {
currentProjectAtlas = new TextureAtlas();
}
}
public ArrayList<FontSizePair> getProjectRequiredFontsList() {
HashSet<FontSizePair> fontsToLoad = new HashSet<>();
for (int i = 0; i < getProjectVO().scenes.size(); i++) {
SceneVO scene = getSceneVO(getProjectVO().scenes.get(i).sceneName);
CompositeVO composite = scene.composite;
if (composite == null) {
continue;
}
FontSizePair[] fonts = composite.getRecursiveFontList();
for (CompositeItemVO library : getProjectVO().libraryItems.values()) {
FontSizePair[] libFonts = library.composite.getRecursiveFontList();
Collections.addAll(fontsToLoad, libFonts);
}
Collections.addAll(fontsToLoad, fonts);
}
return new ArrayList<>(fontsToLoad);
}
public void loadCurrentProjectBitmapFonts(String path, String curResolution) {
bitmapFonts.clear();
ArrayList<FontSizePair> requiredFonts = getProjectRequiredFontsList();
for (int i = 0; i < requiredFonts.size(); i++) {
FontSizePair pair = requiredFonts.get(i);
FileHandle fontFile;
try {
fontFile = getTTFSafely(pair.fontName);
FreeTypeFontGenerator generator = new FreeTypeFontGenerator(fontFile);
FreeTypeFontGenerator.FreeTypeFontParameter parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
parameter.size = Math.round(pair.fontSize * resolutionManager.getCurrentMul());
BitmapFont font = generator.generateFont(parameter);
bitmapFonts.put(pair, font);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void loadCurrentProjectShaders(String path) {
Iterator<Entry<String, ShaderProgram>> it = shaderPrograms.entrySet().iterator();
while (it.hasNext()) {
Entry<String, ShaderProgram> pair = it.next();
pair.getValue().dispose();
it.remove();
}
shaderPrograms.clear();
FileHandle sourceDir = new FileHandle(path);
for (FileHandle entry : sourceDir.list()) {
File file = entry.file();
String filename = file.getName().replace(".vert", "").replace(".frag", "");
if (file.isDirectory() || filename.endsWith(".DS_Store") || shaderPrograms.containsKey(filename)) continue;
// check if pair exists.
if(Gdx.files.internal(path + filename + ".vert").exists() && Gdx.files.internal(path + filename + ".frag").exists()) {
ShaderProgram shaderProgram = new ShaderProgram(Gdx.files.internal(path + filename + ".vert"), Gdx.files.internal(path + filename + ".frag"));
System.out.println(shaderProgram.getLog());
shaderPrograms.put(filename, shaderProgram);
}
}
}
/**
* @param fontPath
* @deprecated
*/
private void loadCurrentProjectSkin(String fontPath) {
/*
File styleFile = new File(fontPath, "styles.dt");
FileHandle f = new FileHandle(styleFile);
if (styleFile.isFile() && styleFile.exists()) {
projectSkin = new MySkin(f);
ObjectMap<String, BitmapFont> map = projectSkin.getAll(BitmapFont.class);
for (ObjectMap.Entry<String, BitmapFont> entry : map.entries()) {
projectSkin.getFont(entry.key).getRegion().getTexture().setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
}
}
*/
}
public FileHandle getTTFSafely(String fontName) throws IOException {
FontManager fontManager = facade.retrieveProxy(FontManager.NAME);
ProjectManager projectManager = facade.retrieveProxy(ProjectManager.NAME);
String expectedPath = projectManager.getFreeTypeFontPath() + File.separator + fontName + ".ttf";
FileHandle expectedFile = Gdx.files.internal(expectedPath);
if (!expectedFile.exists()) {
// let's check if system fonts fot it
HashMap<String, String> fonts = fontManager.getFontsMap();
if (fonts.containsKey(fontName)) {
File source = new File(fonts.get(fontName));
FileUtils.copyFile(source, expectedFile.file());
expectedFile = Gdx.files.internal(expectedPath);
} else {
throw new FileNotFoundException();
}
}
return expectedFile;
}
public void addBitmapFont(String name, int size, BitmapFont font) {
bitmapFonts.put(new FontSizePair(name, size), font);
}
public void flushAllUnusedFonts() {
//List of fonts that are required to be in memory
ArrayList<FontSizePair> requiredFonts = getProjectRequiredFontsList();
ArrayList<FontSizePair> fontsInMemory = new ArrayList<>(bitmapFonts.keySet());
for (FontSizePair font : fontsInMemory) {
if (!requiredFonts.contains(font)) {
bitmapFonts.remove(font);
}
}
}
public boolean isFontLoaded(String shortName, int fontSize) {
return bitmapFonts.containsKey(new FontSizePair(shortName, fontSize));
}
public void prepareEmbeddingFont(String fontfamily, int fontSize) {
flushAllUnusedFonts();
if (isFontLoaded(fontfamily, fontSize)) {
return;
}
FontManager fontManager = facade.retrieveProxy(FontManager.NAME);
FreeTypeFontGenerator.FreeTypeFontParameter parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
parameter.size = fontSize;
FreeTypeFontGenerator generator = new FreeTypeFontGenerator(fontManager.getTTFByName(fontfamily));
BitmapFont font = generator.generateFont(parameter);
addBitmapFont(fontfamily, parameter.size, font);
}
public HashMap<String, SpineAnimData> getProjectSpineAnimationsList() {
return spineAnimAtlases;
}
public HashMap<String, TextureAtlas> getProjectSpriteAnimationsList() {
return spriteAnimAtlases;
}
public HashMap<String, FileHandle> getProjectSpriterAnimationsList() {
return spriterAnimFiles;
}
public TextureAtlas getProjectAssetsList() {
return currentProjectAtlas;
}
public HashMap<String, ParticleEffect> getProjectParticleList() {
return particleEffects;
}
@Override
public ResolutionEntryVO getLoadedResolution() {
if(packResolutionName.equals("orig")) {
return getProjectVO().originalResolution;
}
return getProjectVO().getResolution(packResolutionName);
}
@Override
public ShaderProgram getShaderProgram(String shaderName) {
return shaderPrograms.get(shaderName);
}
public HashMap<String, ShaderProgram> getShaders() {
return shaderPrograms;
}
}