From 65a3638e923fe5555e8fa72419dc9f371936c16d Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Fri, 31 Oct 2025 00:23:31 +0100 Subject: [PATCH 1/3] urcheon/action: add compile_ase and use it for mapmodels --- Urcheon/Action.py | 43 +++++++++++++++++++++++++++++++++++++++- Urcheon/MapCompiler.py | 8 ++++++-- profile/file/daemon.conf | 7 +++++++ profile/map/common.conf | 4 ++++ 4 files changed, 59 insertions(+), 3 deletions(-) diff --git a/Urcheon/Action.py b/Urcheon/Action.py index b4ac496..6f28946 100644 --- a/Urcheon/Action.py +++ b/Urcheon/Action.py @@ -177,6 +177,7 @@ def list(): # and navmesh generation CopyBsp, CompileBsp, + CompileAse, # perhaps one day MergeBsp will be run on a copied bsp # so it must be called after that MergeBsp, @@ -957,7 +958,9 @@ class DumbTransient(Action): def createTransientPath(self): build_path = self.getTargetPath() self.transient_path = tempfile.mkdtemp(suffix="_" + os.path.basename(build_path) + "_transient" + os.path.extsep + "dir") - self.transient_maps_path = os.path.join(self.transient_path, "maps") + logging.debug("transient path: " + self.transient_path) + + self.transient_maps_path = os.path.join(self.transient_path, os.path.dirname(self.file_path)) os.makedirs(self.transient_maps_path, exist_ok=True) def buildTransientPath(self, disabled_action_list=[]): @@ -1115,3 +1118,41 @@ def effective_run(self): def getFileNewName(self): return self.switchExtension("bsp") + + +class CompileAse(DumbTransient): + keyword = "compile_ase" + description = "compile to ase format" + + def effective_run(self): + source_path = self.getSourcePath() + build_path = self.getTargetPath() + bsp_path = self.getFileBspName() + self.createSubdirs() + + self.createTransientPath() + + Ui.laconic("Compiling to ase: " + self.file_path) + + # Do not copy the map source when building in the source directory as a prepare step. + if self.source_dir == self.build_dir: + stage_done = ["copy"] + else: + stage_done = [] + + map_compiler = MapCompiler.Compiler(self.source_tree, map_profile="ase") + map_compiler.compile(source_path, self.transient_maps_path, stage_done=stage_done) + + os.remove(os.path.join(self.transient_path, bsp_path)) + + self.buildTransientPath(disabled_action_list=["copy_bsp", "compile_bsp"]) + + self.setTimeStamp() + + return self.getProducedUnitList() + + def getFileBspName(self): + return self.switchExtension("bsp") + + def getFileNewName(self): + return self.switchExtension("ase") diff --git a/Urcheon/MapCompiler.py b/Urcheon/MapCompiler.py index 8a4942b..333cf1b 100644 --- a/Urcheon/MapCompiler.py +++ b/Urcheon/MapCompiler.py @@ -162,6 +162,7 @@ def readConfig(self, config_file_name, is_parent=False): "light": ["vis"], "minimap": ["vis"], "nav": ["vis"], + "convert": ["bsp"], } for profile_name in self.profile_dict.keys(): @@ -227,6 +228,7 @@ def __init__(self, source_tree, map_profile=None, is_parallel=True): def compile(self, map_path, build_prefix, stage_done=[]): self.map_path = map_path self.build_prefix = build_prefix + self.stage_done = stage_done stage_name = None stage_option_list = [] self.pakpath_list = [] @@ -365,7 +367,7 @@ def q3map2(self, option_list, tool_name="q3map2"): extended_option_list = [] # bsp stage is the one that calls -bsp, etc. - for stage in ["bsp", "vis", "light", "minimap", "nav"]: + for stage in ["bsp", "vis", "light", "minimap", "nav", "convert"]: if "-" + stage in option_list: stage_name = stage logging.debug("stage name: " + stage_name) @@ -383,6 +385,8 @@ def q3map2(self, option_list, tool_name="q3map2"): source_path = bsp_path elif "-minimap" in option_list: source_path = bsp_path + elif "-convert" in option_list: + source_path = bsp_path else: extended_option_list = ["-prtfile", self.prt_path, "-srffile", self.srf_path, "-bspfile", bsp_path] # TODO: define the name somewhere @@ -397,7 +401,7 @@ def q3map2(self, option_list, tool_name="q3map2"): Ui.error("command failed: '" + "' '".join(command_list) + "'") # keep map source - if "-bsp" in option_list and self.map_config.keep_source: + if "-bsp" in option_list and self.map_config.keep_source and "copy" not in self.stage_done: self.copy([]) diff --git a/profile/file/daemon.conf b/profile/file/daemon.conf index 61a2fe5..78e66bf 100644 --- a/profile/file/daemon.conf +++ b/profile/file/daemon.conf @@ -190,6 +190,13 @@ dir_ancestor_name = "maps" description = "Map" build = "compile_bsp" +[daemon_ase] +file_ext = "map" +dir_ancestor_name = "models" +description = "Map object" +prepare = "compile_ase" +build = "copy" + [daemon_bspdir_lump] dir_ancestor_name = "maps" dir_father_ext = ".bspdir" diff --git a/profile/map/common.conf b/profile/map/common.conf index f845dc8..ce8e1b6 100644 --- a/profile/map/common.conf +++ b/profile/map/common.conf @@ -10,3 +10,7 @@ game="${game}" [copy] copy = { tool="copy" } + +[ase] +bsp = { tool="q3map2", options="-bsp -meta -patchmeta" } +convert = { tool="q3map2", options="-convert -format ase" } From 01adc8647dd2e8c76bf157bb7d52999c4416460c Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Fri, 31 Oct 2025 13:41:04 +0100 Subject: [PATCH 2/3] urcheon/action: add compile_obj --- Urcheon/Action.py | 14 +++++++++++--- profile/map/common.conf | 4 ++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Urcheon/Action.py b/Urcheon/Action.py index 6f28946..5ce502a 100644 --- a/Urcheon/Action.py +++ b/Urcheon/Action.py @@ -178,6 +178,7 @@ def list(): CopyBsp, CompileBsp, CompileAse, + CompileObj, # perhaps one day MergeBsp will be run on a copied bsp # so it must be called after that MergeBsp, @@ -1123,6 +1124,7 @@ def getFileNewName(self): class CompileAse(DumbTransient): keyword = "compile_ase" description = "compile to ase format" + extension = "ase" def effective_run(self): source_path = self.getSourcePath() @@ -1132,7 +1134,7 @@ def effective_run(self): self.createTransientPath() - Ui.laconic("Compiling to ase: " + self.file_path) + Ui.laconic("Compiling to " + self.extension + ": " + self.file_path) # Do not copy the map source when building in the source directory as a prepare step. if self.source_dir == self.build_dir: @@ -1140,7 +1142,7 @@ def effective_run(self): else: stage_done = [] - map_compiler = MapCompiler.Compiler(self.source_tree, map_profile="ase") + map_compiler = MapCompiler.Compiler(self.source_tree, map_profile=self.extension) map_compiler.compile(source_path, self.transient_maps_path, stage_done=stage_done) os.remove(os.path.join(self.transient_path, bsp_path)) @@ -1155,4 +1157,10 @@ def getFileBspName(self): return self.switchExtension("bsp") def getFileNewName(self): - return self.switchExtension("ase") + return self.switchExtension(self.extension) + + +class CompileObj(CompileAse): + keyword = "compile_obj" + description = "compile to obj format" + extension = "obj" diff --git a/profile/map/common.conf b/profile/map/common.conf index ce8e1b6..294ca6f 100644 --- a/profile/map/common.conf +++ b/profile/map/common.conf @@ -14,3 +14,7 @@ copy = { tool="copy" } [ase] bsp = { tool="q3map2", options="-bsp -meta -patchmeta" } convert = { tool="q3map2", options="-convert -format ase" } + +[obj] +bsp = { tool="q3map2", options="-bsp -meta -patchmeta" } +convert = { tool="q3map2", options="-convert -format obj" } From 7cad9a880738ad24e6d06337326da2999df2204e Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Fri, 31 Oct 2025 17:22:16 +0100 Subject: [PATCH 3/3] urcheon/action: move compile_ase and compile_obj after iqm model and shader generation --- Urcheon/Action.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Urcheon/Action.py b/Urcheon/Action.py index 5ce502a..ab0a093 100644 --- a/Urcheon/Action.py +++ b/Urcheon/Action.py @@ -177,19 +177,9 @@ def list(): # and navmesh generation CopyBsp, CompileBsp, - CompileAse, - CompileObj, # perhaps one day MergeBsp will be run on a copied bsp # so it must be called after that MergeBsp, - # those are probably the slowest compression image - # formats we know - ConvertKtx, - ConvertDds, - ConvertCrn, - ConvertNormalCrn, - ConvertLosslessWebp, - ConvertLossyWebp, # sloth needs previews to be done before sloth # TODO: be sure Sloth is not called before # all previews are generated @@ -199,6 +189,18 @@ def list(): SlothRun, # usually quick CompileIqm, + # compiling a map model may require shaders and other models + # being generated first. + CompileAse, + CompileObj, + # those are probably the slowest compression image + # formats we know + ConvertKtx, + ConvertDds, + ConvertCrn, + ConvertNormalCrn, + ConvertLosslessWebp, + ConvertLossyWebp, # can take some time but not blocking ConvertVorbis, ConvertOpus,