mirror of
				https://github.com/zenorogue/hyperrogue.git
				synced 2025-10-30 13:32:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1787 lines
		
	
	
		
			52 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1787 lines
		
	
	
		
			52 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Hyperbolic Rogue -- texture mode
 | |
| // Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details
 | |
| 
 | |
| /** \file textures.cpp
 | |
|  *  \brief texture mode, also the basic definitions of textures, used in 3D mode
 | |
|  */
 | |
| 
 | |
| #include "hyper.h"
 | |
| #if CAP_TEXTURE
 | |
| namespace hr {
 | |
| 
 | |
| #if HDR
 | |
| struct texture_triangle {
 | |
|   array<hyperpoint, 3> v;
 | |
|   array<shiftpoint, 3> tv;
 | |
|   texture_triangle(array<hyperpoint, 3> _v, array<shiftpoint, 3> _tv) : v(_v), tv(_tv) {}
 | |
|   };
 | |
| 
 | |
| struct textureinfo : basic_textureinfo {
 | |
|   shiftmatrix M;
 | |
|   vector<texture_triangle> triangles;
 | |
|   vector<glvertex> vertices;
 | |
|   cell *c;
 | |
|   vector<shiftmatrix> matrices;
 | |
|   
 | |
|   // these are required to adjust to geometry changes
 | |
|   int current_type, symmetries;
 | |
|   };
 | |
| #endif
 | |
| 
 | |
| #if HDR
 | |
| string find_file(string s);
 | |
| #endif
 | |
| 
 | |
| EX namespace texture {
 | |
| 
 | |
| #if HDR
 | |
| enum eTextureState {
 | |
|   tsOff, tsAdjusting, tsActive
 | |
|   };
 | |
| 
 | |
| struct texture_data {
 | |
|   GLuint textureid;
 | |
| 
 | |
|   int twidth, theight;
 | |
|   bool stretched, original;
 | |
|   int tx, ty, origdim;
 | |
|   
 | |
|   int strx, stry, base_x, base_y;
 | |
|   
 | |
|   texture_data() { textureid = 0; twidth = 2048; theight = 0; stretched = false; original = false; }
 | |
| 
 | |
|   vector<color_t> texture_pixels;
 | |
| 
 | |
|   color_t& get_texture_pixel(int x, int y) {
 | |
|     return texture_pixels[(y&(theight-1))*twidth+(x&(twidth-1))];
 | |
|     }
 | |
|   
 | |
|   vector<pair<color_t*, color_t>> undos;
 | |
|   vector<tuple<cell*, shiftpoint, int> > pixels_to_draw;
 | |
| 
 | |
|   bool loadTextureGL();
 | |
|   bool whitetexture();
 | |
|   bool readtexture(string tn);
 | |
|   void saveRawTexture(string tn);
 | |
| 
 | |
|   void undo();
 | |
|   void undoLock();
 | |
|   void update();
 | |
|   };
 | |
| 
 | |
| struct texture_config {
 | |
|   string texturename;
 | |
|   string configname;
 | |
|   eTextureState tstate;
 | |
|   eTextureState tstate_max;
 | |
| 
 | |
|   transmatrix itt;
 | |
|   
 | |
|   color_t grid_color;
 | |
|   color_t mesh_color;
 | |
|   color_t master_color;
 | |
|   color_t slave_color;
 | |
|   
 | |
|   int color_alpha;
 | |
|   
 | |
|   int gsplits;
 | |
| 
 | |
|   int recolor(color_t col);
 | |
| 
 | |
|   typedef tuple<eGeometry, eVariation, char, int, eModel, ld, ld> texture_parameters; 
 | |
|   texture_parameters orig_texture_parameters;
 | |
|   
 | |
|   map<int, textureinfo> texture_map, texture_map_orig;
 | |
|   set<cell*> models;
 | |
|   
 | |
|   basic_textureinfo tinf3;
 | |
| 
 | |
|   bool texture_tuned;
 | |
|   string texture_tuner;
 | |
|   vector<shiftpoint*> tuned_vertices;
 | |
| 
 | |
|   bool apply(cell *c, const shiftmatrix &V, color_t col);
 | |
|   void mark_triangles();
 | |
| 
 | |
|   void clear_texture_map();
 | |
|   void perform_mapping();
 | |
|   void mapTextureTriangle(textureinfo &mi, const array<hyperpoint, 3>& v, const array<shiftpoint, 3>& tv, int splits);
 | |
|   void mapTextureTriangle(textureinfo &mi, const array<hyperpoint, 3>& v, const array<shiftpoint, 3>& tv) { mapTextureTriangle(mi, v, tv, gsplits); }
 | |
|   void mapTexture2(textureinfo& mi);
 | |
|   void finish_mapping();
 | |
|   void true_remap();
 | |
|   void remap();
 | |
|   bool correctly_mapped;
 | |
|   hyperpoint texture_coordinates(shiftpoint);
 | |
| 
 | |
|   void drawRawTexture();
 | |
|   void saveFullTexture(string tn);
 | |
| 
 | |
|   bool save();
 | |
|   bool load();
 | |
|   
 | |
|   texture_data data;
 | |
| 
 | |
|   texture_config() {
 | |
|     // argh, no member initialization in some of my compilers
 | |
|     texturename = find_file("textures/hyperrogue-texture.png");
 | |
|     configname = find_file("textures/hyperrogue.txc");
 | |
|     itt = Id; 
 | |
|     grid_color = 0;
 | |
|     mesh_color = 0;
 | |
|     master_color = 0xFFFFFF30;
 | |
|     slave_color = 0xFF000008;
 | |
|     color_alpha = 128;
 | |
|     gsplits = 1;
 | |
|     texture_tuned = false;
 | |
|     }
 | |
|   
 | |
|   };
 | |
| #endif
 | |
| 
 | |
| EX cpatterntype cgroup;
 | |
| 
 | |
| #if CAP_PNG
 | |
| SDL_Surface *convertSurface(SDL_Surface* s) {
 | |
|   #if SDLVER >= 3
 | |
|   return SDL_ConvertSurface(s, SDL_PIXELFORMAT_BGRA8888);
 | |
| 
 | |
| #else
 | |
|   SDL_PixelFormat fmt;
 | |
|   // fmt.format = SDL_PIXELFORMAT_BGRA8888;
 | |
|   fmt.BitsPerPixel = 32;
 | |
|   fmt.BytesPerPixel = 4;
 | |
| 
 | |
|   fmt.Ashift=24;
 | |
|   fmt.Rshift=16; 
 | |
|   fmt.Gshift=8; 
 | |
|   fmt.Bshift=0; 
 | |
|   fmt.Amask=0xff<<24;
 | |
|   fmt.Rmask=0xff<<16;  
 | |
|   fmt.Gmask=0xff<<8;  
 | |
|   fmt.Bmask=0xff;  
 | |
|   fmt.Aloss = fmt.Rloss = fmt.Gloss = fmt.Bloss = 0;
 | |
|   fmt.palette = NULL;
 | |
| 
 | |
| #if SDLVER <= 1
 | |
|   fmt.alpha = 0;
 | |
|   fmt.colorkey = 0x1ffffff;
 | |
| #endif
 | |
|   
 | |
|   return SDL_ConvertSurface(s, &fmt, SDL_SWSURFACE);
 | |
| #endif
 | |
|   }
 | |
| #endif
 | |
| 
 | |
| struct undo {
 | |
|   unsigned* pix;
 | |
|   unsigned last;
 | |
|   };
 | |
| 
 | |
| EX texture_config config;
 | |
| 
 | |
| EX bool saving = false;
 | |
| 
 | |
| template<class T, class U> void scale_colorarray(int origdim, int targetdim, const T& src, const U& dest) {
 | |
|   int ox = 0, tx = 0, partials[4];
 | |
|   int omissing = targetdim, tmissing = origdim;
 | |
|   for(int p=0; p<4; p++) partials[p] = 0;
 | |
|   
 | |
|   while(tx < targetdim) {
 | |
|     int fv = min(omissing, tmissing);
 | |
|     color_t c = src(ox);
 | |
|     for(int p=0; p<4; p++) 
 | |
|       partials[p] += part(c, p) * fv;
 | |
|     omissing -= fv; tmissing -= fv;
 | |
|     if(omissing == 0) { 
 | |
|       ox++; omissing = targetdim; 
 | |
|       }
 | |
|     if(tmissing == 0) {
 | |
|       color_t target;
 | |
|       for(int p=0; p<4; p++) {
 | |
|         part(target, p) = partials[p] / origdim;
 | |
|         partials[p] = 0;
 | |
|         }
 | |
|       dest(tx++, target);
 | |
|       tmissing = origdim;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   
 | |
| bool texture_data::loadTextureGL() {
 | |
| 
 | |
|   if(textureid == 0) glGenTextures(1, &textureid);
 | |
| 
 | |
|   glBindTexture( GL_TEXTURE_2D, textureid);
 | |
|   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
 | |
|   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
 | |
|   
 | |
|   // BGRA may be not supported in the web version
 | |
|   if(ISWEB) for(auto& p: texture_pixels) swap(part(p, 0), part(p, 2));
 | |
|   
 | |
|   glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, twidth, theight, 0,
 | |
|     ISWEB ? GL_RGBA : GL_BGRA, GL_UNSIGNED_BYTE, 
 | |
|     &texture_pixels[0] );
 | |
| 
 | |
|   if(ISWEB) for(auto& p: texture_pixels) swap(part(p, 0), part(p, 2));
 | |
|  
 | |
|   return true;
 | |
|   }
 | |
| 
 | |
| bool texture_data::whitetexture() {
 | |
|   undos.clear();
 | |
|   texture_pixels.resize(0);
 | |
|   if(theight == 0) theight = twidth;
 | |
|   texture_pixels.resize(twidth * theight, 0xFFFFFFFF);
 | |
|   pixels_to_draw.clear();
 | |
|   return true;
 | |
|   }
 | |
| 
 | |
| bool texture_data::readtexture(string tn) {
 | |
| 
 | |
| #if CAP_SDL_IMG || CAP_PNG
 | |
|   undos.clear();
 | |
|   
 | |
| #if CAP_SDL_IMG  
 | |
|   SDL_Surface *txt = IMG_Load(tn.c_str());
 | |
|   if(!txt) {
 | |
|     addMessage(XLAT("Failed to load %1", texturename));
 | |
|     return false;
 | |
|     }
 | |
|   auto txt2 = convertSurface(txt);
 | |
|   SDL_DestroySurface(txt);
 | |
| 
 | |
|   tx = txt2->w, ty = txt2->h;
 | |
|   
 | |
|   auto pix = [&] (int x, int y) { return qpixel(txt2, x, y); };
 | |
| 
 | |
| #elif CAP_PNG
 | |
|   
 | |
|   FILE *f = fopen(tn.c_str(), "rb");
 | |
|   if(!f) { printf("failed to open file\n"); return false; }
 | |
|   png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
 | |
|   if(!png) { printf("failed to create_read_struct\n"); return false; }
 | |
|   if(setjmp(png_jmpbuf(png))) { 
 | |
|     printf("failed to read\n");
 | |
|     return false;  
 | |
|     }
 | |
|   png_init_io(png, f);
 | |
| 
 | |
|   // set the expected format
 | |
|   png_infop info = png_create_info_struct(png);
 | |
|   png_read_info(png, info);
 | |
|   tx = png_get_image_width(png, info);
 | |
|   ty = png_get_image_height(png, info);    
 | |
|   png_byte color_type = png_get_color_type(png, info);
 | |
|   png_byte bit_depth = png_get_bit_depth(png, info);
 | |
|   if(bit_depth == 16) png_set_strip_16(png);  
 | |
|   if(color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png);
 | |
|   if(color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png);
 | |
|   if(png_get_valid(png, info, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png);  
 | |
|   if(color_type == PNG_COLOR_TYPE_RGB ||
 | |
|      color_type == PNG_COLOR_TYPE_GRAY ||
 | |
|      color_type == PNG_COLOR_TYPE_PALETTE)
 | |
|     png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
 | |
| 
 | |
|   if(color_type == PNG_COLOR_TYPE_GRAY ||
 | |
|      color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
 | |
|     png_set_gray_to_rgb(png);  
 | |
|   png_read_update_info(png, info);
 | |
| 
 | |
|   // read png
 | |
|   vector<png_bytep> row_pointers(ty);
 | |
| 
 | |
|   vector<color_t> origpixels(ty * tx);
 | |
| 
 | |
|   for(int y = 0; y < ty; y++) 
 | |
|     row_pointers[y] = (png_bytep) & origpixels[y * tx];
 | |
| 
 | |
|   png_read_image(png, &row_pointers[0]);
 | |
|   fclose(f);
 | |
| 
 | |
|   for(int i=0; i<ty*tx; i++)
 | |
|     swap(part(origpixels[i], 0), part(origpixels[i], 2));
 | |
|     
 | |
|   auto pix = [&] (int x, int y) { 
 | |
|     if(x<0 || y<0 || x >= tx || y >= ty) return (color_t) 0;
 | |
|     return origpixels[y*tx + x];
 | |
|     };
 | |
|   
 | |
|   printf("texture read OK\n");
 | |
| 
 | |
| #endif
 | |
| 
 | |
|   if(twidth == 0) 
 | |
|     twidth = next_p2(tx);
 | |
|   if(theight == 0) theight = (stretched || original) ? next_p2(ty) : twidth;
 | |
| 
 | |
|   texture_pixels.resize(twidth * theight);
 | |
| 
 | |
|   if(stretched) {
 | |
|     int i = 0;
 | |
|     println(hlog, tx, " -> " , twidth, " / " , ty, " -> ", theight);
 | |
|     for(int y=0; y<theight; y++)
 | |
|     for(int x=0; x<twidth; x++)
 | |
|       texture_pixels[i++] = pix(x * tx / twidth, y * ty / theight);
 | |
|     strx = twidth; stry = theight; base_x = base_y = 0;
 | |
|     }
 | |
|   
 | |
|   else if(tx == twidth && ty == theight) {
 | |
|     int i = 0;
 | |
|     for(int y=0; y<ty; y++)
 | |
|     for(int x=0; x<tx; x++)
 | |
|       texture_pixels[i++] = pix(x, y);
 | |
|     strx = twidth; stry = theight; base_x = base_y = 0;
 | |
|     }
 | |
|   
 | |
|   else if(original) {
 | |
|     base_x = 0;
 | |
|     base_y = 0;
 | |
|     strx = tx; stry = ty;
 | |
|     for(int y=0; y<ty; y++)
 | |
|     for(int x=0; x<tx; x++)
 | |
|       get_texture_pixel(x, y) = pix(x,y);
 | |
|     }
 | |
|    
 | |
|   else {
 | |
|   
 | |
|     origdim = max(tx, ty);
 | |
|     base_x = origdim/2 - tx/2;
 | |
|     base_y = origdim/2 - ty/2;
 | |
|     
 | |
|     strx = twidth * tx / origdim;
 | |
|     stry = theight * ty / origdim;
 | |
| 
 | |
|     qpixel_pixel_outside = 0; // outside is black
 | |
|   
 | |
|     vector<int> half_expanded(twidth * ty);  
 | |
|     for(int y=0; y<ty; y++)
 | |
|       scale_colorarray(origdim, twidth,
 | |
|         [&] (int x) { return pix(x - base_x,y); },
 | |
|         [&] (int x, int v) { half_expanded[twidth * y + x] = v; }
 | |
|         );
 | |
| 
 | |
|     for(int x=0; x<twidth; x++)
 | |
|       scale_colorarray(origdim, twidth,
 | |
|         [&] (int y) { return y-base_y < 0 || y-base_y >= ty ? 0 : half_expanded[x + (y-base_y) * twidth]; }, 
 | |
|         [&] (int y, int v) { get_texture_pixel(x, y) = v; }
 | |
|         );
 | |
|     
 | |
|     base_x = base_x * twidth / origdim;
 | |
|     base_y = base_y * theight / origdim;
 | |
|     }
 | |
| 
 | |
| #if CAP_SDL_IMG
 | |
|   SDL_DestroySurface(txt2);
 | |
| #endif
 | |
|   
 | |
|   return true;
 | |
| #else
 | |
|   return false;
 | |
| #endif
 | |
|   }
 | |
| 
 | |
| void texture_data::saveRawTexture(string tn) {
 | |
|   SDL_Surface *sraw = SDL_CreateRGBSurface(SDL_SWSURFACE,twidth,twidth,32,0,0,0,0);
 | |
|   for(int y=0; y<twidth; y++)
 | |
|   for(int x=0; x<twidth; x++)
 | |
|     qpixel(sraw,x,y) = get_texture_pixel(x, y);
 | |
|   IMAGESAVE(sraw, tn.c_str());
 | |
|   SDL_DestroySurface(sraw);
 | |
|   addMessage(XLAT("Saved the raw texture to %1", tn));
 | |
|   }
 | |
| 
 | |
| hyperpoint texture_config::texture_coordinates(shiftpoint h) {
 | |
|   hyperpoint inmodel;
 | |
|   applymodel(h, inmodel);
 | |
|   inmodel[0] *= current_display->radius * 1. / current_display->scrsize;
 | |
|   inmodel[1] *= current_display->radius * pconf.stretch / current_display->scrsize;
 | |
|   inmodel[2] = 1;
 | |
|   inmodel = itt * inmodel;
 | |
|   inmodel[0] = (inmodel[0] + 1) / 2;
 | |
|   inmodel[1] = (inmodel[1] + 1) / 2;
 | |
|   return inmodel;
 | |
|   }
 | |
| 
 | |
| void texture_config::mapTextureTriangle(textureinfo &mi, const array<hyperpoint, 3>& v, const array<shiftpoint, 3>& tv, int splits) {
 | |
| 
 | |
|   if(splits) {
 | |
|     array<hyperpoint, 3> v2 = make_array( mid(v[1], v[2]), mid(v[2], v[0]), mid(v[0], v[1]) );
 | |
|     array<shiftpoint, 3> tv2 = make_array( mid(tv[1], tv[2]), mid(tv[2], tv[0]), mid(tv[0], tv[1]) );
 | |
|     mapTextureTriangle(mi, make_array(v[0], v2[2], v2[1]),  make_array(tv[0], tv2[2], tv2[1]),  splits-1);
 | |
|     mapTextureTriangle(mi, make_array(v[1], v2[0], v2[2]),  make_array(tv[1], tv2[0], tv2[2]),  splits-1);
 | |
|     mapTextureTriangle(mi, make_array(v[2], v2[1], v2[0]),  make_array(tv[2], tv2[1], tv2[0]),  splits-1);
 | |
|     mapTextureTriangle(mi, make_array(v2[0], v2[1], v2[2]), make_array(tv2[0], tv2[1], tv2[2]), splits-1);
 | |
|     return;
 | |
|     }
 | |
|     
 | |
|   for(int i=0; i<3; i++) {
 | |
|     mi.vertices.push_back(glhr::pointtogl(v[i]));
 | |
|     hyperpoint inmodel;
 | |
|     mi.tvertices.push_back(glhr::pointtogl(texture_coordinates(tv[i])));
 | |
|     }
 | |
|   
 | |
|   // when reading a spherical band in a cylindrical projection,
 | |
|   // take care that texture vertices are mapped not around the sphere
 | |
|   if(sphere && mdBandAny()) {
 | |
|     int s = isize(mi.tvertices)-3;
 | |
|     ld xmin = min(mi.tvertices[s][0], min(mi.tvertices[s+1][0], mi.tvertices[s+2][0]));
 | |
|     ld xmax = max(mi.tvertices[s][0], max(mi.tvertices[s+1][0], mi.tvertices[s+2][0]));
 | |
|     if(xmax - xmin > .5) {
 | |
|       for(int ss=s; ss<s+3; ss++) 
 | |
|         if(mi.tvertices[ss][0] < .5) mi.tvertices[ss][0] += 1;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
| texture_triangle *edited_triangle;
 | |
| textureinfo *edited_tinfo;
 | |
| 
 | |
| int celltriangles(cell *c) {
 | |
|   return c->type;
 | |
|   }
 | |
| 
 | |
| array<shiftpoint, 3> findTextureTriangle(cell *c, patterns::patterninfo& si, int i) {
 | |
|   shiftmatrix M = ggmatrix(c) * applyPatterndir(c, si);
 | |
|   return make_array(M * C0, M * get_corner_position(c, i), M * get_corner_position(c, i+1));
 | |
|   }
 | |
| 
 | |
| // using: mouseh, mouseouver
 | |
| int getTriangleID(cell *c, patterns::patterninfo& si, shiftpoint h) {
 | |
|   // auto si = getpatterninfo0(c);
 | |
|   ld quality = 1e10;
 | |
|   int best = 0;
 | |
|   for(int i=0; i<celltriangles(c); i++) {
 | |
|     auto t = findTextureTriangle(c, si, i);
 | |
|     ld q = hdist(t[1], h) + hdist(t[2], h);
 | |
|     if(q < quality) quality = q, best = i;
 | |
|     }
 | |
|   return best;
 | |
|   }
 | |
| 
 | |
| void mapTexture(cell *c, textureinfo& mi, patterns::patterninfo &si, const shiftmatrix& T, int shift = 0) {
 | |
|   mi.c = c;
 | |
|   mi.symmetries = si.symmetries;
 | |
|   mi.current_type = celltriangles(c);
 | |
|   
 | |
|   mi.M = T * applyPatterndir(c, si);
 | |
|   mi.triangles.clear();
 | |
|   
 | |
|   transmatrix iv = inverse(applyPatterndir(c, si));
 | |
| 
 | |
|   int sd = si.dir;
 | |
|   if((NONSTDVAR) || bt::in()) sd = 0;
 | |
|   
 | |
|   for(int i=0; i<c->type; i++) {
 | |
|     hyperpoint h1 = iv * get_corner_position(c, (i + sd + shift) % c->type);
 | |
|     hyperpoint h2 = iv * get_corner_position(c, (i + sd + shift + 1) % c->type);
 | |
|     mi.triangles.emplace_back(make_array(C0, h1, h2), make_array(mi.M*C0, mi.M*h1, mi.M*h2));
 | |
|     }
 | |
|   }
 | |
| 
 | |
| void texture_config::mapTexture2(textureinfo& mi) {
 | |
|   mi.vertices.clear();
 | |
|   mi.tvertices.clear();
 | |
|   for(auto& t: mi.triangles)
 | |
|     mapTextureTriangle(mi, t.v, t.tv);
 | |
|   }
 | |
|   
 | |
| int texture_config::recolor(color_t col) {
 | |
|   if(color_alpha == 0) return col;
 | |
|   for(int i=1; i<4; i++)
 | |
|     part(col, i) = color_alpha + ((255-color_alpha) * part(col,i) + 127) / 255;
 | |
|   return col;
 | |
|   }
 | |
| 
 | |
| EX bool texture_aura;
 | |
| 
 | |
| EX bool using_aura() {
 | |
|   return texture_aura && config.tstate == texture::tsActive;
 | |
|   }
 | |
| 
 | |
| bool texture_config::apply(cell *c, const shiftmatrix &V, color_t col) {
 | |
|   if(config.tstate == tsOff || !correctly_mapped) return false;
 | |
| 
 | |
|   using namespace patterns;
 | |
|   auto si = getpatterninfo0(c);
 | |
| 
 | |
|   if(config.tstate == tsAdjusting) {
 | |
|     dynamicval<color_t> d(poly_outline, slave_color);
 | |
|     draw_floorshape(c, V, cgi.shFullFloor, 0, PPR::LINE);
 | |
|     
 | |
|     curvepoint(C0);
 | |
|     for(int i=0; i<c->type; i++) 
 | |
|       curvepoint(get_corner_position(c, i)), curvepoint(C0);
 | |
|     queuecurve(V, slave_color, 0, PPR::LINE);
 | |
| 
 | |
|     return false;
 | |
|     }
 | |
|   try {
 | |
|     auto& mi = texture_map.at(si.id);
 | |
|     
 | |
|     set_floor(cgi.shFullFloor);
 | |
|     qfi.tinf = &mi;
 | |
|     qfi.spin = applyPatterndir(c, si);
 | |
| 
 | |
|     if(grid_color) {
 | |
|       dynamicval<color_t> d(poly_outline, grid_color);
 | |
|       draw_floorshape(c, V, cgi.shFullFloor, 0, PPR::FLOOR);
 | |
|       }
 | |
|       
 | |
|     if(using_aura()) {
 | |
|       for(int i=0; i<isize(mi.tvertices); i += 3) {
 | |
|         ld p[3];
 | |
|         if(inHighQual)
 | |
|         while(true) {
 | |
|           p[0] = hrandf();
 | |
|           p[1] = hrandf();
 | |
|           p[2] = 1 - p[0] - p[1];
 | |
|           if(p[2] >= 0) break;
 | |
|           }
 | |
|         else p[0] = p[1] = p[2] = 1/3.;
 | |
|         ld v[2] = {0,0};
 | |
|         for(int j=0; j<2; j++) for(int k=0; k<3; k++)
 | |
|           v[j] += mi.tvertices[i+k][j] * p[k];
 | |
|   
 | |
|         int vi[2] = {int(v[0] * config.data.twidth), int(v[1] * config.data.twidth)};
 | |
|   
 | |
|         col = config.data.get_texture_pixel(vi[0], vi[1]);
 | |
|         hyperpoint h = glhr::gltopoint(mi.vertices[i]);
 | |
|         addaura(V*h, col, 0);
 | |
|         }
 | |
|       }
 | |
| 
 | |
|     return true;
 | |
|     }
 | |
|   catch(out_of_range&) {
 | |
|     // printf("Ignoring tile #%d / %08x: not mapped\n", si.id, patterns::subcode(c, si));
 | |
|     return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| void texture_config::mark_triangles() {
 | |
|   if(config.tstate == tsAdjusting) 
 | |
|     for(auto& mi: texture_map) {
 | |
|       for(auto& t: mi.second.triangles) {
 | |
|         vector<hyperpoint> t2;
 | |
|         for(int i=0; i<3; i++)
 | |
|           t2.push_back(unshift(t.tv[i]));
 | |
|         prettypoly(t2, master_color, master_color, gsplits);
 | |
|         }
 | |
|       }
 | |
|   }
 | |
| 
 | |
| static const auto current_texture_parameters = tie(geometry, variation, patterns::whichPattern, patterns::subpattern_flags, pmodel, pconf.scale, pconf.alpha);
 | |
| 
 | |
| void texture_config::clear_texture_map() {
 | |
|   texture_map.clear();
 | |
|   edited_triangle = nullptr;
 | |
|   edited_tinfo = nullptr;
 | |
|   tuned_vertices.clear();
 | |
|   models.clear();
 | |
|   texture_tuned = false;
 | |
|   texture_tuner = "";
 | |
|   }
 | |
|   
 | |
| void texture_config::perform_mapping() {
 | |
|   if(gsplits < 0) gsplits = 0;
 | |
|   if(gsplits > 4) gsplits = 4;
 | |
|   using namespace patterns;
 | |
|   
 | |
|   clear_texture_map();
 | |
| 
 | |
|   for(auto& p: gmatrix) {
 | |
|     cell *c = p.first;
 | |
|     auto si = getpatterninfo0(c);
 | |
|     bool replace = false;
 | |
|     
 | |
|     // int sgn = sphere ? -1 : 1;
 | |
|     
 | |
|     if(!texture_map.count(si.id)) 
 | |
|       replace = true;
 | |
|     else if(hdist0(p.second*sphereflip * C0) < hdist0(texture_map[si.id].M * sphereflip * C0))
 | |
|       replace = true;
 | |
| 
 | |
|     if(replace) {
 | |
|       auto& mi = texture_map[si.id];
 | |
|       mapTexture(c, mi, si, p.second);
 | |
|       mi.texture_id = config.data.textureid;
 | |
|       }
 | |
|     }
 | |
|   
 | |
|   for(auto& t: texture_map) models.insert(t.second.c);
 | |
|   
 | |
|   for(auto& p: gmatrix) {
 | |
|     cell *c = p.first;
 | |
|     bool nearmodel = models.count(c);
 | |
|     forCellEx(c2, c) 
 | |
|       if(models.count(c2)) 
 | |
|         nearmodel = true;
 | |
|     if(nearmodel) {
 | |
|       auto si = getpatterninfo0(c);
 | |
|       texture_map[si.id].matrices.push_back(p.second * applyPatterndir(c, si));
 | |
|       }
 | |
|     }
 | |
|     
 | |
|   }
 | |
| 
 | |
| set<int> missing_cells_known;
 | |
| 
 | |
| void texture_config::finish_mapping() {
 | |
|   tinf3.tvertices.clear();
 | |
|   tinf3.texture_id = config.data.textureid;
 | |
|   if(isize(texture_map) && isize(texture_map.begin()->second.triangles)) {
 | |
|     auto& tris = texture_map.begin()->second.triangles;
 | |
| 
 | |
|     for(int a=0; a<8; a++) {
 | |
|       auto& tri = tris[a % isize(tris)];
 | |
|       shiftpoint center = tri.tv[0];
 | |
|       hyperpoint v1 = unshift(tri.tv[1], center.shift) - center.h;
 | |
|       hyperpoint v2 = unshift(tri.tv[2], center.shift) - center.h;
 | |
|       texture_order([&] (ld x, ld y) {
 | |
|         shiftpoint h = shiftless(normalize(center.h + v1 * x + v2 * y), center.shift);
 | |
|         tinf3.tvertices.push_back(glhr::pointtogl(texture_coordinates(h)));
 | |
|         });
 | |
|       }
 | |
|     }
 | |
|   
 | |
|   if(config.tstate == tsActive) {
 | |
|     for(auto& mi: texture_map)
 | |
|       mapTexture2(mi.second);
 | |
|     correctly_mapped = true;
 | |
|     missing_cells_known.clear();
 | |
|     }
 | |
| 
 | |
|   patterns::computeCgroup();
 | |
|   texture::cgroup = patterns::cgroup;
 | |
|   texture_map_orig = texture_map;
 | |
|   orig_texture_parameters = current_texture_parameters;
 | |
|   // printf("texture_map has %d elements (S%d)\n", isize(texture_map), config.tstate);
 | |
|   }
 | |
| 
 | |
| #if CAP_SHOT
 | |
| void texture_config::saveFullTexture(string tn) {
 | |
|   addMessage(XLAT("Saving full texture to %1...", tn));
 | |
|   dynamicval<color_t> dd(grid_color, 0);
 | |
|   dynamicval<color_t> dm(mesh_color, 0);
 | |
|   dynamicval<ld> dx(pconf.xposition, 0);
 | |
|   dynamicval<ld> dy(pconf.yposition, 0);
 | |
|   dynamicval<ld> dvs(pconf.scale, (pmodel == mdDisk && !euclid) ? 1 : pconf.scale);
 | |
|   dynamicval<bool> dro(rug::rugged, false);
 | |
|   dynamicval<bool> dnh(nohud, true);
 | |
|   texture::saving = true;
 | |
|   drawscreen();
 | |
| 
 | |
|   dynamicval<int> dvx(shot::shotx, data.twidth);
 | |
|   dynamicval<int> dvy(shot::shoty, data.twidth);
 | |
|   dynamicval<int> dvf(shot::shotformat, -1);
 | |
|   shot::take(tn.c_str());
 | |
|   texture::saving = false;
 | |
|   
 | |
|   drawscreen();
 | |
|   if(data.readtexture(tn) && data.loadTextureGL()) {
 | |
|     itt = Id; // xyscale(Id, current_display->scrsize * 1. / current_display->radius);
 | |
|     perform_mapping();
 | |
|     finish_mapping();
 | |
|     }
 | |
|   }
 | |
| #endif
 | |
| 
 | |
| bool newmove = false;
 | |
| 
 | |
| vector<glhr::textured_vertex> rtver(4);
 | |
| 
 | |
| EX int raw_texture_opacity = 32;
 | |
| 
 | |
| void texture_config::drawRawTexture() {
 | |
|   glflush();
 | |
|   current_display->next_shader_flags = GF_TEXTURE;
 | |
|   dynamicval<eModel> m(pmodel, mdPixel);
 | |
|   current_display->set_all(0, 0);
 | |
|   glhr::color2(0xFFFFFF00 + raw_texture_opacity);
 | |
|   glBindTexture(GL_TEXTURE_2D, config.data.textureid);
 | |
|   for(int i=0; i<4; i++) {
 | |
|     int cx[4] = {2, -2, -2, 2};
 | |
|     int cy[4] = {2, 2, -2, -2};
 | |
|     int x = cx[i];
 | |
|     int y = cy[i];
 | |
|     hyperpoint inmodel = hpxyz(x, y, 1);
 | |
|     inmodel = itt * inmodel;
 | |
|     rtver[i].texture[0] = (inmodel[0]+1)/2;
 | |
|     rtver[i].texture[1] = (inmodel[1]+1)/2;
 | |
|     rtver[i].coords[0] = x * current_display->scrsize;
 | |
|     rtver[i].coords[1] = y * current_display->scrsize;
 | |
|     rtver[i].coords[2] = 0;
 | |
|     rtver[i].coords[3] = 1;
 | |
|     }
 | |
|   glhr::id_modelview();
 | |
|   glhr::prepare(rtver);
 | |
|   glhr::set_depthtest(false);
 | |
|   glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 | |
|   }
 | |
| 
 | |
| struct magicmapper_point {
 | |
|   cell *c;
 | |
|   hyperpoint cell_relative;
 | |
|   hyperpoint texture_coords;
 | |
|   };
 | |
| 
 | |
| vector<magicmapper_point> amp;
 | |
| 
 | |
| enum eMagicParameter {
 | |
|   mpScale,
 | |
|   mpProjection,
 | |
|   mpMove,
 | |
|   mpRotate,
 | |
|   mpSlant,
 | |
|   mpStretch,
 | |
|   mpTexPosX,
 | |
|   mpTexPosY,
 | |
|   mpMAX
 | |
|   };
 | |
| 
 | |
| EX vector<string> mpnames = {
 | |
|   "affect model scale",
 | |
|   "affect model projection",
 | |
|   "affect model central point",
 | |
|   "affect model rotation",
 | |
|   "affect texture slanting",
 | |
|   "affect texture stretching",
 | |
|   "affect texture position X",
 | |
|   "affect texture position Y"
 | |
|   };
 | |
| 
 | |
| flagtype current_magic = 15;
 | |
| 
 | |
| bool have_mp(eMagicParameter i) { return (current_magic >> i) & 1; }
 | |
| 
 | |
| struct magic_param {
 | |
|   bool do_spin;
 | |
|   int texmode;
 | |
|   ld spinangle, scale, proj, moveangle, shift, slant, stretch, tx, ty;
 | |
| 
 | |
|   void shuffle() {
 | |
|     do_spin = hrand(2);
 | |
|     spinangle = hrandf() - hrandf();
 | |
|     moveangle = hrandf() * TAU;
 | |
|     shift = hrandf() - hrandf();
 | |
|     scale = hrandf() - hrandf();
 | |
|     proj = hrandf() - hrandf();
 | |
|     texmode = hrand(3);
 | |
|     slant = have_mp(mpSlant) ? hrandf() - hrandf() : 0;
 | |
|     stretch = have_mp(mpStretch) ? hrandf() - hrandf() : 0;
 | |
|     tx = have_mp(mpTexPosX) ? hrandf() - hrandf() : 0;
 | |
|     ty = have_mp(mpTexPosY) ? hrandf() - hrandf() : 0;
 | |
|     }
 | |
|   
 | |
|   void affect_itt(const transmatrix& T) {
 | |
|     transmatrix Ti = inverse(T);
 | |
|     for(auto& p: amp)
 | |
|       p.texture_coords = Ti * p.texture_coords;
 | |
|     config.itt = config.itt * T;
 | |
|     }
 | |
|   
 | |
|   void apply(ld delta) {
 | |
|     if(have_mp(mpProjection))
 | |
|       pconf.alpha *= exp(delta * proj);
 | |
|     if(have_mp(mpScale))
 | |
|       pconf.scale *= exp(delta * scale);
 | |
| 
 | |
|     if(do_spin) {
 | |
|       if(have_mp(mpRotate))
 | |
|         View = spin(delta * spinangle) * View;
 | |
|       }
 | |
|     else {
 | |
|       if(have_mp(mpMove))
 | |
|         View = spin(moveangle) * xpush(delta*shift) * spin(-moveangle) * View;
 | |
|       }
 | |
|     
 | |
|     if(texmode == 0 && have_mp(mpStretch)) 
 | |
|       affect_itt(euaffine(hpxyz(0, delta * stretch, 0)));
 | |
| 
 | |
|     if(texmode == 1 && have_mp(mpSlant))
 | |
|       affect_itt(euaffine(hpxyz(delta * slant, 0, 0)));
 | |
|     
 | |
|     if(texmode == 2 && (have_mp(mpTexPosX) || have_mp(mpTexPosY)))
 | |
|       affect_itt(eupush(delta * tx, delta * ty));
 | |
|     
 | |
|     fixmatrix(View);
 | |
|     }
 | |
|   };
 | |
| 
 | |
| ld magic_quality() {
 | |
|   gmatrix.clear();
 | |
|   calcparam();
 | |
| 
 | |
|   ld q = 0;
 | |
|   for(auto& p: amp) {
 | |
|     hyperpoint inmodel;
 | |
|     applymodel(ggmatrix(p.c) * p.cell_relative, inmodel);
 | |
|     inmodel[0] *= current_display->radius * 1. / current_display->scrsize;
 | |
|     inmodel[1] *= current_display->radius * 1. / current_display->scrsize;
 | |
|     q += sqhypot_d(2, inmodel - p.texture_coords);
 | |
|     }
 | |
|   return q;
 | |
|   }
 | |
| 
 | |
| void applyMagic() {
 | |
|   ld cq = magic_quality();
 | |
|   
 | |
|   int last_success = 0;
 | |
|   
 | |
|   for(int s=0; s<50000 &&  s<last_success + 1000; s++) {
 | |
|     magic_param p;
 | |
|     p.shuffle();
 | |
|     
 | |
|     bool failed = false;
 | |
|     
 | |
|     for(ld delta = 1; abs(delta) > 1e-9; delta *= (failed ? -.7 : 1.2)) {
 | |
|       p.apply(delta);
 | |
|       ld nq = magic_quality();
 | |
|       if(nq < cq) {
 | |
|         cq = nq; 
 | |
|         last_success = s;
 | |
|         }
 | |
|       else {
 | |
|         p.apply(-delta);
 | |
|         failed = true;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   config.perform_mapping();
 | |
|   }
 | |
| 
 | |
| enum eTexturePanstate {tpsModel, tpsMove, tpsScale, tpsAffine, tpsZoom, tpsProjection, tpsCell, tpsTriangle, tpsTune};
 | |
| eTexturePanstate panstate;
 | |
| 
 | |
| void mousemovement() {
 | |
|   static hyperpoint lastmouse;
 | |
|   
 | |
|   hyperpoint mouseeu = hpxyz((mousex - current_display->xcenter + .0) / current_display->scrsize, (mousey - current_display->ycenter + .0) / current_display->scrsize, 1);
 | |
|   bool nonzero = mouseeu[0] || mouseeu[1];
 | |
| 
 | |
|   switch(panstate) {
 | |
|     case tpsModel:
 | |
|       if(!newmove && mouseh[2] < 50 && lastmouse[2] < 50) {
 | |
|         panning(shiftless(lastmouse), mouseh);
 | |
|         config.perform_mapping();
 | |
|         }
 | |
|       lastmouse = unshift(mouseh); newmove = false;
 | |
|       break;
 | |
|     
 | |
|     case tpsMove: {
 | |
|       if(!newmove) 
 | |
|         config.itt = config.itt * inverse(eupush(mouseeu)) * eupush(lastmouse);
 | |
|       lastmouse = mouseeu; newmove = false;
 | |
|       break;
 | |
|       }
 | |
|     
 | |
|     case tpsScale: {
 | |
|       if(nonzero && !newmove) 
 | |
|         config.itt = config.itt * inverse(euscalezoom(mouseeu)) * euscalezoom(lastmouse);
 | |
|       if(nonzero) lastmouse = mouseeu;
 | |
|       newmove = false;
 | |
|       break;
 | |
|       }
 | |
|     
 | |
|     case tpsAffine: {
 | |
|       if(!newmove) 
 | |
|         config.itt = config.itt * inverse(euaffine(mouseeu)) * euaffine(lastmouse);
 | |
|       lastmouse = mouseeu; newmove = false;
 | |
|       break;
 | |
|       }
 | |
| 
 | |
|     case tpsZoom: {
 | |
|       // do not zoom in portrait!
 | |
|       if(nonzero && !newmove) {
 | |
|         View = inverse(spintox(mouseeu)) * spintox(lastmouse) * View;
 | |
|         pconf.scale = pconf.scale * sqhypot_d(2, mouseeu) / sqhypot_d(2, lastmouse);
 | |
|         config.perform_mapping();
 | |
|         }
 | |
|       if(nonzero) lastmouse = mouseeu;
 | |
|       newmove = false;
 | |
|       break;
 | |
|       }
 | |
|     
 | |
|     case tpsProjection: {
 | |
|       if(nonzero && !newmove) {          
 | |
|         pconf.alpha = pconf.alpha * sqhypot_d(2, mouseeu) / sqhypot_d(2, lastmouse);
 | |
|         }
 | |
|       if(nonzero) lastmouse = mouseeu;
 | |
|       newmove = false;
 | |
|       break;
 | |
|       }
 | |
|     
 | |
|     case tpsCell: {
 | |
|       cell *c = mouseover;
 | |
|       if(!c) break;
 | |
|       auto si = patterns::getpatterninfo0(c);
 | |
|       if(newmove) {
 | |
|         edited_tinfo = NULL;
 | |
|         if(config.texture_map.count(si.id)) {
 | |
|           edited_tinfo = &config.texture_map[si.id];
 | |
|           newmove = false;
 | |
|           }
 | |
|         }
 | |
|       if(edited_tinfo && isize(edited_tinfo->triangles) == celltriangles(c)) {
 | |
|         for(int i=0; i<celltriangles(c); i++)
 | |
|           edited_tinfo->triangles[i].tv = findTextureTriangle(c, si, i);
 | |
|         config.texture_tuned = true;
 | |
|         }
 | |
|       break;
 | |
|       }
 | |
|     
 | |
|     case tpsTriangle: {
 | |
|       cell *c = mouseover;
 | |
|       if(!c) break;
 | |
|       auto si = patterns::getpatterninfo0(c);
 | |
|       int i = getTriangleID(c, si, mouseh);
 | |
|       if(newmove) {
 | |
|         edited_triangle = NULL;
 | |
|         if(config.texture_map.count(si.id)) {
 | |
|           edited_triangle = &config.texture_map[si.id].triangles[i];
 | |
|           newmove = false;
 | |
|           }
 | |
|         }
 | |
|       if(edited_triangle) {
 | |
|         edited_triangle->tv = findTextureTriangle(c, si, i);
 | |
|         config.texture_tuned = true;
 | |
|         }
 | |
|       break;
 | |
|       }
 | |
| 
 | |
|     case tpsTune: {
 | |
|       ld tdist = 1e20;
 | |
|       if(newmove) {
 | |
|         config.tuned_vertices.clear();
 | |
|         for(auto& a: config.texture_map)
 | |
|           for(auto& t: a.second.triangles)
 | |
|             for(auto& v: t.tv)
 | |
|               if(hdist(v, mouseh) < tdist)
 | |
|                 tdist = hdist(v, mouseh);
 | |
|         for(auto& a: config.texture_map)
 | |
|           for(auto& t: a.second.triangles)
 | |
|             for(auto& v: t.tv)
 | |
|               if(hdist(v, mouseh) < tdist * (1.000001))
 | |
|                 config.tuned_vertices.push_back(&v);
 | |
|         newmove = false;
 | |
|         }
 | |
|       for(auto v: config.tuned_vertices) {
 | |
|         *v = mouseh;
 | |
|         config.texture_tuned = true;
 | |
|         }
 | |
|       break;
 | |
|       }
 | |
|     
 | |
|     default: break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| patterns::patterninfo si_save;  
 | |
| 
 | |
| paramlist texturesavers;
 | |
| 
 | |
| eVariation targetvariation;
 | |
| eGeometry targetgeometry;
 | |
| 
 | |
| string csymbol;
 | |
| 
 | |
| string tes;
 | |
| 
 | |
| void init_textureconfig() {
 | |
| #if CAP_CONFIG
 | |
|   texturesavers = std::move(params);  params.clear();
 | |
|   for(int i=0; i<3; i++)
 | |
|   for(int j=0; j<3; j++)
 | |
|     param_f(config.itt[i][j], "texturematrix_" + its(i) + its(j), i==j ? 1 : 0);
 | |
| 
 | |
|   for(int i=0; i<3; i++)
 | |
|   for(int j=0; j<3; j++)
 | |
|     param_f(View[i][j], "viewmatrix_" + its(i) + its(j), i==j ? 1 : 0);
 | |
| 
 | |
|   param_enum(targetgeometry, "geometry", gNormal);
 | |
|   param_str(tes, "tes", "");
 | |
|   param_enum(pmodel, "used model", mdDisk);
 | |
|   param_f(vid.yshift, "Y shift", 0);
 | |
|   param_f(pconf.yposition, "Y position", 0);
 | |
|   param_f(pconf.xposition, "X position", 0);
 | |
|   param_matrix((matrix_eq&)pconf.cam(), "camera angle", 2);
 | |
|   param_enum(targetvariation, "bitruncated", eVariation::bitruncated);
 | |
|   // ... geometry parameters
 | |
| 
 | |
|   param_enum(patterns::whichPattern, "pattern", patterns::PAT_TYPES);
 | |
|   param_i(patterns::subpattern_flags, "pattern flags", 0);
 | |
| 
 | |
|   param_i(si_save.id, "center type", 1);
 | |
|   param_i(si_save.dir, "center direction", 0);
 | |
|   param_b(si_save.reflect, "center reflection", false);
 | |
|   param_i(config.data.twidth, "texture resolution", 2048);
 | |
|   param_i(config.gsplits, "precision", 1);
 | |
|   
 | |
|   param_color(config.grid_color, "grid color", true, 0);
 | |
|   param_i(config.color_alpha, "alpha color", 0);
 | |
|   param_color(config.mesh_color, "mesh color", true, 0);
 | |
|   
 | |
|   param_f(pconf.alpha, "projection", 1);
 | |
|   param_f(pconf.scale, "scale", 1);
 | |
|   param_f(pconf.stretch, "stretch", 1);
 | |
|   param_f(vid.binary_width, "binary-tiling-width", 1);
 | |
|   
 | |
|   param_str(config.texturename, "texture filename", "");
 | |
|   param_str(config.texture_tuner, "texture tuning", "");
 | |
|   
 | |
|   param_str(csymbol, "symbol", "");
 | |
|   
 | |
|   swap(texturesavers, params);
 | |
| #endif
 | |
|   }
 | |
| 
 | |
| bool texture_config::save() {
 | |
| #if CAP_CONFIG
 | |
|   init_textureconfig();
 | |
|   FILE *f = fopen(configname.c_str(), "wt");
 | |
|   if(!f) return false;
 | |
|   
 | |
|   if(texture_tuned) {
 | |
|     texture_tuner = "";
 | |
|     for(auto& a: config.texture_map)
 | |
|       for(auto& t: a.second.triangles)
 | |
|         for(auto& v: t.tv) 
 | |
|           for(int i=0; i<3; i++) {
 | |
|             texture_tuner += fts(v[i]);
 | |
|             texture_tuner += ';';
 | |
|             }
 | |
|     }
 | |
| 
 | |
|   targetgeometry = geometry;
 | |
|   targetvariation = variation;
 | |
| 
 | |
|   cell *ctr = centerover;
 | |
|   si_save = patterns::getpatterninfo0(ctr);
 | |
| 
 | |
|   if(arb::in()) tes = arb::current.filename;
 | |
|   
 | |
|   csymbol = "";
 | |
|   #if CAP_ARCM
 | |
|   if(arcm::in()) csymbol = arcm::current.symbol;
 | |
|   #endif
 | |
|   
 | |
|   for(auto s: texturesavers) if(s.second->dosave())
 | |
|     fprintf(f, "%s=%s\n", s.second->name.c_str(), s.second->save().c_str());
 | |
|   
 | |
|   fclose(f);
 | |
| #endif
 | |
|   return true;
 | |
|   }
 | |
| 
 | |
| bool texture_config::load() {
 | |
| #if CAP_CONFIG
 | |
|   init_textureconfig();
 | |
| 
 | |
|   FILE *f = fopen(configname.c_str(), "rt");
 | |
|   if(!f) return false;
 | |
|   swap(texturesavers, params);
 | |
|   for(auto s: params) s.second->reset();
 | |
|   loadNewConfig(f);
 | |
|   swap(texturesavers, params);
 | |
|   fclose(f);
 | |
|   polygonal::solve();
 | |
|   
 | |
|   if(1) {
 | |
|     dynamicval<patterns::ePattern> d1(patterns::whichPattern, patterns::whichPattern);
 | |
|     dynamicval<int> d2(patterns::subpattern_flags, patterns::subpattern_flags);
 | |
| 
 | |
|     if(targetgeometry != geometry) {
 | |
|       stop_game();
 | |
|       #if CAP_ARCM
 | |
|       if(targetgeometry == gArchimedean) {
 | |
|         arcm::current.symbol = csymbol;
 | |
|         arcm::current.parse();
 | |
|         if(arcm::current.errors) {
 | |
|           printf("Error while reading Archimedean texture: %s\n", arcm::current.errormsg.c_str());
 | |
|           addMessage("Error: " + arcm::current.errormsg);
 | |
|           return false;
 | |
|           }
 | |
|         }
 | |
|       #endif
 | |
|       if(targetgeometry == gArbitrary) {
 | |
|         arb::run_raw(tes);
 | |
|         stop_game();
 | |
|         }
 | |
|       set_geometry(targetgeometry);
 | |
|       set_variation(targetvariation);
 | |
|       start_game();
 | |
|       return config.load();
 | |
|       }
 | |
|     else if(variation != targetvariation) {
 | |
|       stop_game();
 | |
|       set_variation(targetvariation);
 | |
|       start_game();
 | |
|       return config.load();
 | |
|       }
 | |
|     }
 | |
|   
 | |
| 
 | |
|   if(true) {
 | |
|     celllister cl(currentmap->gamestart(), 20, 10000, NULL);
 | |
|     bool found = false;
 | |
|     for(cell *c: cl.lst) if(euclid || ctof(c)) {
 | |
|       auto si_here = patterns::getpatterninfo0(c);
 | |
|       if(si_here.id == si_save.id && si_here.reflect == si_save.reflect && si_here.dir == si_save.dir) {
 | |
|         centerover = c;
 | |
|         found = true;
 | |
|         break;
 | |
|         }
 | |
|       }
 | |
|     if(!found)
 | |
|       addMessage(XLAT("warning: unable to find the center"));
 | |
|     }
 | |
|   
 | |
|   if(!data.readtexture(texturename)) return false;
 | |
|   if(!data.loadTextureGL()) return false;
 | |
|   calcparam();
 | |
|   models::configure();
 | |
|   drawthemap();
 | |
|   config.tstate = config.tstate_max = tsActive;
 | |
|   string s = std::move(texture_tuner);
 | |
|   perform_mapping();
 | |
|   
 | |
|   texture_tuner = std::move(s);
 | |
|   
 | |
|   if(texture_tuner != "") {
 | |
|     texture_tuned = true;
 | |
|     vector<ld*> coords;
 | |
|     for(auto& a: config.texture_map)
 | |
|       for(auto& t: a.second.triangles)
 | |
|         for(auto& v: t.tv) 
 | |
|           for(int i=0; i<3; i++) 
 | |
|             coords.push_back(&v[i]);
 | |
|     int semicounter = 0;
 | |
|     for(char c: texture_tuner) if(c == ';') semicounter++;
 | |
|     if(semicounter != isize(coords))
 | |
|       addMessage("Tuning error: wrong number");
 | |
|     else {
 | |
|       string cur = "";
 | |
|       int index = 0;
 | |
|       for(char c: texture_tuner)
 | |
|         if(c == ';') {
 | |
|           *(coords[index++]) = atof(cur.c_str());
 | |
|           cur = "";
 | |
|           }
 | |
|         else cur += c;
 | |
|       printf("index = %d semi = %d sc = %d\n", index, semicounter, isize(coords));
 | |
|       }
 | |
|     }
 | |
|     
 | |
|   finish_mapping();
 | |
| #endif
 | |
|   return true;
 | |
|   }
 | |
| 
 | |
| void showMagicMenu() {
 | |
|   cmode = sm::SIDE | sm::MAYDARK | sm::DIALOG_STRICT_X;
 | |
|   gamescreen();
 | |
| 
 | |
|   dialog::init(XLAT("texture auto-adjustment"));
 | |
| 
 | |
|   dialog::addInfo(XLAT("drag from the model to the texture"));
 | |
|   
 | |
|   for(int i=0; i<mpMAX; i++)
 | |
|     dialog::addBoolItem(XLAT(mpnames[i]), have_mp(eMagicParameter(i)), 'a'+i);
 | |
|   
 | |
|   dialog::addSelItem(XLAT("delete markers"), its(isize(amp)), 'D');
 | |
|   dialog::addItem(XLAT("perform auto-adjustment"), 'R');
 | |
|   dialog::addBack();
 | |
| 
 | |
|   getcstat = '-';
 | |
| 
 | |
|   dialog::display();
 | |
| 
 | |
|   if(holdmouse) {
 | |
|     hyperpoint mouseeu = hpxyz((mousex - current_display->xcenter + .0) / current_display->scrsize, (mousey - current_display->ycenter + .0) / current_display->scrsize, 1);
 | |
|     if(newmove) {
 | |
|       magicmapper_point newpoint;
 | |
|       newpoint.c = mouseover;
 | |
|       newpoint.cell_relative = inverse_shift(gmatrix[mouseover], mouseh);
 | |
|       amp.push_back(newpoint);
 | |
|       newmove = false;
 | |
|       }
 | |
|     amp.back().texture_coords = mouseeu;
 | |
|     }
 | |
|   
 | |
|   if(config.tstate == tsAdjusting) {
 | |
|     initquickqueue();
 | |
|     char letter = 'A';
 | |
|     for(auto& am: amp) {
 | |
|       shiftpoint h = ggmatrix(am.c) * am.cell_relative;
 | |
|       queuestr(h, vid.fsize, s0+letter, 0xC00000, 1);
 | |
| 
 | |
|       /*
 | |
|       hyperpoint inmodel;
 | |
|       applymodel(h, inmodel);
 | |
|       inmodel[0] *= current_display->radius * 1. / current_display->scrsize;
 | |
|       inmodel[1] *= current_display->radius * 1. / current_display->scrsize;
 | |
|       */
 | |
| 
 | |
|       queuestr(
 | |
|         current_display->xcenter + current_display->scrsize * am.texture_coords[0], 
 | |
|         current_display->ycenter + current_display->scrsize * am.texture_coords[1],
 | |
|         0, vid.fsize, s0+letter, 0x00C000, 1);
 | |
|       
 | |
|       letter++;
 | |
|       }
 | |
|     quickqueue();
 | |
|     }
 | |
|   
 | |
|   keyhandler = [] (int sym, int uni) {
 | |
|     // handlePanning(sym, uni);
 | |
|     dialog::handleNavigation(sym, uni);
 | |
|     
 | |
|     if(uni == '-' && config.tstate == tsAdjusting) {
 | |
|       if(!holdmouse) {
 | |
|         holdmouse = true;
 | |
|         newmove = true;
 | |
|         }
 | |
|       }
 | |
|     else if(uni >= 'a' && uni < 'a' + mpMAX) 
 | |
|       current_magic ^= 1<<(uni - 'a');
 | |
| 
 | |
|     else if(uni == 'D') amp.clear();
 | |
|     else if(uni == 'R') applyMagic();
 | |
|     else if(doexiton(sym, uni))
 | |
|       popScreen();
 | |
|     };
 | |
|   }
 | |
| 
 | |
| string texturehelp = 
 | |
|   "This mode lets you change the floor tesselation easily -- "
 | |
|   "select 'paint a new texture' and draw like in a Paint program. "
 | |
|   "The obtained pattern can then be easily changed to another geometry, "
 | |
|   "or saved.\n\n"
 | |
|   "Instead of drawing, it is also possible to use an arbitrary image "
 | |
|   "as a texture. "
 | |
|   "Works best with spherical/Euclidean/hyperbolic tesselations "
 | |
|   "(e.g., a photo of a soccerball, or one of the tesselations by M. C. "
 | |
|   "Escher), but it can be also used on arbitrary photos to make them periodic "
 | |
|   "(these probably work best with the 'large picture' setting in geometry selection). "
 | |
|   "Again, tesselations can have their geometry changed.\n\n";
 | |
| 
 | |
| #if CAP_EDIT
 | |
| EX void start_editor() {
 | |
|   if(config.data.whitetexture() && config.data.loadTextureGL()) {
 | |
|     config.tstate = config.tstate_max = tsActive;
 | |
|     config.perform_mapping();
 | |
|     config.finish_mapping();
 | |
|     mapeditor::initdraw(cwt.at);
 | |
|     mapeditor::intexture = true;
 | |
|     mapeditor::drawing_tool = false;
 | |
|     pushScreen(mapeditor::showDrawEditor);
 | |
|     }
 | |
|   }
 | |
| #endif
 | |
| 
 | |
| EX void showMenu() {
 | |
|   cmode = sm::SIDE | sm::MAYDARK | sm::DIALOG_STRICT_X;
 | |
|   gamescreen();
 | |
|   if(config.tstate == tsAdjusting) {
 | |
|     ptds.clear();
 | |
|     config.mark_triangles();
 | |
|     drawqueue();
 | |
|     }
 | |
|   
 | |
|   if(config.tstate == tsOff) {
 | |
|     dialog::init(XLAT("texture mode (off)"));
 | |
|     dialog::addItem(XLAT("select geometry/pattern"), 'r');
 | |
|     dialog::add_action(patterns::pushChangeablePatterns);
 | |
|     if(config.tstate_max == tsAdjusting || config.tstate_max == tsActive) {
 | |
|       dialog::addItem(XLAT("reactivate the texture"), 't');
 | |
|       dialog::add_action([] {
 | |
|         config.tstate = config.tstate_max;
 | |
|         });    
 | |
|       }
 | |
|     if(GDIM == 2 && !rug::rugged) {
 | |
|       dialog::addItem(XLAT("open PNG as texture"), 'o');
 | |
|       dialog::add_action([] {
 | |
|         dialog::openFileDialog(config.texturename, XLAT("open PNG as texture"), ".png", 
 | |
|           [] () {
 | |
|             if(config.data.readtexture(config.texturename) && config.data.loadTextureGL()) {
 | |
|               if(config.tstate_max == tsOff) config.tstate_max = tsAdjusting;
 | |
|               config.tstate = config.tstate_max;
 | |
|               config.perform_mapping();
 | |
|               config.finish_mapping();
 | |
|               return true;
 | |
|               }
 | |
|             else return false;
 | |
|             });
 | |
|         });
 | |
|       }
 | |
|     dialog::addItem(XLAT("load texture config"), 'l');
 | |
|     dialog::add_action([] {
 | |
|       dialog::openFileDialog(config.configname, XLAT("load texture config"), ".txc", 
 | |
|         [] () {
 | |
|           return config.load();
 | |
|           });
 | |
|       });
 | |
|     dialog::addSelItem(XLAT("texture size"), its(config.data.twidth), 'w');
 | |
|     dialog::add_action([] {
 | |
|       config.data.twidth *= 2;
 | |
|       if(config.data.twidth > 9000) config.data.twidth = 256;
 | |
|       config.tstate_max = tsOff;
 | |
|       });
 | |
| #if CAP_EDIT
 | |
|     if(GDIM == 2) {
 | |
|       dialog::addItem(XLAT("paint a new texture"), 'n');
 | |
|       dialog::add_action(start_editor);
 | |
|       }
 | |
| #endif
 | |
| 
 | |
|     dialog::addBoolItem(XLATN("Canvas"), specialland == laCanvas, 'X');
 | |
|     dialog::add_action([] () {
 | |
|       bool inwhite = specialland == laCanvas && ccolor::which == &ccolor::plain && ccolor::plain.ctab.size() == 1 && ccolor::plain.ctab[0] == 0xFFFFFF;
 | |
|       if(inwhite) 
 | |
|         pushScreen(patterns::showPrePattern);
 | |
|       else {
 | |
|         stop_game();
 | |
|         enable_canvas();
 | |
|         ccolor::set_plain_nowall(0xFFFFFF);
 | |
|         start_game();
 | |
|         }
 | |
|       });
 | |
|     }
 | |
| 
 | |
|   if(config.tstate == tsAdjusting) {
 | |
|     dialog::init(XLAT("texture mode (overlay)"));
 | |
|     dialog::addItem(XLAT("select the texture's pattern"), 'r');
 | |
|     dialog::add_action(patterns::pushChangeablePatterns);
 | |
|     dialog::addItem(XLAT("enable the texture"), 't');
 | |
|     dialog::add_action([] {
 | |
|       config.tstate = config.tstate_max = tsActive;
 | |
|       config.finish_mapping();
 | |
|       });
 | |
|     dialog::addItem(XLAT("cancel the texture"), 'T');
 | |
|     dialog::add_action([] {
 | |
|       config.tstate = tsOff;
 | |
|       config.tstate_max = tsOff;
 | |
|       });    
 | |
|     dialog::addBoolItem_choice(XLAT("move the model"), panstate, tpsModel, 'm');
 | |
|     dialog::addBoolItem_choice(XLAT("move the texture"), panstate, tpsMove, 'a');
 | |
|     dialog::addBoolItem_choice(XLAT("scale/rotate the texture"), panstate, tpsScale, 'x');
 | |
|     dialog::addBoolItem_choice(XLAT("scale/rotate the model"), panstate, tpsZoom, 'z');
 | |
|     dialog::addBoolItem_choice(XLAT("projection"), panstate, tpsProjection, 'p');
 | |
|     dialog::addBoolItem_choice(XLAT("affine transformations"), panstate, tpsAffine, 'y');
 | |
|     dialog::addBoolItem(XLAT("magic"), false, 'A');
 | |
|     dialog::add_action_push(showMagicMenu);
 | |
|     
 | |
|     dialog::addBreak(50);
 | |
| 
 | |
|     dialog::addBoolItem_choice(XLAT("select master cells"), panstate, tpsCell, 'C');
 | |
|     dialog::addBoolItem_choice(XLAT("select master triangles"), panstate, tpsTriangle, 'X');
 | |
|     dialog::addBoolItem_choice(XLAT("fine tune vertices"), panstate, tpsTune, 'F');
 | |
| 
 | |
|     dialog::addColorItem(XLAT("grid color (master)"), config.master_color, 'M');
 | |
|     dialog::add_action([] { dialog::openColorDialog(config.master_color, NULL); });
 | |
|     dialog::addColorItem(XLAT("grid color (copy)"), config.slave_color, 'K');
 | |
|     dialog::add_action([] { dialog::openColorDialog(config.slave_color, NULL); });    
 | |
| 
 | |
|     dialog::addBreak(50);
 | |
| 
 | |
|     if(texture::config.tstate == texture::tsAdjusting)
 | |
|       add_edit(raw_texture_opacity);
 | |
|     
 | |
| #if CAP_SHOT
 | |
|     dialog::addItem(XLAT("save the raw texture"), 'S');
 | |
|     dialog::add_action([] {
 | |
|       dialog::openFileDialog(config.texturename, XLAT("save the raw texture"), ".png", 
 | |
|         [] () {
 | |
|           config.data.saveRawTexture(config.texturename); return true;
 | |
|           });
 | |
|       });
 | |
| #endif
 | |
|     }
 | |
|   
 | |
|   if(config.tstate == tsActive) {
 | |
|     dialog::init(XLAT("texture mode (active)"));
 | |
|     dialog::addItem(XLAT("deactivate the texture"), 't');
 | |
|     dialog::add_action([] { config.tstate = tsOff; });
 | |
|     dialog::addItem(XLAT("back to overlay mode"), 'T');
 | |
|     dialog::add_action([] {config.tstate = tsAdjusting; });
 | |
|     dialog::addItem(XLAT("change the geometry"), 'r');
 | |
|     dialog::add_action(patterns::pushChangeablePatterns);
 | |
|     dialog::addColorItem(XLAT("grid color"), config.grid_color, 'g');
 | |
|     dialog::add_action([] { dialog::openColorDialog(config.grid_color, NULL); });
 | |
|     dialog::addColorItem(XLAT("mesh color"), config.mesh_color, 'm');
 | |
|     dialog::add_action([] { dialog::openColorDialog(config.mesh_color, NULL); });
 | |
|     dialog::addSelItem(XLAT("color alpha"), its(config.color_alpha), 'c');
 | |
|     dialog::add_action([] {
 | |
|       dialog::editNumber(config.color_alpha, 0, 255, 15, 0, XLAT("color alpha"),      
 | |
|         XLAT("The higher the value, the less important the color of underlying terrain is."));
 | |
|       });
 | |
|     dialog::addBoolItem_action(XLAT("aura from texture"), texture_aura, 'a');
 | |
| #if CAP_EDIT
 | |
|     if(GDIM == 2) {
 | |
|       dialog::addItem(XLAT("edit the texture"), 'e');
 | |
|       dialog::add_action([] {
 | |
|         mapeditor::intexture = true;
 | |
|         mapeditor::drawing_tool = false;
 | |
|         mapeditor::initdraw(cwt.at);
 | |
|         pushScreen(mapeditor::showDrawEditor);
 | |
|         });
 | |
|       }
 | |
| #endif
 | |
| #if CAP_SHOT
 | |
|     dialog::addItem(XLAT("save the full texture image"), 'S');
 | |
|     dialog::add_action([] {
 | |
|       dialog::openFileDialog(config.texturename, XLAT("save the full texture image"), ".png", 
 | |
|         [] () {
 | |
|           config.saveFullTexture(config.texturename);
 | |
|           return true;
 | |
|           });
 | |
|       });
 | |
| #endif
 | |
|     dialog::addItem(XLAT("save texture config"), 's');
 | |
|     dialog::add_action([] {
 | |
|       dialog::openFileDialog(config.configname, XLAT("save texture config"), ".txc", 
 | |
|         [] () {
 | |
|           return config.save();
 | |
|           });
 | |
|       });
 | |
|     }
 | |
|   
 | |
|   dialog::addSelItem(XLAT("precision"), its(config.gsplits), 'P');
 | |
|   dialog::add_action([] {
 | |
|     dialog::editNumber(config.gsplits, 0, 4, 1, 1, XLAT("precision"),
 | |
|       XLAT("precision"));
 | |
|     if(config.tstate == tsActive) dialog::get_di().reaction = [] () { config.finish_mapping();
 | |
|       };
 | |
|     });
 | |
|   dialog::addHelp();
 | |
|   dialog::addBack();
 | |
|   
 | |
|   getcstat = '-';
 | |
| 
 | |
|   dialog::display();
 | |
|   
 | |
|   if(holdmouse) mousemovement();
 | |
|   
 | |
|   keyhandler = [] (int sym, int uni) {
 | |
|     // handlePanning(sym, uni);
 | |
|     dialog::handleNavigation(sym, uni);    
 | |
|     if(uni == '-' && config.tstate == tsAdjusting) {
 | |
|       if(!holdmouse) {
 | |
|         holdmouse = true;
 | |
|         newmove = true;
 | |
|         }
 | |
|       }
 | |
|     else if(uni == SDLK_F1)
 | |
|       gotoHelp(texturehelp);
 | |
|     else if(doexiton(sym, uni))
 | |
|       popScreen();
 | |
|     };
 | |
|   }
 | |
| 
 | |
| typedef pair<int,int> point;
 | |
| 
 | |
| point ptc(shiftpoint h) {
 | |
|   hyperpoint inmodel = config.texture_coordinates(h);
 | |
|   return make_pair(int(inmodel[0] * config.data.twidth), int(inmodel[1] * config.data.twidth));
 | |
|   }
 | |
| 
 | |
| array<point, 3> ptc(const array<shiftpoint, 3>& h) {
 | |
|   return make_array(ptc(h[0]), ptc(h[1]), ptc(h[2]));
 | |
|   }
 | |
| 
 | |
| int texture_distance(pair<int, int> p1, pair<int, int> p2) {
 | |
|   return max(abs(p1.first-p2.first), abs(p1.second - p2.second));
 | |
|   }
 | |
| 
 | |
| void fillpixel(int x, int y, unsigned col) {
 | |
|   if(x<0 || y<0 || x >= config.data.twidth || y >= config.data.twidth) return;
 | |
|   auto& pix = config.data.get_texture_pixel(x, y);
 | |
|   if(pix != col) {
 | |
|     config.data.undos.emplace_back(&pix, pix);
 | |
|     pix = col;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| void texture_data::undo() {
 | |
|   texture::config.data.pixels_to_draw.clear();
 | |
|   while(!undos.empty()) {
 | |
|     auto p = undos.back();
 | |
|     undos.pop_back();
 | |
|     if(!p.first) {
 | |
|       loadTextureGL(); 
 | |
|       return;
 | |
|       }
 | |
|     *p.first = p.second;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| void texture_data::undoLock() {
 | |
|   printf("undos size = %d\n", isize(undos));
 | |
|   if(isize(undos) > 2000000) {
 | |
|     // limit undo memory
 | |
|     int moveto = 0;
 | |
|     for(int i=0; i < isize(undos) - 1000000; i++)
 | |
|       if(!undos[i].first) moveto = i;
 | |
|     if(moveto) {
 | |
|       for(int i=0; i+moveto < isize(undos); i++)
 | |
|         undos[i] = undos[i+moveto];
 | |
|       undos.resize(isize(undos) - moveto);
 | |
|       printf("undos sized to = %d\n", isize(undos));
 | |
|       }
 | |
|     }
 | |
|     
 | |
|   undos.emplace_back(nullptr, 1);
 | |
|   }
 | |
| 
 | |
| void filltriangle(const array<shiftpoint, 3>& v, const array<point, 3>& p, color_t col, int lev) {
 | |
|   
 | |
|   int d2 = texture_distance(p[0], p[1]), d1 = texture_distance(p[0], p[2]), d0 = texture_distance(p[1], p[2]);
 | |
|   
 | |
|   int a, b, c;
 | |
| 
 | |
|   if((d0 <= 1 && d1 <= 1 && d2 <= 1) || lev >= 20) {
 | |
|     for(int i=0; i<3; i++)
 | |
|       fillpixel(p[i].first, p[i].second, col);
 | |
|     return;
 | |
|     }
 | |
|   else if(d1 >= d0 && d1 >= d2)
 | |
|     a = 0, b = 2, c = 1;
 | |
|   else if(d2 >= d0 && d2 >= d1)
 | |
|     a = 0, b = 1, c = 2;
 | |
|   else
 | |
|     a = 1, b = 2, c = 0;
 | |
|   
 | |
|   shiftpoint v3 = mid(v[a], v[b]);
 | |
|   point p3 = ptc(v3);
 | |
|   filltriangle(make_array(v[c], v[a], v3), make_array(p[c], p[a], p3), col, lev+1);
 | |
|   filltriangle(make_array(v[c], v[b], v3), make_array(p[c], p[b], p3), col, lev+1);
 | |
|   }
 | |
| 
 | |
| void splitseg(const shiftmatrix& A, const array<ld, 2>& angles, const array<shiftpoint, 2>& h, const array<point, 2>& p, color_t col, int lev) {
 | |
|   ld newangle = (angles[0] + angles[1]) / 2;
 | |
|   shiftpoint nh = A * xspinpush0(newangle, mapeditor::dtwidth);
 | |
|   auto np = ptc(nh);
 | |
|   
 | |
|   filltriangle(make_array(h[0],h[1],nh), make_array(p[0],p[1],np), col, lev);
 | |
|   if(lev < 10) {
 | |
|     if(texture_distance(p[0],np) > 1)
 | |
|       splitseg(A, make_array(angles[0], newangle), make_array(h[0], nh), make_array(p[0], np), col, lev+1);
 | |
|     if(texture_distance(np,p[1]) > 1)
 | |
|       splitseg(A, make_array(newangle, angles[1]), make_array(nh, h[1]), make_array(np, p[1]), col, lev+1);
 | |
|     }
 | |
|   }
 | |
| 
 | |
| void fillcircle(shiftpoint h, color_t col) {
 | |
|   shiftmatrix A = rgpushxto0(h);
 | |
|   
 | |
|   ld step = 120._deg;
 | |
|   
 | |
|   array<shiftpoint, 3> mh = make_array(A * xpush0(mapeditor::dtwidth), A * xspinpush0(step, mapeditor::dtwidth), A * xspinpush0(-step, mapeditor::dtwidth));
 | |
|   auto mp = ptc(mh);
 | |
| 
 | |
|   filltriangle(mh, mp, col, 0);
 | |
|   
 | |
|   for(int i=0; i<3; i++) {
 | |
|     int j = (i+1) % 3;
 | |
|     if(texture_distance(mp[i], mp[j]) > 1)
 | |
|       splitseg(A, make_array(step*i, step*(i+1)), make_array(mh[i], mh[j]), make_array(mp[i], mp[j]), col, 1);
 | |
|     }
 | |
|   }
 | |
| 
 | |
| EX bool texturesym = false;
 | |
| 
 | |
| void actDrawPixel(cell *c, shiftpoint h, color_t col) {
 | |
|   try {
 | |
|     shiftmatrix M = gmatrix.at(c);
 | |
|     auto si = patterns::getpatterninfo0(c);
 | |
|     hyperpoint h1 = inverse_shift(M * applyPatterndir(c, si), h);
 | |
|     auto& tinf = config.texture_map[si.id];
 | |
|     for(auto& M2: tinf.matrices) for(int i = 0; i<c->type; i += si.symmetries) {
 | |
|       fillcircle(M2 * spin(TAU * i / c->type) * h1, col);
 | |
|       if(texturesym)
 | |
|         fillcircle(M2 * spin(TAU * i / c->type) * Mirror * h1, col);
 | |
|       }
 | |
|     }
 | |
|   catch(out_of_range&) {}
 | |
|   }
 | |
|   
 | |
| EX void drawPixel(cell *c, shiftpoint h, color_t col) {
 | |
|   config.data.pixels_to_draw.emplace_back(c, h, col);
 | |
|   }
 | |
| 
 | |
| EX cell *where;
 | |
| 
 | |
| EX void drawPixel(shiftpoint h, color_t col) {
 | |
|   try {
 | |
|     again:
 | |
|     shiftmatrix g0 = gmatrix[where];
 | |
|     ld cdist0 = hdist(tC0(g0), h);
 | |
| 
 | |
|     forCellEx(c, where) 
 | |
|       try {
 | |
|         shiftmatrix g = gmatrix[c];
 | |
|         ld cdist = hdist(tC0(g), h);
 | |
|         if(cdist < cdist0) {
 | |
|           cdist0 = cdist;
 | |
|           where = c; g0 = g;
 | |
|           goto again;
 | |
|           }
 | |
|         }
 | |
|       catch(out_of_range&) {}
 | |
|     drawPixel(where, h, col);
 | |
|     }
 | |
|   catch(out_of_range&) {}
 | |
|   }
 | |
| 
 | |
| EX void drawLine(shiftpoint h1, shiftpoint h2, color_t col, int steps IS(10)) {
 | |
|   if(steps > 0 && hdist(h1, h2) > mapeditor::dtwidth / 3) {
 | |
|     shiftpoint h3 = mid(h1, h2);
 | |
|     drawLine(h1, h3, col, steps-1);
 | |
|     drawLine(h3, h2, col, steps-1);
 | |
|     }
 | |
|   else
 | |
|     drawPixel(h2, col);
 | |
|   }
 | |
| 
 | |
| void texture_config::true_remap() {
 | |
|   models::configure();
 | |
|   drawthemap();
 | |
|   if(GDIM == 3) return;
 | |
|   texture_map.clear();
 | |
|   missing_cells_known.clear();
 | |
|   for(cell *c: dcal) {
 | |
|     auto si = patterns::getpatterninfo0(c);
 | |
|     int oldid = patterns::getpatterninfo(c, patterns::whichPattern, patterns::subpattern_flags | patterns::SPF_NO_SUBCODES).id;
 | |
|     
 | |
|     if(texture_map.count(si.id)) continue;
 | |
|     
 | |
|     int pshift = 0;
 | |
|     if(texture::cgroup == cpSingle) oldid = 0;
 | |
|     if(texture::cgroup == cpFootball && patterns::cgroup == cpThree) {
 | |
|       if(si.id == 4) pshift = 1;
 | |
|       oldid = !si.id;
 | |
|       }
 | |
| 
 | |
|     try {
 | |
| 
 | |
|       auto& mi = texture_map_orig.at(oldid);  
 | |
|       int ncurr = isize(mi.tvertices);  
 | |
|       int ntarget = ncurr * celltriangles(c) / mi.current_type;
 | |
|       vector<glvertex> new_tvertices = mi.tvertices;
 | |
|       new_tvertices.resize(ntarget);
 | |
|       for(int i=ncurr; i<ntarget; i++) {
 | |
|         new_tvertices[i] = new_tvertices[i - ncurr];
 | |
|         }
 | |
|       
 | |
|       auto& mi2 = texture_map[si.id];
 | |
|       mi2 = mi;
 | |
|       if(GOLDBERG || IRREGULAR) pshift += si.dir;
 | |
|       mapTexture(c, mi2, si, ggmatrix(c), pshift);
 | |
|       mapTexture2(mi2);
 | |
|       mi2.tvertices = std::move(new_tvertices);
 | |
|       // printf("%08x remapping %d vertices to %d vertices\n", si.id, isize(mi.tvertices), isize(mi2.tvertices));
 | |
|       }
 | |
|     catch(out_of_range&) { 
 | |
|       if(missing_cells_known.count(si.id) == 0) {
 | |
|         missing_cells_known.insert(si.id);
 | |
|         printf("Unexpected missing cell #%d/%d\n", si.id, oldid);
 | |
|         addMessage(XLAT("Unexpected missing cell #%1/%1", its(si.id), its(oldid)));
 | |
|         }
 | |
|       // config.tstate_max = config.tstate = tsAdjusting;
 | |
|       return;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
| void texture_config::remap() {
 | |
|   if(tstate == tsActive) {
 | |
|     if(geometry == gFake) {
 | |
|       /* always correct */
 | |
|       true_remap();
 | |
|       return;
 | |
|       }
 | |
|     patterns::computeCgroup();
 | |
|     correctly_mapped = patterns::compatible(texture::cgroup, patterns::cgroup);
 | |
|     if(!correctly_mapped)
 | |
|       correctly_mapped = 
 | |
|         current_texture_parameters == config.orig_texture_parameters;
 | |
|     if(correctly_mapped) true_remap();
 | |
|     else addMessage(XLAT("Pattern incompatible."));
 | |
|     }
 | |
|   else if(tstate == tsAdjusting) {
 | |
|     printf("perform_mapping %d/%d\n", config.tstate, config.tstate_max);
 | |
|     calcparam();
 | |
|     models::configure();
 | |
|     drawthemap();
 | |
|     perform_mapping();
 | |
|     finish_mapping();
 | |
|     printf("texture_map size = %d\n", isize(texture_map));
 | |
|     }
 | |
|   }
 | |
| 
 | |
| #if CAP_TEXTURE
 | |
| texture_data txp;
 | |
| 
 | |
| EX double get_txp(ld x, ld y, int p) {
 | |
|   if(txp.texture_pixels.empty()) return 0.5;
 | |
|   color_t col = txp.get_texture_pixel(txp.twidth * x, txp.twidth * y);
 | |
|   return part(col, p) / 255.;
 | |
|   }
 | |
| #endif
 | |
| 
 | |
| int textureArgs() {
 | |
|   using namespace arg;
 | |
|            
 | |
|   if(0) ;
 | |
|   else if(argis("-txpic")) {
 | |
|     shift(); config.texturename = args();
 | |
|     }
 | |
| 
 | |
|   else if(argis("-txp")) {
 | |
|     shift(); config.gsplits = argi();
 | |
|     }
 | |
| 
 | |
|   else if(argis("-txc")) {
 | |
|     shift(); config.configname = args();
 | |
|     }
 | |
| 
 | |
|   else if(argis("-txpsize")) {
 | |
|     shift(); txp.twidth = argi();
 | |
|     }
 | |
| 
 | |
|   else if(argis("-txpf")) {
 | |
|     shift(); txp.readtexture(args());
 | |
|     }
 | |
| 
 | |
|   else if(argis("-txcl")) {
 | |
|     PHASE(3);
 | |
|     config.load();
 | |
|     }
 | |
| 
 | |
|   else return 1;
 | |
|   return 0;
 | |
|   }
 | |
| 
 | |
| auto texture_hook = 
 | |
|   addHook(hooks_args, 100, textureArgs)
 | |
| + addHook(hooks_clearmemory, 100, [] () { config.data.pixels_to_draw.clear(); });
 | |
| 
 | |
| int lastupdate;
 | |
| 
 | |
| void texture_data::update() {
 | |
|   if(!pixels_to_draw.empty()) {
 | |
|     auto t = SDL_GetTicks();
 | |
|     while(SDL_GetTicks() < t + 75 && !pixels_to_draw.empty()) {
 | |
|       auto p = pixels_to_draw.back();
 | |
|       actDrawPixel(get<0>(p), get<1>(p), get<2>(p));
 | |
|       pixels_to_draw.pop_back();
 | |
|       }
 | |
|     loadTextureGL(); 
 | |
|     }
 | |
|   }
 | |
| 
 | |
| EX }
 | |
| }
 | |
| #endif
 | 
