rev |
line source |
nuclear@0
|
1 #include <stdio.h>
|
nuclear@0
|
2 #include <string.h>
|
nuclear@0
|
3 #include <errno.h>
|
nuclear@0
|
4 #include "texture.h"
|
nuclear@0
|
5 #include "object.h"
|
nuclear@0
|
6
|
nuclear@1
|
7 #ifndef WIN32
|
nuclear@1
|
8 #include <unistd.h>
|
nuclear@1
|
9 #else
|
nuclear@1
|
10 #include <io.h>
|
nuclear@1
|
11
|
nuclear@1
|
12 #define R_OK 4
|
nuclear@1
|
13 #endif
|
nuclear@1
|
14
|
nuclear@0
|
15 static inline Color sample_image(const Image &img, float u, float v, Texture::WrapMode wrapping);
|
nuclear@0
|
16
|
nuclear@0
|
17 Texture::Texture()
|
nuclear@0
|
18 : sampling(SampleMode::nearest), wrapping(WrapMode::repeat)
|
nuclear@0
|
19 {
|
nuclear@0
|
20 }
|
nuclear@0
|
21
|
nuclear@0
|
22 Texture::~Texture() {}
|
nuclear@0
|
23
|
nuclear@0
|
24 void Texture::set_name(const char *name)
|
nuclear@0
|
25 {
|
nuclear@0
|
26 this->name = name;
|
nuclear@0
|
27 }
|
nuclear@0
|
28
|
nuclear@0
|
29 const char *Texture::get_name() const
|
nuclear@0
|
30 {
|
nuclear@0
|
31 return name.c_str();
|
nuclear@0
|
32 }
|
nuclear@0
|
33
|
nuclear@0
|
34 void Texture::set_sampling_mode(SampleMode mode)
|
nuclear@0
|
35 {
|
nuclear@0
|
36 sampling = mode;
|
nuclear@0
|
37 }
|
nuclear@0
|
38
|
nuclear@0
|
39 Texture::SampleMode Texture::get_sampling_mode() const
|
nuclear@0
|
40 {
|
nuclear@0
|
41 return sampling;
|
nuclear@0
|
42 }
|
nuclear@0
|
43
|
nuclear@0
|
44 void Texture::set_wrapping_mode(WrapMode mode)
|
nuclear@0
|
45 {
|
nuclear@0
|
46 wrapping = mode;
|
nuclear@0
|
47 }
|
nuclear@0
|
48
|
nuclear@0
|
49 Texture::WrapMode Texture::get_wrapping_mode() const
|
nuclear@0
|
50 {
|
nuclear@0
|
51 return wrapping;
|
nuclear@0
|
52 }
|
nuclear@0
|
53
|
nuclear@0
|
54
|
nuclear@0
|
55 Color Texture::sample(const HitPoint &hit) const
|
nuclear@0
|
56 {
|
nuclear@0
|
57 return sample(hit.texcoord.x, hit.texcoord.y, 0.0f);
|
nuclear@0
|
58 }
|
nuclear@0
|
59
|
nuclear@0
|
60 Image *Texture2D::get_image(int idx)
|
nuclear@0
|
61 {
|
nuclear@0
|
62 return &img;
|
nuclear@0
|
63 }
|
nuclear@0
|
64
|
nuclear@0
|
65 const Image *Texture2D::get_image(int idx) const
|
nuclear@0
|
66 {
|
nuclear@0
|
67 return &img;
|
nuclear@0
|
68 }
|
nuclear@0
|
69
|
nuclear@0
|
70
|
nuclear@0
|
71 bool Texture2D::load(const char *fname)
|
nuclear@0
|
72 {
|
nuclear@0
|
73 name = fname;
|
nuclear@0
|
74 return img.load(fname);
|
nuclear@0
|
75 }
|
nuclear@0
|
76
|
nuclear@0
|
77 Color Texture2D::sample(float u, float v, float w) const
|
nuclear@0
|
78 {
|
nuclear@0
|
79 return sample_image(img, u, v, wrapping);
|
nuclear@0
|
80 }
|
nuclear@0
|
81
|
nuclear@0
|
82 TextureCube::TextureCube()
|
nuclear@0
|
83 {
|
nuclear@0
|
84 wrapping = WrapMode::clamp;
|
nuclear@0
|
85 }
|
nuclear@0
|
86
|
nuclear@0
|
87 Image *TextureCube::get_image(int idx)
|
nuclear@0
|
88 {
|
nuclear@0
|
89 return &img[idx];
|
nuclear@0
|
90 }
|
nuclear@0
|
91
|
nuclear@0
|
92 const Image *TextureCube::get_image(int idx) const
|
nuclear@0
|
93 {
|
nuclear@0
|
94 return &img[idx];
|
nuclear@0
|
95 }
|
nuclear@0
|
96
|
nuclear@0
|
97
|
nuclear@0
|
98 bool TextureCube::load(const char *fname)
|
nuclear@0
|
99 {
|
nuclear@0
|
100 // assume it's a cubemap descriptor file (face path per line)
|
nuclear@0
|
101 FILE *fp;
|
nuclear@0
|
102 if(!(fp = fopen(fname, "r"))) {
|
nuclear@0
|
103 fprintf(stderr, "failed to open the cubemap descriptor %s: %s\n", fname, strerror(errno));
|
nuclear@0
|
104 return false;
|
nuclear@0
|
105 }
|
nuclear@0
|
106
|
nuclear@0
|
107 name = fname;
|
nuclear@0
|
108
|
nuclear@0
|
109 char *prefix = (char*)alloca(strlen(fname) + 1);
|
nuclear@0
|
110 strcpy(prefix, fname);
|
nuclear@0
|
111 char *ptr = strrchr(prefix, '/');
|
nuclear@0
|
112 if(!ptr) {
|
nuclear@0
|
113 ptr = prefix;
|
nuclear@0
|
114 }
|
nuclear@0
|
115 *ptr = 0;
|
nuclear@0
|
116
|
nuclear@0
|
117 int xsz = 0, ysz = 0;
|
nuclear@0
|
118
|
nuclear@0
|
119 // load the faces
|
nuclear@0
|
120 char buf[512];
|
nuclear@0
|
121 for(int i=0; i<6; i++) {
|
nuclear@0
|
122 if(!fgets(buf, sizeof buf, fp)) {
|
nuclear@0
|
123 fprintf(stderr, "invalid cubemap descriptor file: %s\n", fname);
|
nuclear@0
|
124 return false;
|
nuclear@0
|
125 }
|
nuclear@0
|
126 if(buf[strlen(buf) - 1] == '\n') {
|
nuclear@0
|
127 buf[strlen(buf) - 1] = 0;
|
nuclear@0
|
128 }
|
nuclear@0
|
129
|
nuclear@0
|
130 std::string path = std::string(prefix) + "/" + std::string(buf);
|
nuclear@0
|
131 if(!img[i].load(path.c_str())) {
|
nuclear@0
|
132 fprintf(stderr, "failed to load image: %s\n", path.c_str());
|
nuclear@0
|
133 fclose(fp);
|
nuclear@0
|
134 return false;
|
nuclear@0
|
135 }
|
nuclear@0
|
136
|
nuclear@0
|
137 if(i == 0) {
|
nuclear@0
|
138 xsz = img[i].xsz;
|
nuclear@0
|
139 ysz = img[i].ysz;
|
nuclear@0
|
140 } else {
|
nuclear@0
|
141 if(img[i].xsz != xsz || img[i].ysz != ysz) {
|
nuclear@0
|
142 fprintf(stderr, "cubemap %s face image %s size (%dx%d) doesn't match the previous faces (%dx%d)\n",
|
nuclear@0
|
143 fname, path.c_str(), img[i].xsz, img[i].ysz, xsz, ysz);
|
nuclear@0
|
144 fclose(fp);
|
nuclear@0
|
145 return false;
|
nuclear@0
|
146 }
|
nuclear@0
|
147 }
|
nuclear@0
|
148 }
|
nuclear@0
|
149 return true;
|
nuclear@0
|
150 }
|
nuclear@0
|
151
|
nuclear@0
|
152 Color TextureCube::sample(float u, float v, float w) const
|
nuclear@0
|
153 {
|
nuclear@0
|
154 int face;
|
nuclear@0
|
155 Vector2 uv;
|
nuclear@0
|
156
|
nuclear@0
|
157 float abs_u = fabs(u);
|
nuclear@0
|
158 float abs_v = fabs(v);
|
nuclear@0
|
159 float abs_w = fabs(w);
|
nuclear@0
|
160
|
nuclear@0
|
161 if(abs_u > abs_v && abs_u > abs_w) {
|
nuclear@0
|
162 if(u >= 0.0) {
|
nuclear@0
|
163 face = 0;
|
nuclear@0
|
164 uv.x = w / abs_u;
|
nuclear@0
|
165 uv.y = v / abs_u;
|
nuclear@0
|
166 } else {
|
nuclear@0
|
167 face = 1;
|
nuclear@0
|
168 uv.x = -w / abs_u;
|
nuclear@0
|
169 uv.y = v / abs_u;
|
nuclear@0
|
170 }
|
nuclear@0
|
171 } else if(abs_v > abs_w) {
|
nuclear@0
|
172 if(v >= 0.0) {
|
nuclear@0
|
173 face = 2;
|
nuclear@0
|
174 uv.x = u / abs_v;
|
nuclear@0
|
175 uv.y = w / abs_v;
|
nuclear@0
|
176 } else {
|
nuclear@0
|
177 face = 3;
|
nuclear@0
|
178 uv.x = u / abs_v;
|
nuclear@0
|
179 uv.y = -w / abs_v;
|
nuclear@0
|
180 }
|
nuclear@0
|
181 } else {
|
nuclear@0
|
182 if(w >= 0.0) {
|
nuclear@0
|
183 face = 5;
|
nuclear@0
|
184 uv.x = -u / abs_w;
|
nuclear@0
|
185 uv.y = v / abs_w;
|
nuclear@0
|
186 } else {
|
nuclear@0
|
187 face = 4;
|
nuclear@0
|
188 uv.x = u / abs_w;
|
nuclear@0
|
189 uv.y = v / abs_w;
|
nuclear@0
|
190 }
|
nuclear@0
|
191 }
|
nuclear@0
|
192
|
nuclear@0
|
193 return sample_image(img[face], uv.x * 0.5 + 0.5, uv.y * 0.5 + 0.5, wrapping);
|
nuclear@0
|
194 }
|
nuclear@0
|
195
|
nuclear@0
|
196 Color TextureCube::sample(const HitPoint &hit) const
|
nuclear@0
|
197 {
|
nuclear@0
|
198 return sample(hit.normal.x, hit.normal.y, hit.normal.z);
|
nuclear@0
|
199 }
|
nuclear@0
|
200
|
nuclear@0
|
201 Texture *load_texture(const char *fname)
|
nuclear@0
|
202 {
|
nuclear@0
|
203 if(access(fname, R_OK) == -1) {
|
nuclear@0
|
204 fprintf(stderr, "failed to load texture %s: %s\n", fname, strerror(errno));
|
nuclear@0
|
205 return 0;
|
nuclear@0
|
206 }
|
nuclear@0
|
207
|
nuclear@0
|
208 Texture2D *tex2d = new Texture2D;
|
nuclear@0
|
209 if(tex2d->load(fname)) {
|
nuclear@0
|
210 return tex2d;
|
nuclear@0
|
211 }
|
nuclear@0
|
212 delete tex2d;
|
nuclear@0
|
213
|
nuclear@0
|
214 TextureCube *texcube = new TextureCube;
|
nuclear@0
|
215 if(texcube->load(fname)) {
|
nuclear@0
|
216 return texcube;
|
nuclear@0
|
217 }
|
nuclear@0
|
218 delete texcube;
|
nuclear@0
|
219
|
nuclear@0
|
220 return 0;
|
nuclear@0
|
221 }
|
nuclear@0
|
222
|
nuclear@0
|
223 #define CLAMP(x, lo, hi) ((x) < (lo) ? (lo) : ((x) > (hi) ? (hi) : (x)))
|
nuclear@0
|
224
|
nuclear@0
|
225 static inline Color sample_image(const Image &img, float u, float v, Texture::WrapMode wrapping)
|
nuclear@0
|
226 {
|
nuclear@0
|
227 int x = (int)round(u * img.xsz);
|
nuclear@0
|
228 int y = (int)round((1.0 - v) * img.ysz);
|
nuclear@0
|
229
|
nuclear@0
|
230 if(wrapping == Texture::WrapMode::clamp) {
|
nuclear@0
|
231 x = CLAMP(x, 0, img.xsz - 1);
|
nuclear@0
|
232 y = CLAMP(y, 0, img.ysz - 1);
|
nuclear@0
|
233 } else {
|
nuclear@0
|
234 x %= img.xsz;
|
nuclear@0
|
235 y %= img.ysz;
|
nuclear@0
|
236
|
nuclear@0
|
237 if(x < 0)
|
nuclear@0
|
238 x += img.xsz;
|
nuclear@0
|
239 if(y < 0)
|
nuclear@0
|
240 y += img.ysz;
|
nuclear@0
|
241 }
|
nuclear@0
|
242
|
nuclear@0
|
243 return img.pixels[y * img.xsz + x];
|
nuclear@0
|
244 }
|