Jump to content
How to Install Plugins ×

NormalMapPlus (CodeLab Implementation)


MJW

Recommended Posts

This is a CodeLab port of @harold's NormalMapPlus plugin. It's functionally identical, except for some fixes, the slider controls having an extra decimal place (due to the way CodeLab works), and the addition of a brief Help menu. Though the code is functionally the same, it was somewhat rewritten. It's in the Stylize submenu.

 

The fixes are:

1) The ROI boundary pixels are handled correctly.

2) The normals are computed at the correct positions, rather than being shifted one pixel left.

3) The JIT compiler optimization crash doesn't occur. (The crash is almost certainly not due to a bug in the original plugin code, and will probably be fixed in the JIT compiler soon.)

 

Here is the DLL (Version 1.4): NormalMapPlus.zip

 

Here is the code:

Spoiler

// Name: NormalMapPlus
// Submenu: Stylize
// Author: harold, Simon Brown, MJW
// Title: NormalMapPlus
// Version: 1.4
// Desc: CodeLab port of NormalMapPlus
// Keywords: normal map
// URL: https://forums.getpaint.net/index.php?/topic/17010-normalmapplus-v10/
// Help:
#region UICode
DoubleSliderControl Amount1 = 0.3; // [0,1] X
DoubleSliderControl Amount2 = 0.5; // [0,1] Y
DoubleSliderControl Amount3 = 0.11; // [0,1] Z
#endregion

void Render(Surface dst, Surface src, Rectangle rect)
{
    // Delete any of these lines you don't need
    Rectangle selection = EnvironmentParameters.GetSelection(src.Bounds).GetBoundsInt();
    int top = rect.Top, bottom = rect.Bottom, left = rect.Left, right = rect.Right;
    float lightX = (float)Amount1;
    float lightY = (float)Amount2;
    float lightZ = (float)Amount3;
    Float4 lightDir = new Float4(lightX, lightY, lightZ, 0.0f);

    if (IsCancelRequested) return;

    // Handle the boundary conditions.   
    int prevY = (top == 0) ? top : top - 1;
    int leftPrevX = (left == 0) ? left : left - 1;
    int srcMaxY = src.Height - 1;
    int srcMaxX = src.Width - 1;

    for (int y = top; y < bottom; y++)
    {
        int nextY = (y != srcMaxY) ? y + 1 : y;
        Float4 UL = (Float4)src[leftPrevX, prevY];
        Float4 UM = (Float4)src[left, prevY];
        Float4 ML = (Float4)src[leftPrevX, y];
        Float4 MM = (Float4)src[left, y];
        Float4 LL = (Float4)src[leftPrevX, nextY];
        Float4 LM = (Float4)src[left, nextY];

        for (int x = left; x < right; x++)
        {
            // Get the rightward height values.
            int nextX = (x != srcMaxX) ? x + 1 : x;
            Float4 UR = (Float4)src[nextX, prevY];
            Float4 MR = (Float4)src[nextX, y];
            Float4 LR = (Float4)src[nextX, nextY];

            Float4 dx = (UL - UR) + 2.0f * (ML - MR) + (LL - LR);
            Float4 dy = (UL - LL) + 2.0f * (UM - LM) + (UR - LR);

            float u = Float4.Dot(dx, lightDir);
            float v = Float4.Dot(dy, lightDir);
            Float4 normal = new Float4(u, v, 1.0f, 0.0f);
            normal.Normalize();
            normal = 0.5f * normal + 0.5f;
            normal.W = 1.0f;
            dst[x, y] = (ColorBgra)normal;
          
            // Shift the height values left.
            UL = UM;
            UM = UR;
            ML = MM;
            MM = MR;
            LL = LM;           
            LM = LR;           
        }

        prevY = y;
    }
}

public struct Float4
{
    public float A;
    public float R;
    public float G;
    public float B;
    public float X
    {
        get
        {
            return this.R;
        }
        set
        {
            this.R = value;
        }
    }
    public float Y
    {
        get
        {
            return this.G;
        }
        set
        {
            this.G = value;
        }
    }
    public float Z
    {
        get
        {
            return this.B;
        }
        set
        {
            this.B = value;
        }
    }
    public float W
    {
        get
        {
            return this.A;
        }
        set
        {
            this.A = value;
        }
    }
    public static explicit operator ColorBgra(Float4 c)
    {
        return ColorBgra.FromBgra((byte)(255f * c.B), (byte)(255f * c.G), (byte)(255f * c.R), (byte)(255f * c.A));
    }

    public static explicit operator Float4(ColorBgra c)
    {
        return FromBgra(((float)c.B) / 255f, ((float)c.G) / 255f, ((float)c.R) / 255f, ((float)c.A) / 255f);
    }

    public static Float4 operator +(Float4 c, float s)
    {
        c.A += s;
        c.R += s;
        c.G += s;
        c.B += s;        
        return c;
    }

    public static Float4 operator *(Float4 c, float s)
    {
        c.A *= s;
        c.R *= s;
        c.G *= s;
        c.B *= s;
        return c;
    }

