1 
2 /*
3 A* -------------------------------------------------------------------
4 B* This file contains source code for the PyMOL computer program
5 C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific.
6 D* -------------------------------------------------------------------
7 E* It is unlawful to modify or remove this copyright notice.
8 F* -------------------------------------------------------------------
9 G* Please see the accompanying LICENSE file for further information.
10 H* -------------------------------------------------------------------
11 I* Additional authors of this source file include:
12 -*
13 -*
14 -*
15 Z* -------------------------------------------------------------------
16 */
17 #include"os_python.h"
18 
19 #include"os_predef.h"
20 #include"os_std.h"
21 #include"os_gl.h"
22 
23 #include"OOMac.h"
24 #include"ObjectMesh.h"
25 #include"Base.h"
26 #include"MemoryDebug.h"
27 #include"Map.h"
28 #include"Parse.h"
29 #include"Isosurf.h"
30 #include"Vector.h"
31 #include"Color.h"
32 #include"main.h"
33 #include"Scene.h"
34 #include"Setting.h"
35 #include"Executive.h"
36 #include"PConv.h"
37 #include"P.h"
38 #include"Matrix.h"
39 #include"ShaderMgr.h"
40 #include"ObjectCGO.h"
41 
42 static void ObjectMeshRecomputeExtent(ObjectMesh * I);
43 
ObjectMeshStateAsPyList(ObjectMeshState * I)44 static PyObject *ObjectMeshStateAsPyList(ObjectMeshState * I)
45 {
46   PyObject *result = NULL;
47 
48   result = PyList_New(17);
49 
50   PyList_SetItem(result, 0, PyInt_FromLong(I->Active));
51   PyList_SetItem(result, 1, PyString_FromString(I->MapName));
52   PyList_SetItem(result, 2, PyInt_FromLong(I->MapState));
53   PyList_SetItem(result, 3, CrystalAsPyList(&I->Crystal));
54   PyList_SetItem(result, 4, PyInt_FromLong(I->ExtentFlag));
55   PyList_SetItem(result, 5, PConvFloatArrayToPyList(I->ExtentMin, 3));
56   PyList_SetItem(result, 6, PConvFloatArrayToPyList(I->ExtentMax, 3));
57   PyList_SetItem(result, 7, PConvIntArrayToPyList(I->Range, 6));
58   PyList_SetItem(result, 8, PyFloat_FromDouble(I->Level));
59   PyList_SetItem(result, 9, PyFloat_FromDouble(I->Radius));
60   PyList_SetItem(result, 10, PyInt_FromLong(I->CarveFlag));
61   PyList_SetItem(result, 11, PyFloat_FromDouble(I->CarveBuffer));
62   if(I->CarveFlag && I->AtomVertex) {
63     PyList_SetItem(result, 12, PConvFloatVLAToPyList(I->AtomVertex));
64   } else {
65     PyList_SetItem(result, 12, PConvAutoNone(NULL));
66   }
67   PyList_SetItem(result, 13, PyInt_FromLong(I->MeshMode));
68   PyList_SetItem(result, 14, PyFloat_FromDouble(I->AltLevel));
69   PyList_SetItem(result, 15, PyInt_FromLong(I->quiet));
70   if(I->Field) {
71     PyList_SetItem(result, 16, IsosurfAsPyList(I->G, I->Field.get()));
72   } else {
73     PyList_SetItem(result, 16, PConvAutoNone(NULL));
74   }
75   return (PConvAutoNone(result));
76 }
77 
ObjectMeshStateMapExists(ObjectMesh * I,ObjectMeshState * ms)78 static int ObjectMeshStateMapExists(ObjectMesh *I, ObjectMeshState *ms){
79   return ExecutiveFindObjectMapByName(I->G, ms->MapName) ? 1 : 0;
80 }
81 
ObjectMeshAllMapsInStatesExist(ObjectMesh * I)82 int ObjectMeshAllMapsInStatesExist(ObjectMesh * I)
83 {
84   int a;
85   for(a = 0; a < I->NState; a++) {
86     if(I->State[a].Active) {
87       if (!ObjectMeshStateMapExists(I, I->State + a)){
88 	return 0;
89       }
90     }
91   }
92   return 1;
93 }
94 
ObjectMeshAllStatesAsPyList(ObjectMesh * I)95 static PyObject *ObjectMeshAllStatesAsPyList(ObjectMesh * I)
96 {
97 
98   PyObject *result = NULL;
99   int a;
100   result = PyList_New(I->NState);
101   for(a = 0; a < I->NState; a++) {
102     if(I->State[a].Active) {
103       PyList_SetItem(result, a, ObjectMeshStateAsPyList(I->State + a));
104     } else {
105       PyList_SetItem(result, a, PConvAutoNone(NULL));
106     }
107   }
108   return (PConvAutoNone(result));
109 }
110 
ObjectMeshStateFromPyList(PyMOLGlobals * G,ObjectMeshState * I,PyObject * list)111 static int ObjectMeshStateFromPyList(PyMOLGlobals * G, ObjectMeshState * I,
112                                      PyObject * list)
113 {
114   int ok = true;
115   int ll = 0;
116   PyObject *tmp;
117   if(ok)
118     ok = (list != NULL);
119   if(ok) {
120     if(!PyList_Check(list))
121       I->Active = false;
122     else {
123       *I = ObjectMeshState(G);
124       if(ok)
125         ok = (list != NULL);
126       if(ok)
127         ok = PyList_Check(list);
128       if(ok)
129         ll = PyList_Size(list);
130       /* TO SUPPORT BACKWARDS COMPATIBILITY...
131          Always check ll when adding new PyList_GetItem's */
132 
133       if(ok)
134         ok = PConvPyIntToInt(PyList_GetItem(list, 0), &I->Active);
135       if(ok)
136         ok = PConvPyStrToStr(PyList_GetItem(list, 1), I->MapName, WordLength);
137       if(ok)
138         ok = PConvPyIntToInt(PyList_GetItem(list, 2), &I->MapState);
139       if(ok)
140         ok = CrystalFromPyList(&I->Crystal, PyList_GetItem(list, 3));
141       if(ok)
142         ok = PConvPyIntToInt(PyList_GetItem(list, 4), &I->ExtentFlag);
143       if(ok)
144         ok = PConvPyListToFloatArrayInPlace(PyList_GetItem(list, 5), I->ExtentMin, 3);
145       if(ok)
146         ok = PConvPyListToFloatArrayInPlace(PyList_GetItem(list, 6), I->ExtentMax, 3);
147       if(ok)
148         ok = PConvPyListToIntArrayInPlace(PyList_GetItem(list, 7), I->Range, 6);
149       if(ok)
150         ok = PConvPyFloatToFloat(PyList_GetItem(list, 8), &I->Level);
151       if(ok)
152         ok = PConvPyFloatToFloat(PyList_GetItem(list, 9), &I->Radius);
153       if(ok)
154         ok = PConvPyIntToInt(PyList_GetItem(list, 10), &I->CarveFlag);
155       if(ok)
156         ok = PConvPyFloatToFloat(PyList_GetItem(list, 11), &I->CarveBuffer);
157       if(ok) {
158         tmp = PyList_GetItem(list, 12);
159         if(tmp == Py_None)
160           I->AtomVertex = NULL;
161         else
162           ok = PConvPyListToFloatVLA(tmp, &I->AtomVertex);
163       }
164       if(ok)
165         ok = PConvPyIntToInt(PyList_GetItem(list, 13), &I->MeshMode);
166       if(ok) {
167         I->RefreshFlag = true;
168         I->ResurfaceFlag = true;
169       }
170       if(ok && (ll > 14)) {
171         ok = PConvPyFloatToFloat(PyList_GetItem(list, 14), &I->AltLevel);
172       } else {
173         I->AltLevel = I->Level;
174       }
175       if(ok && (ll > 15)) {
176         ok = PConvPyIntToInt(PyList_GetItem(list, 15), &I->quiet);
177       } else {
178         I->quiet = true;
179       }
180       if(ok && (ll > 16)) {
181         tmp = PyList_GetItem(list, 16);
182         if(tmp == Py_None)
183           I->Field = NULL;
184         else {
185           I->Field.reset(IsosurfNewFromPyList(G, tmp));
186           ok = I->Field != nullptr;
187         }
188 	CPythonVal_Free(tmp);
189       }
190     }
191   }
192   return (ok);
193 }
194 
ObjectMeshAllStatesFromPyList(ObjectMesh * I,PyObject * list)195 static int ObjectMeshAllStatesFromPyList(ObjectMesh * I, PyObject * list)
196 {
197 
198   int ok = true;
199   int a;
200   VLACheck(I->State, ObjectMeshState, I->NState);
201   if(ok)
202     ok = PyList_Check(list);
203   if(ok) {
204     for(a = 0; a < I->NState; a++) {
205       auto *el = PyList_GetItem(list, a);
206       ok = ObjectMeshStateFromPyList(I->G, I->State + a, el);
207       if(!ok)
208         break;
209     }
210   }
211   return (ok);
212 }
213 
ObjectMeshNewFromPyList(PyMOLGlobals * G,PyObject * list,ObjectMesh ** result)214 int ObjectMeshNewFromPyList(PyMOLGlobals * G, PyObject * list, ObjectMesh ** result)
215 {
216   int ok = true;
217   ObjectMesh *I = NULL;
218   (*result) = NULL;
219 
220   if(ok)
221     ok = (list != NULL);
222   if(ok)
223     ok = PyList_Check(list);
224   /* TO SUPPORT BACKWARDS COMPATIBILITY...
225      Always check ll when adding new PyList_GetItem's */
226 
227   I = new ObjectMesh(G);
228   CHECKOK(ok, I);
229 
230   if(ok)
231     ok = ObjectFromPyList(G, PyList_GetItem(list, 0), I);
232   if(ok)
233     ok = PConvPyIntToInt(PyList_GetItem(list, 1), &I->NState);
234   if(ok)
235     ok = ObjectMeshAllStatesFromPyList(I, PyList_GetItem(list, 2));
236   if(ok) {
237     (*result) = I;
238     ObjectMeshRecomputeExtent(I);
239   } else {
240     DeleteP(I);
241     (*result) = NULL;
242   }
243   return (ok);
244 }
245 
246 static CGO *ObjectMeshRenderImpl(ObjectMesh * I, RenderInfo * info, int returnCGO, int stateArg);
247 
ObjectMeshAsPyList(ObjectMesh * I)248 PyObject *ObjectMeshAsPyList(ObjectMesh * I)
249 {
250   PyObject *result = NULL;
251 
252   int allMapsExist = ObjectMeshAllMapsInStatesExist(I);
253 
254   if (allMapsExist){
255     result = PyList_New(3);
256     PyList_SetItem(result, 0, ObjectAsPyList(I));
257     PyList_SetItem(result, 1, PyInt_FromLong(I->NState));
258     PyList_SetItem(result, 2, ObjectMeshAllStatesAsPyList(I));
259   } else {
260     /* save ObjectMesh as ObjectCGO */
261     ObjectCGO *retObjectCGO = new ObjectCGO(I->G);
262     ObjectCopyHeader(retObjectCGO, I);
263     retObjectCGO->type = cObjectCGO;
264 
265     int a;
266     PRINTFB(I->G, FB_ObjectMesh, FB_Errors)
267       "ObjectMesh-Warning: map has been deleted, saving as CGO.\n"
268       ENDFB(I->G);
269     for(a = 0; a < I->NState; a++) {
270       CGO *cgo = ObjectMeshRenderImpl(I, 0, 1, a);
271       retObjectCGO = ObjectCGOFromCGO(I->G, retObjectCGO, cgo, a);
272 
273     }
274     ObjectSetRepVisMask(retObjectCGO, cRepCGOBit, cVis_AS);
275     result = ObjectCGOAsPyList(retObjectCGO);
276     DeleteP(retObjectCGO);
277   }
278   return (PConvAutoNone(result));
279 }
280 
ObjectMeshInvalidateMapName(ObjectMesh * I,const char * name,const char * new_name)281 int ObjectMeshInvalidateMapName(ObjectMesh * I, const char *name, const char * new_name)
282 {
283   int a;
284   ObjectMeshState *ms;
285   int result = false;
286   for(a = 0; a < I->NState; a++) {
287     ms = I->State + a;
288     if(ms->Active) {
289       if(strcmp(ms->MapName, name) == 0) {
290         if (new_name)
291           strcpy(ms->MapName, new_name);
292         I->invalidate(cRepAll, cRepInvAll, a);
293         result = true;
294       }
295     }
296   }
297   return result;
298 }
299 
ObjectMeshDump(ObjectMesh * I,const char * fname,int state,int quiet)300 void ObjectMeshDump(ObjectMesh * I, const char *fname, int state, int quiet)
301 {
302   float *v;
303   int *n;
304   int c;
305   FILE *f;
306   f = fopen(fname, "wb");
307   if(!f) {
308     ErrMessage(I->G, "ObjectMeshDump", "can't open file for writing");
309   }
310   else {
311     if(state < I->NState) {
312       n = I->State[state].N.data();
313       v = I->State[state].V.data();
314       if(n && v)
315         while(*n) {
316           c = *(n++);
317           if(!I->State[state].MeshMode) {
318             fprintf(f, "\n");
319           }
320           while(c--) {
321             fprintf(f, "%10.4f%10.4f%10.4f\n", v[0], v[1], v[2]);
322             v += 3;
323           }
324         }
325     }
326     fclose(f);
327     if (!quiet) {
328       PRINTFB(I->G, FB_ObjectMesh, FB_Actions)
329         " ObjectMeshDump: %s written to %s\n", I->Name, fname ENDFB(I->G);
330     }
331   }
332 }
333 
invalidate(int rep,int level,int state)334 void ObjectMesh::invalidate(int rep, int level, int state)
335 {
336   auto I = this;
337   if(level >= cRepInvExtents) {
338     I->ExtentFlag = false;
339   }
340   if((rep == cRepMesh) || (rep == cRepAll) || (rep == cRepCell)) {
341 
342     for(StateIterator iter(I->G, NULL, state, I->NState); iter.next();) {
343       ObjectMeshState *ms = I->State + iter.state;
344 
345       ms->shaderCGO.reset();
346       ms->shaderUnitCellCGO.reset();
347 
348       ms->RefreshFlag = true;
349       if(level >= cRepInvAll) {
350         ms->ResurfaceFlag = true;
351         SceneChanged(I->G);
352       } else if(level >= cRepInvColor) {
353         ms->RecolorFlag = true;
354         SceneChanged(I->G);
355       } else {
356         SceneInvalidate(I->G);
357       }
358     }
359   }
360 }
361 
ObjectMeshGetLevel(ObjectMesh * I,int state)362 pymol::Result<float> ObjectMeshGetLevel(ObjectMesh * I, int state)
363 {
364   if(state >= I->NState) {
365     return pymol::make_error("Invalid Mesh state");
366   } else {
367     if(state < 0) {
368       state = 0;
369     }
370     auto ms = I->State + state;
371     if(ms->Active) {
372       return ms->Level;
373     } else {
374       return pymol::make_error("Invalid Mesh state");
375     }
376   }
377 }
378 
ObjectMeshSetLevel(ObjectMesh * I,float level,int state,int quiet)379 int ObjectMeshSetLevel(ObjectMesh * I, float level, int state, int quiet)
380 {
381   int ok = true;
382   if(state >= I->NState) {
383     ok = false;
384   } else {
385     for(StateIterator iter(I->G, NULL, state, I->NState); iter.next();) {
386       ObjectMeshState *ms = I->State + iter.state;
387       if(ms->Active) {
388         ms->ResurfaceFlag = true;
389         ms->RefreshFlag = true;
390         ms->Level = level;
391         ms->quiet = quiet;
392       }
393     }
394   }
395   return (ok);
396 }
397 
ObjectMeshStateUpdateColors(ObjectMesh * I,ObjectMeshState * ms)398 static void ObjectMeshStateUpdateColors(ObjectMesh * I, ObjectMeshState * ms)
399 {
400   int one_color_flag = true;
401   int cur_color = -1;
402 
403   if(ms->MeshMode == 0) {
404     cur_color = SettingGet_color(I->G, I->Setting, NULL, cSetting_mesh_color);
405   } else if(ms->MeshMode == 1) {
406     cur_color = SettingGet_color(I->G, I->Setting, NULL, cSetting_dot_color);
407   }
408 
409   if(cur_color == -1)
410     cur_color = I->Color;
411 
412   if(ColorCheckRamped(I->G, cur_color))
413     one_color_flag = false;
414 
415   ms->OneColor = cur_color;
416   if(ms->V) {
417     int ramped_flag = false;
418     float *v = ms->V.data();
419     float *vc;
420     int *rc;
421     int a;
422     int state = ms - I->State;
423     int n_vert = VLAGetSize(ms->V) / 3;
424     int base_n_vert = ms->base_n_V / 3;
425 
426     if(!ms->VC.empty() && (ms->VCsize < n_vert)) {
427       ms->VC.clear();
428       ms->RC.clear();
429     }
430 
431     if(ms->VC.empty()) {
432       ms->VCsize = n_vert;
433       ms->VC = std::vector<float>(n_vert * 3);
434     }
435     if(ms->RC.empty()) {
436       ms->RC = std::vector<int>(n_vert);
437     }
438     rc = ms->RC.data();
439     vc = ms->VC.data();
440     if(vc) {
441       for(a = 0; a < n_vert; a++) {
442         if(a == base_n_vert) {
443           int new_color = SettingGet_color(I->G, I->Setting,
444                                            NULL, cSetting_mesh_negative_color);
445           if(new_color == -1)
446             new_color = cur_color;
447           if(new_color != cur_color) {
448             one_color_flag = false;
449             cur_color = new_color;
450           }
451         }
452         if(ColorCheckRamped(I->G, cur_color)) {
453           ColorGetRamped(I->G, cur_color, v, vc, state);
454           *rc = cur_color;
455           ramped_flag = true;
456         } else {
457           const float *col = ColorGet(I->G, cur_color);
458           copy3f(col, vc);
459         }
460         rc++;
461         vc += 3;
462         v += 3;
463       }
464     }
465 
466     if(one_color_flag && (!ramped_flag)) {
467       ms->VC.clear();
468       ms->RC.clear();
469     } else if((!ramped_flag)
470               ||
471               (!SettingGet_b(I->G, NULL, I->Setting, cSetting_ray_color_ramps))) {
472       ms->RC.clear();
473     }
474   }
475 }
476 
update()477 void ObjectMesh::update()
478 {
479   auto I = this;
480   int a;
481   int c;
482   ObjectMeshState *ms;
483   ObjectMapState *oms = NULL;
484   ObjectMap *map = NULL;
485 
486   int *n;
487   float *v;
488   float carve_buffer;
489   int avoid_flag = false;
490   int n_cur;
491   int n_seg;
492   int n_line;
493   int flag;
494   int last_flag = 0;
495   int h, k, l;
496   int i, j;
497   int mesh_skip = SettingGet_i(G, I->Setting, NULL, cSetting_mesh_skip);
498 
499   MapType *voxelmap;            /* this has nothing to do with isosurfaces... */
500   for(a = 0; a < I->NState; a++) {
501     ms = I->State + a;
502     if(ms->Active) {
503 
504       map = ExecutiveFindObjectMapByName(I->G, ms->MapName);
505       if(!map) {
506         PRINTFB(I->G, FB_ObjectMesh, FB_Errors)
507           "ObjectMeshUpdate-Error: map '%s' has been deleted.\n", ms->MapName
508           ENDFB(I->G);
509         ms->ResurfaceFlag = false;
510       }
511       if(map) {
512         oms = ObjectMapGetState(map, ms->MapState);
513       }
514       if(oms) {
515         if(ms->RefreshFlag || ms->ResurfaceFlag) {
516           if(!ms->Field) {
517             ms->Crystal = oms->Symmetry->Crystal;
518           }
519 
520           if((I->visRep & cRepCellBit)) {
521             ms->UnitCellCGO.reset(CrystalGetUnitCellCGO(&ms->Crystal));
522           }
523 
524           if(!oms->Matrix.empty()) {
525             ObjectStateSetMatrix(ms, oms->Matrix.data());
526           } else if(!ms->Matrix.empty()) {
527             ObjectStateResetMatrix(ms);
528           }
529           ms->RefreshFlag = false;
530         }
531       }
532 
533       if(map && oms && ms->N && ms->V && (I->visRep & cRepMeshBit)) {
534         if(ms->ResurfaceFlag) {
535           Isofield *field = NULL;
536           ms->RecolorFlag = true;
537           ms->ResurfaceFlag = false;
538           if(!ms->quiet) {
539             PRINTFB(G, FB_ObjectMesh, FB_Details)
540               " ObjectMesh: updating \"%s\".\n", I->Name ENDFB(G);
541           }
542           if(ms->Field) {
543             field = ms->Field.get();
544           } else if(oms->Field) {
545             field = oms->Field.get();
546           }
547 
548           if(field) {
549             {
550               float *min_ext, *max_ext;
551               float tmp_min[3], tmp_max[3];
552               if(MatrixInvTransformExtentsR44d3f(ms->Matrix.data(),
553                                                  ms->ExtentMin, ms->ExtentMax,
554                                                  tmp_min, tmp_max)) {
555                 min_ext = tmp_min;
556                 max_ext = tmp_max;
557               } else {
558                 min_ext = ms->ExtentMin;
559                 max_ext = ms->ExtentMax;
560               }
561 
562               IsosurfGetRange(I->G, field, &oms->Symmetry->Crystal,
563                               min_ext, max_ext, ms->Range, true);
564             }
565             /*                      printf("Mesh-DEBUG: %d %d %d %d %d %d\n",
566                ms->Range[0],
567                ms->Range[1],
568                ms->Range[2],
569                ms->Range[3],
570                ms->Range[4],
571                ms->Range[5]); */
572             IsosurfVolume(I->G, I->Setting, NULL,
573                           field,
574                           ms->Level,
575                           ms->N, ms->V,
576                           ms->Range, ms->MeshMode, mesh_skip, ms->AltLevel);
577 
578             if(!SettingGet_b
579                (I->G, I->Setting, NULL, cSetting_mesh_negative_visible)) {
580               ms->base_n_V = VLAGetSize(ms->V);
581             } else if(ms->MeshMode != 3) {
582               /* do we want the negative surface too? */
583 
584               pymol::vla<int> N2(10000);
585               pymol::vla<float> V2(10000);
586 
587               IsosurfVolume(I->G, I->Setting, NULL,
588                             field,
589                             -ms->Level,
590                             N2, V2, ms->Range, ms->MeshMode, mesh_skip, ms->AltLevel);
591 
592               if(N2 && V2) {
593 
594                 int base_n_N = VLAGetSize(ms->N);
595                 int base_n_V = VLAGetSize(ms->V);
596                 int addl_n_N = VLAGetSize(N2);
597                 int addl_n_V = VLAGetSize(V2);
598 
599                 ms->base_n_V = base_n_V;
600 
601                 /* make room */
602 
603                 VLASize(ms->N, int, base_n_N + addl_n_N);
604                 VLASize(ms->V, float, base_n_V + addl_n_V);
605 
606                 /* copy vertex data */
607 
608                 memcpy(((char *) ms->V.data()) + (sizeof(float) * base_n_V),
609                        V2, sizeof(float) * addl_n_V);
610 
611                 /* copy strip counts */
612 
613                 memcpy(((char *) ms->N.data()) + (sizeof(int) * (base_n_N - 1)),
614                        N2, sizeof(int) * addl_n_N);
615                 ms->N[base_n_N + addl_n_N - 1] = 0;
616 
617                 VLAFreeP(N2);
618                 VLAFreeP(V2);
619               }
620 
621             }
622 
623             if(!ms->Matrix.empty() && VLAGetSize(ms->N) && VLAGetSize(ms->V)) {
624               int count;
625               /* take map coordinates back to view coordinates if necessary */
626               v = ms->V.data();
627               count = VLAGetSize(ms->V) / 3;
628               while(count--) {
629                 transform44d3f(ms->Matrix.data(), v, v);
630                 v += 3;
631               }
632             }
633 
634           }
635           if(ms->CarveFlag && ms->AtomVertex && VLAGetSize(ms->N) && VLAGetSize(ms->V)) {
636             carve_buffer = ms->CarveBuffer;
637             if(ms->CarveBuffer < 0.0F) {
638               avoid_flag = true;
639               carve_buffer = -carve_buffer;
640             }
641 
642             /* cull my friend, cull */
643             voxelmap = MapNew(I->G,
644                               -carve_buffer, ms->AtomVertex,
645                               VLAGetSize(ms->AtomVertex) / 3, NULL);
646             if(voxelmap) {
647 
648               MapSetupExpress(voxelmap);
649 
650               pymol::vla<int> old_n = std::move(ms->N);
651               pymol::vla<float> old_v = std::move(ms->V);
652               ms->N = pymol::vla<int>(old_n.size());
653               ms->V = pymol::vla<float>(old_v.size());
654 
655               n = old_n.data();
656               v = old_v.data();
657               n_cur = 0;
658               n_seg = 0;
659               n_line = 0;
660               while(*n) {
661                 last_flag = false;
662                 c = *(n++);
663                 while(c--) {
664                   flag = false;
665                   MapLocus(voxelmap, v, &h, &k, &l);
666                   i = *(MapEStart(voxelmap, h, k, l));
667                   if(i) {
668                     j = voxelmap->EList[i++];
669                     while(j >= 0) {
670                       if(within3f(ms->AtomVertex + 3 * j, v, carve_buffer)) {
671                         flag = true;
672                         break;
673                       }
674                       j = voxelmap->EList[i++];
675                     }
676                   }
677                   if(avoid_flag)
678                     flag = !flag;
679                   if(flag && (!last_flag)) {
680                     VLACheck(ms->V, float, 3 * (n_line + 1));
681                     copy3f(v, ms->V + n_line * 3);
682                     n_cur++;
683                     n_line++;
684                   }
685                   if(flag && last_flag) {       /* continue segment */
686                     VLACheck(ms->V, float, 3 * (n_line + 1));
687                     copy3f(v, ms->V + n_line * 3);
688                     n_cur++;
689                     n_line++;
690                   }
691                   if((!flag) && last_flag) {    /* terminate segment */
692                     VLACheck(ms->N, int, n_seg);
693                     ms->N[n_seg] = n_cur;
694                     n_seg++;
695                     n_cur = 0;
696                   }
697                   last_flag = flag;
698                   v += 3;
699                 }
700                 if(last_flag) { /* terminate segment */
701                   VLACheck(ms->N, int, n_seg);
702                   ms->N[n_seg] = n_cur;
703                   n_seg++;
704                   n_cur = 0;
705                 }
706               }
707               VLACheck(ms->N, int, n_seg);
708               ms->N[n_seg] = 0;
709               MapFree(voxelmap);
710             }
711           }
712         }
713         if(ms->RecolorFlag) {
714           ObjectMeshStateUpdateColors(I, ms);
715           ms->RecolorFlag = false;
716         }
717       }
718 
719       ms->shaderCGO.reset();
720       ms->shaderUnitCellCGO.reset();
721     }
722     SceneInvalidate(I->G);
723   }
724   if(!I->ExtentFlag) {
725     ObjectMeshRecomputeExtent(I);
726     if(I->ExtentFlag)
727       SceneInvalidate(I->G);
728   }
729 }
730 
731 
render(RenderInfo * info)732 void ObjectMesh::render(RenderInfo * info)
733 {
734   ObjectMeshRenderImpl(this, info, false, 0);
735 }
736 
ObjectMeshStateRenderShader(ObjectMeshState * ms,ObjectMesh * I,RenderInfo * info,short mesh_as_cylinders,float mesh_width)737 static short ObjectMeshStateRenderShader(ObjectMeshState *ms, ObjectMesh *I,
738     RenderInfo *info, short mesh_as_cylinders, float mesh_width)
739 {
740   PyMOLGlobals *G = I->G;
741   CShaderPrg *shaderPrg = nullptr;
742 
743   if (!mesh_as_cylinders) {
744     shaderPrg = G->ShaderMgr->Enable_DefaultShader(info->pass);
745     shaderPrg->SetLightingEnabled(0);
746     shaderPrg->Set1i("two_sided_lighting_enabled",
747 		     SceneGetTwoSidedLighting(G));
748   }
749 
750   CGORenderGL(ms->shaderCGO.get(), NULL, NULL, NULL, info, NULL);
751 
752   if (shaderPrg) {
753     shaderPrg->Disable();
754   }
755 
756   if (ms->shaderUnitCellCGO){
757     shaderPrg = G->ShaderMgr->Enable_DefaultShader(info->pass);
758     shaderPrg->SetLightingEnabled(0);
759     CGORenderGL(ms->shaderUnitCellCGO.get(), NULL, NULL, NULL, info, NULL);
760     shaderPrg->Disable();
761   }
762 
763   return true;
764 }
765 
ObjectMeshRenderImpl(ObjectMesh * I,RenderInfo * info,int returnCGO,int stateArg)766 static CGO *ObjectMeshRenderImpl(ObjectMesh * I, RenderInfo * info, int returnCGO, int stateArg)
767 {
768   PyMOLGlobals *G = I->G;
769   float *v = NULL;
770   float *vc;
771   int *rc;
772   float radius;
773   int state = 0;
774   CRay *ray = 0;
775   bool pick = false;
776   int pass = 0;
777   int *n = NULL;
778   int c;
779   float line_width, mesh_width = SettingGet_f(I->G, I->Setting, NULL, cSetting_mesh_width);
780   ObjectMeshState *ms = NULL;
781   int ok = true;
782 
783   if (info){
784     state = info->state;
785     ray = info->ray;
786     pick = info->pick;
787     pass = info->pass;
788   } else {
789     state = stateArg;
790   }
791 
792   line_width = SceneGetDynamicLineWidth(info, mesh_width);
793   ObjectPrepareContext(I, info);
794 
795   for(StateIterator iter(I->G, I->Setting, state, I->NState); iter.next();) {
796     ms = I->State + iter.state;
797 
798     if(!ms->Active || !ms->V || !ms->N)
799       continue;
800 
801     {
802         v = ms->V.data();
803         n = ms->N.data();
804         if(ok && ray) {
805           if(ms->UnitCellCGO && (I->visRep & cRepCellBit)){
806             ok &= CGORenderRay(ms->UnitCellCGO.get(), ray, info, ColorGet(I->G, I->Color),
807 			       NULL, I->Setting, NULL);
808 	    if (!ok){
809 	      ms->UnitCellCGO.reset();
810 	      break;
811 	    }
812 	  }
813           if(ms->MeshMode != 1) {
814             radius = SettingGet_f(I->G, I->Setting, NULL, cSetting_mesh_radius);
815 
816             if(radius == 0.0F) {
817               radius = ray->PixelRadius * line_width / 2.0F;
818             }
819           } else {
820             radius = SettingGet_f(I->G, I->Setting, NULL, cSetting_dot_radius);
821             if(radius == 0.0F) {
822               radius =
823                 ray->PixelRadius * SettingGet_f(I->G, I->Setting, NULL,
824                                                 cSetting_dot_width) / 1.4142F;
825             }
826           }
827 
828           if(ok && n && v && (I->visRep & cRepMeshBit)) {
829             float cc[3];
830             float colA[3], colB[3];
831             ColorGetEncoded(G, ms->OneColor, cc);
832             vc = ms->VC.data();
833             rc = ms->RC.data();
834             if(ms->MeshMode == 1) {
835               ray->color3fv(cc);
836               while(ok && *n) {
837                 c = *(n++);
838                 while(ok && c--) {
839                   if(vc) {
840                     float *cA = vc;
841                     if(rc) {
842                       if(rc[0] < -1)
843                         ColorGetEncoded(G, rc[0], (cA = colA));
844                       rc++;
845                     }
846                     ray->color3fv(cA);
847                     ok &= ray->sphere3fv(v, radius);
848                     vc += 3;
849                   } else {
850                     ok &= ray->sphere3fv(v, radius);
851                   }
852                   v += 3;
853                 }
854               }
855             } else {
856               while(ok && *n) {
857                 c = *(n++);
858                 if(c--) {
859                   v += 3;
860                   if(vc) {
861                     vc += 3;
862                     if(rc)
863                       rc++;
864                   }
865                   while(ok && c--) {
866                     if(vc) {
867                       float *cA = vc - 3, *cB = vc;
868                       if(rc) {
869                         if(rc[-1] < -1)
870                           ColorGetEncoded(G, rc[-1], (cA = colA));
871                         if(rc[0] < -1)
872                           ColorGetEncoded(G, rc[0], (cB = colB));
873                         rc++;
874                       }
875                       ok &= ray->sausage3fv(v - 3, v, radius, cA, cB);
876                       vc += 3;
877                     } else {
878                       ok &= ray->sausage3fv(v - 3, v, radius, cc, cc);
879                     }
880                     v += 3;
881                   }
882                 }
883               }
884             }
885           }
886         } else if((G->HaveGUI && G->ValidContext) || returnCGO) {
887           if(!pick && !pass) {
888 	      short use_shader;
889 	      short mesh_as_cylinders ;
890 	      CGO *shaderCGO = NULL;
891 	      use_shader = ( SettingGetGlobal_b(G, cSetting_mesh_use_shader) & SettingGetGlobal_b(G, cSetting_use_shaders)) | returnCGO;
892 	      mesh_as_cylinders = SettingGetGlobal_b(G, cSetting_render_as_cylinders) && SettingGetGlobal_b(G, cSetting_mesh_as_cylinders) && ms->MeshMode != 1;
893 
894 	      if (ms->shaderCGO && (!use_shader || (mesh_as_cylinders ^ ms->shaderCGO->has_draw_cylinder_buffers))){
895             ms->shaderCGO.reset();
896             ms->shaderUnitCellCGO.reset();
897 	      }
898 
899 	      if (ms->shaderCGO && !returnCGO) {
900 		ok &= ObjectMeshStateRenderShader(ms, I, info, mesh_as_cylinders, mesh_width);
901 		continue;
902 	      }
903 
904 	      if (use_shader){
905 		shaderCGO = CGONew(G);
906 		if(!shaderCGO) {
907 		  ok = false;
908 		  break;
909 		}
910 		shaderCGO->use_shader = true;
911 	      }
912 
913 	      if(ms->UnitCellCGO && (I->visRep & cRepCellBit)) {
914 		const float *color = ColorGet(I->G, I->Color);
915 		if (!use_shader) {
916 		  CGORenderGL(ms->UnitCellCGO.get(), color, I->Setting, NULL, info, NULL);
917 		} else if(!ms->shaderUnitCellCGO) {
918 		  CGO *newUnitCellCGO = CGONewSized(G, 0);
919 		  CGOColorv(newUnitCellCGO, color);
920 		  CGOAppend(newUnitCellCGO, ms->UnitCellCGO.get());
921 		  ms->shaderUnitCellCGO.reset(CGOOptimizeToVBONotIndexedNoShader(newUnitCellCGO, 0));
922 		  CGOFree(newUnitCellCGO);
923 		  ms->shaderUnitCellCGO->use_shader = true;
924 		}
925 	      }
926 
927 	      if(info && !info->line_lighting){
928 		if(!use_shader){
929 		  glDisable(GL_LIGHTING);
930 		} else if(!mesh_as_cylinders) {
931 		  ok &= CGODisable(shaderCGO, GL_LIGHTING);
932 		}
933 	      }
934 	      if(!ok) break;
935 
936 	      if (use_shader){
937 		ok &= CGOResetNormal(shaderCGO, true);
938 	      } else {
939 		SceneResetNormal(I->G, false);
940 	      }
941 	      if(n && v && (I->visRep & cRepMeshBit)) {
942 		if(use_shader) {
943 		  vc = ms->VC.data();
944 
945 		  if(!vc)
946 		    ok &= CGOColorv(shaderCGO, ColorGet(I->G, ms->OneColor));
947 
948 		  if (!mesh_as_cylinders){
949 		    if(ms->MeshMode == 1){
950 		      ok &= CGODotwidth(shaderCGO, SettingGet_f
951 				  (I->G, I->Setting, NULL, cSetting_dot_width));
952 		    } else {
953 		      ok &= CGOSpecial(shaderCGO, LINEWIDTH_DYNAMIC_MESH);
954 		    }
955 		  }
956 
957 		  if(!ok) break;
958 
959 		  if (mesh_as_cylinders){
960 		    if(returnCGO) {
961 		      ok &= CGOSpecial(shaderCGO, CYLINDERWIDTH_DYNAMIC_MESH);
962 		    }
963                     for(; ok && (c = *(n++)); v += 3, vc && (vc += 3)) {
964                       for(; ok && (--c); v += 3) {
965                         float axis[] = {
966                           v[3] - v[0],
967                           v[4] - v[1],
968                           v[5] - v[2]
969                         };
970                         if(vc) {
971                           ok &= CGOColorv(shaderCGO, vc);
972 			  vc += 3;
973 			}
974                         if(vc && memcmp(vc - 3, vc, sizeof(float) * 3)) {
975                           ok &= (bool)shaderCGO->add<cgo::draw::shadercylinder2ndcolor>(shaderCGO, v, axis, 1.f, 15, vc);
976                         } else {
977                           ok &= (bool)shaderCGO->add<cgo::draw::shadercylinder>(v, axis, 1.f, 15);
978                         }
979                       }
980 		    }
981 		  } else {
982 		    while(ok && *n) {
983 		      c = *(n++);
984 		      if(ms->MeshMode == 1)
985 			ok &= CGOBegin(shaderCGO, GL_POINTS);
986 		      else {
987 			if (c < 2){
988 			  while(c--) {
989 			    if(vc) {
990 			      vc += 3;
991 			    }
992 			    v += 3;
993 			  }
994 			  continue;
995 			}
996 			ok &= CGOBegin(shaderCGO, GL_LINE_STRIP);
997 		      }
998 		      while(ok && c--) {
999 			if(vc) {
1000 			  ok &= CGOColorv(shaderCGO, vc);
1001 			  vc += 3;
1002 			}
1003 			if (ok)
1004 			  ok &= CGOVertexv(shaderCGO, v);
1005 			v += 3;
1006 		      }
1007 		      if (ok)
1008 			ok &= CGOEnd(shaderCGO);
1009 		    }
1010 		  }
1011 		} else {
1012 #ifndef PURE_OPENGL_ES_2
1013 		  vc = ms->VC.data();
1014 
1015 		  if(!vc)
1016 		    glColor3fv(ColorGet(I->G, ms->OneColor));
1017 		  if(ms->MeshMode == 1){
1018 		    glPointSize(SettingGet_f
1019 				(I->G, I->Setting, NULL, cSetting_dot_width));
1020 		  } else {
1021 		    glLineWidth(line_width);
1022 		  }
1023 		  while(*n) {
1024 		    c = *(n++);
1025 		    if(ms->MeshMode == 1)
1026 		      glBegin(GL_POINTS);
1027 		    else
1028 		      glBegin(GL_LINE_STRIP);
1029 		    while(c--) {
1030 		      if(vc) {
1031 			glColor3fv(vc);
1032 			vc += 3;
1033 		      }
1034 		      glVertex3fv(v);
1035 		      v += 3;
1036 		    }
1037 		    glEnd();
1038 		  }
1039 #endif
1040 		}
1041 	      }
1042 	      if(info && !info->line_lighting){
1043 		if(!use_shader){
1044 		  glEnable(GL_LIGHTING);
1045 		} else if (ok && !mesh_as_cylinders){
1046 		  ok &= CGOEnable(shaderCGO, GL_LIGHTING);
1047 		}
1048 	      }
1049 
1050 	      if (use_shader) {
1051 		if (ok){
1052 		  CGO *convertcgo = NULL;
1053 		  if (ok)
1054 		    ok &= CGOStop(shaderCGO);
1055 		  if (ok)
1056 		    convertcgo = CGOCombineBeginEnd(shaderCGO, 0);
1057 		  CHECKOK(ok, convertcgo);
1058 		  CGOFree(shaderCGO);
1059 		  shaderCGO = convertcgo;
1060 		  if (returnCGO){
1061 		    return (shaderCGO);
1062 		  } else {
1063 		    ms->shaderCGO.reset(shaderCGO);
1064 		  }
1065 		  if (ok){
1066 		    if (mesh_as_cylinders){
1067                       CGO *tmpCGO = CGONew(G);
1068                       ok &= CGOEnable(tmpCGO, GL_CYLINDER_SHADER);
1069                       if (ok) ok &= CGOSpecial(tmpCGO, MESH_WIDTH_FOR_SURFACES);
1070                       convertcgo = CGOConvertShaderCylindersToCylinderShader(ms->shaderCGO.get(), tmpCGO);
1071                       if (ok) ok &= CGOAppendNoStop(tmpCGO, convertcgo);
1072                       if (ok) ok &= CGODisable(tmpCGO, GL_CYLINDER_SHADER);
1073                       if (ok) ok &= CGOStop(tmpCGO);
1074                       CGOFreeWithoutVBOs(convertcgo);
1075                       convertcgo = tmpCGO;
1076                       convertcgo->use_shader = convertcgo->has_draw_cylinder_buffers = true;
1077 		    } else {
1078 		      convertcgo = CGOOptimizeToVBONotIndexedWithReturnedData(ms->shaderCGO.get(), 0, false, NULL);
1079 		    }
1080 		    CHECKOK(ok, convertcgo);
1081 		  }
1082 		  if (convertcgo){
1083 		    ms->shaderCGO.reset(convertcgo);
1084 		  }
1085 		}
1086 
1087 		if(!ok) break;
1088 
1089                 ok &= ObjectMeshStateRenderShader(ms, I, info, mesh_as_cylinders, mesh_width);
1090 	      }
1091 	  }
1092 	}
1093     }
1094   }
1095   if (!ok){
1096     I->invalidate(cRepMesh, cRepInvPurge, -1);
1097     I->invalidate(cRepCGO, cRepInvPurge, -1);
1098     ObjectSetRepVisMask(I, 0, cVis_AS);
1099   }
1100 
1101   return NULL;
1102 }
1103 
1104 
1105 /*========================================================================*/
1106 
getNFrame() const1107 int ObjectMesh::getNFrame() const
1108 {
1109   return NState;
1110 }
1111 
1112 
1113 /*========================================================================*/
ObjectMesh(PyMOLGlobals * G)1114 ObjectMesh::ObjectMesh(PyMOLGlobals * G) : CObject(G)
1115 {
1116   auto I = this;
1117   I->State = pymol::vla<ObjectMeshState>(10);   /* autozero important */
1118   I->type = cObjectMesh;
1119 }
1120 
1121 
1122 /*========================================================================*/
ObjectMeshState(PyMOLGlobals * G)1123 ObjectMeshState::ObjectMeshState(PyMOLGlobals* G)
1124     : CObjectState(G)
1125     , Crystal(G)
1126 {
1127   V = pymol::vla<float>(10000);
1128   N = pymol::vla<int>(10000);
1129 }
1130 
1131 
1132 /*========================================================================*/
ObjectMeshFromXtalSym(PyMOLGlobals * G,ObjectMesh * obj,ObjectMap * map,CSymmetry * sym,int map_state,int state,float * mn,float * mx,float level,int meshMode,float carve,float * vert_vla,float alt_level,int quiet)1133 ObjectMesh *ObjectMeshFromXtalSym(PyMOLGlobals * G, ObjectMesh * obj, ObjectMap * map,
1134                                   CSymmetry * sym,
1135                                   int map_state,
1136                                   int state, float *mn, float *mx,
1137                                   float level, int meshMode,
1138                                   float carve, float *vert_vla,
1139                                   float alt_level, int quiet)
1140 {
1141   int ok = true;
1142   ObjectMesh *I = NULL;
1143   ObjectMeshState *ms = NULL;
1144   ObjectMapState *oms = NULL;
1145   int created = !obj;
1146 
1147   if(created) {
1148     I = new ObjectMesh(G);
1149   } else {
1150     I = obj;
1151   }
1152   CHECKOK(ok, I);
1153 
1154   if (ok){
1155     if(state < 0)
1156       state = I->NState;
1157     if(I->NState <= state) {
1158       VLACheck(I->State, ObjectMeshState, state);
1159       CHECKOK(ok, I->State);
1160       if (ok)
1161 	I->NState = state + 1;
1162     }
1163   }
1164 
1165   if (ok){
1166     ms = I->State + state;
1167     *ms = ObjectMeshState(G);
1168   }
1169 
1170   if (ok){
1171     strcpy(ms->MapName, map->Name);
1172     ms->MapState = map_state;
1173     oms = ObjectMapGetState(map, map_state);
1174 
1175     ms->Level = level;
1176     ms->AltLevel = alt_level;
1177     ms->MeshMode = meshMode;
1178     ms->quiet = quiet;
1179   }
1180   if(ok && oms) {
1181     if((meshMode == 3) && (ms->AltLevel < ms->Level)) {
1182       /* gradient object -- need to auto-set range */
1183       if(!ObjectMapStateGetDataRange(G, oms, &ms->Level, &ms->AltLevel)) {
1184         ms->Level = -1.0F;
1185         ms->AltLevel = 1.0F;
1186       }
1187     }
1188 
1189     copy3f(mn, ms->ExtentMin);  /* this is not exactly correct...should actually take vertex points from range */
1190     copy3f(mx, ms->ExtentMax);
1191 
1192     if(!oms->Matrix.empty()) {
1193       ok &= ObjectStateSetMatrix(ms, oms->Matrix.data());
1194     } else if(!ms->Matrix.empty()) {
1195       ObjectStateResetMatrix(ms);
1196     }
1197 
1198     if (ok) {
1199       float *min_ext, *max_ext;
1200       float tmp_min[3], tmp_max[3];
1201       if(MatrixInvTransformExtentsR44d3f(ms->Matrix.data(),
1202                                          ms->ExtentMin, ms->ExtentMax,
1203                                          tmp_min, tmp_max)) {
1204         min_ext = tmp_min;
1205         max_ext = tmp_max;
1206       } else {
1207         min_ext = ms->ExtentMin;
1208         max_ext = ms->ExtentMax;
1209       }
1210 
1211       if(sym) {
1212         int eff_range[6];
1213 
1214         if(IsosurfGetRange
1215            (G, oms->Field.get(), &oms->Symmetry->Crystal, min_ext, max_ext, eff_range, false)) {
1216           int fdim[3];
1217           int expand_result;
1218           /* need to generate symmetry-expanded temporary map */
1219 
1220           ms->Crystal = (oms->Symmetry->Crystal);
1221           fdim[0] = eff_range[3] - eff_range[0];
1222           fdim[1] = eff_range[4] - eff_range[1];
1223           fdim[2] = eff_range[5] - eff_range[2];
1224           ms->Field = pymol::make_copyable<Isofield>(I->G, fdim);
1225 
1226           expand_result =
1227             IsosurfExpand(oms->Field.get(), ms->Field.get(), &oms->Symmetry->Crystal, sym, eff_range);
1228 
1229           if(expand_result == 0) {
1230             ok = false;
1231             if(!quiet) {
1232               PRINTFB(G, FB_ObjectMesh, FB_Warnings)
1233                 " ObjectMesh-Warning: no symmetry expanded map points found.\n" ENDFB(G);
1234             }
1235           } else {
1236             if(!quiet) {
1237               PRINTFB(G, FB_ObjectMesh, FB_Warnings)
1238                 " ObjectMesh-Warning: not all symmetry expanded points covered by map.\n"
1239                 ENDFB(G);
1240             }
1241           }
1242 
1243           ms->Range[0] = 0;
1244           ms->Range[1] = 0;
1245           ms->Range[2] = 0;
1246           ms->Range[3] = fdim[0];
1247           ms->Range[4] = fdim[1];
1248           ms->Range[5] = fdim[2];
1249 
1250         } else {
1251           /* mesh entirely contained within bounds of current map */
1252           int a;
1253           for(a = 0; a < 6; a++) {
1254             ms->Range[a] = eff_range[a];
1255           }
1256         }
1257       } else {
1258         IsosurfGetRange(G, oms->Field.get(), &oms->Symmetry->Crystal, min_ext, max_ext, ms->Range, true);
1259       }
1260     }
1261     ms->ExtentFlag = true;
1262   }
1263   if(ok) {
1264     if(carve != 0.0) {
1265       ms->CarveFlag = true;
1266       ms->CarveBuffer = carve;
1267       ms->AtomVertex = pymol::vla_take_ownership(vert_vla);
1268     }
1269     if(I) {
1270       ObjectMeshRecomputeExtent(I);
1271     }
1272     I->ExtentFlag = true;
1273     /*  printf("Brick %d %d %d %d %d %d\n",I->Range[0],I->Range[1],I->Range[2],I->Range[3],I->Range[4],I->Range[5]); */
1274   }
1275   if(!ok && created) {
1276     DeleteP(I);
1277   }
1278   SceneChanged(G);
1279   SceneCountFrames(G);
1280   return (I);
1281 }
1282 
1283 
1284 /*========================================================================*/
ObjectMeshFromBox(PyMOLGlobals * G,ObjectMesh * obj,ObjectMap * map,int map_state,int state,float * mn,float * mx,float level,int meshMode,float carve,float * vert_vla,float alt_level,int quiet)1285 ObjectMesh *ObjectMeshFromBox(PyMOLGlobals * G, ObjectMesh * obj, ObjectMap * map,
1286                               int map_state,
1287                               int state, float *mn, float *mx,
1288                               float level, int meshMode,
1289                               float carve, float *vert_vla, float alt_level, int quiet)
1290 {
1291   return ObjectMeshFromXtalSym(G, obj, map, NULL, map_state, state, mn, mx,
1292       level, meshMode, carve, vert_vla, alt_level, quiet);
1293 }
1294 
1295 
1296 /*========================================================================*/
1297 
ObjectMeshRecomputeExtent(ObjectMesh * I)1298 void ObjectMeshRecomputeExtent(ObjectMesh * I)
1299 {
1300   int extent_flag = false;
1301   int a;
1302   ObjectMeshState *ms;
1303 
1304   for(a = 0; a < I->NState; a++) {
1305     ms = I->State + a;
1306     if(ms->Active) {
1307       if(ms->ExtentFlag) {
1308         if(!extent_flag) {
1309           extent_flag = true;
1310           copy3f(ms->ExtentMax, I->ExtentMax);
1311           copy3f(ms->ExtentMin, I->ExtentMin);
1312         } else {
1313           max3f(ms->ExtentMax, I->ExtentMax, I->ExtentMax);
1314           min3f(ms->ExtentMin, I->ExtentMin, I->ExtentMin);
1315         }
1316       }
1317     }
1318   }
1319 
1320   I->ExtentFlag = extent_flag;
1321 
1322   if(I->TTTFlag && I->ExtentFlag) {
1323     const float *ttt;
1324     double tttd[16];
1325     if(ObjectGetTTT(I, &ttt, -1)) {
1326       convertTTTfR44d(ttt, tttd);
1327       MatrixTransformExtentsR44d3f(tttd,
1328                                    I->ExtentMin, I->ExtentMax,
1329                                    I->ExtentMin, I->ExtentMax);
1330     }
1331   }
1332 }
1333 
clone() const1334 CObject* ObjectMesh::clone() const
1335 {
1336   return new ObjectMesh(*this);
1337 }
1338 
1339