Possible define an image and assign to a material?

#1

Hi guys,
is it possible for me to define an image (from scratch, pixle by pixle) and assign that as a texture to a material?

I was thinking in the same way an image file can be embedded into Rhino that maybe we could have “virtual” image files too.

I would need this to make a “terrain shader” and I would prefer not to generate a png/bmp/jpeg and save it next to the Rhino file.

Thanks!

(Nathan 'jesterKing' Letwory) #2

You can create an in-memory bitmap. Here is an old sample I created:

The code for lazy users (who don’t want to click through):

using System;
using System.Runtime.InteropServices;
using Rhino.Render;
using Rhino.Geometry;
using Rhino.UI;
using Rhino.DocObjects;
using Rhino.Commands;
using Rhino;
using System.Drawing;
using Rhino.Display;

namespace Commands.Commands.Developer
{

	public static class NathanData
	{
		public static Bitmap CreateMemoryBitmap()
		{
			var width = 10;
			var height = width;
			var rnd = new Random();
			Bitmap m_testBmp = new Bitmap(width, height);
			for(var x = 0; x< width; x++)
				for(var y = 0; y< height; y++)
					m_testBmp.SetPixel(x, y, Color.FromArgb(rnd.Next(255), rnd.Next(255), rnd.Next(255)));
			return m_testBmp;
		}

		public static double Fraction(this double nr)
		{
			if(nr >= 0.0) return nr - Math.Floor(nr);
			return nr - Math.Ceiling(nr);
			
		}
		public static double Floor(this double nr)
		{
			return Math.Floor(nr);
		}

	}
	[CommandStyle(Style.Hidden)]
	public class TestNathanMemoryTexture : Command
	{
		public override string EnglishName { get { return "TestNathanMemoryTexture"; } }

		protected override Result RunCommand(RhinoDoc doc, RunMode mode)
		{
			if (Result.Success == Rhino.Input.RhinoGet.GetOneObject("Select object", false, ObjectType.Brep, out ObjRef obj))
			{
				var rtex = new MemoryBitmapTexture() { BitmapTexture = NathanData.CreateMemoryBitmap() };
				var mat = new Material();
				mat.Name = "Testing MemoryBitmapTexture";
				var rm = RenderMaterial.CreateBasicMaterial(mat);
				rm.SetChild(rtex, "bitmap-texture");
				rm.SetChildSlotOn("bitmap-texture", true, RenderContent.ChangeContexts.Ignore);
				rm.SetChildSlotAmount("bitmap-texture", 100.0, RenderContent.ChangeContexts.Ignore);
				var t = rm.GetTextureFromUsage(RenderMaterial.StandardChildSlots.Diffuse);

				rm.DocumentAssoc = doc;

				var hash1 = rm.RenderHash;

				doc.RenderMaterials.Add(rm);
				rtex.BeginChange(RenderContent.ChangeContexts.Ignore);
				rtex.SetProjectionMode(TextureProjectionMode.WcsBox, RenderContent.ChangeContexts.Ignore);
				rtex.SetRepeat(new Vector3d(1.0, 1.0, 0.0), RenderContent.ChangeContexts.Ignore);
				rtex.EndChange();

				var hash2 = rm.RenderHash;

				var o = obj.Object();

				o.RenderMaterial = rm;
				o.CommitChanges();

				return Result.Success;
			}

			return Result.Failure;
		}
	}

	/// <summary>
	/// Evaluator used by MemoryBitmapEvaluator.
	/// </summary>
	internal class MemoryBitmapEvaluator : TextureEvaluator
	{
		private static readonly object locker = new object();
		public Bitmap BitmapTexture { get; set; }
		public MemoryBitmapEvaluator(RenderTexture.TextureEvaluatorFlags evaluatorFlags) : base(evaluatorFlags)
		{
		}

		/// <summary>
		/// Get the color specified at uvw. Currently duvwdx and duvwdy aren't used (but probably should).
		/// </summary>
		/// <param name="uvw"></param>
		/// <param name="duvwdx"></param>
		/// <param name="duvwdy"></param>
		/// <returns>The color for the given uvw coordinate.</returns>
		public override Color4f GetColor(Point3d uvw, Vector3d duvwdx, Vector3d duvwdy)
		{
			Color c = Color.AliceBlue;
			lock (locker)
			{
				var x = Math.Max(0, Math.Min((int)(uvw.X.Fraction() * BitmapTexture.Width), BitmapTexture.Width));
				var y = Math.Max(0, Math.Min((int)(uvw.Y.Fraction() * BitmapTexture.Height), BitmapTexture.Height));
				c = BitmapTexture.GetPixel(x, y);
			}
			var cl = new Color4f(c);

			return cl;
		}
	}


	/// <summary>
	/// A texture that provides a way to use an in-memory Bitmap as data.
	/// </summary>
	[Guid("34D9AC50-B958-476B-82D4-50F4DA1A5511")]
	[CustomRenderContent(Category = "image-based", IsElevated = false, IsBuiltIn = true, ImageBased = true, IsPrivate = true)]
	public class MemoryBitmapTexture : RenderTexture
	{
		/// <summary>
		/// Set or get the Bitmap for this texture.
		/// </summary>
		public Bitmap BitmapTexture { get; set; }

		/// <summary>
		/// Create a new MemoryBitmapTexture instance.
		/// </summary>
		public MemoryBitmapTexture()
		{
			ModifyRenderContentStyles(RenderContentStyles.LocalTextureMapping, RenderContentStyles.TextureSummary | RenderContentStyles.ModalEditing | RenderContentStyles.GraphDisplay);

			SetProjectionMode(TextureProjectionMode.WcsBox, ChangeContexts.Ignore);

			SetRepeat(new Vector3d(1.0, 1.0, 1.0), ChangeContexts.Ignore);
			SetRotation(new Vector3d(0.0, 0.0, 0.0), ChangeContexts.Ignore);
		}