    public static Float4 operator *(float s, Float4 c)
    {
        c.A *= s;
        c.R *= s;
        c.G *= s;
        c.B *= s;
        return c;
    }

    public static Float4 operator +(Float4 c0, Float4 c1)
    {
        c0.A += c1.A;
        c0.R += c1.R;
        c0.G += c1.G;
        c0.B += c1.B;
        return c0;
    }

    public static float Dot(Float4 c0, Float4 c1)
    {
        return ((((c0.A * c1.A) + (c0.R * c1.R)) + (c0.G * c1.G)) + (c0.B * c1.B));
    }

    public static Float4 operator -(Float4 c0, Float4 c1)
    {
        c0.A -= c1.A;
        c0.R -= c1.R;
        c0.G -= c1.G;
        c0.B -= c1.B;
        return c0;
    }

    public void Normalize()
    {
        float num = 1f / ((float)Math.Sqrt((double)((((this.A * this.A) + (this.R * this.R)) + (this.G * this.G)) + (this.B * this.B))));
        this = (Float4)(this * num);
    }

    public Float4(float x, float y, float z, float w)
    {
        this.A = w;
        this.R = x;
        this.G = y;
        this.B = z;
    }

    public static Float4 FromBgra(float b, float g, float r, float a)
    {
        Float4 num = new Float4();

        num.A = a;
        num.R = r;   
        num.G = g;
        num.B = b;
        return num;
    }
}

 

 

EDIT 01 AUG 1017: Moved to Stylize window. Changed version to 1.4.

  • Like 1
  • Upvote 6
Link to comment
Share on other sites

Thanks for the update MJW! And a special thanks for uploading the source ;)

 

What, no submenu? There are many to choose from...

 

