1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

Blender: Translate UV-Map on [0,1]²

Discussion in 'Civ4 - Creation & Customization' started by Ramkhamhaeng, Apr 23, 2020.

  1. Ramkhamhaeng

    Ramkhamhaeng Warlord

    Feb 24, 2014
    Hello guys,

    I did not use the Blender version 2.49b, but a newer one. So I can not import Nifs directly in blender and I'm using the import/export-options from Nifskope to edit a mesh in Blender and bring it back to Nifskope later.
    The tool chain looks like:
    Nifskope -> Export as *.obj-file -> Blender -> Export as *3ds-file -> Nifskope

    Unfortunately at some stage the UV-Map of my mesh will be changed from [0,1]²-values on [0,1]x[-1,0]. In most cases the change is not visible for the user, but if you apply e.g. a texture-rotation around the its center it made a big difference!

    Does anybody know how I could shift the v-coordinate back onto the [0,1]-range? (Editing by hand in Nifskope is no option ;))

  2. MightyToad

    MightyToad Warlord

    Sep 11, 2015
    You're going through enormous trouble to avoid 2.49b. You could still use newer versions of blender, and only use 2.49b to import/export nifs.

    Anyway, I tried your nif/obj/3ds method and got the reversed UV coordinates as well. No clue why that happens, or how it works at all. I didn't know UV coordinates could be "off the grid" so to speak.

    The coordinates are normal when I exported as "obj" instead of 3ds. So, you could try that. In both cases it was buggy trying to swap the object into a nif. Said the two versions were incompatible.
  3. Ramkhamhaeng

    Ramkhamhaeng Warlord

    Feb 24, 2014
    Thank for your analysis and confirmation.
    If I made more stuff I probably will install the old blender version.

    For this issue I now wrote a tiny script to change the map in the nif file. For my little feature, it will be good enough.
    I add it here just for reference.
    Spoiler :

    Use the 'file-offset' menu point in Nifskope to get find out the correct offset for your UVMaps.
    # -*- coding: utf-8 -*-
    # Helper script to reset coordinates in UVMap.
    # Position of UVMap is hardcoded in this script, but not evaluated!
    # Dependencies: pip3 install bitstring
    import sys
    import math
    import re
    import bitstring as bs
    keywords = [b'MyMesh1']  # NiTriStrips node name
    NiTriShapeData_NumVertices = NiTriStrips_pos + (19341 - 18535) * 8
    NiTriShapeData_UVMap_pos = NiTriStrips_pos + (21310 - 18535) * 8
    class WriteDummy:
        # Used if no output file is given.
        def write(self, b):
        def close(self):
    def update(filename, filename_out=""):
        s = bs.BitStream(filename=filename)
        if len(filename_out) > 0:
            fout = open(filename_out, "wb")
            fout = WriteDummy()
        n_hits = 0
        for keyword in keywords:
            start = 0
            s.pos = 0
            hit = s.find(keyword, start=start)
            while hit:
                n_hits += 1
                # Read NiTriStrips name
                NiTriStrips_pos = hit[0]
                # 1. Len of UVMap
                # Number of vertices stored in chiled NiTriShapeData.
                # Note that offset of this child node differs. Hard coded offset
                # for my nif-Model
                s.pos = NiTriShapeData_NumVertices
                UVMap_len = s.readlist('intle:32')[0]
                # Manual definition
                # UVMap_len = int((21958-21310) / ( 2 * 4 )) # (OffsetNextNode - OffsetUVMap) / 2 * len(float)
                print("Len of UVMap: {}".format(UVMap_len))
                # 2. UVMap
                # Position of first u value of UVMap. (Also hard coded...)
                s.pos = NiTriShapeData_UVMap_pos
                print("Byte-Position of UVMap: {}".format(s.pos/8)) # Ok
                if True:
                    # Check position
                    uvmap = s.readlist('{}*floatle:32'.format(UVMap_len * 2))
                    print("Old values: ")
                    print("{}\n{}\n[…]\n{}\n{}".format(uvmap[0:2], uvmap[2:4], uvmap[-4:-2], uvmap[-2:]))
                    # Increment v value, if negative
                    vmap = uvmap[1::2]
                    vmap_inc = [ v if v >= 0.0 else v + 1.0 for v in vmap]
                    uvmap[1::2] = vmap_inc
                    print("New values:")
                    print("{}\n{}\n[…]\n{}\n{}".format(uvmap[0:2], uvmap[2:4], uvmap[-4:-2], uvmap[-2:]))
                    # Reset position for write
                    s.pos -= 32 * UVMap_len * 2
                    print("Pos? {}".format(s.pos/8))  # Ok
                    write_keys(s, uvmap, 32)
                # Prepare next loop
                start = s.pos
                hit = s.find(keyword, start=start)
        print("Number of changed uvmaps: {}".format(n_hits))
        # Save result
    def write_keys(stream_out, new_values, dist=8*4):
        start_pos = stream_out.pos
        for val in new_values[:1]:
            # print("Write {}".format(val))
            replace_float(stream_out.pos, stream_out, val, True)
        for val in new_values[1:]:
            # print("Write {}".format(val))
            stream_out.pos += dist  # Move to next key position
            replace_float(stream_out.pos, stream_out, val, True)
        stream_out.pos = start_pos
    def replace_float(pos_out, stream_out, float_in, reset_pos=False):
        x = bs.Bits(length=32, floatle=float_in)
        replace(pos_out, stream_out, x, reset_pos)
    def replace(pos_out, stream_out, bits_in, reset_pos=False):
        if reset_pos:
            backup_pos = stream_out.pos
        stream_out.overwrite(bits_in, pos=pos_out)
        if reset_pos:
            stream_out.pos = backup_pos
    if __name__ == "__main__":
        args = dict(zip(range(len(sys.argv)), sys.argv))
        update(args.get(1, ""), args.get(2, ""))
  4. Ramkhamhaeng

    Ramkhamhaeng Warlord

    Feb 24, 2014
    I avoid this problem by copy the NiTriShapeData-Node from the blender import into an existing NiTriStripe-Node with proper texturing, etc.
  5. MightyToad

    MightyToad Warlord

    Sep 11, 2015
    You wrote a script to change all the UV coordinates? Damn dude. You know, if you redirected this determination you could do some cool stuff. Like finding a way to automatically set the starting position and rotation of bones in the .kf files.

Share This Page