package org.openrndr.extra.glslify import khttp.responses.Response import mu.KotlinLogging import java.io.ByteArrayInputStream import java.io.File import java.io.InputStream const val BASE_URL = "https://registry.npmjs.org" const val GLSLIFY_PATH = "src/main/resources/glslify" const val IMPORT_PATT = """#pragma\sglslify:\s*(.*)\s=\s*require\('([\w\-]+)'\)""" const val EXPORT_PATT = """#pragma\sglslify:\s*export\(([\w\-]+)\)""" internal val shaderExtensions = arrayOf("glsl", "frag") data class GlslifyImport( val functionName: String, val pkgName: String, var exists: Boolean ) private val logger = KotlinLogging.logger {} fun glslify(module: String, renameFunctionTo: String? = null): String { val (moduleName: String, shaderPath: String) = parseModule(module) val moduleDir = File("$GLSLIFY_PATH/$moduleName") val packageUrl = getPackageUrl(moduleName) if (packageUrl.isNullOrEmpty()) { throw error("[glslify] $moduleName not found") } if (!moduleDir.exists()) { try { moduleDir.mkdirs() val response: Response = khttp.get(packageUrl, stream = true) val tarStream: InputStream = ByteArrayInputStream(response.content) extractGzipStream(tarStream, moduleDir) moveFilesUp(moduleDir) File("$moduleDir/package").deleteRecursively() logger.trace("[glslify] $moduleName downloaded") } catch (ex: Exception) { logger.error(ex) { "[glslify]: There was an error getting $moduleName" } return "" } } else { logger.trace("[glslify] $moduleName already exists.. Skipping download") } val shaderFile: File try { shaderFile = shaderExtensions.map { File("$GLSLIFY_PATH/$moduleName/$shaderPath.$it") }.first { it.exists() } } catch (ex: NoSuchElementException) { logger.trace("[glslify] $moduleName: $shaderPath doesn't lead to any shader file") return "" } val shader = mutableListOf() val imports = mutableListOf() var exportName: String? = null shaderFile.useLines { sequence -> for (line in sequence.iterator()) { if (line.contains("#pragma")) { Regex(IMPORT_PATT).find(line)?.let { if (it.groupValues.size > 1) { val importExists = File("$GLSLIFY_PATH/${it.groupValues[2]}").exists() imports.add(GlslifyImport(it.groupValues[1], it.groupValues[2], importExists)) } } Regex(EXPORT_PATT).find(line)?.let { if (it.groupValues.size > 1) { exportName = it.groupValues[1] } } } else { shader.add(line) } } } val missingImports = imports.filter { !it.exists } if (missingImports.isNotEmpty()) { missingImports.forEach { logger.info("Missing package: ${it.pkgName} - Import name: ${it.functionName}") } throw error("[glslify] Please declare the missing imports") } var shaderString = shader.joinToString("\n") if (renameFunctionTo != null && exportName != null) { shaderString = shaderString.replace( exportName!!, renameFunctionTo) } return shaderString }