Effects > Stylize (Simon's version was in this submenu)

Effects > The Normal Tools (gOUJOSAMMA's Normal Tools)

 

I'd even prefer Effects > Advanced over leaving this in Effects >

 

 

  • Upvote 1
Link to comment
Share on other sites

I agree it should be in a submenu. I incorrectly thought the original wasn't, and was just trying to preserve that. I'll probably put in in Stylize. I hope to create a version that also supports 24-bit height maps, and put that in my Height Map submenu. I'll create a new submenu-ed version in a day or so.

 

(As far as Advanced, I know others disagree, but I think that submenu should be reserved for "meta" plugins such as CodeLab and ScriptLab.)

  • Upvote 1
Link to comment
Share on other sites

Thank you. I'll flag that change for the next Plugin Index.

  • Upvote 1
Link to comment
Share on other sites

  • 7 months later...

hi i have tried to get this plugin to work but im only getting an error when i try to use the plugin, first i found a very old version, some way google thinks an article that are over 4 years old should be in the top, tried that version but that was many errors then i found this now but it still wont work, maybe im are doing it wrong but i have redone it several times as i was instructed to install it., this is my error log, strangely i have my language set to English but it still writes some words on Swedish, 

File: C:\Program Files\Paint.NET\Effects\NormalMapPlus.dll

      Name: NormalMapPlusEffect.NormalMapPlusEffectPlugin

      Version: 1.4.6422.43071

      Author: Copyright © harold, Simon Brown, MJW

      Copyright: CodeLab port of NormalMapPlus

      Website: https://forums.getpaint.net/index.php?/topic/17010-normalmapplus-v10/

      Full error message: System.TypeLoadException: Det gick inte att läsa in typen PaintDotNet.IndirectUI.WindowHelpContentType från sammansättningen PaintDotNet.Core, Version=4.5.5454.39504, Culture=neutral, PublicKeyToken=null.

   vid NormalMapPlusEffect.NormalMapPlusEffectPlugin.OnCustomizeConfigUIWindowProperties(PropertyCollection props)

   vid PaintDotNet.Effects.PropertyBasedEffect.CreateConfigDialog() i d:\src\pdn\paintdotnet\src\Effects\PropertyBasedEffect.cs:rad 86

   vid PaintDotNet.Menus.EffectMenuBase.RunEffectImpl(Type effectType) i d:\src\pdn\paintdotnet\src\PaintDotNet\Menus\EffectMenuBase.cs:rad 910

 

if i dont restart paint when prompted to do it then it looks like it maybe are working but then when i try to use it i gives one bigger error log 

 

File: C:\Program Files\Paint.NET\Effects\NormalMapPlus.dll
      Name: NormalMapPlusEffect.NormalMapPlusEffectPlugin
      Version: 1.4.6422.43071
      Author: Copyright © harold, Simon Brown, MJW
      Copyright: CodeLab port of NormalMapPlus
      Full error message: PaintDotNet.WorkerThreadException: Worker thread threw an exception ---> System.NullReferenceException: Objektreferensen har inte angetts till en instans av ett objekt.
   vid NormalMapPlusEffect.NormalMapPlusEffectPlugin.OnSetRenderInfo(PropertyBasedEffectConfigToken newToken, RenderArgs dstArgs, RenderArgs srcArgs)
   vid PaintDotNet.Effects.Effect`1.OnSetRenderInfo(EffectConfigToken parameters, RenderArgs dstArgs, RenderArgs srcArgs) i d:\src\pdn\paintdotnet\src\Effects\Effect`1.cs:rad 67
   vid PaintDotNet.Effects.BackgroundEffectRenderer.ThreadFunction() i d:\src\pdn\paintdotnet\src\PaintDotNet\Effects\BackgroundEffectRenderer.cs:rad 218
   --- Slut på stackspårning för interna undantag ---
   vid PaintDotNet.Effects.BackgroundEffectRenderer.DrainExceptions() i d:\src\pdn\paintdotnet\src\PaintDotNet\Effects\BackgroundEffectRenderer.cs:rad 431
   vid PaintDotNet.Menus.EffectMenuBase.DoEffect(Effect effect, EffectConfigToken token, PdnRegion selectedRegion, PdnRegion regionToRender, IRenderer`1 clipMaskRenderer, Surface originalSurface, Exception& exception) i d:\src\pdn\paintdotnet\src\PaintDotNet\Menus\EffectMenuBase.cs:rad 1527
Link to comment
Share on other sites

so sorry i had missed a program needed for it to work, it was not mentioned in the first post i did read that codelab was needed, tried it for another program and now this work sorry to bother you :(  this program now works like i had hoped so big thanks for this pluging anyway :), tried to delete my first post but dont know how to do it    

Link to comment
Share on other sites

@VolvobmT24, CodeLab isn't needed to make the plugin work. You can just download and unzip the DLL. I included the code, so if someone wants to, they can paste it into CodeLab and build the plugin, but that's not what most people would do.

 

I think you need to use a newer version of Paint.Net.

Link to comment
Share on other sites

  • 2 years later...
  • 3 months later...

@LoudSilence, you've made a few of these 'plugin installation issue' posts. You should know the troubleshooting steps by now.  You are turning into The Boy Who Cried Wolf, and it's obnoxious.

  • Upvote 3

(September 25th, 2023)  Sorry about any broken images in my posts. I am aware of the issue.

bp-sig.png
My Gallery  |  My Plugin Pack

Layman's Guide to CodeLab

Link to comment
Share on other sites

  • 1 month later...

I am trying to use this tool for textures for Fallout 3.  The "3D effect" seems all right, but the suit is too glossy.

 

Do the X, Y and Z parameters refer to something specific? (Height, transparency, ...)

 

Thanks!

Link to comment
Share on other sites

(Keep in mind I only ported some existing code, so I take no responsibility for the philosophy behind it.)

 

The X, Y, Z values are really just RGB weights used to convert a color image to an intensity image. The intensity image is what's used to as the height map, for which the partial derivatives in each direction are computed. The default values for the weights are (0.3, 0.5, 0.11), which are sort of close to the usual color-to-intensity weights, giving most weight to green and least to blue.

 

EDIT: I should mention that the way the code is written, along with the names of the variables, makes me question whether the original author fully understood that's what he was doing. Specifically, the differences used to approximate the differentials are computed first, then the weights applied. However, the results are the same.

  • Upvote 1
Link to comment
Share on other sites

2 hours ago, YoJeff said:

I am trying to use this tool for textures for Fallout 3.  The "3D effect" seems all right, but the suit is too glossy.

 

Many games use the alpha channel to control glossiness (specularity) of a normal map.

PdnSig.png

Plugin Pack | PSFilterPdn | Content Aware Fill | G'MICPaint Shop Pro Filetype | RAW Filetype | WebP Filetype

The small increase in performance you get coding in C++ over C# is hardly enough to offset the headache of coding in the C++ language. ~BoltBait

 

Link to comment
Share on other sites

27 minutes ago, null54 said:

Many games use the alpha channel to control glossiness (specularity) of a normal map.

 

It appears to me that this plugin completely ignores the alpha channel. In the computations, it uses a zero weight for W (which is alpha), then in the end, sets it to 1.0, which gets re-scaled to 255 upon conversion to ColorBgra. As far as I can see, no matter what alpha is in the original image, it ignores it, and replaces it by 255 in the final image. (Keep in mind I'm merely looking at the code, so perhaps I'm missing something.)

 

It would be easy to add the option of preserving alpha, if that would be useful.

Link to comment
Share on other sites

Thanks for the answers. I found another plugin on the Skyrim Nexus.

The initial texture files in Fallout 3 have an alpha of 255, so preserving the alpha wouldn't change anything (in my case).

Link to comment
Share on other sites

  • 2 months later...
On 11/10/2020 at 10:56 PM, null54 said:

 

Many games use the alpha channel to control glossiness (specularity) of a normal map.

Adjust the specular color in the BSLightingShader with Nifskope.  It's a quick-fix for that problem and depending on the material that I'm adjusting the shine on, depends on how dark I adjust the color.  Wood --- especially aged/damaged --- I often change it to charcoal or black.

DarkWoodgrain01-NormMapPlus.png.351026ce5d0f057102088ca7634e1794.png          commoncrate01_share.a.png.5ac0bd6e5cb7aecdedfa66d35fc14c69.png

I adjusted the specular color to black and the strength from 81.0000 to 20.0000 which gave me the results seen above... Hope that helps. (Skyrim modder lol)  For my textures I rely heavily on this plugin and Normalizer for sure!

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...