use of io.xol.chunkstories.api.content.Asset in project chunkstories by Hugobros3.
the class ShaderGL method load.
private void load() {
this.samplers.clear();
shaderProgramId = glCreateProgram();
StringBuilder vertexSource = new StringBuilder();
StringBuilder fragSource = new StringBuilder();
StringBuilder geometrySource = null;
try {
Asset vertexShader = modsManager.getAsset("./shaders/" + shaderName + "/" + shaderName + ".vs");
Asset fragmentShader = modsManager.getAsset("./shaders/" + shaderName + "/" + shaderName + ".fs");
// This might not exist !
Asset geometryShader = modsManager.getAsset("./shaders/" + shaderName + "/" + shaderName + ".gs");
GLSLPreprocessor.loadRecursivly(modsManager, vertexShader, vertexSource, false, null);
GLSLPreprocessor.loadRecursivly(modsManager, fragmentShader, fragSource, true, null);
// If a geometry shader asset was found
if (geometryShader != null) {
geometrySource = new StringBuilder();
GLSLPreprocessor.loadRecursivly(modsManager, geometryShader, geometrySource, true, null);
}
} catch (IOException e) {
logger().error("Failed to load shader program " + shaderName);
logger().error("Exception: {}", e);
return;
}
vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
fragShaderId = glCreateShader(GL_FRAGMENT_SHADER);
if (geometrySource != null)
geometryShaderId = glCreateShader(GL_GEOMETRY_SHADER);
// Anti-AMD bullshit : AMD drivers have this stupid design decision of attributing vertex attributes by lexicographical order instead of
// order of apparition, leading to these annoying issues where an optional attribute is in index zero and disabling it screws the drawcalls
// To counter this, this piece of code forces the attributes locations to be declared in proper order
int i = 0;
for (String line : vertexSource.toString().split("\n")) {
if (line.startsWith("in ")) {
String attributeName = line.split(" ")[2].replace(";", "");
glBindAttribLocation(shaderProgramId, i, attributeName);
i++;
}
}
glShaderSource(vertexShaderId, vertexSource);
glCompileShader(vertexShaderId);
// Parse the fragment shader to look for outputs and assign them locations based on their order of appearance
// Also look for samplers
int j = 0;
for (String line : fragSource.toString().split("\n")) {
if (line.startsWith("out ")) {
String outputName = line.split(" ")[2].replace(";", "");
if (outputName.equals("gl_FragDepth")) {
logger.info("Writing to depth in frag enabled");
continue;
}
glBindFragDataLocation(shaderProgramId, j, outputName);
j++;
} else if (line.startsWith("uniform ")) {
String[] tokens = line.split(" ");
if (tokens.length >= 3) {
if (tokens[1].startsWith("sampler") || tokens[1].startsWith("usampler") || tokens[1].startsWith("isampler")) {
SamplerType type = null;
if (tokens[1].endsWith("Shadow"))
tokens[1] = tokens[1].substring(0, tokens[1].length() - 6);
if (tokens[1].endsWith("1D"))
type = SamplerType.TEXTURE_1D;
else if (tokens[1].endsWith("2D"))
type = SamplerType.TEXTURE_2D;
else if (tokens[1].endsWith("3D"))
type = SamplerType.TEXTURE_3D;
else if (tokens[1].endsWith("Cube"))
type = SamplerType.CUBEMAP;
else if (tokens[1].endsWith("2DArray"))
type = SamplerType.ARRAY_TEXTURE_2D;
else {
logger.error("Could not recognize the sampler type: " + tokens[1]);
}
tokens[2] = tokens[2].substring(0, tokens[2].length() - 1);
if (type != null) {
samplers.put(tokens[2], type);
}
}
}
}
}
glShaderSource(fragShaderId, fragSource);
glCompileShader(fragShaderId);
if (geometrySource != null) {
glShaderSource(geometryShaderId, geometrySource);
glCompileShader(geometryShaderId);
}
if (glGetShaderi(fragShaderId, GL_COMPILE_STATUS) == GL_FALSE) {
logger().error("Failed to compile shader program " + shaderName + " (fragment)");
String errorsSource = glGetShaderInfoLog(fragShaderId, 5000);
String[] errorsLines = errorsSource.split("\n");
String[] sourceLines = fragSource.toString().split("\n");
for (String line : errorsLines) {
logger().debug(line);
if (line.toLowerCase().startsWith("error: ")) {
String[] parsed = line.split(":");
if (parsed.length >= 3) {
try {
int lineNumber = Integer.parseInt(parsed[2]);
if (sourceLines.length > lineNumber) {
logger.debug("@line: " + lineNumber + ": " + sourceLines[lineNumber]);
}
} catch (Exception e) {
logger.debug(line);
}
}
}
}
return;
}
if (glGetShaderi(vertexShaderId, GL_COMPILE_STATUS) == GL_FALSE) {
logger().error("Failed to compile shader program " + shaderName + " (vertex)");
String errorsSource = glGetShaderInfoLog(vertexShaderId, 5000);
String[] errorsLines = errorsSource.split("\n");
String[] sourceLines = vertexSource.toString().split("\n");
for (String line : errorsLines) {
logger().debug(line);
if (line.toLowerCase().startsWith("error: ")) {
String[] parsed = line.split(":");
if (parsed.length >= 3) {
try {
int lineNumber = Integer.parseInt(parsed[2]);
if (sourceLines.length > lineNumber) {
logger.debug("@line: " + lineNumber + ": " + sourceLines[lineNumber]);
}
} catch (Exception e) {
logger.debug(line);
}
}
}
}
return;
}
if (geometrySource != null && glGetShaderi(geometryShaderId, GL_COMPILE_STATUS) == GL_FALSE) {
logger().error("Failed to compile shader program " + shaderName + " (geometry)");
String errorsSource = glGetShaderInfoLog(geometryShaderId, 5000);
String[] errorsLines = errorsSource.split("\n");
String[] sourceLines = geometrySource.toString().split("\n");
for (String line : errorsLines) {
logger().debug(line);
if (line.toLowerCase().startsWith("error: ")) {
String[] parsed = line.split(":");
if (parsed.length >= 3) {
try {
int lineNumber = Integer.parseInt(parsed[2]);
if (sourceLines.length > lineNumber) {
logger.debug("@line: " + lineNumber + ": " + sourceLines[lineNumber]);
}
} catch (Exception e) {
logger.debug(line);
}
}
}
}
return;
}
glAttachShader(shaderProgramId, vertexShaderId);
glAttachShader(shaderProgramId, fragShaderId);
if (geometrySource != null)
glAttachShader(shaderProgramId, geometryShaderId);
glLinkProgram(shaderProgramId);
if (glGetProgrami(shaderProgramId, GL_LINK_STATUS) == GL_FALSE) {
logger().error("Failed to link program " + shaderName + "");
String errorsSource = glGetProgramInfoLog(shaderProgramId, 5000);
String[] errorsLines = errorsSource.split("\n");
for (String line : errorsLines) {
logger().debug(line);
}
return;
}
needValidation = true;
loadedCorrectly = true;
}
use of io.xol.chunkstories.api.content.Asset in project chunkstories by Hugobros3.
the class VoxelTexturesStoreAndAtlaser method buildTextureAtlas.
public void buildTextureAtlas() {
try {
// Clear previous values
texMap.clear();
// colors.clear();
// Compute all sizes first.
int totalSurfacedNeeded = 0;
// File folder = new File("./res/voxels/textures/");
// Get all sizes :
List<VoxelTextureAtlased> voxelTexturesSortedBySize = new ArrayList<VoxelTextureAtlased>();
// First we want to iterate over every file to get an idea of how many textures (and of how many sizes) we are dealing
Iterator<AssetHierarchy> allFiles = content.modsManager().getAllUniqueEntries();
AssetHierarchy entry;
Asset f;
while (allFiles.hasNext()) {
entry = allFiles.next();
if (entry.getName().startsWith("./voxels/textures/")) {
String name = entry.getName().replace("./voxels/textures/", "");
if (name.contains("/"))
continue;
f = entry.topInstance();
if (f.getName().endsWith(".png")) {
String textureName = name.replace(".png", "");
// System.out.println("texName:"+textureName+" "+entry.getKey());
if (!texMap.containsKey(textureName)) {
VoxelTextureAtlased voxelTexture = new VoxelTextureAtlased(textureName, uniquesIds);
uniquesIds++;
voxelTexture.imageFileDimensions = getImageSize(f);
voxelTexturesSortedBySize.add(voxelTexture);
totalSurfacedNeeded += voxelTexture.imageFileDimensions * voxelTexture.imageFileDimensions;
}
}
}
}
// Sort them by size
Collections.sort(voxelTexturesSortedBySize, new Comparator<VoxelTextureAtlased>() {
@Override
public int compare(VoxelTextureAtlased a, VoxelTextureAtlased b) {
return Integer.compare(b.imageFileDimensions, a.imageFileDimensions);
}
});
for (VoxelTextureAtlased voxelTexture : voxelTexturesSortedBySize) {
// System.out.println(vt.imageFileDimensions);
texMap.put(voxelTexture.getName(), voxelTexture);
}
// Estimates the required texture atlas size by surface
int sizeRequired = 16;
for (int i = 4; i < 14; i++) {
int iSize = (int) Math.pow(2, i);
if (iSize * iSize >= totalSurfacedNeeded) {
sizeRequired = iSize;
break;
}
}
// ChunkStoriesLogger.getInstance().info("At least " + sizeRequired + " by " + sizeRequired + " for TextureAtlas (surfacedNeeded : " + totalSurfacedNeeded + ")");
// Delete previous atlases
File diffuseTextureFile = new File(GameDirectory.getGameFolderPath() + "/cache/tiles_merged_albedo.png");
if (diffuseTextureFile.exists())
diffuseTextureFile.delete();
File normalTextureFile = new File(GameDirectory.getGameFolderPath() + "/cache/tiles_merged_normal.png");
if (normalTextureFile.exists())
normalTextureFile.delete();
File materialTextureFile = new File(GameDirectory.getGameFolderPath() + "/cache/tiles_merged_material.png");
if (materialTextureFile.exists())
materialTextureFile.delete();
// Build the new one
boolean loadedOK = false;
while (// Security to prevend
!loadedOK && sizeRequired <= 8192) // HUGE-ASS textures
{
// We need this
BLOCK_ATLAS_SIZE = sizeRequired;
BLOCK_ATLAS_FACTOR = 32768 / BLOCK_ATLAS_SIZE;
loadedOK = true;
// Create boolean bitfield
boolean[][] used = new boolean[sizeRequired / 16][sizeRequired / 16];
diffuseTextureImage = null;
normalTextureImage = null;
materialTextureImage = null;
if (content.getContext() instanceof ClientInterface) {
diffuseTextureImage = new BufferedImage(sizeRequired, sizeRequired, Transparency.TRANSLUCENT);
normalTextureImage = new BufferedImage(sizeRequired, sizeRequired, Transparency.TRANSLUCENT);
materialTextureImage = new BufferedImage(sizeRequired, sizeRequired, Transparency.TRANSLUCENT);
logger.debug("This is a client so we'll make the texture atlas");
}
BufferedImage imageBuffer;
for (VoxelTextureAtlased vt : voxelTexturesSortedBySize) {
// Find a free spot on the atlas
boolean foundSpot = false;
int spotX = 0, spotY = 0;
for (int a = 0; (a < sizeRequired / 16 && !foundSpot); a++) for (int b = 0; (b < sizeRequired / 16 && !foundSpot); b++) {
if (// Unused
used[a][b] == false && a + vt.imageFileDimensions / 16 <= sizeRequired / 16 && b + vt.imageFileDimensions / 16 <= sizeRequired / 16) {
boolean usedAlready = false;
// Not pretty loops that do clamped space checks
for (int i = 0; (i < vt.imageFileDimensions / 16 && a + i < sizeRequired / 16); i++) for (int j = 0; (j < vt.imageFileDimensions / 16 && b + j < sizeRequired / 16); j++) if (// Well
used[a + i][b + j] == true)
// fuck
// it
usedAlready = true;
if (!usedAlready) {
spotX = a * 16;
spotY = b * 16;
vt.setAtlasS(spotX * BLOCK_ATLAS_FACTOR);
vt.setAtlasT(spotY * BLOCK_ATLAS_FACTOR);
vt.setAtlasOffset(vt.imageFileDimensions * BLOCK_ATLAS_FACTOR);
foundSpot = true;
for (int i = 0; (i < vt.imageFileDimensions / 16 && a + i < sizeRequired / 16); i++) for (int j = 0; (j < vt.imageFileDimensions / 16 && b + j < sizeRequired / 16); j++) used[a + i][b + j] = true;
}
}
}
if (!foundSpot) {
System.out.println("Failed to find a space to place the texture in. Retrying with a larger atlas.");
loadedOK = false;
break;
}
imageBuffer = ImageIO.read(content.modsManager().getAsset("./voxels/textures/" + vt.getName() + ".png").read());
// imageBuffer = ImageIO.read(GameContent.getTextureFileLocation());
float alphaTotal = 0;
int nonNullPixels = 0;
Vector3f color = new Vector3f();
for (int x = 0; x < vt.imageFileDimensions; x++) {
for (int y = 0; y < vt.imageFileDimensions; y++) {
int rgb = imageBuffer.getRGB(x, y);
if (diffuseTextureImage != null)
diffuseTextureImage.setRGB(spotX + x, spotY + y, rgb);
float alpha = ((rgb & 0xFF000000) >>> 24) / 255f;
// System.out.println("a:"+alpha);
alphaTotal += alpha;
if (alpha > 0)
nonNullPixels++;
float red = ((rgb & 0xFF0000) >> 16) / 255f * alpha;
float green = ((rgb & 0x00FF00) >> 8) / 255f * alpha;
float blue = (rgb & 0x0000FF) / 255f * alpha;
color.add(new Vector3f(red, green, blue));
// Vector3f.add(color, new Vector3f(red, green, blue), color);
}
}
color.mul(1f / alphaTotal);
if (nonNullPixels > 0)
alphaTotal /= nonNullPixels;
vt.setColor(new Vector4f(color.x(), color.y(), color.z(), alphaTotal));
// Don't bother if it's not a Client context
if (diffuseTextureImage == null)
continue;
// Do also the normal maps !
Asset normalMap = content.modsManager().getAsset("./voxels/textures/normal/" + vt.getName() + ".png");
if (normalMap == null)
normalMap = content.modsManager().getAsset("./voxels/textures/normal/notex.png");
imageBuffer = ImageIO.read(normalMap.read());
for (int x = 0; x < vt.imageFileDimensions; x++) {
for (int y = 0; y < vt.imageFileDimensions; y++) {
int rgb = imageBuffer.getRGB(x % imageBuffer.getWidth(), y % imageBuffer.getHeight());
normalTextureImage.setRGB(spotX + x, spotY + y, rgb);
}
}
// And the materials !
Asset materialMap = content.modsManager().getAsset("./voxels/textures/material/" + vt.getName() + ".png");
if (materialMap == null)
materialMap = content.modsManager().getAsset("./voxels/textures/material/notex.png");
imageBuffer = ImageIO.read(materialMap.read());
for (int x = 0; x < vt.imageFileDimensions; x++) {
for (int y = 0; y < vt.imageFileDimensions; y++) {
int rgb = imageBuffer.getRGB(x % imageBuffer.getWidth(), y % imageBuffer.getHeight());
materialTextureImage.setRGB(spotX + x, spotY + y, rgb);
}
}
}
if (loadedOK && diffuseTextureImage != null) {
// save it son
ImageIO.write(diffuseTextureImage, "PNG", diffuseTextureFile);
ImageIO.write(normalTextureImage, "PNG", normalTextureFile);
ImageIO.write(materialTextureImage, "PNG", materialTextureFile);
diffuseTexture = null;
normalTexture = null;
materialTexture = null;
} else
// It's too small, initial estimation was wrong !
sizeRequired *= 2;
}
// Read textures metadata
// TODO read all overrides in priority
readTexturesMeta(content.modsManager().getAsset("./voxels/textures/meta.txt"));
} catch (Exception e) {
e.printStackTrace();
}
}
Aggregations