Custom Portraits

Show off your mod creations or just a work in progress.
Post Reply
User avatar
King of Worms
Posts: 2462
Joined: Mon Oct 17, 2016 11:18 pm
Location: Scourg Barrow (CZ)
Contact:

Re: Custom Portraits

Post by King of Worms » Sat Oct 19, 2019 8:43 pm

I still like the new armor presented :)

Firebrand
Posts: 237
Joined: Thu Jul 18, 2019 6:07 pm

Re: Custom Portraits

Post by Firebrand » Sun Oct 20, 2019 10:30 am

I like it very much too 8-)
Only I was worrying that such breastplate offer almost no protection for the belly, so it looks like not very useful for the wearer :mrgreen: ;)

Sitrus
Posts: 40
Joined: Sat Sep 21, 2019 5:46 pm

Re: Custom Portraits

Post by Sitrus » Sun Oct 20, 2019 1:16 pm

Firebrand wrote:
Sun Oct 20, 2019 10:30 am
I like it very much too 8-)
Only I was worrying that such breastplate offer almost no protection for the belly, so it looks like not very useful for the wearer :mrgreen: ;)
Most of Daggerfall armors rely on "boobplate", which I personally detest, but since the parts are cutouts, to make everything compatible, you have to make the cutout point as close as possible to where the ribs end, unfortunately. (As can be seen on this armor)

First attempt at a Dwarven armor. I'll get around to fixing the left arm here sometime, when I have something for all armor tiers :P
Daggerfallprogress20.png
Daggerfallprogress20.png (545.73 KiB) Viewed 493 times

Sitrus
Posts: 40
Joined: Sat Sep 21, 2019 5:46 pm

Re: Custom Portraits

Post by Sitrus » Fri Oct 25, 2019 6:51 pm

I am still working on this, I'll just be updating less as there's not much to show at this stage. I'm mostly fixing items and trying to come up with a solution on how to automate the recolouring, because I am a lazy fuck.

Right now I'm trying to copy the colour scheme from a source image, attach it to the target and then copy the alpha channel over from the original image and parts that I don't want recoloured from another source overlaid to the generated image. I have done some experiments, but they color schemes are not turning out as how I want them.

If anyone wants the script (in python) I'm using for generating the xml files for items or the generic items for ones that do not have recolors, here you go.
Spoiler!

Code: Select all

import shutil

def copy_dfucln(directory, old_file_name,copyxml = True, newdirectory = None):
    #colors used in daggerfall
        items = ['Red', 'Blue','Green','Orange','Black', 'Grey', 'Aquamarine', 'White', 'LightBrown', 'Purple', 'DarkBrown']
        
        if newdirectory == None:
            newdirectory = directory
            
        newfilename= old_file_name.replace('.png', '')
        for i in items:
            path = str( directory + old_file_name)
            nf = str(newdirectory + newfilename + '_' + i + ".png")
            shutil.copyfile(path, nf)
            #tries to replace the xml file
            if copyxml is True:
                xmlpath = str( directory + newfilename + '.xml')
                nfxl =  str(newdirectory + newfilename + '_' + i + ".xml")
                try:
                    shutil.copyfile(xmlpath, nfxl)
                except FileNotFoundError:
                    copyxml = False

def copy_xmldf(directory, old_file_name, newdirectory = None):
    items = ['Red', 'Blue','green','orange','Grey' ,'Black','Aquamarine', 'White', 'LightBrown', 'Purple', 'DarkBrown']
    if newdirectory == None:
            newdirectory = directory
    newfilename= old_file_name.replace('.xml', '')
    #tries to replace the xml file
    for i in items:
        xmlpath = str( directory + newfilename + '.xml')
        nfxl =  str(newdirectory + newfilename + '_' + i + ".xml")
        try:
            shutil.copyfile(xmlpath, nfxl)
        except FileNotFoundError:
            print('No file' + xmlpath + ' found.')

Usage, i.e.

Code: Select all

copy_dfucln("DaggerfallUnity_Data/StreamingAssets/Textures/" ,  "236_4-0.png")

copy_xmldf("DaggerfallUnity_Data/StreamingAssets/Textures/" , '236_9-0.xml')
Last edited by Sitrus on Sat Oct 26, 2019 2:37 pm, edited 1 time in total.

User avatar
King of Worms
Posts: 2462
Joined: Mon Oct 17, 2016 11:18 pm
Location: Scourg Barrow (CZ)
Contact:

Re: Custom Portraits

Post by King of Worms » Fri Oct 25, 2019 7:18 pm

Hi! Thanks for the update - The recoloring can be painful, I hope you can find some easy way thru it! I cant code jack so all I can do is pray :)

Sitrus
Posts: 40
Joined: Sat Sep 21, 2019 5:46 pm

Re: Custom Portraits

Post by Sitrus » Sat Oct 26, 2019 12:33 pm

and I solved the recoloring issue, it's not perfect but for now this method works. :lol: I'll fiddle around more as I get to it


Here's the script I wrote for it, if someone else needs it. Usage only recommended with the blue items, as the coloring is hard coded. The overlaying works as follows, have an image of exact same proportions with the areas you do not want to recolor and rest as alpha. Have it in the same folder as the file you're recoloring with the suffix "_overlay.png", so if you have "236_84-0.png", for overlay you need "236_84-0_overlay.png".
Do not feed in images with the color suffix, unless you want to manually rename the items afterwards. It will save those items with the suffixes.
Spoiler!

Code: Select all

from PIL import Image
from matplotlib import pyplot as plt


