clray
diff src/scene.cc @ 23:51f115e337c2
separated obj loading and vector class
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Fri, 13 Aug 2010 18:20:45 +0100 |
parents | 6c44e4b1726d |
children | 13091c00d7ca |
line diff
1.1 --- a/src/scene.cc Wed Aug 11 04:30:35 2010 +0100 1.2 +++ b/src/scene.cc Fri Aug 13 18:20:45 2010 +0100 1.3 @@ -1,130 +1,6 @@ 1.4 -#include <stdio.h> 1.5 -#include <stdlib.h> 1.6 -#include <string.h> 1.7 #include <math.h> 1.8 -#include <errno.h> 1.9 -#include <limits.h> 1.10 -#include <string> 1.11 -#include <vector> 1.12 -#include <map> 1.13 #include "scene.h" 1.14 1.15 -#ifndef PATH_MAX 1.16 -#define PATH_MAX 512 1.17 -#endif 1.18 - 1.19 -using namespace std; 1.20 - 1.21 -#define COMMANDS \ 1.22 - CMD(V), \ 1.23 - CMD(VN), \ 1.24 - CMD(VT), \ 1.25 - CMD(F), \ 1.26 - CMD(O), \ 1.27 - CMD(G), \ 1.28 - CMD(MTLLIB), \ 1.29 - CMD(USEMTL), \ 1.30 - CMD(NEWMTL), \ 1.31 - CMD(KA), \ 1.32 - CMD(KD), \ 1.33 - CMD(KS), \ 1.34 - CMD(KR), \ 1.35 - CMD(NS), \ 1.36 - CMD(NI), \ 1.37 - CMD(D), \ 1.38 - CMD(TR), \ 1.39 - CMD(MAP_KD), \ 1.40 - CMD(MAP_KS), \ 1.41 - CMD(MAP_NS), \ 1.42 - CMD(MAP_D), \ 1.43 - CMD(REFL), \ 1.44 - CMD(BUMP) 1.45 - 1.46 -#define CMD(x) CMD_##x 1.47 -enum { 1.48 - COMMANDS, 1.49 - CMD_UNK 1.50 -}; 1.51 -#undef CMD 1.52 - 1.53 -#define CMD(x) #x 1.54 -static const char *cmd_names[] = { 1.55 - COMMANDS, 1.56 - 0 1.57 -}; 1.58 -#undef CMD 1.59 - 1.60 -struct Vector3 { 1.61 - float x, y, z; 1.62 - 1.63 - Vector3() { x = y = z = 0.0; } 1.64 - Vector3(float a, float b, float c) { x = a; y = b; z = c; } 1.65 - 1.66 - void normalize() { float len = sqrt(x * x + y * y + z * z); x /= len; y /= len; z /= len; } 1.67 -}; 1.68 - 1.69 -struct Vector2 { 1.70 - float x, y; 1.71 - 1.72 - Vector2() { x = y = 0.0; } 1.73 - Vector2(float a, float b) { x = a; y = b; } 1.74 -}; 1.75 - 1.76 -struct obj_face { 1.77 - int elem; 1.78 - int v[4], n[4], t[4]; 1.79 -}; 1.80 - 1.81 -struct obj_file { 1.82 - string cur_obj, cur_mat; 1.83 - vector<Vector3> v, vn, vt; 1.84 - vector<obj_face> f; 1.85 -}; 1.86 - 1.87 -struct obj_mat { 1.88 - string name; // newmtl <name> 1.89 - Vector3 ambient, diffuse, specular; // Ka, Kd, Ks 1.90 - float shininess; // Ns 1.91 - float ior; // Ni 1.92 - float alpha; // d, Tr 1.93 - float refl; // Kr (my extesnsion) 1.94 - 1.95 - string tex_dif, tex_spec, tex_shin, tex_alpha; // map_Kd, map_Ks, map_Ns, map_d 1.96 - string tex_refl; // refl -type sphere|cube file 1.97 - string tex_bump; // bump 1.98 - 1.99 - obj_mat() { reset(); } 1.100 - 1.101 - void reset() { 1.102 - ambient = diffuse = Vector3(0.5, 0.5, 0.5); 1.103 - specular = Vector3(0.0, 0.0, 0.0); 1.104 - name = tex_dif = tex_spec = tex_shin = tex_alpha = tex_refl = tex_bump = ""; 1.105 - shininess = 0; 1.106 - ior = alpha = 1; 1.107 - refl = 0.0; 1.108 - } 1.109 -}; 1.110 - 1.111 -static bool read_materials(FILE *fp, vector<obj_mat> *vmtl); 1.112 -static Mesh *cons_mesh(obj_file *obj); 1.113 - 1.114 -static int get_cmd(char *str); 1.115 -static bool is_int(const char *str); 1.116 -static bool is_float(const char *str); 1.117 -static bool parse_vec(Vector3 *vec); 1.118 -static bool parse_color(Vector3 *col); 1.119 -static bool parse_face(obj_face *face); 1.120 -static const char *parse_map(); 1.121 - 1.122 -static bool find_file(char *res, int sz, const char *fname, const char *path = ".", const char *mode = "rb"); 1.123 -static const char *dirname(const char *str); 1.124 - 1.125 -static Vector3 operator -(const Vector3 &a, const Vector3 &b); 1.126 -static Vector3 cross(const Vector3 &a, const Vector3 &b); 1.127 - 1.128 -static map<string, int> matnames; 1.129 - 1.130 - 1.131 #define FEQ(a, b) (fabs((a) - (b)) < 1e-8) 1.132 bool Face::operator ==(const Face &f) const 1.133 { 1.134 @@ -188,568 +64,3 @@ 1.135 } 1.136 return &matlib[0]; 1.137 } 1.138 - 1.139 - 1.140 -#define INVALID_IDX INT_MIN 1.141 - 1.142 -#define SEP " \t\n\r\v" 1.143 -#define BUF_SZ 512 1.144 - 1.145 -bool Scene::load(const char *fname) 1.146 -{ 1.147 - FILE *fp; 1.148 - 1.149 - if(!(fp = fopen(fname, "rb"))) { 1.150 - fprintf(stderr, "failed to open %s: %s\n", fname, strerror(errno)); 1.151 - return false; 1.152 - } 1.153 - 1.154 - bool res = load(fp); 1.155 - fclose(fp); 1.156 - return res; 1.157 -} 1.158 - 1.159 -bool Scene::load(FILE *fp) 1.160 -{ 1.161 - static int seq; 1.162 - char cur_name[16]; 1.163 - 1.164 - obj_file obj; 1.165 - 1.166 - sprintf(cur_name, "default%02d.obj", seq++); 1.167 - obj.cur_obj = cur_name; 1.168 - 1.169 - int prev_cmd = 0, obj_added = 0; 1.170 - for(;;) { 1.171 - Vector3 vec; 1.172 - obj_face face; 1.173 - 1.174 - char line[BUF_SZ]; 1.175 - fgets(line, sizeof line, fp); 1.176 - if(feof(fp)) { 1.177 - break; 1.178 - } 1.179 - 1.180 - char *tok; 1.181 - if(!(tok = strtok(line, SEP))) { 1.182 - continue; // ignore empty lines 1.183 - } 1.184 - 1.185 - int cmd; 1.186 - if((cmd = get_cmd(tok)) == -1) { 1.187 - continue; // ignore unknown commands ... 1.188 - } 1.189 - 1.190 - switch(cmd) { 1.191 - case CMD_V: 1.192 - if(!parse_vec(&vec)) { 1.193 - continue; 1.194 - } 1.195 - obj.v.push_back(vec); 1.196 - break; 1.197 - 1.198 - case CMD_VN: 1.199 - if(!parse_vec(&vec)) { 1.200 - continue; 1.201 - } 1.202 - obj.vn.push_back(vec); 1.203 - break; 1.204 - 1.205 - case CMD_VT: 1.206 - if(!parse_vec(&vec)) { 1.207 - continue; 1.208 - } 1.209 - vec.y = 1.0 - vec.y; 1.210 - obj.vt.push_back(vec); 1.211 - break; 1.212 - 1.213 - case CMD_O: 1.214 - case CMD_G: 1.215 - if(prev_cmd == CMD_O || prev_cmd == CMD_G) { 1.216 - break; // just in case we've got both of them in a row 1.217 - } 1.218 - /* if we have any previous data, group them up, add the object 1.219 - * and continue with the new one... 1.220 - */ 1.221 - if(!obj.f.empty()) { 1.222 - Mesh *mesh = cons_mesh(&obj); 1.223 - mesh->matid = matnames[obj.cur_mat]; 1.224 - add_mesh(mesh); 1.225 - obj_added++; 1.226 - 1.227 - obj.f.clear(); // clean the face list 1.228 - } 1.229 - if((tok = strtok(0, SEP))) { 1.230 - obj.cur_obj = tok; 1.231 - } else { 1.232 - sprintf(cur_name, "default%02d.obj", seq++); 1.233 - obj.cur_obj = cur_name; 1.234 - } 1.235 - break; 1.236 - 1.237 - case CMD_MTLLIB: 1.238 - if((tok = strtok(0, SEP))) { 1.239 - char path[PATH_MAX]; 1.240 - 1.241 - sprintf(path, ".:%s", dirname(tok)); 1.242 - if(!find_file(path, PATH_MAX, tok, path)) { 1.243 - fprintf(stderr, "material library not found: %s\n", tok); 1.244 - continue; 1.245 - } 1.246 - 1.247 - FILE *mfile; 1.248 - if(!(mfile = fopen(path, "rb"))) { 1.249 - fprintf(stderr, "failed to open material library: %s\n", path); 1.250 - continue; 1.251 - } 1.252 - 1.253 - // load all materials of the mtl file into a vector 1.254 - vector<obj_mat> vmtl; 1.255 - if(!read_materials(mfile, &vmtl)) { 1.256 - continue; 1.257 - } 1.258 - fclose(mfile); 1.259 - 1.260 - // and add them all to the scene 1.261 - for(size_t i=0; i<vmtl.size(); i++) { 1.262 - Material mat; 1.263 - 1.264 - mat.kd[0] = vmtl[i].diffuse.x; 1.265 - mat.kd[1] = vmtl[i].diffuse.y; 1.266 - mat.kd[2] = vmtl[i].diffuse.z; 1.267 - 1.268 - mat.ks[0] = vmtl[i].specular.x; 1.269 - mat.ks[1] = vmtl[i].specular.y; 1.270 - mat.ks[2] = vmtl[i].specular.z; 1.271 - 1.272 - mat.kt = 1.0 - vmtl[i].alpha; 1.273 - mat.kr = vmtl[i].refl; 1.274 - mat.spow = vmtl[i].shininess; 1.275 - 1.276 - matlib.push_back(mat); 1.277 - matnames[vmtl[i].name] = i; 1.278 - } 1.279 - } 1.280 - break; 1.281 - 1.282 - case CMD_USEMTL: 1.283 - if((tok = strtok(0, SEP))) { 1.284 - obj.cur_mat = tok; 1.285 - } else { 1.286 - obj.cur_mat = ""; 1.287 - } 1.288 - break; 1.289 - 1.290 - case CMD_F: 1.291 - if(!parse_face(&face)) { 1.292 - continue; 1.293 - } 1.294 - 1.295 - // convert negative indices to regular indices 1.296 - for(int i=0; i<4; i++) { 1.297 - if(face.v[i] < 0 && face.v[i] != INVALID_IDX) { 1.298 - face.v[i] = obj.v.size() + face.v[i]; 1.299 - } 1.300 - if(face.n[i] < 0 && face.n[i] != INVALID_IDX) { 1.301 - face.n[i] = obj.vn.size() + face.n[i]; 1.302 - } 1.303 - if(face.t[i] < 0 && face.t[i] != INVALID_IDX) { 1.304 - face.t[i] = obj.vt.size() + face.t[i]; 1.305 - } 1.306 - } 1.307 - 1.308 - // break quads into triangles if needed 1.309 - obj.f.push_back(face); 1.310 - if(face.elem == 4) { 1.311 - face.v[1] = face.v[2]; 1.312 - face.n[1] = face.n[2]; 1.313 - face.t[1] = face.t[2]; 1.314 - 1.315 - face.v[2] = face.v[3]; 1.316 - face.n[2] = face.n[3]; 1.317 - face.t[2] = face.t[3]; 1.318 - 1.319 - obj.f.push_back(face); 1.320 - } 1.321 - break; 1.322 - 1.323 - default: 1.324 - break; // ignore unknown commands 1.325 - } 1.326 - 1.327 - prev_cmd = cmd; 1.328 - } 1.329 - 1.330 - // reached end of file... 1.331 - if(!obj.f.empty()) { 1.332 - Mesh *mesh = cons_mesh(&obj); 1.333 - mesh->matid = matnames[obj.cur_mat]; 1.334 - add_mesh(mesh); 1.335 - obj_added++; 1.336 - } 1.337 - 1.338 - return obj_added > 0; 1.339 -} 1.340 - 1.341 -static Mesh *cons_mesh(obj_file *obj) 1.342 -{ 1.343 - Mesh *mesh; 1.344 - 1.345 - // need at least one of each element 1.346 - bool added_norm = false, added_tc = false; 1.347 - if(obj->vn.empty()) { 1.348 - obj->vn.push_back(Vector3(0, 0, 0)); 1.349 - added_norm = true; 1.350 - } 1.351 - if(obj->vt.empty()) { 1.352 - obj->vt.push_back(Vector3(0, 0, 0)); 1.353 - added_tc = true; 1.354 - } 1.355 - 1.356 - mesh = new Mesh; 1.357 - 1.358 - for(size_t i=0; i<obj->f.size(); i++) { 1.359 - Face face; 1.360 - Vector3 v[3]; 1.361 - 1.362 - for(int j=0; j<3; j++) { 1.363 - obj_face *f = &obj->f[i]; 1.364 - 1.365 - face.v[j].pos[0] = v[j].x = obj->v[f->v[j]].x; 1.366 - face.v[j].pos[1] = v[j].y = obj->v[f->v[j]].y; 1.367 - face.v[j].pos[2] = v[j].z = obj->v[f->v[j]].z; 1.368 - face.v[j].pos[3] = 0.0; 1.369 - 1.370 - int nidx = f->n[j] < 0 ? 0 : f->n[j]; 1.371 - face.v[j].normal[0] = obj->vn[nidx].x; 1.372 - face.v[j].normal[1] = obj->vn[nidx].y; 1.373 - face.v[j].normal[2] = obj->vn[nidx].z; 1.374 - face.v[j].normal[3] = 0.0; 1.375 - 1.376 - int tidx = f->t[j] < 0 ? 0 : f->t[j]; 1.377 - face.v[j].tex[0] = obj->vt[tidx].x; 1.378 - face.v[j].tex[1] = obj->vt[tidx].y; 1.379 - } 1.380 - 1.381 - Vector3 a = v[1] - v[0]; 1.382 - Vector3 b = v[2] - v[0]; 1.383 - Vector3 n = cross(a, b); 1.384 - n.normalize(); 1.385 - 1.386 - face.normal[0] = n.x; 1.387 - face.normal[1] = n.y; 1.388 - face.normal[2] = n.z; 1.389 - face.normal[3] = 0.0; 1.390 - 1.391 - mesh->faces.push_back(face); 1.392 - } 1.393 - 1.394 - if(added_norm) { 1.395 - obj->vn.pop_back(); 1.396 - } 1.397 - if(added_tc) { 1.398 - obj->vt.pop_back(); 1.399 - } 1.400 - 1.401 - return mesh; 1.402 -} 1.403 - 1.404 -static bool read_materials(FILE *fp, vector<obj_mat> *vmtl) 1.405 -{ 1.406 - obj_mat mat; 1.407 - 1.408 - for(;;) { 1.409 - char line[BUF_SZ]; 1.410 - fgets(line, sizeof line, fp); 1.411 - if(feof(fp)) { 1.412 - break; 1.413 - } 1.414 - 1.415 - char *tok; 1.416 - if(!(tok = strtok(line, SEP))) { 1.417 - continue; 1.418 - } 1.419 - 1.420 - int cmd; 1.421 - if((cmd = get_cmd(tok)) == -1) { 1.422 - continue; 1.423 - } 1.424 - 1.425 - switch(cmd) { 1.426 - case CMD_NEWMTL: 1.427 - // add the previous material, and start a new one 1.428 - if(mat.name.length() > 0) { 1.429 - printf("Adding material: %s\n", mat.name.c_str()); 1.430 - vmtl->push_back(mat); 1.431 - mat.reset(); 1.432 - } 1.433 - if((tok = strtok(0, SEP))) { 1.434 - mat.name = tok; 1.435 - } 1.436 - break; 1.437 - 1.438 - case CMD_KA: 1.439 - parse_color(&mat.ambient); 1.440 - break; 1.441 - 1.442 - case CMD_KD: 1.443 - parse_color(&mat.diffuse); 1.444 - break; 1.445 - 1.446 - case CMD_KS: 1.447 - parse_color(&mat.specular); 1.448 - break; 1.449 - 1.450 - case CMD_KR: 1.451 - if((tok = strtok(0, SEP)) && is_float(tok)) { 1.452 - mat.refl = atof(tok); 1.453 - } 1.454 - break; 1.455 - 1.456 - case CMD_NS: 1.457 - if((tok = strtok(0, SEP)) && is_float(tok)) { 1.458 - mat.shininess = atof(tok); 1.459 - } 1.460 - break; 1.461 - 1.462 - case CMD_NI: 1.463 - if((tok = strtok(0, SEP)) && is_float(tok)) { 1.464 - mat.ior = atof(tok); 1.465 - } 1.466 - break; 1.467 - 1.468 - case CMD_D: 1.469 - case CMD_TR: 1.470 - { 1.471 - Vector3 c; 1.472 - if(parse_color(&c)) { 1.473 - mat.alpha = cmd == CMD_D ? c.x : 1.0 - c.x; 1.474 - } 1.475 - } 1.476 - break; 1.477 - 1.478 - case CMD_MAP_KD: 1.479 - mat.tex_dif = parse_map(); 1.480 - break; 1.481 - 1.482 - default: 1.483 - break; 1.484 - } 1.485 - } 1.486 - 1.487 - if(mat.name.length() > 0) { 1.488 - printf("Adding material: %s\n", mat.name.c_str()); 1.489 - vmtl->push_back(mat); 1.490 - } 1.491 - return true; 1.492 -} 1.493 - 1.494 -static int get_cmd(char *str) 1.495 -{ 1.496 - char *s = str; 1.497 - while((*s = toupper(*s))) s++; 1.498 - 1.499 - for(int i=0; cmd_names[i]; i++) { 1.500 - if(strcmp(str, cmd_names[i]) == 0) { 1.501 - return i; 1.502 - } 1.503 - } 1.504 - return CMD_UNK; 1.505 -} 1.506 - 1.507 -static bool is_int(const char *str) 1.508 -{ 1.509 - char *tmp; 1.510 - strtol(str, &tmp, 10); 1.511 - return tmp != str; 1.512 -} 1.513 - 1.514 -static bool is_float(const char *str) 1.515 -{ 1.516 - char *tmp; 1.517 - strtod(str, &tmp); 1.518 - return tmp != str; 1.519 -} 1.520 - 1.521 -static bool parse_vec(Vector3 *vec) 1.522 -{ 1.523 - for(int i=0; i<3; i++) { 1.524 - char *tok; 1.525 - 1.526 - if(!(tok = strtok(0, SEP)) || !is_float(tok)) { 1.527 - if(i < 2) { 1.528 - return false; 1.529 - } 1.530 - vec->z = 0.0; 1.531 - } else { 1.532 - float v = atof(tok); 1.533 - 1.534 - switch(i) { 1.535 - case 0: 1.536 - vec->x = v; 1.537 - break; 1.538 - case 1: 1.539 - vec->y = v; 1.540 - break; 1.541 - case 2: 1.542 - vec->z = v; 1.543 - break; 1.544 - } 1.545 - } 1.546 - } 1.547 - return true; 1.548 -} 1.549 - 1.550 -static bool parse_color(Vector3 *col) 1.551 -{ 1.552 - for(int i=0; i<3; i++) { 1.553 - char *tok; 1.554 - 1.555 - if(!(tok = strtok(0, SEP)) || !is_float(tok)) { 1.556 - col->y = col->z = col->x; 1.557 - return i > 0 ? true : false; 1.558 - } 1.559 - 1.560 - float v = atof(tok); 1.561 - switch(i) { 1.562 - case 0: 1.563 - col->x = v; 1.564 - break; 1.565 - case 1: 1.566 - col->y = v; 1.567 - break; 1.568 - case 2: 1.569 - col->z = v; 1.570 - break; 1.571 - } 1.572 - } 1.573 - return true; 1.574 -} 1.575 - 1.576 -static bool parse_face(obj_face *face) 1.577 -{ 1.578 - char *tok[] = {0, 0, 0, 0}; 1.579 - face->elem = 0; 1.580 - 1.581 - for(int i=0; i<4; i++) { 1.582 - if((!(tok[i] = strtok(0, SEP)) || !is_int(tok[i]))) { 1.583 - if(i < 3) return false; // less than 3 verts? not a polygon 1.584 - } else { 1.585 - face->elem++; 1.586 - } 1.587 - } 1.588 - 1.589 - for(int i=0; i<4; i++) { 1.590 - char *subtok = tok[i]; 1.591 - 1.592 - if(!subtok || !*subtok || !is_int(subtok)) { 1.593 - if(i < 3) { 1.594 - return false; 1.595 - } 1.596 - face->v[i] = INVALID_IDX; 1.597 - } else { 1.598 - face->v[i] = atoi(subtok); 1.599 - if(face->v[i] > 0) face->v[i]--; /* convert to 0-based */ 1.600 - } 1.601 - 1.602 - while(subtok && *subtok && *subtok != '/') { 1.603 - subtok++; 1.604 - } 1.605 - if(subtok && *subtok && *++subtok && is_int(subtok)) { 1.606 - face->t[i] = atoi(subtok); 1.607 - if(face->t[i] > 0) face->t[i]--; /* convert to 0-based */ 1.608 - } else { 1.609 - face->t[i] = INVALID_IDX; 1.610 - } 1.611 - 1.612 - while(subtok && *subtok && *subtok != '/') { 1.613 - subtok++; 1.614 - } 1.615 - if(subtok && *subtok && *++subtok && is_int(subtok)) { 1.616 - face->n[i] = atoi(subtok); 1.617 - if(face->n[i] > 0) face->n[i]--; /* convert to 0-based */ 1.618 - } else { 1.619 - face->n[i] = INVALID_IDX; 1.620 - } 1.621 - } 1.622 - 1.623 - return true; 1.624 -} 1.625 - 1.626 -static const char *parse_map() 1.627 -{ 1.628 - char *tok, *prev = 0; 1.629 - 1.630 - while((tok = strtok(0, SEP))) { 1.631 - prev = tok; 1.632 - } 1.633 - 1.634 - return prev ? prev : ""; 1.635 -} 1.636 - 1.637 -static bool find_file(char *res, int sz, const char *fname, const char *path, const char *mode) 1.638 -{ 1.639 - FILE *fp; 1.640 - const char *beg, *end; 1.641 - int fnamelen = strlen(fname); 1.642 - 1.643 - beg = path; 1.644 - while(beg && *beg) { 1.645 - end = beg; 1.646 - while(*end && *end != ':') { 1.647 - end++; 1.648 - } 1.649 - 1.650 - int res_len = end - beg; 1.651 - char *pathname = (char*)alloca(res_len + fnamelen + 2); 1.652 - memcpy(pathname, beg, res_len); 1.653 - pathname[res_len] = 0; 1.654 - if(res_len) { 1.655 - strcat(pathname, "/"); 1.656 - } 1.657 - strcat(pathname, fname); 1.658 - 1.659 - if((fp = fopen(pathname, mode))) { 1.660 - fclose(fp); 1.661 - strncpy(res, pathname, sz); 1.662 - return true; 1.663 - } 1.664 - 1.665 - beg += res_len; 1.666 - if(*beg == ':') beg++; 1.667 - } 1.668 - return false; 1.669 -} 1.670 - 1.671 -static const char *dirname(const char *str) 1.672 -{ 1.673 - static char buf[PATH_MAX]; 1.674 - 1.675 - if(!str || !*str) { 1.676 - strcpy(buf, "."); 1.677 - } else { 1.678 - strncpy(buf, str, PATH_MAX); 1.679 - char *ptr = strrchr(buf, '/'); 1.680 - 1.681 - if(ptr && *ptr) { 1.682 - *ptr = 0; 1.683 - } else { 1.684 - strcpy(buf, "."); 1.685 - } 1.686 - } 1.687 - return buf; 1.688 -} 1.689 - 1.690 -static Vector3 operator -(const Vector3 &a, const Vector3 &b) 1.691 -{ 1.692 - return Vector3(a.x - b.x, a.y - b.y, a.z - b.z); 1.693 -} 1.694 - 1.695 -static Vector3 cross(const Vector3 &a, const Vector3 &b) 1.696 -{ 1.697 - Vector3 res; 1.698 - res.x = a.y * b.z - a.z * b.y; 1.699 - res.y = a.z * b.x - a.x * b.z; 1.700 - res.z = a.x * b.y - a.y * b.x; 1.701 - return res; 1.702 -}