MJW

NormalMapPlus (CodeLab Implementation)

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.

  • Upvote 4

Share this post


Link to post
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

Share this post


Link to post
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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
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    

Share this post


Link to post
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.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now