Class: NormalMap
- Inherits:
-
Object
- Object
- NormalMap
- Includes:
- Magick
- Defined in:
- lib/normal_map.rb,
lib/normal_map/version.rb
Defined Under Namespace
Constant Summary collapse
Instance Attribute Summary collapse
-
#colors ⇒ Object
readonly
Returns the value of attribute colors.
Instance Method Summary collapse
- #calculate_normal(x, y) ⇒ Object
- #convert! ⇒ Object
-
#diagonal? ⇒ Boolean
If true, normals will be calculated from diagonally adjacent pixels as well as vertically and horizontally adjacent.
- #height ⇒ Object
-
#initialize(filename, opts = {}) ⇒ NormalMap
constructor
A new instance of NormalMap.
- #intensity(x, y) ⇒ Object
- #normalize(vec) ⇒ Object
- #normals ⇒ Object
- #offset(x, y) ⇒ Object
-
#out ⇒ Object
The output stream to receive status updates.
- #pixels ⇒ Object
- #print(*args) ⇒ Object
- #progress(percent, increment) ⇒ Object
- #puts(*args) ⇒ Object
- #raw_intensity(x, y) ⇒ Object
-
#silent? ⇒ Boolean
If true, nothing will be output during generation.
-
#smooth? ⇒ Boolean
If true, calculated normals will be averaged with adjacent normals to produce a smoother, but less accurate, normal map.
- #to_blob(*a, &b) ⇒ Object
- #to_image ⇒ Object
- #width ⇒ Object
-
#wrap? ⇒ Boolean
If true, pixel coordinates out of range will be wrapped to the other side of the texture; otherwise, they will be clamped to the edge of the texture.
Constructor Details
#initialize(filename, opts = {}) ⇒ NormalMap
Returns a new instance of NormalMap.
11 12 13 14 15 16 17 |
# File 'lib/normal_map.rb', line 11 def initialize filename, opts = {} @colors = ImageList.new(filename)[0] opts.each do |k, v| instance_variable_set :"@#{k}", v end convert! end |
Instance Attribute Details
#colors ⇒ Object (readonly)
Returns the value of attribute colors.
9 10 11 |
# File 'lib/normal_map.rb', line 9 def colors @colors end |
Instance Method Details
#calculate_normal(x, y) ⇒ Object
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/normal_map.rb', line 108 def calculate_normal x, y sx, sy = width, height # vertical/horizontal az = intensity x+1, y bz = intensity x, y+1 cz = intensity x-1, y dz = intensity x, y-1 normal = normalize [cz - az, dz - bz, 2 ] if diagonal? # diagonal az = intensity x+1, y+1 bz = intensity x-1, y+1 cz = intensity x+1, y-1 dz = intensity x-1, y-1 normal2 = normalize [cz - az, dz - bz, 2 ] # average them together normal[0] = (normal[0] + normal2[0]) * 0.5 normal[1] = (normal[1] + normal2[1]) * 0.5 normal[2] = (normal[2] + normal2[2]) * 0.5 end normal end |
#convert! ⇒ Object
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/normal_map.rb', line 70 def convert! total = width * height increment = 0.05 current = 0 frac = 1.0 / total pixels = Array.new total width.times do |x| height.times do |y| current += frac increment = progress current, increment normal = calculate_normal x, y normal[0] = normal[0] * 0.5 + 0.5 normal[1] = normal[1] * 0.5 + 0.5 normal[2] = normal[2] * 0.5 + 0.5 pixels[offset x, y] = Pixel.new normal[0] * QuantumRange, normal[1] * QuantumRange, normal[2] * QuantumRange, QuantumRange end end normals.store_pixels 0, 0, width, height, pixels puts end |
#diagonal? ⇒ Boolean
If true, normals will be calculated from diagonally adjacent pixels as well as vertically and horizontally adjacent. If ‘smooth` is also true, diagonal pixels will be considered in the smoothing algorithm as well.
53 54 55 |
# File 'lib/normal_map.rb', line 53 def diagonal? !!@diagonal end |
#height ⇒ Object
190 191 192 |
# File 'lib/normal_map.rb', line 190 def height @height ||= colors.rows end |
#intensity(x, y) ⇒ Object
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/normal_map.rb', line 135 def intensity(x, y) # return intensity averaged with all adjacent pixels to produce # a smoother result. Weight the current pixel to give it a little # more influence. if smooth? sum = 0.0 sum += raw_intensity x-1, y sum += raw_intensity x+1, y sum += raw_intensity x, y-1 sum += raw_intensity x, y+1 if diagonal? sum += raw_intensity x-1, y-1 sum += raw_intensity x-1, y+1 sum += raw_intensity x+1, y-1 sum += raw_intensity x+1, y+1 sum /= 8.0 else sum / 4.0 end else raw_intensity x, y end end |
#normalize(vec) ⇒ Object
178 179 180 181 182 183 184 |
# File 'lib/normal_map.rb', line 178 def normalize vec mag = 1.0 / Math.sqrt(vec[0]**2 + vec[1]**2 + vec[2]**2) vec[0] *= mag vec[1] *= mag vec[2] *= mag vec end |
#normals ⇒ Object
66 67 68 |
# File 'lib/normal_map.rb', line 66 def normals @normals ||= Image.new width, height end |
#offset(x, y) ⇒ Object
174 175 176 |
# File 'lib/normal_map.rb', line 174 def offset x, y y * width + x end |
#out ⇒ Object
The output stream to receive status updates. Defaults to ‘$stdout`.
58 59 60 |
# File 'lib/normal_map.rb', line 58 def out @out ||= $stdout end |
#pixels ⇒ Object
104 105 106 |
# File 'lib/normal_map.rb', line 104 def pixels @pixels ||= colors.get_pixels 0, 0, width, height end |
#print(*args) ⇒ Object
23 24 25 |
# File 'lib/normal_map.rb', line 23 def print *args out.print *args unless silent? end |
#progress(percent, increment) ⇒ Object
94 95 96 97 98 99 100 101 102 |
# File 'lib/normal_map.rb', line 94 def progress percent, increment if percent >= increment print '.' increment += 0.05 end percent = (percent*100).to_i.to_s print " => ", percent, "%", "\b" * (percent.length + 6) increment end |
#puts(*args) ⇒ Object
27 28 29 |
# File 'lib/normal_map.rb', line 27 def puts *args out.puts *args unless silent? end |
#raw_intensity(x, y) ⇒ Object
159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/normal_map.rb', line 159 def raw_intensity(x, y) if wrap? x += width if x < 0 x -= width if x >= width y += height if y < 0 y -= height if y >= height else x = 0 if x < 0 x = width - 1 if x >= width y = 0 if y < 0 y = height - 1 if y >= height end pixels[offset x, y].intensity.to_f / QuantumRange.to_f end |
#silent? ⇒ Boolean
If true, nothing will be output during generation.
45 46 47 |
# File 'lib/normal_map.rb', line 45 def silent? !!@silent end |
#smooth? ⇒ Boolean
If true, calculated normals will be averaged with adjacent normals to produce a smoother, but less accurate, normal map.
33 34 35 |
# File 'lib/normal_map.rb', line 33 def smooth? !!@smooth end |
#to_blob(*a, &b) ⇒ Object
19 20 21 |
# File 'lib/normal_map.rb', line 19 def to_blob *a, &b to_image.to_blob *a, &b end |
#to_image ⇒ Object
62 63 64 |
# File 'lib/normal_map.rb', line 62 def to_image normals end |
#width ⇒ Object
186 187 188 |
# File 'lib/normal_map.rb', line 186 def width @width ||= colors.columns end |
#wrap? ⇒ Boolean
If true, pixel coordinates out of range will be wrapped to the other side of the texture; otherwise, they will be clamped to the edge of the texture.
40 41 42 |
# File 'lib/normal_map.rb', line 40 def wrap? !!@wrap end |