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