-
Notifications
You must be signed in to change notification settings - Fork 24
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
Implementing CommonMCOBJ Support #555
Conversation
Sometimes an exception may not be so clear cut, so let's allow an optional message to return
In this commit, we do the following: - Convert more functions to use MCprepError - Remove Blender Internal and 2.7X specific code
This refactors the following functions to use the new error type: - bAppendLink - open_program In addition, the function bAppendLink has had all 2.7X related code removed, although an argument related to 2.7X layers is kept to avoid widescale breakage
This file is not used at all, and is poorly written. If we need to revisit this idea, we can use Vivy as reference
Great to have this support! Small but important request from my side: can you implement at least one parsing test? I suggest you do so by adding manually created, simple obj file with and without a header which is saved into the testing directories of the addon (so it's not shipped). This is the number 1 way we can ensure it continues to function as intended and to spec, though I'm ok if the test isn't exactly exhaustive over all scenarios. Let me know if you have trouble running the tests, they should be nicely cross platform by now though. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just leaving some initial comments
MCprep_addon/materials/prep.py
Outdated
# TODO: Rework exporter tracking | ||
# addon_prefs = util.get_user_preferences(context) | ||
# self.track_exporter = addon_prefs.MCprep_exporter_type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the tracker is still worthwhile, as it helps us understand the ecosystem of what's being used and how to best support everyone.
Main question is: do we have the header auto assign a value of e.g. jmc2obj to the exporter type, or should we add two more types which are jmc2obj_cmobj (or something like that) and the same for Mineways? I'm a bit torn. It could be quite nice to know how well adopted the common format is, to know what feature work and whether it applies to a good % of the MCprep userbase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could use the output of get_exporter
(something like get_exporter(context).value
to retrive the underlying string from the enum, assuming it's not None
) as it would be a 1-1 mapping, although we'd then have to concert the Mineways and jmc2OBJ references in the tracker to mineways-c
(Mineways before CommonMCOBJ) and jmc2obj-c
(jmc2OBJ before CommonMCOBJ). It would also allow tracking the use of unknown exporters as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking abut this further, I think we should use the string in the exporter
key of the header. If we use the enum, we won't be able to know if a new OBJ exporter is gaining traction (and thus could be better supported in MCprep), but the exporter key would make it more clear what specific exporters are being used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remind me, what would be the values we potentially get in the exporter? that's the mineways-defined or jmc2obj-defined names for themselves, right?
I think having something like jmc2obj_cmobj would be fantastic or at least somehow_ knowing which imports comes form the commonmcobj spec in the analytics data. A canned suffix added to the exporter
string could do. Knowing the depth of adoption of the new standard helps understand how many people can make use of a tool we build that depends on that (if e.g. less than 10% of obj imports are using the spec, that could be good rationale to not yet spend ages working on the bridge tool just yet for instance).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remind me, what would be the values we potentially get in the exporter? that's the mineways-defined or jmc2obj-defined names for themselves, right?
It would be the name of the exporter, so jmc2obj
, mineways
, helpicantexitvim
, etc. For the pre-CommonMCOBJ exporters, we could put jmc2obj-classic
and mineways-classic
, and convert our existing data (a cmobj
suffix works, but I'm not the biggest fan of the idea)
MCprep_addon/world_tools.py
Outdated
elif header in tiles: # If the OBJ uses individual textures | ||
obj_header.set_seperated() | ||
return | ||
cmc_header = parse_header(obj_fd) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also based on the implementation above, wouldn't the readlines() line within parse_header clear out the remainder of readlines below? Would be good to double check all tests still pass as you're developing here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out this seems to be the source of the problem, indeed the parse_header above if commented out makes more of the tests pass. Which also means we really do need to add also tests for the common obj part too, otherwise we're not really ensuring this continues to work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Through investigating this, I have also come to realize the existing commonmcobj parser actually always parses the entire obj file, line by line, in search of the header. This could be a massive slowdown for large obj files.
FYI I'm looking into the best option to short circuit @StandingPadAnimations, most likely if we haven't seen the anchor COMMON_MC_OBJ_START
within the first 100 lines of the header, then we're probably safe to say it's safe to exit early. Started this diff locally, will need to verify against actual Mineways and jmc2obj example commonmcOBJ exports when I get a chance:
# Read in the header
- for l in f:
- tl = " ".join(l.rstrip().split())
- if tl == "# COMMON_MC_OBJ_START":
+ lines_read = 0
+ for _l in f:
+ tl = " ".join(_l.rstrip().split())
+ lines_read += 1
+ if lines_read > 100 and tl and not tl.startswith("#"):
+ break # no need to parse further than the true header area
elif tl == "# COMMON_MC_OBJ_START":
header.append(tl)
found_header = True
continue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CommonMCOBJ allows for headers to be anywhere in the file technically, either in the beginning or the end. That said, we could check for both 100 lines in the beginning and 100 lines at the end, and allow it to be configurable for users
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I briefly looked at efficiently reading the last N lines of a file without actually iterating over all of them. It's a little complicated. The other more practical consideration is that both jmc2obj and Mineways, as I've just verified, put commonobj towards the top. Currently in both exports I've done, the header starts ~line 55, and for jmc2obj it's at the very start. So, Seems like a buffer of the first 100 should be safe. I might time-test the difference later to see how much it would end up saving
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we do implement a 100 line limit, then we should also allow the user to be able to configure it somewhere. That way, if an OBJ that is a CommonMCOBJ export doesn't put it in the first 100 lines, users wouldn't have to go down into the Python code and modify the limit.
I think a good way to test would be to have an OBJ file where the header is at the end (which should be trivial since OBJ is a text format) and benchmark the time difference in MCprep. Not only would it give us insights into whether or not it makes a practical difference, it would also test to see if any unexpected issues occur
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be in favor of marking that a TODO if you're ok way it, to keep parsing simple and fast for now (otherwise it'll be seen as a regression).
Also spoiler I will not finish this tonight, I had to update and create a test world for v1.21 of MC and do exports for both jmc2obj and mineways. While that is working, it perhaps has revealed some issues with the mapping script which I am looking into still along with setting up tests to make sure parsing is working against real commonmcobj world exports.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me
@TheDuckCow by the way, should we implement the "legacy" flag for OBJ imports now and have it enabled by default (then disable it later on) or should we wait until MCprep 4.0 (which if I recall correctly will have UI refactorimg anyway) |
(copied from commonmcobj_parser.py) Normally, I wouldn't do dual licensing, but in this case, it makes sense as it would allow developers to reuse this parser for their own uses under more permissive terms. This doesn't change anything related to MCprep, which is GPL, as BSD 3-Clause is compatible with GPL. The only part that might conflict is Clause 3, but it could be argued that one can't do that under GPL anyway, or any license for that matter, and that Clause 3 is just a reminder to developers.
Alright, normally I'm not a fan of dual licensing, but I've decided to put the CommonMCOBJ parser under the more permissive BSD 3-Clause license (compatible with GPL), mainly to make it easier for developers to reuse in their own projects without worrying about GPL. This should be fine as the parser doesn't interact with BPY and BSD 3-Clause is compatible with GPL. If wanted, I'm fine with changing it back to GPL, but I'd rather have it under a more permissive license (especially as it doesn't depend on Blender and could be reused anywhere) EDIT: only the CommonMCOBJ parser is under a separate license (as it's really an independent component), the rest of MCprep is still under GPL |
Yup I'm supportive of this, thanks for thinking of it. Will review when I'm back home later On Mar 30, 2024 12:48 PM, Mahid Sheikh ***@***.***> wrote:
Alright, normally I'm not a fan of dual licensing, but I've decided to put the CommonMCOBJ parser under the more permissive BSD 3-Clause license (compatible with GPL), mainly to make it easier for developers to reuse in their own projects without worrying about GPL. This should be fine as the parser doesn't interact with BPY and BSD 3-Clause is compatible with GPL.
If wanted, I'm fine with changing it back to GPL, but I'd rather have it under a more permissive license (especially as it doesn't depend on Blender and could be reused anywhere)
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: ***@***.***>
|
Ran the tests locally on my machine (after updating this branch with
This confirms that now, tests are reproducible (at least on macOS, GitHub Actions with the Ubuntu runner, and Arch Linux on bare metal) |
One of the errors I'm getting locally is the following:
Are we missing some files in EDIT: just realized this might be intentional |
Signed-off-by: Mahid Sheikh <mahid@standingpad.org>
Alright resolved With some fixes to the test, my results look as such:
|
Signed-off-by: Mahid Sheikh <mahid@standingpad.org>
Got
I made an error when adapting textureswap to the new system, where it wouldn't show if diff --git a/MCprep_addon/materials/prep.py b/MCprep_addon/materials/prep.py
index 260942d..f2f9e6d 100644
--- a/MCprep_addon/materials/prep.py
+++ b/MCprep_addon/materials/prep.py
@@ -423,7 +423,7 @@ class MCPREP_OT_swap_texture_pack(
@classmethod
def poll(cls, context):
- if world_tools.get_exporter(context) != world_tools.WorldExporter.Unknown:
+ if world_tools.get_exporter(context) is not None:
return util.is_atlas_export(context)
return False |
Signed-off-by: Mahid Sheikh <mahid@standingpad.org>
Pushed my fixes for 2 of the tests, now we should have only one failing test remaining |
Signed-off-by: Mahid Sheikh <mahid@standingpad.org>
Signed-off-by: Mahid Sheikh <mahid@standingpad.org>
By the way, how would one go about fixing the mappings? I'm assuming we have an automated script that just needs to be modified, but I'm not sure where to start with that |
I'll revert the changes related to |
Signed-off-by: Mahid Sheikh <mahid@standingpad.org>
Making progress on the mapping issue - it's because, sometime since the version of jmc2obj I used to export our test world, exports started to be prefixed with "minecraft_block-" which was throwing off my test code for checking mappings. Thing is I do check for that already in code so could be the test wasn't comprehensive enough, I'm checking further to see if there's a common prefix-stripping function we're using in code, so that this mapping check file works equally. Also we had an arbitrary threshold of 30 keys not mapped to canonical texture names - I'm updating that threshold to be 40, since these pretty much seem reasonable to not be mapped (in that they are kind of special textures, not typical blocks). Point was never to have 100% coverage, but just a sanity check to help make sure we have mostly overlapping mappings.
The separation here could actually be useful as these are mostly special entities which we might not want to accidentally overlap with block textures, something right now we don't have great control over. But, nothing to block this PR. |
Ok I think this 'otta wrap this branch up! All tests are passing 🎉 but, there are a couple outstanding issues worth addressing before the release:
I think all of these should be handled in their own, bespoke specific branches so we don't get hung up on any one individually in case we can't easily resolve. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approving my own work from my side lol but let me know @StandingPadAnimations if you're good with the plan to merge this now, or if there's anything outstanding (related to CommonMCOBJ specifically)
Signed-off-by: Mahid Sheikh <mahid@standingpad.org>
Merging now, I'll create a new RC release for this |
This implements parsing and handling for CommonMCOBJ headers in OBJs, and reworks exporter checking code to be more exporter agnostic.
CommonMCOBJ is a new exporter standard that Mineways and jmc2OBJ are currently working on supporting. CommonMCOBJ provides more information for MCprep to utilize for OBJs, and is beneficial overall in the long run.
When an OBJ with a CommonMCOBJ header is imported, MCprep will parse the header and add all parsed options as custom properties to the OBJ. In addition, as the plan is to phase out global exporter options in MCprep 4.0, OBJs without the CommonMCOBJ header will gain an additional
MCPREP_OBJ_EXPORTER
property, for backwards compatibility in MCprep 4.0. OBJs without either the CommonMCOBJ properties or theMCPREP_OBJ_EXPORTER
will fall back to the globally set exporter (though globally set exporters will become a feature behind a legacy flag in MCprep 4.0)When it comes to exporters, CommonMCOBJ is exporter agnostic, and provides a key for the exporter name. To handle that, all exporter checks in MCprep will now be done with an enum rather than pure strings. The options in the enum are:
Mineways
andJmc2OBJ
)Cmc2OBJ
(which for the most part is treated as unsupported, but exists as a distinct option for testing purposes)ClassicMW
(Mineways before CommonMCOBJ) andClassicJmc
(jmc2OBJ before CommonMCOBJ), which uses either the globally set exporter in the N panel or theMCPREP_OBJ_EXPORTER
attribute on the active objectUnknown
for all other exportersEDIT 30/03/2024:
commonmcobj_parser.py
is, unlike the rest of MCprep, licensed under the BSD 3-Clause license to allow developers to reuse without worrying about GPL. As such, the README has been updated to account for that, andLICENSE-3RD-PARTY.txt
has also been added.Closes #553