		/// <summary>
		/// Get the TextureEvaluator for this texture.
		/// </summary>
		/// <param name="evaluatorFlags">currently not used</param>
		/// <returns>A TextureEvaluator, or null if no Bitmap was set to BitmapTexture.</returns>
		public override TextureEvaluator CreateEvaluator(TextureEvaluatorFlags evaluatorFlags)
		{
			var bm = BitmapTexture;
			if (bm == null) return null;
			var eval = new MemoryBitmapEvaluator(evaluatorFlags) { BitmapTexture = bm };
			return eval;
		}

		public override string TypeName
		{
			get { return Localization.LocalizeString("MemoryBitmap texture", 1402); }
		}

		public override string TypeDescription
		{
			get { return Localization.LocalizeString("MemoryBitmap texture that is able to read from in-memory Bitmap objects", 1403); }
		}
	}

}
2 Likes
#3

Hi @nathanletwory, is this the same as posted here ? How would a pure python version look like and how would the class decoration with the guid and command style be translated ?

_
c.

(Nathan 'jesterKing' Letwory) #4

It is the same yes.

I don’t know how such a material would look like in Python as I don’t know how to decorate the custom classes with Guids in Python - or rather how to decorate Python classes with the .NET attributes

Once that is figured out the next step is to register the custom RenderContent so the RDK and Rhino know about it. I don’t know how that works from Python, as this is a plug-in mechanism thing:

https://developer.rhino3d.com/api/RhinoCommon/html/Overload_Rhino_Render_RenderContent_RegisterContent.htm

#5

Hi Natan, OK thats what i figured the last time i tried it, it will not work in python. btw. why is the texture written to the temp dir at all if it is a memory texture ? I’ve found that i can access and change it ?

_
c.

(Nathan 'jesterKing' Letwory) #6

The RDK writes one or more versions of any texture that is used. If you add a procedural texture to a material you’ll find that you get an image in tmp for that procedural as well - it is also just a memory/on-the-fly texture.

#7

Thanks, i know that it writes a ton of images. I need to clear these folders often, otherwise i would run out of disk space.

_
c.

#8

I’m one of those lazy users which find it useful when I search the forum and get hits in the source code. :wink:

Thank you for the useful code example which made me end up in this thread. :+1:

// Rolf

#9

Hi guys, thanks for the info, but I realize that this is a bit over my skills.

So a quick question: What is the simplest way to save an image from python when I have a image defined the way I want it?

(I start off with:
NewWidth=100
NewHeight=100
bmpOut = System.Drawing.Bitmap(NewWidth, NewHeight)
And then I set the pixles)

Is bmpOut.Save(saveImagePath) a way to do it?
(Sorry for asking obvious questions, but it’s a bit new for me :slight_smile: )

(Nathan 'jesterKing' Letwory) #10

Yes.

1 Like
#11

Thanks, I’ll figure that out and should be up and running with an OK solution soon.
…when I have a few hours to spare…

But first I shall attend my Finnish Yoga class for some friday relaxation:

image

Loosing up those hard to reach back muscles are important after a long week in front of the monitor

Cheers!

1 Like
#12

All relaxed and managed to make a 5 color gradient saved as png next to the Rhino model.
So that’s the big step towards the goal.
Thanks for the help so far @nathanletwory !
(And Cheers!)

#13

Small update for those interested:

Here’s what I’m wokring on:
A “shader” that generates a 5 color gradient to illustrate altitude on terrains.
The 5 colors are customizable and so are the “steps” in the colors. I want those so it easy to follow a contour.

Here you can see the same gradient with out steps:

And shown from top on a peaky landscape:

And a with/without comparison:

Now I just need to iron out a few material generating issues, but it serves my purpose so I can live with a few hickups for a while :slight_smile:

Cheers!

3 Likes
(Nathan 'jesterKing' Letwory) #14

In v7 there is a gradient hatch created by @stevebaer - maybe it can be used for this type of shading as well?

#15

Thanks, that would probably have been good enough if it was available in V6, but now that I started the journey I sat my eyes on a higher goal and that is to add steps and also subtle contourlines that brightens or darkens the underlying color.
So as always, A BIG Thank You for all your help Nathan, you really help me out a lot!!!

(Nathan 'jesterKing' Letwory) #16

With Raytraced you could make a custom material and have a color ramp driven by that height (:

For fun

terrain_height.3dm (655.7 KB)
terrain_height.gh (15.0 KB)

here a slightly cleaned up GH with some groupings and namings. Maybe a bit easier to understand: terrain_height.gh (15.7 KB)

1 Like
#17

I love it, here I work my butt off at night time and you just cook an interactive tool together for fun… :smiley: Thanks for sharing, I’ll dig into it and see what I can learn. And come over to Norway for a weekend, I have some beers here with your name on it!

1 Like
(Wim Dekeyser) #18

To give you a bit more work, I suppose that perhaps ideally, you would have a shader that is both elevation and slope dependent. On flatter parts, up to a certain elevation, you would have more greens than on steeper parts.

1 Like
#19

Ah, yes, for renderings a slope based material blend with procedurals would be nice… for sure!
Something like this:

3 Likes
(Nathan 'jesterKing' Letwory) #20

Then also take from the geometry input node the normal and factor that into the equation as well.

I leave that as an excercise to all readers (:

1 Like