normal_map 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in normalmapper.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Colin MacKenzie IV
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # Normalmapper
2
+
3
+ Command line tool and Ruby library for generating normal maps.
4
+
5
+ Generates DOT3 bump maps, also known as normal maps, for use in 3D computing.
6
+
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'normal_map'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install normal_map
21
+
22
+
23
+ ## Command Line Usage
24
+
25
+ Generate a normal map from a regular full-color image:
26
+
27
+ Usage:
28
+ normal-map generate [options] COLORS NORMALS
29
+
30
+ Options:
31
+ -s, [--smooth] # Average adjacent pixels to produce a smoother map
32
+ -d, [--diagonal] # Consider diagonally-adjacent pixels in normal
33
+ calculations
34
+ -w, [--wrap] # Wrap edge pixels to the opposite edge, to make the
35
+ map more tile-friendly
36
+
37
+ Generates a normal map from COLORS and saves it to NORMALS
38
+
39
+
40
+
41
+ ## Contributing
42
+
43
+ 1. Fork it
44
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
45
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
46
+ 4. Push to the branch (`git push origin my-new-feature`)
47
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/bin/normal-map ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'normal_map'
4
+ NormalMap::CLI.start ARGV
data/lib/normal_map.rb ADDED
@@ -0,0 +1,193 @@
1
+ require 'RMagick'
2
+
3
+ class NormalMap
4
+ autoload :CLI, 'normal_map/cli'
5
+ autoload :Version, 'normal_map/version'
6
+ autoload :VERSION, 'normal_map/version'
7
+
8
+ include Magick
9
+ attr_reader :colors
10
+
11
+ def initialize filename, opts = {}
12
+ @colors = ImageList.new(filename)[0]
13
+ opts.each do |k, v|
14
+ instance_variable_set :"@#{k}", v
15
+ end
16
+ convert!
17
+ end
18
+
19
+ def to_blob *a, &b
20
+ to_image.to_blob *a, &b
21
+ end
22
+
23
+ def print *args
24
+ out.print *args unless silent?
25
+ end
26
+
27
+ def puts *args
28
+ out.puts *args unless silent?
29
+ end
30
+
31
+ # If true, calculated normals will be averaged with adjacent normals
32
+ # to produce a smoother, but less accurate, normal map.
33
+ def smooth?
34
+ !!@smooth
35
+ end
36
+
37
+ # If true, pixel coordinates out of range will be wrapped to the other
38
+ # side of the texture; otherwise, they will be clamped to the edge of
39
+ # the texture.
40
+ def wrap?
41
+ !!@wrap
42
+ end
43
+
44
+ # If true, nothing will be output during generation.
45
+ def silent?
46
+ !!@silent
47
+ end
48
+
49
+ # If true, normals will be calculated from diagonally adjacent pixels
50
+ # as well as vertically and horizontally adjacent. If `smooth` is also
51
+ # true, diagonal pixels will be considered in the smoothing algorithm
52
+ # as well.
53
+ def diagonal?
54
+ !!@diagonal
55
+ end
56
+
57
+ # The output stream to receive status updates. Defaults to `$stdout`.
58
+ def out
59
+ @out ||= $stdout
60
+ end
61
+
62
+ def to_image
63
+ normals
64
+ end
65
+
66
+ def normals
67
+ @normals ||= Image.new width, height
68
+ end
69
+
70
+ def convert!
71
+ total = width * height
72
+ increment = 0.05
73
+ current = 0
74
+ frac = 1.0 / total
75
+ pixels = Array.new total
76
+ width.times do |x|
77
+ height.times do |y|
78
+ current += frac
79
+ increment = progress current, increment
80
+ normal = calculate_normal x, y
81
+ normal[0] = normal[0] * 0.5 + 0.5
82
+ normal[1] = normal[1] * 0.5 + 0.5
83
+ normal[2] = normal[2] * 0.5 + 0.5
84
+ pixels[offset x, y] = Pixel.new normal[0] * QuantumRange,
85
+ normal[1] * QuantumRange,
86
+ normal[2] * QuantumRange,
87
+ QuantumRange
88
+ end
89
+ end
90
+ normals.store_pixels 0, 0, width, height, pixels
91
+ puts
92
+ end
93
+
94
+ def progress percent, increment
95
+ if percent >= increment
96
+ print '.'
97
+ increment += 0.05
98
+ end
99
+ percent = (percent*100).to_i.to_s
100
+ print " => ", percent, "%", "\b" * (percent.length + 6)
101
+ increment
102
+ end
103
+
104
+ def pixels
105
+ @pixels ||= colors.get_pixels 0, 0, width, height
106
+ end
107
+
108
+ def calculate_normal x, y
109
+ sx, sy = width, height
110
+
111
+ # vertical/horizontal
112
+ az = intensity x+1, y
113
+ bz = intensity x, y+1
114
+ cz = intensity x-1, y
115
+ dz = intensity x, y-1
116
+ normal = normalize [cz - az, dz - bz, 2 ]
117
+
118
+ if diagonal?
119
+ # diagonal
120
+ az = intensity x+1, y+1
121
+ bz = intensity x-1, y+1
122
+ cz = intensity x+1, y-1
123
+ dz = intensity x-1, y-1
124
+ normal2 = normalize [cz - az, dz - bz, 2 ]
125
+
126
+ # average them together
127
+ normal[0] = (normal[0] + normal2[0]) * 0.5
128
+ normal[1] = (normal[1] + normal2[1]) * 0.5
129
+ normal[2] = (normal[2] + normal2[2]) * 0.5
130
+ end
131
+
132
+ normal
133
+ end
134
+
135
+ def intensity(x, y)
136
+ # return intensity averaged with all adjacent pixels to produce
137
+ # a smoother result. Weight the current pixel to give it a little
138
+ # more influence.
139
+ if smooth?
140
+ sum = 0.0
141
+ sum += raw_intensity x-1, y
142
+ sum += raw_intensity x+1, y
143
+ sum += raw_intensity x, y-1
144
+ sum += raw_intensity x, y+1
145
+ if diagonal?
146
+ sum += raw_intensity x-1, y-1
147
+ sum += raw_intensity x-1, y+1
148
+ sum += raw_intensity x+1, y-1
149
+ sum += raw_intensity x+1, y+1
150
+ sum /= 8.0
151
+ else
152
+ sum / 4.0
153
+ end
154
+ else
155
+ raw_intensity x, y
156
+ end
157
+ end
158
+
159
+ def raw_intensity(x, y)
160
+ if wrap?
161
+ x += width if x < 0
162
+ x -= width if x >= width
163
+ y += height if y < 0
164
+ y -= height if y >= height
165
+ else
166
+ x = 0 if x < 0
167
+ x = width - 1 if x >= width
168
+ y = 0 if y < 0
169
+ y = height - 1 if y >= height
170
+ end
171
+ pixels[offset x, y].intensity.to_f / QuantumRange.to_f
172
+ end
173
+
174
+ def offset x, y
175
+ y * width + x
176
+ end
177
+
178
+ def normalize vec
179
+ mag = 1.0 / Math.sqrt(vec[0]**2 + vec[1]**2 + vec[2]**2)
180
+ vec[0] *= mag
181
+ vec[1] *= mag
182
+ vec[2] *= mag
183
+ vec
184
+ end
185
+
186
+ def width
187
+ @width ||= colors.columns
188
+ end
189
+
190
+ def height
191
+ @height ||= colors.rows
192
+ end
193
+ end
@@ -0,0 +1,16 @@
1
+ require 'thor'
2
+
3
+ class NormalMap::CLI < Thor
4
+ include Thor::Actions
5
+
6
+ desc "generate [options] COLORS NORMALS",
7
+ "Generates a normal map from COLORS and saves it to NORMALS"
8
+ method_option :smooth, :type => :boolean, :default => false, :aliases => '-s', :desc => "Average adjacent pixels to produce a smoother accurate map"
9
+ method_option :diagonal, :type => :boolean, :default => false, :aliases => '-d', :desc => "Consider diagonally-adjacent pixels in normal calculations"
10
+ method_option :wrap, :type => :boolean, :default => false, :aliases => '-w', :desc => "Wrap edge pixels to the opposite edge, to make the map more tile-friendly"
11
+ def generate colors_fn, normals_fn
12
+ create_file normals_fn do
13
+ NormalMap.new(colors_fn, options).to_blob { |f| f.format = "PNG" }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,8 @@
1
+ class NormalMap
2
+ module Version
3
+ MAJOR, MINOR, TINY = 0, 0, 1
4
+ STRING = [MAJOR, MINOR, TINY].join('.')
5
+ end
6
+
7
+ VERSION = Version::STRING
8
+ end
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/normal_map/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Colin MacKenzie IV"]
6
+ gem.email = ["sinisterchipmunk@gmail.com"]
7
+ gem.description = %q{Generates DOT3 bump maps, also known as normal maps, for use in 3D computing.}
8
+ gem.summary = %q{Command line tool and Ruby library for generating normal maps.}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "normal_map"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = NormalMap::VERSION
17
+ gem.add_dependency 'thor'
18
+ gem.add_dependency 'rmagick'
19
+ gem.add_development_dependency 'rspec'
20
+ end
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe NormalMap do
4
+ let(:filename) { fixture_path 'in.png' }
5
+ let(:options) { {} }
6
+ subject { NormalMap.new filename, options }
7
+
8
+ shared_examples_for "expected result" do
9
+ it "should produce expected result" do
10
+ subject.to_blob { self.format = 'PNG' }.should == expected_result
11
+ end
12
+ end
13
+
14
+ describe "with no options" do
15
+ let(:expected_result) { fixture 'out_defaults.png' }
16
+ it_should_behave_like "expected result"
17
+ end
18
+
19
+ describe "with --smooth" do
20
+ let(:options) { { smooth: true } }
21
+ let(:expected_result) { fixture 'out_smooth.png' }
22
+ it_should_behave_like "expected result"
23
+ end
24
+
25
+ describe "with --diagonal" do
26
+ let(:options) { { diagonal: true } }
27
+ let(:expected_result) { fixture 'out_diagonal.png' }
28
+ it_should_behave_like "expected result"
29
+ end
30
+
31
+ describe "with --wrap" do
32
+ let(:options) { { wrap: true } }
33
+ let(:expected_result) { fixture 'out_wrap.png' }
34
+ it_should_behave_like "expected result"
35
+ end
36
+ end
@@ -0,0 +1,9 @@
1
+ require 'normal_map'
2
+
3
+ Dir[File.expand_path('support/**/*.rb', File.dirname(__FILE__))].each do |f|
4
+ require f
5
+ end
6
+
7
+ RSpec.configure do |conf|
8
+ conf.include Fixtures
9
+ end
@@ -0,0 +1,12 @@
1
+ module Fixtures
2
+ def fixture_path relative
3
+ File.expand_path File.join('../fixtures', relative),
4
+ File.dirname(__FILE__)
5
+ end
6
+
7
+ def fixture relative
8
+ File.open fixture_path(relative), 'rb' do |f|
9
+ f.read
10
+ end
11
+ end
12
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: normal_map
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Colin MacKenzie IV
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: thor
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rmagick
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Generates DOT3 bump maps, also known as normal maps, for use in 3D computing.
63
+ email:
64
+ - sinisterchipmunk@gmail.com
65
+ executables:
66
+ - normal-map
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - .gitignore
71
+ - Gemfile
72
+ - LICENSE
73
+ - README.md
74
+ - Rakefile
75
+ - bin/normal-map
76
+ - lib/normal_map.rb
77
+ - lib/normal_map/cli.rb
78
+ - lib/normal_map/version.rb
79
+ - normal_map.gemspec
80
+ - spec/fixtures/in.png
81
+ - spec/fixtures/out_defaults.png
82
+ - spec/fixtures/out_diagonal.png
83
+ - spec/fixtures/out_smooth.png
84
+ - spec/fixtures/out_wrap.png
85
+ - spec/lib/normal_map_spec.rb
86
+ - spec/spec_helper.rb
87
+ - spec/support/fixtures.rb
88
+ homepage: ''
89
+ licenses: []
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 1.8.24
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: Command line tool and Ruby library for generating normal maps.
112
+ test_files:
113
+ - spec/fixtures/in.png
114
+ - spec/fixtures/out_defaults.png
115
+ - spec/fixtures/out_diagonal.png
116
+ - spec/fixtures/out_smooth.png
117
+ - spec/fixtures/out_wrap.png
118
+ - spec/lib/normal_map_spec.rb
119
+ - spec/spec_helper.rb
120
+ - spec/support/fixtures.rb