def recolor(target, color = "Black", overlayimage = True):   
    items = ['Red', 'Blue','Green','Orange','Grey', 'Black' , 'Aquamarine', 'White', 'LightBrown', 'Purple', 'DarkBrown']
    newimages = []
    with Image.open(target) as img:
        width, height = img.size
        img.load()
    print(width)
    # Process every pixel
    for item in items:
        print("On item " + item)
        newitem = img.copy()
        if item == "Blue": 
            newimages.append(newitem)
            continue
        for x in range(width):
           for y in range(height):
               current_color = img.getpixel( (x,y) )
               if current_color == (255, 255, 255, 0):
                   pass
               else:
                   current_color = list(current_color)
                   if item == "Purple":  new_color = (int(current_color[1]*0.85),int(current_color[0] * 0.55), int(current_color[2] *0.85), current_color[3])
                   elif item == "Grey": new_color = (current_color[1],current_color[1], current_color[1], current_color[3])
                   elif item == "White": new_color = (current_color[2],current_color[2], current_color[2], current_color[3])
                   elif item == "Orange": new_color = (int(current_color[2]* 1.15),int(current_color[1]* 0.8), int(current_color[0]* 0.55), current_color[3])
                   elif item == "Red": new_color = (int(current_color[2]* 0.75),int(current_color[1]* 0.1), int(current_color[0]* 0.1), current_color[3])
                   elif item == "Aquamarine": new_color = (int(current_color[0]* 0.5),int(current_color[1]* 1.2), int(current_color[2]* 1.05), current_color[3])
                   elif item == "Green": new_color = (int(current_color[0]* 0.3),int(current_color[1]* 0.7), int(current_color[2]* 0.3), current_color[3])
                   elif item == "Yellow": new_color = (int(current_color[2]* 1.1),int(current_color[1]* 1.3), int(current_color[0]* 0.8), current_color[3])
                   elif item == "DarkBrown": new_color = (int(current_color[0]* 0.7),int(current_color[0]* 0.4), int(current_color[2]* 0.1), current_color[3])
                   elif item == "LightBrown":new_color = (int(current_color[0]* 1.15),int(current_color[0]* 0.7), int(current_color[2]* 0.25), current_color[3])
                   elif item == "Black": new_color = (int(current_color[0] *0.15),int(current_color[1]* 0.15), int(current_color[2]* 0.15), current_color[3])
                   else:
                       pass
                   newitem.putpixel( (x,y), new_color)
        newimages.append(newitem)      
    savename = target.replace('.png', '')
    for i, item in enumerate(newimages):
        if overlayimage is not None:
            overlayimage = Image.open(savename + '_overlay.png')
            item = Image.alpha_composite(item, overlayimage)
        plt.imshow(item)
        savn = savename +'_'+  items[i] + '.png'
        item.save(savn)
        plt.show()
    return newimages

usage

Code: Select all

recolor("DaggerfallUnity_Data/StreamingAssets/Textures/236_84-0.png")
Attachments
Daggerfallprogress21.png
Daggerfallprogress21.png (587.78 KiB) Viewed 396 times

User avatar
King of Worms
Posts: 2462
Joined: Mon Oct 17, 2016 11:18 pm
Location: Scourg Barrow (CZ)
Contact:

Re: Custom Portraits

Post by King of Worms » Sat Oct 26, 2019 6:12 pm

Thats impressive :)

User avatar
Hazelnut
Posts: 1694
Joined: Sat Aug 26, 2017 2:46 pm
Contact:

Re: Custom Portraits

Post by Hazelnut » Sat Oct 26, 2019 8:25 pm

I have absolutely no conception about what you're doing here, but it looks great.

One thing that occurs to me is that if computing the recolouring could be done in-game to avoid the disk/memory overhead of replacementImages * numColours? Just curious whether that may be feasible.

User avatar
King of Worms
Posts: 2462
Joined: Mon Oct 17, 2016 11:18 pm
Location: Scourg Barrow (CZ)
Contact:

Re: Custom Portraits

Post by King of Worms » Sat Oct 26, 2019 9:00 pm

Hazelnut wrote:
Sat Oct 26, 2019 8:25 pm
I have absolutely no conception about what you're doing here, but it looks great.

One thing that occurs to me is that if computing the recolouring could be done in-game to avoid the disk/memory overhead of replacementImages * numColours? Just curious whether that may be feasible.
That would be a question for The Lacus, but he decided to do it this way, so I guess there were reasons. I think the ingame recoloring works with the 256color palette and changing it to a full color we use nowadays is the problem here. Changing the upscaled images to 256colors does not help. There are only specific shades of each color in Daggerfall clothing. So I guess there are 16 or 32 specific shades of the base blue color and than the same amount of shades corresponding to the different dyes like red, green etc.. and these shades need to be exact match for this ingame recoloring (palette swap) system to work.

PS: judging by the tests Ive run, using your add_all commands show that the amount of these images is not a problem for the game, even if you have all these items in the inventory at the same time, which will not happen in a regular gameplay.

Sitrus
Posts: 40
Joined: Sat Sep 21, 2019 5:46 pm

Re: Custom Portraits

Post by Sitrus » Sun Oct 27, 2019 2:34 pm

Hazelnut wrote:
Sat Oct 26, 2019 8:25 pm
I have absolutely no conception about what you're doing here, but it looks great.

One thing that occurs to me is that if computing the recolouring could be done in-game to avoid the disk/memory overhead of replacementImages * numColours? Just curious whether that may be feasible.
As King of Worms said, the Unity engine seems to be able to handle all items in the inventory at once just fine. Besides that, as I do the recoloring pixel by pixel to retain all information, instead of a filter; it would be extremely heavy to do constantly at the run time.

Post Reply