diff --git a/.gitignore b/.gitignore index f174fc585..b8c997d74 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ .cproject .project .settings/ +.cache/ +.DS_Store /dhewm* /*-releases/ diff --git a/base/models/materialeditor/box128x32.ase b/base/models/materialeditor/box128x32.ase new file mode 100644 index 000000000..a15158f82 --- /dev/null +++ b/base/models/materialeditor/box128x32.ase @@ -0,0 +1,1650 @@ +*3DSMAX_ASCIIEXPORT 200 +*COMMENT "AsciiExport Version 2.00 - Tue Dec 09 14:00:37 2003" +*SCENE { + *SCENE_FILENAME "t.max" + *SCENE_FIRSTFRAME 0 + *SCENE_LASTFRAME 300 + *SCENE_FRAMESPEED 30 + *SCENE_TICKSPERFRAME 160 + *SCENE_BACKGROUND_STATIC 0.0000 0.0000 0.0000 + *SCENE_AMBIENT_STATIC 0.0000 0.0000 0.0000 +} +*MATERIAL_LIST { + *MATERIAL_COUNT 6 + *MATERIAL 0 { + *MATERIAL_NAME "1 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4706 0.4706 0.4706 + *MATERIAL_DIFFUSE 0.4706 0.4706 0.4706 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 1 { + *MATERIAL_NAME "9 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5882 0.5882 0.5882 + *MATERIAL_DIFFUSE 0.5882 0.5882 0.5882 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + *MAP_DIFFUSE { + *MAP_NAME "Map #1" + *MAP_CLASS "Bitmap" + *MAP_SUBNO 1 + *MAP_AMOUNT 1.0000 + *BITMAP "C:\Documents and Settings\Jay Brushwood\Desktop\base\Newguy\t.jpg" + *MAP_TYPE Screen + *UVW_U_OFFSET 0.0000 + *UVW_V_OFFSET 0.0000 + *UVW_U_TILING 1.0000 + *UVW_V_TILING 1.0000 + *UVW_ANGLE 0.0000 + *UVW_BLUR 1.0000 + *UVW_BLUR_OFFSET 0.0000 + *UVW_NOUSE_AMT 1.0000 + *UVW_NOISE_SIZE 1.0000 + *UVW_NOISE_LEVEL 1 + *UVW_NOISE_PHASE 0.0000 + *BITMAP_FILTER Pyramidal + } + } + *MATERIAL 2 { + *MATERIAL_NAME "Material #12" + *MATERIAL_CLASS "Multi/Sub-Object" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *NUMSUBMTLS 2 + *SUBMATERIAL 0 { + *MATERIAL_NAME "1 - Defaultas" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *SUBMATERIAL 1 { + *MATERIAL_NAME "1 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4706 0.4706 0.4706 + *MATERIAL_DIFFUSE 0.4706 0.4706 0.4706 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + } + *MATERIAL 3 { + *MATERIAL_NAME "1 - Defaultas" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 4 { + *MATERIAL_NAME "14 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5882 0.5882 0.5882 + *MATERIAL_DIFFUSE 0.5882 0.5882 0.5882 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 5 { + *MATERIAL_NAME "7 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4353 0.4471 0.6157 + *MATERIAL_DIFFUSE 0.4353 0.4471 0.6157 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } +} +*GEOMOBJECT { + *NODE_NAME "Box03_128x32" + *NODE_TM { + *NODE_NAME "Box03_128x32" + *INHERIT_POS 0 0 0 + *INHERIT_ROT 0 0 0 + *INHERIT_SCL 0 0 0 + *TM_ROW0 -0.0000 -1.0000 0.0000 + *TM_ROW1 -0.0000 0.0000 1.0000 + *TM_ROW2 -1.0000 0.0000 -0.0000 + *TM_ROW3 -0.0000 -0.0000 -0.0000 + *TM_POS -0.0000 -0.0000 -0.0000 + *TM_ROTAXIS 0.5774 -0.5774 -0.5774 + *TM_ROTANGLE 4.1888 + *TM_SCALE 1.0000 1.0000 1.0000 + *TM_SCALEAXIS 0.0000 0.0000 0.0000 + *TM_SCALEAXISANG 0.0000 + } + *MESH { + *TIMEVALUE 0 + *MESH_NUMVERTEX 150 + *MESH_NUMFACES 192 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 64.0000 64.0000 -16.0000 + *MESH_VERTEX 1 64.0000 32.0000 -16.0000 + *MESH_VERTEX 2 64.0000 -0.0000 -16.0000 + *MESH_VERTEX 3 64.0000 -32.0000 -16.0000 + *MESH_VERTEX 4 64.0000 -64.0000 -16.0000 + *MESH_VERTEX 5 64.0000 64.0000 -8.0000 + *MESH_VERTEX 6 64.0000 32.0000 -8.0000 + *MESH_VERTEX 7 64.0000 -0.0000 -8.0000 + *MESH_VERTEX 8 64.0000 -32.0000 -8.0000 + *MESH_VERTEX 9 64.0000 -64.0000 -8.0000 + *MESH_VERTEX 10 64.0000 64.0000 0.0000 + *MESH_VERTEX 11 64.0000 32.0000 0.0000 + *MESH_VERTEX 12 64.0000 -0.0000 0.0000 + *MESH_VERTEX 13 64.0000 -32.0000 0.0000 + *MESH_VERTEX 14 64.0000 -64.0000 0.0000 + *MESH_VERTEX 15 64.0000 64.0000 8.0000 + *MESH_VERTEX 16 64.0000 32.0000 8.0000 + *MESH_VERTEX 17 64.0000 -0.0000 8.0000 + *MESH_VERTEX 18 64.0000 -32.0000 8.0000 + *MESH_VERTEX 19 64.0000 -64.0000 8.0000 + *MESH_VERTEX 20 64.0000 64.0000 16.0000 + *MESH_VERTEX 21 64.0000 32.0000 16.0000 + *MESH_VERTEX 22 64.0000 -0.0000 16.0000 + *MESH_VERTEX 23 64.0000 -32.0000 16.0000 + *MESH_VERTEX 24 64.0000 -64.0000 16.0000 + *MESH_VERTEX 25 -64.0000 64.0000 -16.0000 + *MESH_VERTEX 26 -64.0000 32.0000 -16.0000 + *MESH_VERTEX 27 -64.0000 0.0000 -16.0000 + *MESH_VERTEX 28 -64.0000 -32.0000 -16.0000 + *MESH_VERTEX 29 -64.0000 -64.0000 -16.0000 + *MESH_VERTEX 30 -64.0000 64.0000 -8.0000 + *MESH_VERTEX 31 -64.0000 32.0000 -8.0000 + *MESH_VERTEX 32 -64.0000 0.0000 -8.0000 + *MESH_VERTEX 33 -64.0000 -32.0000 -8.0000 + *MESH_VERTEX 34 -64.0000 -64.0000 -8.0000 + *MESH_VERTEX 35 -64.0000 64.0000 -0.0000 + *MESH_VERTEX 36 -64.0000 32.0000 -0.0000 + *MESH_VERTEX 37 -64.0000 0.0000 -0.0000 + *MESH_VERTEX 38 -64.0000 -32.0000 -0.0000 + *MESH_VERTEX 39 -64.0000 -64.0000 -0.0000 + *MESH_VERTEX 40 -64.0000 64.0000 8.0000 + *MESH_VERTEX 41 -64.0000 32.0000 8.0000 + *MESH_VERTEX 42 -64.0000 0.0000 8.0000 + *MESH_VERTEX 43 -64.0000 -32.0000 8.0000 + *MESH_VERTEX 44 -64.0000 -64.0000 8.0000 + *MESH_VERTEX 45 -64.0000 64.0000 16.0000 + *MESH_VERTEX 46 -64.0000 32.0000 16.0000 + *MESH_VERTEX 47 -64.0000 0.0000 16.0000 + *MESH_VERTEX 48 -64.0000 -32.0000 16.0000 + *MESH_VERTEX 49 -64.0000 -64.0000 16.0000 + *MESH_VERTEX 50 32.0000 64.0000 -16.0000 + *MESH_VERTEX 51 32.0000 32.0000 -16.0000 + *MESH_VERTEX 52 32.0000 -0.0000 -16.0000 + *MESH_VERTEX 53 32.0000 -32.0000 -16.0000 + *MESH_VERTEX 54 32.0000 -64.0000 -16.0000 + *MESH_VERTEX 55 32.0000 -64.0000 -8.0000 + *MESH_VERTEX 56 32.0000 -64.0000 -0.0000 + *MESH_VERTEX 57 32.0000 -64.0000 8.0000 + *MESH_VERTEX 58 32.0000 -64.0000 16.0000 + *MESH_VERTEX 59 32.0000 -32.0000 16.0000 + *MESH_VERTEX 60 32.0000 -0.0000 16.0000 + *MESH_VERTEX 61 32.0000 32.0000 16.0000 + *MESH_VERTEX 62 32.0000 64.0000 16.0000 + *MESH_VERTEX 63 32.0000 64.0000 8.0000 + *MESH_VERTEX 64 32.0000 64.0000 -0.0000 + *MESH_VERTEX 65 32.0000 64.0000 -8.0000 + *MESH_VERTEX 66 0.0000 64.0000 -16.0000 + *MESH_VERTEX 67 0.0000 32.0000 -16.0000 + *MESH_VERTEX 68 0.0000 -0.0000 -16.0000 + *MESH_VERTEX 69 -0.0000 -32.0000 -16.0000 + *MESH_VERTEX 70 -0.0000 -64.0000 -16.0000 + *MESH_VERTEX 71 -0.0000 -64.0000 -8.0000 + *MESH_VERTEX 72 -0.0000 -64.0000 -0.0000 + *MESH_VERTEX 73 -0.0000 -64.0000 8.0000 + *MESH_VERTEX 74 -0.0000 -64.0000 16.0000 + *MESH_VERTEX 75 -0.0000 -32.0000 16.0000 + *MESH_VERTEX 76 -0.0000 -0.0000 16.0000 + *MESH_VERTEX 77 0.0000 32.0000 16.0000 + *MESH_VERTEX 78 0.0000 64.0000 16.0000 + *MESH_VERTEX 79 0.0000 64.0000 8.0000 + *MESH_VERTEX 80 0.0000 64.0000 -0.0000 + *MESH_VERTEX 81 0.0000 64.0000 -8.0000 + *MESH_VERTEX 82 -32.0000 64.0000 -16.0000 + *MESH_VERTEX 83 -32.0000 32.0000 -16.0000 + *MESH_VERTEX 84 -32.0000 0.0000 -16.0000 + *MESH_VERTEX 85 -32.0000 -32.0000 -16.0000 + *MESH_VERTEX 86 -32.0000 -64.0000 -16.0000 + *MESH_VERTEX 87 -32.0000 -64.0000 -8.0000 + *MESH_VERTEX 88 -32.0000 -64.0000 -0.0000 + *MESH_VERTEX 89 -32.0000 -64.0000 8.0000 + *MESH_VERTEX 90 -32.0000 -64.0000 16.0000 + *MESH_VERTEX 91 -32.0000 -32.0000 16.0000 + *MESH_VERTEX 92 -32.0000 0.0000 16.0000 + *MESH_VERTEX 93 -32.0000 32.0000 16.0000 + *MESH_VERTEX 94 -32.0000 64.0000 16.0000 + *MESH_VERTEX 95 -32.0000 64.0000 8.0000 + *MESH_VERTEX 96 -32.0000 64.0000 -0.0000 + *MESH_VERTEX 97 -32.0000 64.0000 -8.0000 + *MESH_VERTEX 98 64.0000 64.0000 -16.0000 + *MESH_VERTEX 99 64.0000 -64.0000 -16.0000 + *MESH_VERTEX 100 64.0000 64.0000 -8.0000 + *MESH_VERTEX 101 64.0000 -64.0000 -8.0000 + *MESH_VERTEX 102 64.0000 64.0000 0.0000 + *MESH_VERTEX 103 64.0000 -64.0000 0.0000 + *MESH_VERTEX 104 64.0000 64.0000 8.0000 + *MESH_VERTEX 105 64.0000 -64.0000 8.0000 + *MESH_VERTEX 106 64.0000 64.0000 16.0000 + *MESH_VERTEX 107 64.0000 -64.0000 16.0000 + *MESH_VERTEX 108 -64.0000 64.0000 -16.0000 + *MESH_VERTEX 109 -64.0000 -64.0000 -16.0000 + *MESH_VERTEX 110 -64.0000 64.0000 -8.0000 + *MESH_VERTEX 111 -64.0000 -64.0000 -8.0000 + *MESH_VERTEX 112 -64.0000 64.0000 -0.0000 + *MESH_VERTEX 113 -64.0000 -64.0000 -0.0000 + *MESH_VERTEX 114 -64.0000 64.0000 8.0000 + *MESH_VERTEX 115 -64.0000 -64.0000 8.0000 + *MESH_VERTEX 116 -64.0000 64.0000 16.0000 + *MESH_VERTEX 117 -64.0000 -64.0000 16.0000 + *MESH_VERTEX 118 32.0000 64.0000 -16.0000 + *MESH_VERTEX 119 32.0000 -64.0000 -16.0000 + *MESH_VERTEX 120 32.0000 -64.0000 16.0000 + *MESH_VERTEX 121 32.0000 64.0000 16.0000 + *MESH_VERTEX 122 0.0000 64.0000 -16.0000 + *MESH_VERTEX 123 -0.0000 -64.0000 -16.0000 + *MESH_VERTEX 124 -0.0000 -64.0000 16.0000 + *MESH_VERTEX 125 0.0000 64.0000 16.0000 + *MESH_VERTEX 126 -32.0000 64.0000 -16.0000 + *MESH_VERTEX 127 -32.0000 -64.0000 -16.0000 + *MESH_VERTEX 128 -32.0000 -64.0000 16.0000 + *MESH_VERTEX 129 -32.0000 64.0000 16.0000 + *MESH_VERTEX 130 64.0000 64.0000 -16.0000 + *MESH_VERTEX 131 64.0000 32.0000 -16.0000 + *MESH_VERTEX 132 64.0000 -0.0000 -16.0000 + *MESH_VERTEX 133 64.0000 -32.0000 -16.0000 + *MESH_VERTEX 134 64.0000 -64.0000 -16.0000 + *MESH_VERTEX 135 64.0000 64.0000 16.0000 + *MESH_VERTEX 136 64.0000 32.0000 16.0000 + *MESH_VERTEX 137 64.0000 -0.0000 16.0000 + *MESH_VERTEX 138 64.0000 -32.0000 16.0000 + *MESH_VERTEX 139 64.0000 -64.0000 16.0000 + *MESH_VERTEX 140 -64.0000 64.0000 -16.0000 + *MESH_VERTEX 141 -64.0000 32.0000 -16.0000 + *MESH_VERTEX 142 -64.0000 0.0000 -16.0000 + *MESH_VERTEX 143 -64.0000 -32.0000 -16.0000 + *MESH_VERTEX 144 -64.0000 -64.0000 -16.0000 + *MESH_VERTEX 145 -64.0000 64.0000 16.0000 + *MESH_VERTEX 146 -64.0000 32.0000 16.0000 + *MESH_VERTEX 147 -64.0000 0.0000 16.0000 + *MESH_VERTEX 148 -64.0000 -32.0000 16.0000 + *MESH_VERTEX 149 -64.0000 -64.0000 16.0000 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 130 B: 5 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 1: A: 6 B: 131 C: 130 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 2: A: 131 B: 6 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 3: A: 7 B: 132 C: 131 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 4: A: 132 B: 7 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 5: A: 8 B: 133 C: 132 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 6: A: 133 B: 8 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 7: A: 9 B: 134 C: 133 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 8: A: 5 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 9: A: 11 B: 6 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 10: A: 6 B: 11 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 11: A: 12 B: 7 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 12: A: 7 B: 12 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 13: A: 13 B: 8 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 14: A: 8 B: 13 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 15: A: 14 B: 9 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 16: A: 10 B: 15 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 17: A: 16 B: 11 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 18: A: 11 B: 16 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 19: A: 17 B: 12 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 20: A: 12 B: 17 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 21: A: 18 B: 13 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 22: A: 13 B: 18 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 23: A: 19 B: 14 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 24: A: 15 B: 135 C: 136 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 25: A: 136 B: 16 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 26: A: 16 B: 136 C: 137 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 27: A: 137 B: 17 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 28: A: 17 B: 137 C: 138 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 29: A: 138 B: 18 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 30: A: 18 B: 138 C: 139 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 31: A: 139 B: 19 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 32: A: 140 B: 141 C: 31 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 33: A: 31 B: 30 C: 140 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 34: A: 141 B: 142 C: 32 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 35: A: 32 B: 31 C: 141 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 36: A: 142 B: 143 C: 33 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 37: A: 33 B: 32 C: 142 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 38: A: 143 B: 144 C: 34 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 39: A: 34 B: 33 C: 143 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 40: A: 30 B: 31 C: 36 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 41: A: 36 B: 35 C: 30 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 42: A: 31 B: 32 C: 37 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 43: A: 37 B: 36 C: 31 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 44: A: 32 B: 33 C: 38 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 45: A: 38 B: 37 C: 32 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 46: A: 33 B: 34 C: 39 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 47: A: 39 B: 38 C: 33 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 48: A: 35 B: 36 C: 41 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 49: A: 41 B: 40 C: 35 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 50: A: 36 B: 37 C: 42 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 51: A: 42 B: 41 C: 36 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 52: A: 37 B: 38 C: 43 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 53: A: 43 B: 42 C: 37 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 54: A: 38 B: 39 C: 44 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 55: A: 44 B: 43 C: 38 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 56: A: 40 B: 41 C: 146 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 57: A: 146 B: 145 C: 40 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 58: A: 41 B: 42 C: 147 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 59: A: 147 B: 146 C: 41 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 60: A: 42 B: 43 C: 148 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 61: A: 148 B: 147 C: 42 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 62: A: 43 B: 44 C: 149 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 63: A: 149 B: 148 C: 43 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 64: A: 0 B: 1 C: 51 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 65: A: 51 B: 50 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 66: A: 1 B: 2 C: 52 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 67: A: 52 B: 51 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 68: A: 2 B: 3 C: 53 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 69: A: 53 B: 52 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 70: A: 3 B: 4 C: 54 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 71: A: 54 B: 53 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 72: A: 50 B: 51 C: 67 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 73: A: 67 B: 66 C: 50 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 74: A: 51 B: 52 C: 68 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 75: A: 68 B: 67 C: 51 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 76: A: 52 B: 53 C: 69 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 77: A: 69 B: 68 C: 52 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 78: A: 53 B: 54 C: 70 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 79: A: 70 B: 69 C: 53 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 80: A: 66 B: 67 C: 83 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 81: A: 83 B: 82 C: 66 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 82: A: 67 B: 68 C: 84 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 83: A: 84 B: 83 C: 67 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 84: A: 68 B: 69 C: 85 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 85: A: 85 B: 84 C: 68 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 86: A: 69 B: 70 C: 86 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 87: A: 86 B: 85 C: 69 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 88: A: 82 B: 83 C: 26 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 89: A: 26 B: 25 C: 82 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 90: A: 83 B: 84 C: 27 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 91: A: 27 B: 26 C: 83 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 92: A: 84 B: 85 C: 28 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 93: A: 28 B: 27 C: 84 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 94: A: 85 B: 86 C: 29 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 95: A: 29 B: 28 C: 85 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 96: A: 99 B: 101 C: 55 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 97: A: 55 B: 119 C: 99 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 98: A: 101 B: 103 C: 56 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 99: A: 56 B: 55 C: 101 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 100: A: 103 B: 105 C: 57 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 101: A: 57 B: 56 C: 103 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 102: A: 105 B: 107 C: 120 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 103: A: 120 B: 57 C: 105 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 104: A: 119 B: 55 C: 71 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 105: A: 71 B: 123 C: 119 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 106: A: 55 B: 56 C: 72 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 107: A: 72 B: 71 C: 55 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 108: A: 56 B: 57 C: 73 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 109: A: 73 B: 72 C: 56 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 110: A: 57 B: 120 C: 124 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 111: A: 124 B: 73 C: 57 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 112: A: 123 B: 71 C: 87 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 113: A: 87 B: 127 C: 123 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 114: A: 71 B: 72 C: 88 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 115: A: 88 B: 87 C: 71 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 116: A: 72 B: 73 C: 89 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 117: A: 89 B: 88 C: 72 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 118: A: 73 B: 124 C: 128 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 119: A: 128 B: 89 C: 73 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 120: A: 127 B: 87 C: 111 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 121: A: 111 B: 109 C: 127 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 122: A: 87 B: 88 C: 113 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 123: A: 113 B: 111 C: 87 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 124: A: 88 B: 89 C: 115 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 125: A: 115 B: 113 C: 88 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 126: A: 89 B: 128 C: 117 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 127: A: 117 B: 115 C: 89 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 128: A: 24 B: 23 C: 59 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 129: A: 59 B: 58 C: 24 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 130: A: 23 B: 22 C: 60 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 131: A: 60 B: 59 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 132: A: 22 B: 21 C: 61 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 133: A: 61 B: 60 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 134: A: 21 B: 20 C: 62 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 135: A: 62 B: 61 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 136: A: 58 B: 59 C: 75 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 137: A: 75 B: 74 C: 58 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 138: A: 59 B: 60 C: 76 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 139: A: 76 B: 75 C: 59 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 140: A: 60 B: 61 C: 77 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 141: A: 77 B: 76 C: 60 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 142: A: 61 B: 62 C: 78 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 143: A: 78 B: 77 C: 61 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 144: A: 74 B: 75 C: 91 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 145: A: 91 B: 90 C: 74 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 146: A: 75 B: 76 C: 92 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 147: A: 92 B: 91 C: 75 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 148: A: 76 B: 77 C: 93 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 149: A: 93 B: 92 C: 76 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 150: A: 77 B: 78 C: 94 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 151: A: 94 B: 93 C: 77 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 152: A: 90 B: 91 C: 48 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 153: A: 48 B: 49 C: 90 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 154: A: 91 B: 92 C: 47 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 155: A: 47 B: 48 C: 91 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 156: A: 92 B: 93 C: 46 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 157: A: 46 B: 47 C: 92 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 158: A: 93 B: 94 C: 45 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 159: A: 45 B: 46 C: 93 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 160: A: 106 B: 104 C: 63 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 161: A: 63 B: 121 C: 106 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 162: A: 104 B: 102 C: 64 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 163: A: 64 B: 63 C: 104 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 164: A: 102 B: 100 C: 65 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 165: A: 65 B: 64 C: 102 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 166: A: 100 B: 98 C: 118 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 167: A: 118 B: 65 C: 100 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 168: A: 121 B: 63 C: 79 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 169: A: 79 B: 125 C: 121 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 170: A: 63 B: 64 C: 80 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 171: A: 80 B: 79 C: 63 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 172: A: 64 B: 65 C: 81 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 173: A: 81 B: 80 C: 64 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 174: A: 65 B: 118 C: 122 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 175: A: 122 B: 81 C: 65 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 176: A: 125 B: 79 C: 95 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 177: A: 95 B: 129 C: 125 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 178: A: 79 B: 80 C: 96 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 179: A: 96 B: 95 C: 79 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 180: A: 80 B: 81 C: 97 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 181: A: 97 B: 96 C: 80 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 182: A: 81 B: 122 C: 126 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 183: A: 126 B: 97 C: 81 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 184: A: 129 B: 95 C: 114 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 185: A: 114 B: 116 C: 129 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 186: A: 95 B: 96 C: 112 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 187: A: 112 B: 114 C: 95 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 188: A: 96 B: 97 C: 110 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 189: A: 110 B: 112 C: 96 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 190: A: 97 B: 126 C: 108 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 191: A: 108 B: 110 C: 97 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 150 + *MESH_TVERTLIST { + *MESH_TVERT 0 1.0000 0.2500 0.0000 + *MESH_TVERT 1 0.7500 0.2500 0.0000 + *MESH_TVERT 2 0.5000 0.2500 0.0000 + *MESH_TVERT 3 0.2500 0.2500 0.0000 + *MESH_TVERT 4 0.0000 0.2500 0.0000 + *MESH_TVERT 5 1.0000 0.5000 0.0000 + *MESH_TVERT 6 0.7500 0.5000 0.0000 + *MESH_TVERT 7 0.5000 0.5000 0.0000 + *MESH_TVERT 8 0.2500 0.5000 0.0000 + *MESH_TVERT 9 0.0000 0.5000 0.0000 + *MESH_TVERT 10 1.0000 0.7500 0.0000 + *MESH_TVERT 11 0.7500 0.7500 0.0000 + *MESH_TVERT 12 0.5000 0.7500 0.0000 + *MESH_TVERT 13 0.2500 0.7500 0.0000 + *MESH_TVERT 14 0.0000 0.7500 0.0000 + *MESH_TVERT 15 0.0000 0.2500 0.0000 + *MESH_TVERT 16 0.2500 0.2500 0.0000 + *MESH_TVERT 17 0.5000 0.2500 0.0000 + *MESH_TVERT 18 0.7500 0.2500 0.0000 + *MESH_TVERT 19 1.0000 0.2500 0.0000 + *MESH_TVERT 20 0.0000 0.5000 0.0000 + *MESH_TVERT 21 0.2500 0.5000 0.0000 + *MESH_TVERT 22 0.5000 0.5000 0.0000 + *MESH_TVERT 23 0.7500 0.5000 0.0000 + *MESH_TVERT 24 1.0000 0.5000 0.0000 + *MESH_TVERT 25 0.0000 0.7500 0.0000 + *MESH_TVERT 26 0.2500 0.7500 0.0000 + *MESH_TVERT 27 0.5000 0.7500 0.0000 + *MESH_TVERT 28 0.7500 0.7500 0.0000 + *MESH_TVERT 29 1.0000 0.7500 0.0000 + *MESH_TVERT 30 1.0000 1.0000 0.0000 + *MESH_TVERT 31 0.7500 1.0000 0.0000 + *MESH_TVERT 32 0.5000 1.0000 0.0000 + *MESH_TVERT 33 0.2500 1.0000 0.0000 + *MESH_TVERT 34 0.0000 1.0000 0.0000 + *MESH_TVERT 35 1.0000 0.7500 0.0000 + *MESH_TVERT 36 0.7500 0.7500 0.0000 + *MESH_TVERT 37 0.5000 0.7500 0.0000 + *MESH_TVERT 38 0.2500 0.7500 0.0000 + *MESH_TVERT 39 -0.0000 0.7500 0.0000 + *MESH_TVERT 40 1.0000 0.5000 0.0000 + *MESH_TVERT 41 0.7500 0.5000 0.0000 + *MESH_TVERT 42 0.5000 0.5000 0.0000 + *MESH_TVERT 43 0.2500 0.5000 0.0000 + *MESH_TVERT 44 -0.0000 0.5000 0.0000 + *MESH_TVERT 45 1.0000 0.2500 0.0000 + *MESH_TVERT 46 0.7500 0.2500 0.0000 + *MESH_TVERT 47 0.5000 0.2500 0.0000 + *MESH_TVERT 48 0.2500 0.2500 0.0000 + *MESH_TVERT 49 -0.0000 0.2500 0.0000 + *MESH_TVERT 50 1.0000 -0.0000 0.0000 + *MESH_TVERT 51 0.7500 -0.0000 0.0000 + *MESH_TVERT 52 0.5000 -0.0000 0.0000 + *MESH_TVERT 53 0.2500 -0.0000 0.0000 + *MESH_TVERT 54 -0.0000 0.0000 0.0000 + *MESH_TVERT 55 0.7500 0.2500 0.0000 + *MESH_TVERT 56 0.7500 0.5000 0.0000 + *MESH_TVERT 57 0.7500 0.7500 0.0000 + *MESH_TVERT 58 0.5000 0.2500 0.0000 + *MESH_TVERT 59 0.5000 0.5000 0.0000 + *MESH_TVERT 60 0.5000 0.7500 0.0000 + *MESH_TVERT 61 0.2500 0.2500 0.0000 + *MESH_TVERT 62 0.2500 0.5000 0.0000 + *MESH_TVERT 63 0.2500 0.7500 0.0000 + *MESH_TVERT 64 1.0000 1.0000 0.0000 + *MESH_TVERT 65 0.7500 1.0000 0.0000 + *MESH_TVERT 66 0.5000 1.0000 0.0000 + *MESH_TVERT 67 0.2500 1.0000 0.0000 + *MESH_TVERT 68 0.0000 1.0000 0.0000 + *MESH_TVERT 69 1.0000 0.7500 0.0000 + *MESH_TVERT 70 0.7500 0.7500 0.0000 + *MESH_TVERT 71 0.5000 0.7500 0.0000 + *MESH_TVERT 72 0.2500 0.7500 0.0000 + *MESH_TVERT 73 -0.0000 0.7500 0.0000 + *MESH_TVERT 74 1.0000 0.5000 0.0000 + *MESH_TVERT 75 0.7500 0.5000 0.0000 + *MESH_TVERT 76 0.5000 0.5000 0.0000 + *MESH_TVERT 77 0.2500 0.5000 0.0000 + *MESH_TVERT 78 -0.0000 0.5000 0.0000 + *MESH_TVERT 79 1.0000 0.2500 0.0000 + *MESH_TVERT 80 0.7500 0.2500 0.0000 + *MESH_TVERT 81 0.5000 0.2500 0.0000 + *MESH_TVERT 82 0.2500 0.2500 0.0000 + *MESH_TVERT 83 -0.0000 0.2500 0.0000 + *MESH_TVERT 84 1.0000 -0.0000 0.0000 + *MESH_TVERT 85 0.7500 -0.0000 0.0000 + *MESH_TVERT 86 0.5000 -0.0000 0.0000 + *MESH_TVERT 87 0.2500 -0.0000 0.0000 + *MESH_TVERT 88 -0.0000 0.0000 0.0000 + *MESH_TVERT 89 0.2500 0.7500 0.0000 + *MESH_TVERT 90 0.2500 0.5000 0.0000 + *MESH_TVERT 91 0.2500 0.2500 0.0000 + *MESH_TVERT 92 0.5000 0.7500 0.0000 + *MESH_TVERT 93 0.5000 0.5000 0.0000 + *MESH_TVERT 94 0.5000 0.2500 0.0000 + *MESH_TVERT 95 0.7500 0.7500 0.0000 + *MESH_TVERT 96 0.7500 0.5000 0.0000 + *MESH_TVERT 97 0.7500 0.2500 0.0000 + *MESH_TVERT 98 -0.0000 0.0000 0.0000 + *MESH_TVERT 99 1.0000 -0.0000 0.0000 + *MESH_TVERT 100 0.0000 0.2500 0.0000 + *MESH_TVERT 101 1.0000 0.2500 0.0000 + *MESH_TVERT 102 0.0000 0.5000 0.0000 + *MESH_TVERT 103 1.0000 0.5000 0.0000 + *MESH_TVERT 104 0.0000 0.7500 0.0000 + *MESH_TVERT 105 1.0000 0.7500 0.0000 + *MESH_TVERT 106 0.0000 1.0000 0.0000 + *MESH_TVERT 107 1.0000 1.0000 0.0000 + *MESH_TVERT 108 1.0000 -0.0000 0.0000 + *MESH_TVERT 109 -0.0000 0.0000 0.0000 + *MESH_TVERT 110 1.0000 0.2500 0.0000 + *MESH_TVERT 111 -0.0000 0.2500 0.0000 + *MESH_TVERT 112 1.0000 0.5000 0.0000 + *MESH_TVERT 113 -0.0000 0.5000 0.0000 + *MESH_TVERT 114 1.0000 0.7500 0.0000 + *MESH_TVERT 115 0.0000 0.7500 0.0000 + *MESH_TVERT 116 1.0000 1.0000 0.0000 + *MESH_TVERT 117 0.0000 1.0000 0.0000 + *MESH_TVERT 118 0.2500 0.0000 0.0000 + *MESH_TVERT 119 0.7500 -0.0000 0.0000 + *MESH_TVERT 120 0.7500 1.0000 0.0000 + *MESH_TVERT 121 0.2500 1.0000 0.0000 + *MESH_TVERT 122 0.5000 -0.0000 0.0000 + *MESH_TVERT 123 0.5000 -0.0000 0.0000 + *MESH_TVERT 124 0.5000 1.0000 0.0000 + *MESH_TVERT 125 0.5000 1.0000 0.0000 + *MESH_TVERT 126 0.7500 -0.0000 0.0000 + *MESH_TVERT 127 0.2500 0.0000 0.0000 + *MESH_TVERT 128 0.2500 1.0000 0.0000 + *MESH_TVERT 129 0.7500 1.0000 0.0000 + *MESH_TVERT 130 1.0000 0.0000 0.0000 + *MESH_TVERT 131 0.7500 0.0000 0.0000 + *MESH_TVERT 132 0.5000 0.0000 0.0000 + *MESH_TVERT 133 0.2500 0.0000 0.0000 + *MESH_TVERT 134 0.0000 0.0000 0.0000 + *MESH_TVERT 135 1.0000 1.0000 0.0000 + *MESH_TVERT 136 0.7500 1.0000 0.0000 + *MESH_TVERT 137 0.5000 1.0000 0.0000 + *MESH_TVERT 138 0.2500 1.0000 0.0000 + *MESH_TVERT 139 0.0000 1.0000 0.0000 + *MESH_TVERT 140 0.0000 0.0000 0.0000 + *MESH_TVERT 141 0.2500 0.0000 0.0000 + *MESH_TVERT 142 0.5000 0.0000 0.0000 + *MESH_TVERT 143 0.7500 0.0000 0.0000 + *MESH_TVERT 144 1.0000 0.0000 0.0000 + *MESH_TVERT 145 0.0000 1.0000 0.0000 + *MESH_TVERT 146 0.2500 1.0000 0.0000 + *MESH_TVERT 147 0.5000 1.0000 0.0000 + *MESH_TVERT 148 0.7500 1.0000 0.0000 + *MESH_TVERT 149 1.0000 1.0000 0.0000 + } + *MESH_NUMTVFACES 192 + *MESH_TFACELIST { + *MESH_TFACE 0 130 0 1 + *MESH_TFACE 1 1 131 130 + *MESH_TFACE 2 131 1 2 + *MESH_TFACE 3 2 132 131 + *MESH_TFACE 4 132 2 3 + *MESH_TFACE 5 3 133 132 + *MESH_TFACE 6 133 3 4 + *MESH_TFACE 7 4 134 133 + *MESH_TFACE 8 0 5 6 + *MESH_TFACE 9 6 1 0 + *MESH_TFACE 10 1 6 7 + *MESH_TFACE 11 7 2 1 + *MESH_TFACE 12 2 7 8 + *MESH_TFACE 13 8 3 2 + *MESH_TFACE 14 3 8 9 + *MESH_TFACE 15 9 4 3 + *MESH_TFACE 16 5 10 11 + *MESH_TFACE 17 11 6 5 + *MESH_TFACE 18 6 11 12 + *MESH_TFACE 19 12 7 6 + *MESH_TFACE 20 7 12 13 + *MESH_TFACE 21 13 8 7 + *MESH_TFACE 22 8 13 14 + *MESH_TFACE 23 14 9 8 + *MESH_TFACE 24 10 135 136 + *MESH_TFACE 25 136 11 10 + *MESH_TFACE 26 11 136 137 + *MESH_TFACE 27 137 12 11 + *MESH_TFACE 28 12 137 138 + *MESH_TFACE 29 138 13 12 + *MESH_TFACE 30 13 138 139 + *MESH_TFACE 31 139 14 13 + *MESH_TFACE 32 140 141 16 + *MESH_TFACE 33 16 15 140 + *MESH_TFACE 34 141 142 17 + *MESH_TFACE 35 17 16 141 + *MESH_TFACE 36 142 143 18 + *MESH_TFACE 37 18 17 142 + *MESH_TFACE 38 143 144 19 + *MESH_TFACE 39 19 18 143 + *MESH_TFACE 40 15 16 21 + *MESH_TFACE 41 21 20 15 + *MESH_TFACE 42 16 17 22 + *MESH_TFACE 43 22 21 16 + *MESH_TFACE 44 17 18 23 + *MESH_TFACE 45 23 22 17 + *MESH_TFACE 46 18 19 24 + *MESH_TFACE 47 24 23 18 + *MESH_TFACE 48 20 21 26 + *MESH_TFACE 49 26 25 20 + *MESH_TFACE 50 21 22 27 + *MESH_TFACE 51 27 26 21 + *MESH_TFACE 52 22 23 28 + *MESH_TFACE 53 28 27 22 + *MESH_TFACE 54 23 24 29 + *MESH_TFACE 55 29 28 23 + *MESH_TFACE 56 25 26 146 + *MESH_TFACE 57 146 145 25 + *MESH_TFACE 58 26 27 147 + *MESH_TFACE 59 147 146 26 + *MESH_TFACE 60 27 28 148 + *MESH_TFACE 61 148 147 27 + *MESH_TFACE 62 28 29 149 + *MESH_TFACE 63 149 148 28 + *MESH_TFACE 64 30 31 36 + *MESH_TFACE 65 36 35 30 + *MESH_TFACE 66 31 32 37 + *MESH_TFACE 67 37 36 31 + *MESH_TFACE 68 32 33 38 + *MESH_TFACE 69 38 37 32 + *MESH_TFACE 70 33 34 39 + *MESH_TFACE 71 39 38 33 + *MESH_TFACE 72 35 36 41 + *MESH_TFACE 73 41 40 35 + *MESH_TFACE 74 36 37 42 + *MESH_TFACE 75 42 41 36 + *MESH_TFACE 76 37 38 43 + *MESH_TFACE 77 43 42 37 + *MESH_TFACE 78 38 39 44 + *MESH_TFACE 79 44 43 38 + *MESH_TFACE 80 40 41 46 + *MESH_TFACE 81 46 45 40 + *MESH_TFACE 82 41 42 47 + *MESH_TFACE 83 47 46 41 + *MESH_TFACE 84 42 43 48 + *MESH_TFACE 85 48 47 42 + *MESH_TFACE 86 43 44 49 + *MESH_TFACE 87 49 48 43 + *MESH_TFACE 88 45 46 51 + *MESH_TFACE 89 51 50 45 + *MESH_TFACE 90 46 47 52 + *MESH_TFACE 91 52 51 46 + *MESH_TFACE 92 47 48 53 + *MESH_TFACE 93 53 52 47 + *MESH_TFACE 94 48 49 54 + *MESH_TFACE 95 54 53 48 + *MESH_TFACE 96 99 101 55 + *MESH_TFACE 97 55 119 99 + *MESH_TFACE 98 101 103 56 + *MESH_TFACE 99 56 55 101 + *MESH_TFACE 100 103 105 57 + *MESH_TFACE 101 57 56 103 + *MESH_TFACE 102 105 107 120 + *MESH_TFACE 103 120 57 105 + *MESH_TFACE 104 119 55 58 + *MESH_TFACE 105 58 123 119 + *MESH_TFACE 106 55 56 59 + *MESH_TFACE 107 59 58 55 + *MESH_TFACE 108 56 57 60 + *MESH_TFACE 109 60 59 56 + *MESH_TFACE 110 57 120 124 + *MESH_TFACE 111 124 60 57 + *MESH_TFACE 112 123 58 61 + *MESH_TFACE 113 61 127 123 + *MESH_TFACE 114 58 59 62 + *MESH_TFACE 115 62 61 58 + *MESH_TFACE 116 59 60 63 + *MESH_TFACE 117 63 62 59 + *MESH_TFACE 118 60 124 128 + *MESH_TFACE 119 128 63 60 + *MESH_TFACE 120 127 61 111 + *MESH_TFACE 121 111 109 127 + *MESH_TFACE 122 61 62 113 + *MESH_TFACE 123 113 111 61 + *MESH_TFACE 124 62 63 115 + *MESH_TFACE 125 115 113 62 + *MESH_TFACE 126 63 128 117 + *MESH_TFACE 127 117 115 63 + *MESH_TFACE 128 64 65 70 + *MESH_TFACE 129 70 69 64 + *MESH_TFACE 130 65 66 71 + *MESH_TFACE 131 71 70 65 + *MESH_TFACE 132 66 67 72 + *MESH_TFACE 133 72 71 66 + *MESH_TFACE 134 67 68 73 + *MESH_TFACE 135 73 72 67 + *MESH_TFACE 136 69 70 75 + *MESH_TFACE 137 75 74 69 + *MESH_TFACE 138 70 71 76 + *MESH_TFACE 139 76 75 70 + *MESH_TFACE 140 71 72 77 + *MESH_TFACE 141 77 76 71 + *MESH_TFACE 142 72 73 78 + *MESH_TFACE 143 78 77 72 + *MESH_TFACE 144 74 75 80 + *MESH_TFACE 145 80 79 74 + *MESH_TFACE 146 75 76 81 + *MESH_TFACE 147 81 80 75 + *MESH_TFACE 148 76 77 82 + *MESH_TFACE 149 82 81 76 + *MESH_TFACE 150 77 78 83 + *MESH_TFACE 151 83 82 77 + *MESH_TFACE 152 79 80 85 + *MESH_TFACE 153 85 84 79 + *MESH_TFACE 154 80 81 86 + *MESH_TFACE 155 86 85 80 + *MESH_TFACE 156 81 82 87 + *MESH_TFACE 157 87 86 81 + *MESH_TFACE 158 82 83 88 + *MESH_TFACE 159 88 87 82 + *MESH_TFACE 160 106 104 89 + *MESH_TFACE 161 89 121 106 + *MESH_TFACE 162 104 102 90 + *MESH_TFACE 163 90 89 104 + *MESH_TFACE 164 102 100 91 + *MESH_TFACE 165 91 90 102 + *MESH_TFACE 166 100 98 118 + *MESH_TFACE 167 118 91 100 + *MESH_TFACE 168 121 89 92 + *MESH_TFACE 169 92 125 121 + *MESH_TFACE 170 89 90 93 + *MESH_TFACE 171 93 92 89 + *MESH_TFACE 172 90 91 94 + *MESH_TFACE 173 94 93 90 + *MESH_TFACE 174 91 118 122 + *MESH_TFACE 175 122 94 91 + *MESH_TFACE 176 125 92 95 + *MESH_TFACE 177 95 129 125 + *MESH_TFACE 178 92 93 96 + *MESH_TFACE 179 96 95 92 + *MESH_TFACE 180 93 94 97 + *MESH_TFACE 181 97 96 93 + *MESH_TFACE 182 94 122 126 + *MESH_TFACE 183 126 97 94 + *MESH_TFACE 184 129 95 114 + *MESH_TFACE 185 114 116 129 + *MESH_TFACE 186 95 96 112 + *MESH_TFACE 187 112 114 95 + *MESH_TFACE 188 96 97 110 + *MESH_TFACE 189 110 112 96 + *MESH_TFACE 190 97 126 108 + *MESH_TFACE 191 108 110 97 + } + *MESH_NORMALS { + *MESH_FACENORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 130 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 5 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 1 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 131 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 130 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 2 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 131 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 3 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 132 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 131 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 4 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 132 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 5 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 133 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 132 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 133 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 9 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 9 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 134 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 133 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 5 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 10 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 9 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 5 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 10 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 14 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 14 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 15 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 14 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 9 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 10 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 15 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 10 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 19 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 20 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 21 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 22 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 19 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 23 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 19 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 14 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 24 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 15 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 135 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 136 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 25 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 136 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 15 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 26 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 136 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 137 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 27 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 137 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 28 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 137 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 138 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 29 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 138 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 30 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 138 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 139 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 31 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 139 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 19 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 140 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 141 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 30 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 140 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 34 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 141 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 142 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 35 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 141 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 142 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 143 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 142 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 143 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 144 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 34 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 39 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 34 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 143 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 40 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 30 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 35 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 30 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 44 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 45 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 46 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 34 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 39 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 47 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 39 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 48 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 35 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 49 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 40 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 35 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 50 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 51 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 52 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 53 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 54 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 39 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 44 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 55 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 44 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 56 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 40 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 146 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 57 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 146 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 145 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 40 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 58 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 147 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 59 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 147 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 146 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 60 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 148 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 61 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 148 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 147 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 62 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 44 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 149 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 63 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 149 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 148 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 64 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 0 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 65 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 50 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 0 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 66 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 70 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 4 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 54 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 71 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 54 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 72 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 50 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 73 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 66 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 50 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 74 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 75 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 76 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 77 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 78 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 54 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 70 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 79 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 70 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 80 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 66 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 81 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 82 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 66 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 82 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 86 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 70 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 86 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 87 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 86 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 88 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 82 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 26 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 89 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 26 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 25 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 82 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 90 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 27 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 91 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 27 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 26 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 92 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 28 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 93 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 28 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 27 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 94 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 86 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 29 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 95 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 29 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 28 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 96 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 99 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 101 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 97 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 119 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 99 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 98 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 101 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 103 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 99 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 101 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 100 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 103 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 105 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 101 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 103 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 102 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 105 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 107 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 120 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 103 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 120 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 105 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 104 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 119 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 105 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 123 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 119 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 106 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 107 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 108 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 109 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 110 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 120 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 124 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 111 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 124 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 112 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 123 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 113 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 127 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 123 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 114 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 115 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 116 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 117 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 118 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 124 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 128 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 119 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 128 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 120 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 127 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 111 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 121 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 111 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 109 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 127 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 122 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 113 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 123 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 113 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 111 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 124 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 115 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 125 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 115 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 113 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 126 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 128 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 117 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 127 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 117 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 115 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 128 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 24 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 129 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 58 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 24 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 130 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 131 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 132 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 21 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 133 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 134 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 21 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 20 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 62 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 135 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 62 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 21 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 136 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 58 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 137 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 74 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 58 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 138 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 139 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 140 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 141 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 142 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 62 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 78 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 143 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 78 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 144 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 74 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 145 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 90 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 74 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 146 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 147 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 148 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 149 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 150 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 78 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 94 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 151 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 94 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 152 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 90 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 48 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 153 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 48 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 49 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 90 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 154 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 47 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 155 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 47 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 48 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 156 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 46 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 157 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 46 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 47 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 158 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 94 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 45 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 159 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 45 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 46 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 160 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 106 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 104 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 161 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 121 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 106 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 162 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 104 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 102 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 163 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 104 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 164 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 102 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 100 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 165 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 102 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 166 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 100 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 98 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 118 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 167 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 118 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 100 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 168 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 121 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 169 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 125 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 121 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 170 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 171 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 172 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 173 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 174 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 118 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 122 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 175 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 122 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 176 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 125 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 177 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 129 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 125 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 178 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 179 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 180 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 181 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 182 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 122 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 126 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 183 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 126 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 184 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 129 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 114 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 185 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 114 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 116 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 129 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 186 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 112 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 187 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 112 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 114 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 188 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 110 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 189 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 110 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 112 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 190 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 126 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 108 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 191 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 108 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 110 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + } + } + *PROP_MOTIONBLUR 0 + *PROP_CASTSHADOW 1 + *PROP_RECVSHADOW 1 + *MATERIAL_REF 1 +} diff --git a/base/models/materialeditor/box128x64.ase b/base/models/materialeditor/box128x64.ase new file mode 100644 index 000000000..a6a844364 --- /dev/null +++ b/base/models/materialeditor/box128x64.ase @@ -0,0 +1,5778 @@ +*3DSMAX_ASCIIEXPORT 200 +*COMMENT "AsciiExport Version 2.00 - Tue Dec 09 14:13:34 2003" +*SCENE { + *SCENE_FILENAME "t.max" + *SCENE_FIRSTFRAME 0 + *SCENE_LASTFRAME 300 + *SCENE_FRAMESPEED 30 + *SCENE_TICKSPERFRAME 160 + *SCENE_BACKGROUND_STATIC 0.0000 0.0000 0.0000 + *SCENE_AMBIENT_STATIC 0.0000 0.0000 0.0000 +} +*MATERIAL_LIST { + *MATERIAL_COUNT 6 + *MATERIAL 0 { + *MATERIAL_NAME "1 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4706 0.4706 0.4706 + *MATERIAL_DIFFUSE 0.4706 0.4706 0.4706 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 1 { + *MATERIAL_NAME "9 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5882 0.5882 0.5882 + *MATERIAL_DIFFUSE 0.5882 0.5882 0.5882 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + *MAP_DIFFUSE { + *MAP_NAME "Map #1" + *MAP_CLASS "Bitmap" + *MAP_SUBNO 1 + *MAP_AMOUNT 1.0000 + *BITMAP "C:\Documents and Settings\Jay Brushwood\Desktop\base\Newguy\t.jpg" + *MAP_TYPE Screen + *UVW_U_OFFSET 0.0000 + *UVW_V_OFFSET 0.0000 + *UVW_U_TILING 1.0000 + *UVW_V_TILING 1.0000 + *UVW_ANGLE 0.0000 + *UVW_BLUR 1.0000 + *UVW_BLUR_OFFSET 0.0000 + *UVW_NOUSE_AMT 1.0000 + *UVW_NOISE_SIZE 1.0000 + *UVW_NOISE_LEVEL 1 + *UVW_NOISE_PHASE 0.0000 + *BITMAP_FILTER Pyramidal + } + } + *MATERIAL 2 { + *MATERIAL_NAME "Material #12" + *MATERIAL_CLASS "Multi/Sub-Object" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *NUMSUBMTLS 2 + *SUBMATERIAL 0 { + *MATERIAL_NAME "1 - Defaultas" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *SUBMATERIAL 1 { + *MATERIAL_NAME "1 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4706 0.4706 0.4706 + *MATERIAL_DIFFUSE 0.4706 0.4706 0.4706 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + } + *MATERIAL 3 { + *MATERIAL_NAME "1 - Defaultas" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 4 { + *MATERIAL_NAME "14 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5882 0.5882 0.5882 + *MATERIAL_DIFFUSE 0.5882 0.5882 0.5882 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 5 { + *MATERIAL_NAME "7 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4353 0.4471 0.6157 + *MATERIAL_DIFFUSE 0.4353 0.4471 0.6157 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } +} +*GEOMOBJECT { + *NODE_NAME "Box02_128x64" + *NODE_TM { + *NODE_NAME "Box02_128x64" + *INHERIT_POS 0 0 0 + *INHERIT_ROT 0 0 0 + *INHERIT_SCL 0 0 0 + *TM_ROW0 -0.0000 -1.0000 0.0000 + *TM_ROW1 -0.0000 0.0000 1.0000 + *TM_ROW2 -1.0000 0.0000 -0.0000 + *TM_ROW3 -0.0000 -0.0000 -0.0000 + *TM_POS -0.0000 -0.0000 -0.0000 + *TM_ROTAXIS 0.5774 -0.5774 -0.5774 + *TM_ROTANGLE 4.1888 + *TM_SCALE 1.0000 1.0000 1.0000 + *TM_SCALEAXIS 0.0000 0.0000 0.0000 + *TM_SCALEAXISANG 0.0000 + } + *MESH { + *TIMEVALUE 0 + *MESH_NUMVERTEX 486 + *MESH_NUMFACES 768 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 64.0000 64.0000 -32.0000 + *MESH_VERTEX 1 64.0000 32.0000 -32.0000 + *MESH_VERTEX 2 64.0000 -0.0000 -32.0000 + *MESH_VERTEX 3 64.0000 -32.0000 -32.0000 + *MESH_VERTEX 4 64.0000 -64.0000 -32.0000 + *MESH_VERTEX 5 64.0000 64.0000 -16.0000 + *MESH_VERTEX 6 64.0000 32.0000 -16.0000 + *MESH_VERTEX 7 64.0000 -0.0000 -16.0000 + *MESH_VERTEX 8 64.0000 -32.0000 -16.0000 + *MESH_VERTEX 9 64.0000 -64.0000 -16.0000 + *MESH_VERTEX 10 64.0000 64.0000 0.0000 + *MESH_VERTEX 11 64.0000 32.0000 0.0000 + *MESH_VERTEX 12 64.0000 -0.0000 0.0000 + *MESH_VERTEX 13 64.0000 -32.0000 0.0000 + *MESH_VERTEX 14 64.0000 -64.0000 0.0000 + *MESH_VERTEX 15 64.0000 64.0000 16.0000 + *MESH_VERTEX 16 64.0000 32.0000 16.0000 + *MESH_VERTEX 17 64.0000 -0.0000 16.0000 + *MESH_VERTEX 18 64.0000 -32.0000 16.0000 + *MESH_VERTEX 19 64.0000 -64.0000 16.0000 + *MESH_VERTEX 20 64.0000 64.0000 32.0000 + *MESH_VERTEX 21 64.0000 32.0000 32.0000 + *MESH_VERTEX 22 64.0000 -0.0000 32.0000 + *MESH_VERTEX 23 64.0000 -32.0000 32.0000 + *MESH_VERTEX 24 64.0000 -64.0000 32.0000 + *MESH_VERTEX 25 -64.0000 64.0000 -32.0000 + *MESH_VERTEX 26 -64.0000 32.0000 -32.0000 + *MESH_VERTEX 27 -64.0000 0.0000 -32.0000 + *MESH_VERTEX 28 -64.0000 -32.0000 -32.0000 + *MESH_VERTEX 29 -64.0000 -64.0000 -32.0000 + *MESH_VERTEX 30 -64.0000 64.0000 -16.0000 + *MESH_VERTEX 31 -64.0000 32.0000 -16.0000 + *MESH_VERTEX 32 -64.0000 0.0000 -16.0000 + *MESH_VERTEX 33 -64.0000 -32.0000 -16.0000 + *MESH_VERTEX 34 -64.0000 -64.0000 -16.0000 + *MESH_VERTEX 35 -64.0000 64.0000 -0.0000 + *MESH_VERTEX 36 -64.0000 32.0000 -0.0000 + *MESH_VERTEX 37 -64.0000 0.0000 -0.0000 + *MESH_VERTEX 38 -64.0000 -32.0000 -0.0000 + *MESH_VERTEX 39 -64.0000 -64.0000 -0.0000 + *MESH_VERTEX 40 -64.0000 64.0000 16.0000 + *MESH_VERTEX 41 -64.0000 32.0000 16.0000 + *MESH_VERTEX 42 -64.0000 0.0000 16.0000 + *MESH_VERTEX 43 -64.0000 -32.0000 16.0000 + *MESH_VERTEX 44 -64.0000 -64.0000 16.0000 + *MESH_VERTEX 45 -64.0000 64.0000 32.0000 + *MESH_VERTEX 46 -64.0000 32.0000 32.0000 + *MESH_VERTEX 47 -64.0000 0.0000 32.0000 + *MESH_VERTEX 48 -64.0000 -32.0000 32.0000 + *MESH_VERTEX 49 -64.0000 -64.0000 32.0000 + *MESH_VERTEX 50 32.0000 64.0000 -32.0000 + *MESH_VERTEX 51 32.0000 32.0000 -32.0000 + *MESH_VERTEX 52 32.0000 -0.0000 -32.0000 + *MESH_VERTEX 53 32.0000 -32.0000 -32.0000 + *MESH_VERTEX 54 32.0000 -64.0000 -32.0000 + *MESH_VERTEX 55 32.0000 -64.0000 -16.0000 + *MESH_VERTEX 56 32.0000 -64.0000 -0.0000 + *MESH_VERTEX 57 32.0000 -64.0000 16.0000 + *MESH_VERTEX 58 32.0000 -64.0000 32.0000 + *MESH_VERTEX 59 32.0000 -32.0000 32.0000 + *MESH_VERTEX 60 32.0000 -0.0000 32.0000 + *MESH_VERTEX 61 32.0000 32.0000 32.0000 + *MESH_VERTEX 62 32.0000 64.0000 32.0000 + *MESH_VERTEX 63 32.0000 64.0000 16.0000 + *MESH_VERTEX 64 32.0000 64.0000 -0.0000 + *MESH_VERTEX 65 32.0000 64.0000 -16.0000 + *MESH_VERTEX 66 0.0000 64.0000 -32.0000 + *MESH_VERTEX 67 0.0000 32.0000 -32.0000 + *MESH_VERTEX 68 0.0000 -0.0000 -32.0000 + *MESH_VERTEX 69 0.0000 -32.0000 -32.0000 + *MESH_VERTEX 70 -0.0000 -64.0000 -32.0000 + *MESH_VERTEX 71 -0.0000 -64.0000 -16.0000 + *MESH_VERTEX 72 -0.0000 -64.0000 -0.0000 + *MESH_VERTEX 73 -0.0000 -64.0000 16.0000 + *MESH_VERTEX 74 -0.0000 -64.0000 32.0000 + *MESH_VERTEX 75 -0.0000 -32.0000 32.0000 + *MESH_VERTEX 76 -0.0000 -0.0000 32.0000 + *MESH_VERTEX 77 0.0000 32.0000 32.0000 + *MESH_VERTEX 78 0.0000 64.0000 32.0000 + *MESH_VERTEX 79 0.0000 64.0000 16.0000 + *MESH_VERTEX 80 0.0000 64.0000 -0.0000 + *MESH_VERTEX 81 0.0000 64.0000 -16.0000 + *MESH_VERTEX 82 -32.0000 64.0000 -32.0000 + *MESH_VERTEX 83 -32.0000 32.0000 -32.0000 + *MESH_VERTEX 84 -32.0000 0.0000 -32.0000 + *MESH_VERTEX 85 -32.0000 -32.0000 -32.0000 + *MESH_VERTEX 86 -32.0000 -64.0000 -32.0000 + *MESH_VERTEX 87 -32.0000 -64.0000 -16.0000 + *MESH_VERTEX 88 -32.0000 -64.0000 -0.0000 + *MESH_VERTEX 89 -32.0000 -64.0000 16.0000 + *MESH_VERTEX 90 -32.0000 -64.0000 32.0000 + *MESH_VERTEX 91 -32.0000 -32.0000 32.0000 + *MESH_VERTEX 92 -32.0000 0.0000 32.0000 + *MESH_VERTEX 93 -32.0000 32.0000 32.0000 + *MESH_VERTEX 94 -32.0000 64.0000 32.0000 + *MESH_VERTEX 95 -32.0000 64.0000 16.0000 + *MESH_VERTEX 96 -32.0000 64.0000 -0.0000 + *MESH_VERTEX 97 -32.0000 64.0000 -16.0000 + *MESH_VERTEX 98 64.0000 64.0000 -32.0000 + *MESH_VERTEX 99 64.0000 32.0000 -32.0000 + *MESH_VERTEX 100 64.0000 -0.0000 -32.0000 + *MESH_VERTEX 101 64.0000 -32.0000 -32.0000 + *MESH_VERTEX 102 64.0000 -64.0000 -32.0000 + *MESH_VERTEX 103 64.0000 64.0000 -16.0000 + *MESH_VERTEX 104 64.0000 -64.0000 -16.0000 + *MESH_VERTEX 105 64.0000 64.0000 0.0000 + *MESH_VERTEX 106 64.0000 -64.0000 0.0000 + *MESH_VERTEX 107 64.0000 64.0000 16.0000 + *MESH_VERTEX 108 64.0000 -64.0000 16.0000 + *MESH_VERTEX 109 64.0000 64.0000 32.0000 + *MESH_VERTEX 110 64.0000 32.0000 32.0000 + *MESH_VERTEX 111 64.0000 -0.0000 32.0000 + *MESH_VERTEX 112 64.0000 -32.0000 32.0000 + *MESH_VERTEX 113 64.0000 -64.0000 32.0000 + *MESH_VERTEX 114 -64.0000 64.0000 -32.0000 + *MESH_VERTEX 115 -64.0000 32.0000 -32.0000 + *MESH_VERTEX 116 -64.0000 0.0000 -32.0000 + *MESH_VERTEX 117 -64.0000 -32.0000 -32.0000 + *MESH_VERTEX 118 -64.0000 -64.0000 -32.0000 + *MESH_VERTEX 119 -64.0000 64.0000 -16.0000 + *MESH_VERTEX 120 -64.0000 -64.0000 -16.0000 + *MESH_VERTEX 121 -64.0000 64.0000 -0.0000 + *MESH_VERTEX 122 -64.0000 -64.0000 -0.0000 + *MESH_VERTEX 123 -64.0000 64.0000 16.0000 + *MESH_VERTEX 124 -64.0000 -64.0000 16.0000 + *MESH_VERTEX 125 -64.0000 64.0000 32.0000 + *MESH_VERTEX 126 -64.0000 32.0000 32.0000 + *MESH_VERTEX 127 -64.0000 0.0000 32.0000 + *MESH_VERTEX 128 -64.0000 -32.0000 32.0000 + *MESH_VERTEX 129 -64.0000 -64.0000 32.0000 + *MESH_VERTEX 130 64.0000 64.0000 -32.0000 + *MESH_VERTEX 131 64.0000 -64.0000 -32.0000 + *MESH_VERTEX 132 64.0000 64.0000 32.0000 + *MESH_VERTEX 133 64.0000 -64.0000 32.0000 + *MESH_VERTEX 134 -64.0000 64.0000 -32.0000 + *MESH_VERTEX 135 -64.0000 -64.0000 -32.0000 + *MESH_VERTEX 136 -64.0000 64.0000 32.0000 + *MESH_VERTEX 137 -64.0000 -64.0000 32.0000 + *MESH_VERTEX 138 32.0000 64.0000 -32.0000 + *MESH_VERTEX 139 32.0000 -64.0000 -32.0000 + *MESH_VERTEX 140 32.0000 -64.0000 32.0000 + *MESH_VERTEX 141 32.0000 64.0000 32.0000 + *MESH_VERTEX 142 0.0000 64.0000 -32.0000 + *MESH_VERTEX 143 -0.0000 -64.0000 -32.0000 + *MESH_VERTEX 144 -0.0000 -64.0000 32.0000 + *MESH_VERTEX 145 0.0000 64.0000 32.0000 + *MESH_VERTEX 146 -32.0000 64.0000 -32.0000 + *MESH_VERTEX 147 -32.0000 -64.0000 -32.0000 + *MESH_VERTEX 148 -32.0000 -64.0000 32.0000 + *MESH_VERTEX 149 -32.0000 64.0000 32.0000 + *MESH_VERTEX 150 64.0000 48.0000 -24.0000 + *MESH_VERTEX 151 64.0000 16.0000 -24.0000 + *MESH_VERTEX 152 64.0000 -16.0000 -24.0000 + *MESH_VERTEX 153 64.0000 -48.0000 -24.0000 + *MESH_VERTEX 154 64.0000 48.0000 -8.0000 + *MESH_VERTEX 155 64.0000 16.0000 -8.0000 + *MESH_VERTEX 156 64.0000 -16.0000 -8.0000 + *MESH_VERTEX 157 64.0000 -48.0000 -8.0000 + *MESH_VERTEX 158 64.0000 48.0000 8.0000 + *MESH_VERTEX 159 64.0000 16.0000 8.0000 + *MESH_VERTEX 160 64.0000 -16.0000 8.0000 + *MESH_VERTEX 161 64.0000 -48.0000 8.0000 + *MESH_VERTEX 162 64.0000 48.0000 24.0000 + *MESH_VERTEX 163 64.0000 16.0000 24.0000 + *MESH_VERTEX 164 64.0000 -16.0000 24.0000 + *MESH_VERTEX 165 64.0000 -48.0000 24.0000 + *MESH_VERTEX 166 -64.0000 48.0000 -24.0000 + *MESH_VERTEX 167 -64.0000 16.0000 -24.0000 + *MESH_VERTEX 168 -64.0000 -16.0000 -24.0000 + *MESH_VERTEX 169 -64.0000 -48.0000 -24.0000 + *MESH_VERTEX 170 -64.0000 48.0000 -8.0000 + *MESH_VERTEX 171 -64.0000 16.0000 -8.0000 + *MESH_VERTEX 172 -64.0000 -16.0000 -8.0000 + *MESH_VERTEX 173 -64.0000 -48.0000 -8.0000 + *MESH_VERTEX 174 -64.0000 48.0000 8.0000 + *MESH_VERTEX 175 -64.0000 16.0000 8.0000 + *MESH_VERTEX 176 -64.0000 -16.0000 8.0000 + *MESH_VERTEX 177 -64.0000 -48.0000 8.0000 + *MESH_VERTEX 178 -64.0000 48.0000 24.0000 + *MESH_VERTEX 179 -64.0000 16.0000 24.0000 + *MESH_VERTEX 180 -64.0000 -16.0000 24.0000 + *MESH_VERTEX 181 -64.0000 -48.0000 24.0000 + *MESH_VERTEX 182 48.0000 48.0000 -32.0000 + *MESH_VERTEX 183 48.0000 16.0000 -32.0000 + *MESH_VERTEX 184 48.0000 -16.0000 -32.0000 + *MESH_VERTEX 185 48.0000 -48.0000 -32.0000 + *MESH_VERTEX 186 16.0000 48.0000 -32.0000 + *MESH_VERTEX 187 16.0000 16.0000 -32.0000 + *MESH_VERTEX 188 16.0000 -16.0000 -32.0000 + *MESH_VERTEX 189 16.0000 -48.0000 -32.0000 + *MESH_VERTEX 190 -16.0000 48.0000 -32.0000 + *MESH_VERTEX 191 -16.0000 16.0000 -32.0000 + *MESH_VERTEX 192 -16.0000 -16.0000 -32.0000 + *MESH_VERTEX 193 -16.0000 -48.0000 -32.0000 + *MESH_VERTEX 194 -48.0000 48.0000 -32.0000 + *MESH_VERTEX 195 -48.0000 16.0000 -32.0000 + *MESH_VERTEX 196 -48.0000 -16.0000 -32.0000 + *MESH_VERTEX 197 -48.0000 -48.0000 -32.0000 + *MESH_VERTEX 198 48.0000 -64.0000 -24.0000 + *MESH_VERTEX 199 48.0000 -64.0000 -8.0000 + *MESH_VERTEX 200 48.0000 -64.0000 8.0000 + *MESH_VERTEX 201 48.0000 -64.0000 24.0000 + *MESH_VERTEX 202 16.0000 -64.0000 -24.0000 + *MESH_VERTEX 203 16.0000 -64.0000 -8.0000 + *MESH_VERTEX 204 16.0000 -64.0000 8.0000 + *MESH_VERTEX 205 16.0000 -64.0000 24.0000 + *MESH_VERTEX 206 -16.0000 -64.0000 -24.0000 + *MESH_VERTEX 207 -16.0000 -64.0000 -8.0000 + *MESH_VERTEX 208 -16.0000 -64.0000 8.0000 + *MESH_VERTEX 209 -16.0000 -64.0000 24.0000 + *MESH_VERTEX 210 -48.0000 -64.0000 -24.0000 + *MESH_VERTEX 211 -48.0000 -64.0000 -8.0000 + *MESH_VERTEX 212 -48.0000 -64.0000 8.0000 + *MESH_VERTEX 213 -48.0000 -64.0000 24.0000 + *MESH_VERTEX 214 48.0000 -48.0000 32.0000 + *MESH_VERTEX 215 48.0000 -16.0000 32.0000 + *MESH_VERTEX 216 48.0000 16.0000 32.0000 + *MESH_VERTEX 217 48.0000 48.0000 32.0000 + *MESH_VERTEX 218 16.0000 -48.0000 32.0000 + *MESH_VERTEX 219 16.0000 -16.0000 32.0000 + *MESH_VERTEX 220 16.0000 16.0000 32.0000 + *MESH_VERTEX 221 16.0000 48.0000 32.0000 + *MESH_VERTEX 222 -16.0000 -48.0000 32.0000 + *MESH_VERTEX 223 -16.0000 -16.0000 32.0000 + *MESH_VERTEX 224 -16.0000 16.0000 32.0000 + *MESH_VERTEX 225 -16.0000 48.0000 32.0000 + *MESH_VERTEX 226 -48.0000 -48.0000 32.0000 + *MESH_VERTEX 227 -48.0000 -16.0000 32.0000 + *MESH_VERTEX 228 -48.0000 16.0000 32.0000 + *MESH_VERTEX 229 -48.0000 48.0000 32.0000 + *MESH_VERTEX 230 48.0000 64.0000 24.0000 + *MESH_VERTEX 231 48.0000 64.0000 8.0000 + *MESH_VERTEX 232 48.0000 64.0000 -8.0000 + *MESH_VERTEX 233 48.0000 64.0000 -24.0000 + *MESH_VERTEX 234 16.0000 64.0000 24.0000 + *MESH_VERTEX 235 16.0000 64.0000 8.0000 + *MESH_VERTEX 236 16.0000 64.0000 -8.0000 + *MESH_VERTEX 237 16.0000 64.0000 -24.0000 + *MESH_VERTEX 238 -16.0000 64.0000 24.0000 + *MESH_VERTEX 239 -16.0000 64.0000 8.0000 + *MESH_VERTEX 240 -16.0000 64.0000 -8.0000 + *MESH_VERTEX 241 -16.0000 64.0000 -24.0000 + *MESH_VERTEX 242 -48.0000 64.0000 24.0000 + *MESH_VERTEX 243 -48.0000 64.0000 8.0000 + *MESH_VERTEX 244 -48.0000 64.0000 -8.0000 + *MESH_VERTEX 245 -48.0000 64.0000 -24.0000 + *MESH_VERTEX 246 64.0000 64.0000 -24.0000 + *MESH_VERTEX 247 64.0000 48.0000 -16.0000 + *MESH_VERTEX 248 64.0000 32.0000 -24.0000 + *MESH_VERTEX 249 64.0000 48.0000 -32.0000 + *MESH_VERTEX 250 64.0000 16.0000 -16.0000 + *MESH_VERTEX 251 64.0000 -0.0000 -24.0000 + *MESH_VERTEX 252 64.0000 16.0000 -32.0000 + *MESH_VERTEX 253 64.0000 -16.0000 -16.0000 + *MESH_VERTEX 254 64.0000 -32.0000 -24.0000 + *MESH_VERTEX 255 64.0000 -16.0000 -32.0000 + *MESH_VERTEX 256 64.0000 -48.0000 -16.0000 + *MESH_VERTEX 257 64.0000 -64.0000 -24.0000 + *MESH_VERTEX 258 64.0000 -48.0000 -32.0000 + *MESH_VERTEX 259 64.0000 64.0000 -8.0000 + *MESH_VERTEX 260 64.0000 48.0000 0.0000 + *MESH_VERTEX 261 64.0000 32.0000 -8.0000 + *MESH_VERTEX 262 64.0000 16.0000 0.0000 + *MESH_VERTEX 263 64.0000 -0.0000 -8.0000 + *MESH_VERTEX 264 64.0000 -16.0000 0.0000 + *MESH_VERTEX 265 64.0000 -32.0000 -8.0000 + *MESH_VERTEX 266 64.0000 -48.0000 0.0000 + *MESH_VERTEX 267 64.0000 -64.0000 -8.0000 + *MESH_VERTEX 268 64.0000 64.0000 8.0000 + *MESH_VERTEX 269 64.0000 48.0000 16.0000 + *MESH_VERTEX 270 64.0000 32.0000 8.0000 + *MESH_VERTEX 271 64.0000 16.0000 16.0000 + *MESH_VERTEX 272 64.0000 -0.0000 8.0000 + *MESH_VERTEX 273 64.0000 -16.0000 16.0000 + *MESH_VERTEX 274 64.0000 -32.0000 8.0000 + *MESH_VERTEX 275 64.0000 -48.0000 16.0000 + *MESH_VERTEX 276 64.0000 -64.0000 8.0000 + *MESH_VERTEX 277 64.0000 64.0000 24.0000 + *MESH_VERTEX 278 64.0000 48.0000 32.0000 + *MESH_VERTEX 279 64.0000 32.0000 24.0000 + *MESH_VERTEX 280 64.0000 16.0000 32.0000 + *MESH_VERTEX 281 64.0000 -0.0000 24.0000 + *MESH_VERTEX 282 64.0000 -16.0000 32.0000 + *MESH_VERTEX 283 64.0000 -32.0000 24.0000 + *MESH_VERTEX 284 64.0000 -48.0000 32.0000 + *MESH_VERTEX 285 64.0000 -64.0000 24.0000 + *MESH_VERTEX 286 -64.0000 48.0000 -32.0000 + *MESH_VERTEX 287 -64.0000 32.0000 -24.0000 + *MESH_VERTEX 288 -64.0000 48.0000 -16.0000 + *MESH_VERTEX 289 -64.0000 64.0000 -24.0000 + *MESH_VERTEX 290 -64.0000 16.0000 -32.0000 + *MESH_VERTEX 291 -64.0000 0.0000 -24.0000 + *MESH_VERTEX 292 -64.0000 16.0000 -16.0000 + *MESH_VERTEX 293 -64.0000 -16.0000 -32.0000 + *MESH_VERTEX 294 -64.0000 -32.0000 -24.0000 + *MESH_VERTEX 295 -64.0000 -16.0000 -16.0000 + *MESH_VERTEX 296 -64.0000 -48.0000 -32.0000 + *MESH_VERTEX 297 -64.0000 -64.0000 -24.0000 + *MESH_VERTEX 298 -64.0000 -48.0000 -16.0000 + *MESH_VERTEX 299 -64.0000 32.0000 -8.0000 + *MESH_VERTEX 300 -64.0000 48.0000 -0.0000 + *MESH_VERTEX 301 -64.0000 64.0000 -8.0000 + *MESH_VERTEX 302 -64.0000 0.0000 -8.0000 + *MESH_VERTEX 303 -64.0000 16.0000 -0.0000 + *MESH_VERTEX 304 -64.0000 -32.0000 -8.0000 + *MESH_VERTEX 305 -64.0000 -16.0000 -0.0000 + *MESH_VERTEX 306 -64.0000 -64.0000 -8.0000 + *MESH_VERTEX 307 -64.0000 -48.0000 -0.0000 + *MESH_VERTEX 308 -64.0000 32.0000 8.0000 + *MESH_VERTEX 309 -64.0000 48.0000 16.0000 + *MESH_VERTEX 310 -64.0000 64.0000 8.0000 + *MESH_VERTEX 311 -64.0000 0.0000 8.0000 + *MESH_VERTEX 312 -64.0000 16.0000 16.0000 + *MESH_VERTEX 313 -64.0000 -32.0000 8.0000 + *MESH_VERTEX 314 -64.0000 -16.0000 16.0000 + *MESH_VERTEX 315 -64.0000 -64.0000 8.0000 + *MESH_VERTEX 316 -64.0000 -48.0000 16.0000 + *MESH_VERTEX 317 -64.0000 32.0000 24.0000 + *MESH_VERTEX 318 -64.0000 48.0000 32.0000 + *MESH_VERTEX 319 -64.0000 64.0000 24.0000 + *MESH_VERTEX 320 -64.0000 0.0000 24.0000 + *MESH_VERTEX 321 -64.0000 16.0000 32.0000 + *MESH_VERTEX 322 -64.0000 -32.0000 24.0000 + *MESH_VERTEX 323 -64.0000 -16.0000 32.0000 + *MESH_VERTEX 324 -64.0000 -64.0000 24.0000 + *MESH_VERTEX 325 -64.0000 -48.0000 32.0000 + *MESH_VERTEX 326 48.0000 32.0000 -32.0000 + *MESH_VERTEX 327 32.0000 48.0000 -32.0000 + *MESH_VERTEX 328 48.0000 64.0000 -32.0000 + *MESH_VERTEX 329 48.0000 -0.0000 -32.0000 + *MESH_VERTEX 330 32.0000 16.0000 -32.0000 + *MESH_VERTEX 331 48.0000 -32.0000 -32.0000 + *MESH_VERTEX 332 32.0000 -16.0000 -32.0000 + *MESH_VERTEX 333 48.0000 -64.0000 -32.0000 + *MESH_VERTEX 334 32.0000 -48.0000 -32.0000 + *MESH_VERTEX 335 16.0000 32.0000 -32.0000 + *MESH_VERTEX 336 0.0000 48.0000 -32.0000 + *MESH_VERTEX 337 16.0000 64.0000 -32.0000 + *MESH_VERTEX 338 16.0000 -0.0000 -32.0000 + *MESH_VERTEX 339 0.0000 16.0000 -32.0000 + *MESH_VERTEX 340 16.0000 -32.0000 -32.0000 + *MESH_VERTEX 341 0.0000 -16.0000 -32.0000 + *MESH_VERTEX 342 16.0000 -64.0000 -32.0000 + *MESH_VERTEX 343 -0.0000 -48.0000 -32.0000 + *MESH_VERTEX 344 -16.0000 32.0000 -32.0000 + *MESH_VERTEX 345 -32.0000 48.0000 -32.0000 + *MESH_VERTEX 346 -16.0000 64.0000 -32.0000 + *MESH_VERTEX 347 -16.0000 -0.0000 -32.0000 + *MESH_VERTEX 348 -32.0000 16.0000 -32.0000 + *MESH_VERTEX 349 -16.0000 -32.0000 -32.0000 + *MESH_VERTEX 350 -32.0000 -16.0000 -32.0000 + *MESH_VERTEX 351 -16.0000 -64.0000 -32.0000 + *MESH_VERTEX 352 -32.0000 -48.0000 -32.0000 + *MESH_VERTEX 353 -48.0000 32.0000 -32.0000 + *MESH_VERTEX 354 -48.0000 64.0000 -32.0000 + *MESH_VERTEX 355 -48.0000 0.0000 -32.0000 + *MESH_VERTEX 356 -48.0000 -32.0000 -32.0000 + *MESH_VERTEX 357 -48.0000 -64.0000 -32.0000 + *MESH_VERTEX 358 48.0000 -64.0000 -16.0000 + *MESH_VERTEX 359 32.0000 -64.0000 -24.0000 + *MESH_VERTEX 360 48.0000 -64.0000 -0.0000 + *MESH_VERTEX 361 32.0000 -64.0000 -8.0000 + *MESH_VERTEX 362 48.0000 -64.0000 16.0000 + *MESH_VERTEX 363 32.0000 -64.0000 8.0000 + *MESH_VERTEX 364 48.0000 -64.0000 32.0000 + *MESH_VERTEX 365 32.0000 -64.0000 24.0000 + *MESH_VERTEX 366 16.0000 -64.0000 -16.0000 + *MESH_VERTEX 367 -0.0000 -64.0000 -24.0000 + *MESH_VERTEX 368 16.0000 -64.0000 -0.0000 + *MESH_VERTEX 369 -0.0000 -64.0000 -8.0000 + *MESH_VERTEX 370 16.0000 -64.0000 16.0000 + *MESH_VERTEX 371 -0.0000 -64.0000 8.0000 + *MESH_VERTEX 372 16.0000 -64.0000 32.0000 + *MESH_VERTEX 373 -0.0000 -64.0000 24.0000 + *MESH_VERTEX 374 -16.0000 -64.0000 -16.0000 + *MESH_VERTEX 375 -32.0000 -64.0000 -24.0000 + *MESH_VERTEX 376 -16.0000 -64.0000 -0.0000 + *MESH_VERTEX 377 -32.0000 -64.0000 -8.0000 + *MESH_VERTEX 378 -16.0000 -64.0000 16.0000 + *MESH_VERTEX 379 -32.0000 -64.0000 8.0000 + *MESH_VERTEX 380 -16.0000 -64.0000 32.0000 + *MESH_VERTEX 381 -32.0000 -64.0000 24.0000 + *MESH_VERTEX 382 -48.0000 -64.0000 -16.0000 + *MESH_VERTEX 383 -48.0000 -64.0000 -0.0000 + *MESH_VERTEX 384 -48.0000 -64.0000 16.0000 + *MESH_VERTEX 385 -48.0000 -64.0000 32.0000 + *MESH_VERTEX 386 48.0000 -32.0000 32.0000 + *MESH_VERTEX 387 32.0000 -48.0000 32.0000 + *MESH_VERTEX 388 48.0000 -0.0000 32.0000 + *MESH_VERTEX 389 32.0000 -16.0000 32.0000 + *MESH_VERTEX 390 48.0000 32.0000 32.0000 + *MESH_VERTEX 391 32.0000 16.0000 32.0000 + *MESH_VERTEX 392 48.0000 64.0000 32.0000 + *MESH_VERTEX 393 32.0000 48.0000 32.0000 + *MESH_VERTEX 394 16.0000 -32.0000 32.0000 + *MESH_VERTEX 395 -0.0000 -48.0000 32.0000 + *MESH_VERTEX 396 16.0000 -0.0000 32.0000 + *MESH_VERTEX 397 -0.0000 -16.0000 32.0000 + *MESH_VERTEX 398 16.0000 32.0000 32.0000 + *MESH_VERTEX 399 -0.0000 16.0000 32.0000 + *MESH_VERTEX 400 16.0000 64.0000 32.0000 + *MESH_VERTEX 401 0.0000 48.0000 32.0000 + *MESH_VERTEX 402 -16.0000 -32.0000 32.0000 + *MESH_VERTEX 403 -32.0000 -48.0000 32.0000 + *MESH_VERTEX 404 -16.0000 -0.0000 32.0000 + *MESH_VERTEX 405 -32.0000 -16.0000 32.0000 + *MESH_VERTEX 406 -16.0000 32.0000 32.0000 + *MESH_VERTEX 407 -32.0000 16.0000 32.0000 + *MESH_VERTEX 408 -16.0000 64.0000 32.0000 + *MESH_VERTEX 409 -32.0000 48.0000 32.0000 + *MESH_VERTEX 410 -48.0000 -32.0000 32.0000 + *MESH_VERTEX 411 -48.0000 0.0000 32.0000 + *MESH_VERTEX 412 -48.0000 32.0000 32.0000 + *MESH_VERTEX 413 -48.0000 64.0000 32.0000 + *MESH_VERTEX 414 48.0000 64.0000 16.0000 + *MESH_VERTEX 415 32.0000 64.0000 24.0000 + *MESH_VERTEX 416 48.0000 64.0000 -0.0000 + *MESH_VERTEX 417 32.0000 64.0000 8.0000 + *MESH_VERTEX 418 48.0000 64.0000 -16.0000 + *MESH_VERTEX 419 32.0000 64.0000 -8.0000 + *MESH_VERTEX 420 32.0000 64.0000 -24.0000 + *MESH_VERTEX 421 16.0000 64.0000 16.0000 + *MESH_VERTEX 422 0.0000 64.0000 24.0000 + *MESH_VERTEX 423 16.0000 64.0000 -0.0000 + *MESH_VERTEX 424 0.0000 64.0000 8.0000 + *MESH_VERTEX 425 16.0000 64.0000 -16.0000 + *MESH_VERTEX 426 0.0000 64.0000 -8.0000 + *MESH_VERTEX 427 0.0000 64.0000 -24.0000 + *MESH_VERTEX 428 -16.0000 64.0000 16.0000 + *MESH_VERTEX 429 -32.0000 64.0000 24.0000 + *MESH_VERTEX 430 -16.0000 64.0000 -0.0000 + *MESH_VERTEX 431 -32.0000 64.0000 8.0000 + *MESH_VERTEX 432 -16.0000 64.0000 -16.0000 + *MESH_VERTEX 433 -32.0000 64.0000 -8.0000 + *MESH_VERTEX 434 -32.0000 64.0000 -24.0000 + *MESH_VERTEX 435 -48.0000 64.0000 16.0000 + *MESH_VERTEX 436 -48.0000 64.0000 -0.0000 + *MESH_VERTEX 437 -48.0000 64.0000 -16.0000 + *MESH_VERTEX 438 64.0000 64.0000 -24.0000 + *MESH_VERTEX 439 64.0000 48.0000 -32.0000 + *MESH_VERTEX 440 64.0000 16.0000 -32.0000 + *MESH_VERTEX 441 64.0000 -16.0000 -32.0000 + *MESH_VERTEX 442 64.0000 -64.0000 -24.0000 + *MESH_VERTEX 443 64.0000 -48.0000 -32.0000 + *MESH_VERTEX 444 64.0000 64.0000 -8.0000 + *MESH_VERTEX 445 64.0000 -64.0000 -8.0000 + *MESH_VERTEX 446 64.0000 64.0000 8.0000 + *MESH_VERTEX 447 64.0000 -64.0000 8.0000 + *MESH_VERTEX 448 64.0000 64.0000 24.0000 + *MESH_VERTEX 449 64.0000 48.0000 32.0000 + *MESH_VERTEX 450 64.0000 16.0000 32.0000 + *MESH_VERTEX 451 64.0000 -16.0000 32.0000 + *MESH_VERTEX 452 64.0000 -48.0000 32.0000 + *MESH_VERTEX 453 64.0000 -64.0000 24.0000 + *MESH_VERTEX 454 -64.0000 48.0000 -32.0000 + *MESH_VERTEX 455 -64.0000 64.0000 -24.0000 + *MESH_VERTEX 456 -64.0000 16.0000 -32.0000 + *MESH_VERTEX 457 -64.0000 -16.0000 -32.0000 + *MESH_VERTEX 458 -64.0000 -48.0000 -32.0000 + *MESH_VERTEX 459 -64.0000 -64.0000 -24.0000 + *MESH_VERTEX 460 -64.0000 64.0000 -8.0000 + *MESH_VERTEX 461 -64.0000 -64.0000 -8.0000 + *MESH_VERTEX 462 -64.0000 64.0000 8.0000 + *MESH_VERTEX 463 -64.0000 -64.0000 8.0000 + *MESH_VERTEX 464 -64.0000 48.0000 32.0000 + *MESH_VERTEX 465 -64.0000 64.0000 24.0000 + *MESH_VERTEX 466 -64.0000 16.0000 32.0000 + *MESH_VERTEX 467 -64.0000 -16.0000 32.0000 + *MESH_VERTEX 468 -64.0000 -64.0000 24.0000 + *MESH_VERTEX 469 -64.0000 -48.0000 32.0000 + *MESH_VERTEX 470 48.0000 64.0000 -32.0000 + *MESH_VERTEX 471 48.0000 -64.0000 -32.0000 + *MESH_VERTEX 472 16.0000 64.0000 -32.0000 + *MESH_VERTEX 473 16.0000 -64.0000 -32.0000 + *MESH_VERTEX 474 -16.0000 64.0000 -32.0000 + *MESH_VERTEX 475 -16.0000 -64.0000 -32.0000 + *MESH_VERTEX 476 -48.0000 64.0000 -32.0000 + *MESH_VERTEX 477 -48.0000 -64.0000 -32.0000 + *MESH_VERTEX 478 48.0000 -64.0000 32.0000 + *MESH_VERTEX 479 16.0000 -64.0000 32.0000 + *MESH_VERTEX 480 -16.0000 -64.0000 32.0000 + *MESH_VERTEX 481 -48.0000 -64.0000 32.0000 + *MESH_VERTEX 482 48.0000 64.0000 32.0000 + *MESH_VERTEX 483 16.0000 64.0000 32.0000 + *MESH_VERTEX 484 -16.0000 64.0000 32.0000 + *MESH_VERTEX 485 -48.0000 64.0000 32.0000 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 98 B: 438 C: 150 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 1: A: 150 B: 439 C: 98 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 2: A: 103 B: 247 C: 150 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 3: A: 150 B: 438 C: 103 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 4: A: 6 B: 248 C: 150 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 5: A: 150 B: 247 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 6: A: 99 B: 439 C: 150 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 7: A: 150 B: 248 C: 99 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 8: A: 99 B: 248 C: 151 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 9: A: 151 B: 440 C: 99 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 10: A: 6 B: 250 C: 151 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 11: A: 151 B: 248 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 12: A: 7 B: 251 C: 151 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 13: A: 151 B: 250 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 14: A: 100 B: 440 C: 151 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 15: A: 151 B: 251 C: 100 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 16: A: 100 B: 251 C: 152 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 17: A: 152 B: 441 C: 100 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 18: A: 7 B: 253 C: 152 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 19: A: 152 B: 251 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 20: A: 8 B: 254 C: 152 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 21: A: 152 B: 253 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 22: A: 101 B: 441 C: 152 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 23: A: 152 B: 254 C: 101 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 24: A: 101 B: 254 C: 153 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 25: A: 153 B: 443 C: 101 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 26: A: 8 B: 256 C: 153 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 27: A: 153 B: 254 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 28: A: 104 B: 442 C: 153 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 29: A: 153 B: 256 C: 104 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 30: A: 102 B: 443 C: 153 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 31: A: 153 B: 442 C: 102 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 32: A: 103 B: 444 C: 154 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 33: A: 154 B: 247 C: 103 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 34: A: 105 B: 260 C: 154 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 35: A: 154 B: 444 C: 105 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 36: A: 11 B: 261 C: 154 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 37: A: 154 B: 260 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 38: A: 6 B: 247 C: 154 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 39: A: 154 B: 261 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 40: A: 6 B: 261 C: 155 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 41: A: 155 B: 250 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 42: A: 11 B: 262 C: 155 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 43: A: 155 B: 261 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 44: A: 12 B: 263 C: 155 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 45: A: 155 B: 262 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 46: A: 7 B: 250 C: 155 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 47: A: 155 B: 263 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 48: A: 7 B: 263 C: 156 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 49: A: 156 B: 253 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 50: A: 12 B: 264 C: 156 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 51: A: 156 B: 263 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 52: A: 13 B: 265 C: 156 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 53: A: 156 B: 264 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 54: A: 8 B: 253 C: 156 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 55: A: 156 B: 265 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 56: A: 8 B: 265 C: 157 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 57: A: 157 B: 256 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 58: A: 13 B: 266 C: 157 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 59: A: 157 B: 265 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 60: A: 106 B: 445 C: 157 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 61: A: 157 B: 266 C: 106 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 62: A: 104 B: 256 C: 157 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 63: A: 157 B: 445 C: 104 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 64: A: 105 B: 446 C: 158 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 65: A: 158 B: 260 C: 105 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 66: A: 107 B: 269 C: 158 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 67: A: 158 B: 446 C: 107 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 68: A: 16 B: 270 C: 158 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 69: A: 158 B: 269 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 70: A: 11 B: 260 C: 158 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 71: A: 158 B: 270 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 72: A: 11 B: 270 C: 159 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 73: A: 159 B: 262 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 74: A: 16 B: 271 C: 159 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 75: A: 159 B: 270 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 76: A: 17 B: 272 C: 159 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 77: A: 159 B: 271 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 78: A: 12 B: 262 C: 159 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 79: A: 159 B: 272 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 80: A: 12 B: 272 C: 160 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 81: A: 160 B: 264 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 82: A: 17 B: 273 C: 160 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 83: A: 160 B: 272 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 84: A: 18 B: 274 C: 160 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 85: A: 160 B: 273 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 86: A: 13 B: 264 C: 160 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 87: A: 160 B: 274 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 88: A: 13 B: 274 C: 161 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 89: A: 161 B: 266 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 90: A: 18 B: 275 C: 161 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 91: A: 161 B: 274 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 92: A: 108 B: 447 C: 161 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 93: A: 161 B: 275 C: 108 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 94: A: 106 B: 266 C: 161 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 95: A: 161 B: 447 C: 106 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 96: A: 107 B: 448 C: 162 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 97: A: 162 B: 269 C: 107 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 98: A: 109 B: 449 C: 162 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 99: A: 162 B: 448 C: 109 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 100: A: 110 B: 279 C: 162 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 101: A: 162 B: 449 C: 110 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 102: A: 16 B: 269 C: 162 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 103: A: 162 B: 279 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 104: A: 16 B: 279 C: 163 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 105: A: 163 B: 271 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 106: A: 110 B: 450 C: 163 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 107: A: 163 B: 279 C: 110 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 108: A: 111 B: 281 C: 163 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 109: A: 163 B: 450 C: 111 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 110: A: 17 B: 271 C: 163 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 111: A: 163 B: 281 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 112: A: 17 B: 281 C: 164 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 113: A: 164 B: 273 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 114: A: 111 B: 451 C: 164 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 115: A: 164 B: 281 C: 111 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 116: A: 112 B: 283 C: 164 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 117: A: 164 B: 451 C: 112 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 118: A: 18 B: 273 C: 164 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 119: A: 164 B: 283 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 120: A: 18 B: 283 C: 165 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 121: A: 165 B: 275 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 122: A: 112 B: 452 C: 165 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 123: A: 165 B: 283 C: 112 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 124: A: 113 B: 453 C: 165 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 125: A: 165 B: 452 C: 113 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 126: A: 108 B: 275 C: 165 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 127: A: 165 B: 453 C: 108 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 128: A: 114 B: 454 C: 166 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 129: A: 166 B: 455 C: 114 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 130: A: 115 B: 287 C: 166 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 131: A: 166 B: 454 C: 115 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 132: A: 31 B: 288 C: 166 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 133: A: 166 B: 287 C: 31 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 134: A: 119 B: 455 C: 166 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 135: A: 166 B: 288 C: 119 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 136: A: 115 B: 456 C: 167 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 137: A: 167 B: 287 C: 115 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 138: A: 116 B: 291 C: 167 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 139: A: 167 B: 456 C: 116 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 140: A: 32 B: 292 C: 167 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 141: A: 167 B: 291 C: 32 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 142: A: 31 B: 287 C: 167 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 143: A: 167 B: 292 C: 31 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 144: A: 116 B: 457 C: 168 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 145: A: 168 B: 291 C: 116 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 146: A: 117 B: 294 C: 168 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 147: A: 168 B: 457 C: 117 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 148: A: 33 B: 295 C: 168 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 149: A: 168 B: 294 C: 33 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 150: A: 32 B: 291 C: 168 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 151: A: 168 B: 295 C: 32 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 152: A: 117 B: 458 C: 169 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 153: A: 169 B: 294 C: 117 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 154: A: 118 B: 459 C: 169 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 155: A: 169 B: 458 C: 118 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 156: A: 120 B: 298 C: 169 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 157: A: 169 B: 459 C: 120 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 158: A: 33 B: 294 C: 169 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 159: A: 169 B: 298 C: 33 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 160: A: 119 B: 288 C: 170 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 161: A: 170 B: 460 C: 119 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 162: A: 31 B: 299 C: 170 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 163: A: 170 B: 288 C: 31 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 164: A: 36 B: 300 C: 170 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 165: A: 170 B: 299 C: 36 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 166: A: 121 B: 460 C: 170 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 167: A: 170 B: 300 C: 121 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 168: A: 31 B: 292 C: 171 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 169: A: 171 B: 299 C: 31 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 170: A: 32 B: 302 C: 171 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 171: A: 171 B: 292 C: 32 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 172: A: 37 B: 303 C: 171 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 173: A: 171 B: 302 C: 37 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 174: A: 36 B: 299 C: 171 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 175: A: 171 B: 303 C: 36 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 176: A: 32 B: 295 C: 172 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 177: A: 172 B: 302 C: 32 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 178: A: 33 B: 304 C: 172 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 179: A: 172 B: 295 C: 33 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 180: A: 38 B: 305 C: 172 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 181: A: 172 B: 304 C: 38 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 182: A: 37 B: 302 C: 172 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 183: A: 172 B: 305 C: 37 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 184: A: 33 B: 298 C: 173 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 185: A: 173 B: 304 C: 33 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 186: A: 120 B: 461 C: 173 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 187: A: 173 B: 298 C: 120 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 188: A: 122 B: 307 C: 173 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 189: A: 173 B: 461 C: 122 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 190: A: 38 B: 304 C: 173 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 191: A: 173 B: 307 C: 38 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 192: A: 121 B: 300 C: 174 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 193: A: 174 B: 462 C: 121 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 194: A: 36 B: 308 C: 174 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 195: A: 174 B: 300 C: 36 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 196: A: 41 B: 309 C: 174 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 197: A: 174 B: 308 C: 41 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 198: A: 123 B: 462 C: 174 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 199: A: 174 B: 309 C: 123 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 200: A: 36 B: 303 C: 175 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 201: A: 175 B: 308 C: 36 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 202: A: 37 B: 311 C: 175 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 203: A: 175 B: 303 C: 37 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 204: A: 42 B: 312 C: 175 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 205: A: 175 B: 311 C: 42 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 206: A: 41 B: 308 C: 175 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 207: A: 175 B: 312 C: 41 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 208: A: 37 B: 305 C: 176 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 209: A: 176 B: 311 C: 37 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 210: A: 38 B: 313 C: 176 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 211: A: 176 B: 305 C: 38 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 212: A: 43 B: 314 C: 176 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 213: A: 176 B: 313 C: 43 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 214: A: 42 B: 311 C: 176 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 215: A: 176 B: 314 C: 42 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 216: A: 38 B: 307 C: 177 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 217: A: 177 B: 313 C: 38 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 218: A: 122 B: 463 C: 177 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 219: A: 177 B: 307 C: 122 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 220: A: 124 B: 316 C: 177 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 221: A: 177 B: 463 C: 124 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 222: A: 43 B: 313 C: 177 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 223: A: 177 B: 316 C: 43 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 224: A: 123 B: 309 C: 178 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 225: A: 178 B: 465 C: 123 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 226: A: 41 B: 317 C: 178 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 227: A: 178 B: 309 C: 41 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 228: A: 126 B: 464 C: 178 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 229: A: 178 B: 317 C: 126 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 230: A: 125 B: 465 C: 178 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 231: A: 178 B: 464 C: 125 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 232: A: 41 B: 312 C: 179 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 233: A: 179 B: 317 C: 41 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 234: A: 42 B: 320 C: 179 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 235: A: 179 B: 312 C: 42 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 236: A: 127 B: 466 C: 179 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 237: A: 179 B: 320 C: 127 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 238: A: 126 B: 317 C: 179 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 239: A: 179 B: 466 C: 126 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 240: A: 42 B: 314 C: 180 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 241: A: 180 B: 320 C: 42 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 242: A: 43 B: 322 C: 180 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 243: A: 180 B: 314 C: 43 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 244: A: 128 B: 467 C: 180 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 245: A: 180 B: 322 C: 128 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 246: A: 127 B: 320 C: 180 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 247: A: 180 B: 467 C: 127 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 248: A: 43 B: 316 C: 181 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 249: A: 181 B: 322 C: 43 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 250: A: 124 B: 468 C: 181 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 251: A: 181 B: 316 C: 124 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 252: A: 129 B: 469 C: 181 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 253: A: 181 B: 468 C: 129 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 254: A: 128 B: 322 C: 181 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 255: A: 181 B: 469 C: 128 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 256: A: 0 B: 249 C: 182 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 257: A: 182 B: 328 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 258: A: 1 B: 326 C: 182 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 259: A: 182 B: 249 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 260: A: 51 B: 327 C: 182 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 261: A: 182 B: 326 C: 51 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 262: A: 50 B: 328 C: 182 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 263: A: 182 B: 327 C: 50 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 264: A: 1 B: 252 C: 183 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 265: A: 183 B: 326 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 266: A: 2 B: 329 C: 183 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 267: A: 183 B: 252 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 268: A: 52 B: 330 C: 183 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 269: A: 183 B: 329 C: 52 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 270: A: 51 B: 326 C: 183 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 271: A: 183 B: 330 C: 51 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 272: A: 2 B: 255 C: 184 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 273: A: 184 B: 329 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 274: A: 3 B: 331 C: 184 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 275: A: 184 B: 255 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 276: A: 53 B: 332 C: 184 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 277: A: 184 B: 331 C: 53 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 278: A: 52 B: 329 C: 184 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 279: A: 184 B: 332 C: 52 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 280: A: 3 B: 258 C: 185 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 281: A: 185 B: 331 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 282: A: 4 B: 333 C: 185 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 283: A: 185 B: 258 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 284: A: 54 B: 334 C: 185 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 285: A: 185 B: 333 C: 54 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 286: A: 53 B: 331 C: 185 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 287: A: 185 B: 334 C: 53 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 288: A: 50 B: 327 C: 186 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 289: A: 186 B: 337 C: 50 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 290: A: 51 B: 335 C: 186 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 291: A: 186 B: 327 C: 51 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 292: A: 67 B: 336 C: 186 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 293: A: 186 B: 335 C: 67 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 294: A: 66 B: 337 C: 186 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 295: A: 186 B: 336 C: 66 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 296: A: 51 B: 330 C: 187 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 297: A: 187 B: 335 C: 51 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 298: A: 52 B: 338 C: 187 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 299: A: 187 B: 330 C: 52 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 300: A: 68 B: 339 C: 187 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 301: A: 187 B: 338 C: 68 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 302: A: 67 B: 335 C: 187 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 303: A: 187 B: 339 C: 67 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 304: A: 52 B: 332 C: 188 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 305: A: 188 B: 338 C: 52 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 306: A: 53 B: 340 C: 188 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 307: A: 188 B: 332 C: 53 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 308: A: 69 B: 341 C: 188 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 309: A: 188 B: 340 C: 69 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 310: A: 68 B: 338 C: 188 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 311: A: 188 B: 341 C: 68 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 312: A: 53 B: 334 C: 189 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 313: A: 189 B: 340 C: 53 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 314: A: 54 B: 342 C: 189 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 315: A: 189 B: 334 C: 54 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 316: A: 70 B: 343 C: 189 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 317: A: 189 B: 342 C: 70 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 318: A: 69 B: 340 C: 189 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 319: A: 189 B: 343 C: 69 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 320: A: 66 B: 336 C: 190 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 321: A: 190 B: 346 C: 66 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 322: A: 67 B: 344 C: 190 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 323: A: 190 B: 336 C: 67 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 324: A: 83 B: 345 C: 190 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 325: A: 190 B: 344 C: 83 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 326: A: 82 B: 346 C: 190 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 327: A: 190 B: 345 C: 82 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 328: A: 67 B: 339 C: 191 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 329: A: 191 B: 344 C: 67 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 330: A: 68 B: 347 C: 191 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 331: A: 191 B: 339 C: 68 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 332: A: 84 B: 348 C: 191 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 333: A: 191 B: 347 C: 84 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 334: A: 83 B: 344 C: 191 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 335: A: 191 B: 348 C: 83 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 336: A: 68 B: 341 C: 192 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 337: A: 192 B: 347 C: 68 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 338: A: 69 B: 349 C: 192 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 339: A: 192 B: 341 C: 69 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 340: A: 85 B: 350 C: 192 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 341: A: 192 B: 349 C: 85 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 342: A: 84 B: 347 C: 192 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 343: A: 192 B: 350 C: 84 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 344: A: 69 B: 343 C: 193 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 345: A: 193 B: 349 C: 69 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 346: A: 70 B: 351 C: 193 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 347: A: 193 B: 343 C: 70 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 348: A: 86 B: 352 C: 193 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 349: A: 193 B: 351 C: 86 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 350: A: 85 B: 349 C: 193 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 351: A: 193 B: 352 C: 85 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 352: A: 82 B: 345 C: 194 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 353: A: 194 B: 354 C: 82 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 354: A: 83 B: 353 C: 194 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 355: A: 194 B: 345 C: 83 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 356: A: 26 B: 286 C: 194 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 357: A: 194 B: 353 C: 26 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 358: A: 25 B: 354 C: 194 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 359: A: 194 B: 286 C: 25 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 360: A: 83 B: 348 C: 195 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 361: A: 195 B: 353 C: 83 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 362: A: 84 B: 355 C: 195 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 363: A: 195 B: 348 C: 84 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 364: A: 27 B: 290 C: 195 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 365: A: 195 B: 355 C: 27 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 366: A: 26 B: 353 C: 195 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 367: A: 195 B: 290 C: 26 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 368: A: 84 B: 350 C: 196 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 369: A: 196 B: 355 C: 84 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 370: A: 85 B: 356 C: 196 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 371: A: 196 B: 350 C: 85 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 372: A: 28 B: 293 C: 196 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 373: A: 196 B: 356 C: 28 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 374: A: 27 B: 355 C: 196 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 375: A: 196 B: 293 C: 27 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 376: A: 85 B: 352 C: 197 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 377: A: 197 B: 356 C: 85 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 378: A: 86 B: 357 C: 197 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 379: A: 197 B: 352 C: 86 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 380: A: 29 B: 296 C: 197 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 381: A: 197 B: 357 C: 29 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 382: A: 28 B: 356 C: 197 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 383: A: 197 B: 296 C: 28 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 384: A: 131 B: 257 C: 198 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 385: A: 198 B: 471 C: 131 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 386: A: 9 B: 358 C: 198 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 387: A: 198 B: 257 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 388: A: 55 B: 359 C: 198 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 389: A: 198 B: 358 C: 55 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 390: A: 139 B: 471 C: 198 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 391: A: 198 B: 359 C: 139 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 392: A: 9 B: 267 C: 199 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 393: A: 199 B: 358 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 394: A: 14 B: 360 C: 199 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 395: A: 199 B: 267 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 396: A: 56 B: 361 C: 199 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 397: A: 199 B: 360 C: 56 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 398: A: 55 B: 358 C: 199 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 399: A: 199 B: 361 C: 55 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 400: A: 14 B: 276 C: 200 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 401: A: 200 B: 360 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 402: A: 19 B: 362 C: 200 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 403: A: 200 B: 276 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 404: A: 57 B: 363 C: 200 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 405: A: 200 B: 362 C: 57 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 406: A: 56 B: 360 C: 200 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 407: A: 200 B: 363 C: 56 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 408: A: 19 B: 285 C: 201 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 409: A: 201 B: 362 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 410: A: 133 B: 478 C: 201 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 411: A: 201 B: 285 C: 133 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 412: A: 140 B: 365 C: 201 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 413: A: 201 B: 478 C: 140 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 414: A: 57 B: 362 C: 201 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 415: A: 201 B: 365 C: 57 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 416: A: 139 B: 359 C: 202 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 417: A: 202 B: 473 C: 139 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 418: A: 55 B: 366 C: 202 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 419: A: 202 B: 359 C: 55 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 420: A: 71 B: 367 C: 202 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 421: A: 202 B: 366 C: 71 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 422: A: 143 B: 473 C: 202 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 423: A: 202 B: 367 C: 143 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 424: A: 55 B: 361 C: 203 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 425: A: 203 B: 366 C: 55 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 426: A: 56 B: 368 C: 203 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 427: A: 203 B: 361 C: 56 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 428: A: 72 B: 369 C: 203 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 429: A: 203 B: 368 C: 72 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 430: A: 71 B: 366 C: 203 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 431: A: 203 B: 369 C: 71 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 432: A: 56 B: 363 C: 204 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 433: A: 204 B: 368 C: 56 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 434: A: 57 B: 370 C: 204 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 435: A: 204 B: 363 C: 57 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 436: A: 73 B: 371 C: 204 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 437: A: 204 B: 370 C: 73 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 438: A: 72 B: 368 C: 204 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 439: A: 204 B: 371 C: 72 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 440: A: 57 B: 365 C: 205 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 441: A: 205 B: 370 C: 57 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 442: A: 140 B: 479 C: 205 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 443: A: 205 B: 365 C: 140 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 444: A: 144 B: 373 C: 205 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 445: A: 205 B: 479 C: 144 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 446: A: 73 B: 370 C: 205 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 447: A: 205 B: 373 C: 73 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 448: A: 143 B: 367 C: 206 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 449: A: 206 B: 475 C: 143 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 450: A: 71 B: 374 C: 206 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 451: A: 206 B: 367 C: 71 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 452: A: 87 B: 375 C: 206 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 453: A: 206 B: 374 C: 87 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 454: A: 147 B: 475 C: 206 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 455: A: 206 B: 375 C: 147 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 456: A: 71 B: 369 C: 207 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 457: A: 207 B: 374 C: 71 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 458: A: 72 B: 376 C: 207 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 459: A: 207 B: 369 C: 72 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 460: A: 88 B: 377 C: 207 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 461: A: 207 B: 376 C: 88 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 462: A: 87 B: 374 C: 207 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 463: A: 207 B: 377 C: 87 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 464: A: 72 B: 371 C: 208 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 465: A: 208 B: 376 C: 72 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 466: A: 73 B: 378 C: 208 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 467: A: 208 B: 371 C: 73 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 468: A: 89 B: 379 C: 208 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 469: A: 208 B: 378 C: 89 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 470: A: 88 B: 376 C: 208 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 471: A: 208 B: 379 C: 88 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 472: A: 73 B: 373 C: 209 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 473: A: 209 B: 378 C: 73 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 474: A: 144 B: 480 C: 209 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 475: A: 209 B: 373 C: 144 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 476: A: 148 B: 381 C: 209 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 477: A: 209 B: 480 C: 148 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 478: A: 89 B: 378 C: 209 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 479: A: 209 B: 381 C: 89 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 480: A: 147 B: 375 C: 210 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 481: A: 210 B: 477 C: 147 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 482: A: 87 B: 382 C: 210 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 483: A: 210 B: 375 C: 87 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 484: A: 34 B: 297 C: 210 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 485: A: 210 B: 382 C: 34 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 486: A: 135 B: 477 C: 210 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 487: A: 210 B: 297 C: 135 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 488: A: 87 B: 377 C: 211 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 489: A: 211 B: 382 C: 87 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 490: A: 88 B: 383 C: 211 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 491: A: 211 B: 377 C: 88 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 492: A: 39 B: 306 C: 211 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 493: A: 211 B: 383 C: 39 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 494: A: 34 B: 382 C: 211 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 495: A: 211 B: 306 C: 34 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 496: A: 88 B: 379 C: 212 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 497: A: 212 B: 383 C: 88 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 498: A: 89 B: 384 C: 212 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 499: A: 212 B: 379 C: 89 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 500: A: 44 B: 315 C: 212 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 501: A: 212 B: 384 C: 44 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 502: A: 39 B: 383 C: 212 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 503: A: 212 B: 315 C: 39 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 504: A: 89 B: 381 C: 213 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 505: A: 213 B: 384 C: 89 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 506: A: 148 B: 481 C: 213 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 507: A: 213 B: 381 C: 148 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 508: A: 137 B: 324 C: 213 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 509: A: 213 B: 481 C: 137 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 510: A: 44 B: 384 C: 213 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 511: A: 213 B: 324 C: 44 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 512: A: 24 B: 284 C: 214 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 513: A: 214 B: 364 C: 24 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 514: A: 23 B: 386 C: 214 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 515: A: 214 B: 284 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 516: A: 59 B: 387 C: 214 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 517: A: 214 B: 386 C: 59 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 518: A: 58 B: 364 C: 214 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 519: A: 214 B: 387 C: 58 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 520: A: 23 B: 282 C: 215 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 521: A: 215 B: 386 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 522: A: 22 B: 388 C: 215 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 523: A: 215 B: 282 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 524: A: 60 B: 389 C: 215 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 525: A: 215 B: 388 C: 60 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 526: A: 59 B: 386 C: 215 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 527: A: 215 B: 389 C: 59 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 528: A: 22 B: 280 C: 216 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 529: A: 216 B: 388 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 530: A: 21 B: 390 C: 216 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 531: A: 216 B: 280 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 532: A: 61 B: 391 C: 216 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 533: A: 216 B: 390 C: 61 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 534: A: 60 B: 388 C: 216 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 535: A: 216 B: 391 C: 60 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 536: A: 21 B: 278 C: 217 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 537: A: 217 B: 390 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 538: A: 20 B: 392 C: 217 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 539: A: 217 B: 278 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 540: A: 62 B: 393 C: 217 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 541: A: 217 B: 392 C: 62 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 542: A: 61 B: 390 C: 217 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 543: A: 217 B: 393 C: 61 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 544: A: 58 B: 387 C: 218 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 545: A: 218 B: 372 C: 58 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 546: A: 59 B: 394 C: 218 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 547: A: 218 B: 387 C: 59 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 548: A: 75 B: 395 C: 218 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 549: A: 218 B: 394 C: 75 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 550: A: 74 B: 372 C: 218 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 551: A: 218 B: 395 C: 74 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 552: A: 59 B: 389 C: 219 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 553: A: 219 B: 394 C: 59 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 554: A: 60 B: 396 C: 219 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 555: A: 219 B: 389 C: 60 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 556: A: 76 B: 397 C: 219 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 557: A: 219 B: 396 C: 76 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 558: A: 75 B: 394 C: 219 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 559: A: 219 B: 397 C: 75 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 560: A: 60 B: 391 C: 220 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 561: A: 220 B: 396 C: 60 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 562: A: 61 B: 398 C: 220 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 563: A: 220 B: 391 C: 61 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 564: A: 77 B: 399 C: 220 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 565: A: 220 B: 398 C: 77 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 566: A: 76 B: 396 C: 220 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 567: A: 220 B: 399 C: 76 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 568: A: 61 B: 393 C: 221 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 569: A: 221 B: 398 C: 61 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 570: A: 62 B: 400 C: 221 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 571: A: 221 B: 393 C: 62 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 572: A: 78 B: 401 C: 221 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 573: A: 221 B: 400 C: 78 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 574: A: 77 B: 398 C: 221 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 575: A: 221 B: 401 C: 77 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 576: A: 74 B: 395 C: 222 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 577: A: 222 B: 380 C: 74 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 578: A: 75 B: 402 C: 222 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 579: A: 222 B: 395 C: 75 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 580: A: 91 B: 403 C: 222 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 581: A: 222 B: 402 C: 91 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 582: A: 90 B: 380 C: 222 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 583: A: 222 B: 403 C: 90 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 584: A: 75 B: 397 C: 223 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 585: A: 223 B: 402 C: 75 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 586: A: 76 B: 404 C: 223 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 587: A: 223 B: 397 C: 76 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 588: A: 92 B: 405 C: 223 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 589: A: 223 B: 404 C: 92 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 590: A: 91 B: 402 C: 223 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 591: A: 223 B: 405 C: 91 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 592: A: 76 B: 399 C: 224 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 593: A: 224 B: 404 C: 76 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 594: A: 77 B: 406 C: 224 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 595: A: 224 B: 399 C: 77 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 596: A: 93 B: 407 C: 224 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 597: A: 224 B: 406 C: 93 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 598: A: 92 B: 404 C: 224 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 599: A: 224 B: 407 C: 92 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 600: A: 77 B: 401 C: 225 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 601: A: 225 B: 406 C: 77 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 602: A: 78 B: 408 C: 225 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 603: A: 225 B: 401 C: 78 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 604: A: 94 B: 409 C: 225 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 605: A: 225 B: 408 C: 94 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 606: A: 93 B: 406 C: 225 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 607: A: 225 B: 409 C: 93 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 608: A: 90 B: 403 C: 226 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 609: A: 226 B: 385 C: 90 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 610: A: 91 B: 410 C: 226 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 611: A: 226 B: 403 C: 91 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 612: A: 48 B: 325 C: 226 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 613: A: 226 B: 410 C: 48 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 614: A: 49 B: 385 C: 226 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 615: A: 226 B: 325 C: 49 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 616: A: 91 B: 405 C: 227 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 617: A: 227 B: 410 C: 91 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 618: A: 92 B: 411 C: 227 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 619: A: 227 B: 405 C: 92 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 620: A: 47 B: 323 C: 227 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 621: A: 227 B: 411 C: 47 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 622: A: 48 B: 410 C: 227 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 623: A: 227 B: 323 C: 48 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 624: A: 92 B: 407 C: 228 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 625: A: 228 B: 411 C: 92 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 626: A: 93 B: 412 C: 228 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 627: A: 228 B: 407 C: 93 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 628: A: 46 B: 321 C: 228 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 629: A: 228 B: 412 C: 46 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 630: A: 47 B: 411 C: 228 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 631: A: 228 B: 321 C: 47 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 632: A: 93 B: 409 C: 229 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 633: A: 229 B: 412 C: 93 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 634: A: 94 B: 413 C: 229 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 635: A: 229 B: 409 C: 94 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 636: A: 45 B: 318 C: 229 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 637: A: 229 B: 413 C: 45 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 638: A: 46 B: 412 C: 229 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 639: A: 229 B: 318 C: 46 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 640: A: 132 B: 277 C: 230 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 641: A: 230 B: 482 C: 132 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 642: A: 15 B: 414 C: 230 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 643: A: 230 B: 277 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 644: A: 63 B: 415 C: 230 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 645: A: 230 B: 414 C: 63 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 646: A: 141 B: 482 C: 230 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 647: A: 230 B: 415 C: 141 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 648: A: 15 B: 268 C: 231 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 649: A: 231 B: 414 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 650: A: 10 B: 416 C: 231 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 651: A: 231 B: 268 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 652: A: 64 B: 417 C: 231 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 653: A: 231 B: 416 C: 64 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 654: A: 63 B: 414 C: 231 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 655: A: 231 B: 417 C: 63 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 656: A: 10 B: 259 C: 232 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 657: A: 232 B: 416 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 658: A: 5 B: 418 C: 232 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 659: A: 232 B: 259 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 660: A: 65 B: 419 C: 232 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 661: A: 232 B: 418 C: 65 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 662: A: 64 B: 416 C: 232 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 663: A: 232 B: 419 C: 64 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 664: A: 5 B: 246 C: 233 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 665: A: 233 B: 418 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 666: A: 130 B: 470 C: 233 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 667: A: 233 B: 246 C: 130 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 668: A: 138 B: 420 C: 233 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 669: A: 233 B: 470 C: 138 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 670: A: 65 B: 418 C: 233 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 671: A: 233 B: 420 C: 65 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 672: A: 141 B: 415 C: 234 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 673: A: 234 B: 483 C: 141 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 674: A: 63 B: 421 C: 234 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 675: A: 234 B: 415 C: 63 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 676: A: 79 B: 422 C: 234 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 677: A: 234 B: 421 C: 79 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 678: A: 145 B: 483 C: 234 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 679: A: 234 B: 422 C: 145 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 680: A: 63 B: 417 C: 235 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 681: A: 235 B: 421 C: 63 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 682: A: 64 B: 423 C: 235 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 683: A: 235 B: 417 C: 64 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 684: A: 80 B: 424 C: 235 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 685: A: 235 B: 423 C: 80 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 686: A: 79 B: 421 C: 235 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 687: A: 235 B: 424 C: 79 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 688: A: 64 B: 419 C: 236 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 689: A: 236 B: 423 C: 64 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 690: A: 65 B: 425 C: 236 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 691: A: 236 B: 419 C: 65 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 692: A: 81 B: 426 C: 236 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 693: A: 236 B: 425 C: 81 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 694: A: 80 B: 423 C: 236 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 695: A: 236 B: 426 C: 80 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 696: A: 65 B: 420 C: 237 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 697: A: 237 B: 425 C: 65 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 698: A: 138 B: 472 C: 237 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 699: A: 237 B: 420 C: 138 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 700: A: 142 B: 427 C: 237 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 701: A: 237 B: 472 C: 142 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 702: A: 81 B: 425 C: 237 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 703: A: 237 B: 427 C: 81 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 704: A: 145 B: 422 C: 238 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 705: A: 238 B: 484 C: 145 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 706: A: 79 B: 428 C: 238 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 707: A: 238 B: 422 C: 79 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 708: A: 95 B: 429 C: 238 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 709: A: 238 B: 428 C: 95 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 710: A: 149 B: 484 C: 238 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 711: A: 238 B: 429 C: 149 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 712: A: 79 B: 424 C: 239 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 713: A: 239 B: 428 C: 79 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 714: A: 80 B: 430 C: 239 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 715: A: 239 B: 424 C: 80 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 716: A: 96 B: 431 C: 239 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 717: A: 239 B: 430 C: 96 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 718: A: 95 B: 428 C: 239 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 719: A: 239 B: 431 C: 95 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 720: A: 80 B: 426 C: 240 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 721: A: 240 B: 430 C: 80 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 722: A: 81 B: 432 C: 240 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 723: A: 240 B: 426 C: 81 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 724: A: 97 B: 433 C: 240 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 725: A: 240 B: 432 C: 97 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 726: A: 96 B: 430 C: 240 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 727: A: 240 B: 433 C: 96 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 728: A: 81 B: 427 C: 241 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 729: A: 241 B: 432 C: 81 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 730: A: 142 B: 474 C: 241 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 731: A: 241 B: 427 C: 142 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 732: A: 146 B: 434 C: 241 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 733: A: 241 B: 474 C: 146 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 734: A: 97 B: 432 C: 241 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 735: A: 241 B: 434 C: 97 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 736: A: 149 B: 429 C: 242 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 737: A: 242 B: 485 C: 149 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 738: A: 95 B: 435 C: 242 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 739: A: 242 B: 429 C: 95 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 740: A: 40 B: 319 C: 242 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 741: A: 242 B: 435 C: 40 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 742: A: 136 B: 485 C: 242 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 743: A: 242 B: 319 C: 136 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 744: A: 95 B: 431 C: 243 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 745: A: 243 B: 435 C: 95 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 746: A: 96 B: 436 C: 243 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 747: A: 243 B: 431 C: 96 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 748: A: 35 B: 310 C: 243 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 749: A: 243 B: 436 C: 35 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 750: A: 40 B: 435 C: 243 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 751: A: 243 B: 310 C: 40 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 752: A: 96 B: 433 C: 244 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 753: A: 244 B: 436 C: 96 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 754: A: 97 B: 437 C: 244 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 755: A: 244 B: 433 C: 97 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 756: A: 30 B: 301 C: 244 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 757: A: 244 B: 437 C: 30 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 758: A: 35 B: 436 C: 244 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 759: A: 244 B: 301 C: 35 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 760: A: 97 B: 434 C: 245 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 761: A: 245 B: 437 C: 97 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 762: A: 146 B: 476 C: 245 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 763: A: 245 B: 434 C: 146 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 764: A: 134 B: 289 C: 245 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 765: A: 245 B: 476 C: 134 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 766: A: 30 B: 437 C: 245 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 767: A: 245 B: 289 C: 30 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 486 + *MESH_TVERTLIST { + *MESH_TVERT 0 0.7500 0.2500 0.0000 + *MESH_TVERT 1 0.5000 0.2500 0.0000 + *MESH_TVERT 2 0.2500 0.2500 0.0000 + *MESH_TVERT 3 0.7500 0.5000 0.0000 + *MESH_TVERT 4 0.5000 0.5000 0.0000 + *MESH_TVERT 5 0.2500 0.5000 0.0000 + *MESH_TVERT 6 0.7500 0.7500 0.0000 + *MESH_TVERT 7 0.5000 0.7500 0.0000 + *MESH_TVERT 8 0.2500 0.7500 0.0000 + *MESH_TVERT 9 0.2500 0.2500 0.0000 + *MESH_TVERT 10 0.5000 0.2500 0.0000 + *MESH_TVERT 11 0.7500 0.2500 0.0000 + *MESH_TVERT 12 0.2500 0.5000 0.0000 + *MESH_TVERT 13 0.5000 0.5000 0.0000 + *MESH_TVERT 14 0.7500 0.5000 0.0000 + *MESH_TVERT 15 0.2500 0.7500 0.0000 + *MESH_TVERT 16 0.5000 0.7500 0.0000 + *MESH_TVERT 17 0.7500 0.7500 0.0000 + *MESH_TVERT 18 1.0000 1.0000 0.0000 + *MESH_TVERT 19 0.7500 1.0000 0.0000 + *MESH_TVERT 20 0.5000 1.0000 0.0000 + *MESH_TVERT 21 0.2500 1.0000 0.0000 + *MESH_TVERT 22 0.0000 1.0000 0.0000 + *MESH_TVERT 23 1.0000 0.7500 0.0000 + *MESH_TVERT 24 0.7500 0.7500 0.0000 + *MESH_TVERT 25 0.5000 0.7500 0.0000 + *MESH_TVERT 26 0.2500 0.7500 0.0000 + *MESH_TVERT 27 0.0000 0.7500 0.0000 + *MESH_TVERT 28 1.0000 0.5000 0.0000 + *MESH_TVERT 29 0.7500 0.5000 0.0000 + *MESH_TVERT 30 0.5000 0.5000 0.0000 + *MESH_TVERT 31 0.2500 0.5000 0.0000 + *MESH_TVERT 32 0.0000 0.5000 0.0000 + *MESH_TVERT 33 1.0000 0.2500 0.0000 + *MESH_TVERT 34 0.7500 0.2500 0.0000 + *MESH_TVERT 35 0.5000 0.2500 0.0000 + *MESH_TVERT 36 0.2500 0.2500 0.0000 + *MESH_TVERT 37 0.0000 0.2500 0.0000 + *MESH_TVERT 38 1.0000 -0.0000 0.0000 + *MESH_TVERT 39 0.7500 -0.0000 0.0000 + *MESH_TVERT 40 0.5000 0.0000 0.0000 + *MESH_TVERT 41 0.2500 0.0000 0.0000 + *MESH_TVERT 42 -0.0000 0.0000 0.0000 + *MESH_TVERT 43 1.0000 0.2500 0.0000 + *MESH_TVERT 44 1.0000 0.5000 0.0000 + *MESH_TVERT 45 1.0000 0.7500 0.0000 + *MESH_TVERT 46 0.7500 0.2500 0.0000 + *MESH_TVERT 47 0.7500 0.5000 0.0000 + *MESH_TVERT 48 0.7500 0.7500 0.0000 + *MESH_TVERT 49 0.5000 0.2500 0.0000 + *MESH_TVERT 50 0.5000 0.5000 0.0000 + *MESH_TVERT 51 0.5000 0.7500 0.0000 + *MESH_TVERT 52 0.2500 0.2500 0.0000 + *MESH_TVERT 53 0.2500 0.5000 0.0000 + *MESH_TVERT 54 0.2500 0.7500 0.0000 + *MESH_TVERT 55 0.0000 0.2500 0.0000 + *MESH_TVERT 56 0.0000 0.5000 0.0000 + *MESH_TVERT 57 0.0000 0.7500 0.0000 + *MESH_TVERT 58 1.0000 1.0000 0.0000 + *MESH_TVERT 59 0.7500 1.0000 0.0000 + *MESH_TVERT 60 0.5000 1.0000 0.0000 + *MESH_TVERT 61 0.2500 1.0000 0.0000 + *MESH_TVERT 62 0.0000 1.0000 0.0000 + *MESH_TVERT 63 1.0000 0.7500 0.0000 + *MESH_TVERT 64 0.7500 0.7500 0.0000 + *MESH_TVERT 65 0.5000 0.7500 0.0000 + *MESH_TVERT 66 0.2500 0.7500 0.0000 + *MESH_TVERT 67 -0.0000 0.7500 0.0000 + *MESH_TVERT 68 1.0000 0.5000 0.0000 + *MESH_TVERT 69 0.7500 0.5000 0.0000 + *MESH_TVERT 70 0.5000 0.5000 0.0000 + *MESH_TVERT 71 0.2500 0.5000 0.0000 + *MESH_TVERT 72 -0.0000 0.5000 0.0000 + *MESH_TVERT 73 1.0000 0.2500 0.0000 + *MESH_TVERT 74 0.7500 0.2500 0.0000 + *MESH_TVERT 75 0.5000 0.2500 0.0000 + *MESH_TVERT 76 0.2500 0.2500 0.0000 + *MESH_TVERT 77 -0.0000 0.2500 0.0000 + *MESH_TVERT 78 1.0000 -0.0000 0.0000 + *MESH_TVERT 79 0.7500 -0.0000 0.0000 + *MESH_TVERT 80 0.5000 -0.0000 0.0000 + *MESH_TVERT 81 0.2500 -0.0000 0.0000 + *MESH_TVERT 82 -0.0000 0.0000 0.0000 + *MESH_TVERT 83 0.0000 0.7500 0.0000 + *MESH_TVERT 84 0.0000 0.5000 0.0000 + *MESH_TVERT 85 0.0000 0.2500 0.0000 + *MESH_TVERT 86 0.2500 0.7500 0.0000 + *MESH_TVERT 87 0.2500 0.5000 0.0000 + *MESH_TVERT 88 0.2500 0.2500 0.0000 + *MESH_TVERT 89 0.5000 0.7500 0.0000 + *MESH_TVERT 90 0.5000 0.5000 0.0000 + *MESH_TVERT 91 0.5000 0.2500 0.0000 + *MESH_TVERT 92 0.7500 0.7500 0.0000 + *MESH_TVERT 93 0.7500 0.5000 0.0000 + *MESH_TVERT 94 0.7500 0.2500 0.0000 + *MESH_TVERT 95 1.0000 0.7500 0.0000 + *MESH_TVERT 96 1.0000 0.5000 0.0000 + *MESH_TVERT 97 1.0000 0.2500 0.0000 + *MESH_TVERT 98 1.0000 0.0000 0.0000 + *MESH_TVERT 99 0.7500 0.0000 0.0000 + *MESH_TVERT 100 0.5000 0.0000 0.0000 + *MESH_TVERT 101 0.2500 0.0000 0.0000 + *MESH_TVERT 102 0.0000 0.0000 0.0000 + *MESH_TVERT 103 1.0000 0.2500 0.0000 + *MESH_TVERT 104 0.0000 0.2500 0.0000 + *MESH_TVERT 105 1.0000 0.5000 0.0000 + *MESH_TVERT 106 0.0000 0.5000 0.0000 + *MESH_TVERT 107 1.0000 0.7500 0.0000 + *MESH_TVERT 108 0.0000 0.7500 0.0000 + *MESH_TVERT 109 1.0000 1.0000 0.0000 + *MESH_TVERT 110 0.7500 1.0000 0.0000 + *MESH_TVERT 111 0.5000 1.0000 0.0000 + *MESH_TVERT 112 0.2500 1.0000 0.0000 + *MESH_TVERT 113 0.0000 1.0000 0.0000 + *MESH_TVERT 114 0.0000 0.0000 0.0000 + *MESH_TVERT 115 0.2500 0.0000 0.0000 + *MESH_TVERT 116 0.5000 0.0000 0.0000 + *MESH_TVERT 117 0.7500 0.0000 0.0000 + *MESH_TVERT 118 1.0000 0.0000 0.0000 + *MESH_TVERT 119 0.0000 0.2500 0.0000 + *MESH_TVERT 120 1.0000 0.2500 0.0000 + *MESH_TVERT 121 0.0000 0.5000 0.0000 + *MESH_TVERT 122 1.0000 0.5000 0.0000 + *MESH_TVERT 123 0.0000 0.7500 0.0000 + *MESH_TVERT 124 1.0000 0.7500 0.0000 + *MESH_TVERT 125 0.0000 1.0000 0.0000 + *MESH_TVERT 126 0.2500 1.0000 0.0000 + *MESH_TVERT 127 0.5000 1.0000 0.0000 + *MESH_TVERT 128 0.7500 1.0000 0.0000 + *MESH_TVERT 129 1.0000 1.0000 0.0000 + *MESH_TVERT 130 0.0000 0.0000 0.0000 + *MESH_TVERT 131 1.0000 0.0000 0.0000 + *MESH_TVERT 132 0.0000 1.0000 0.0000 + *MESH_TVERT 133 1.0000 1.0000 0.0000 + *MESH_TVERT 134 1.0000 -0.0000 0.0000 + *MESH_TVERT 135 0.0000 -0.0000 0.0000 + *MESH_TVERT 136 1.0000 1.0000 0.0000 + *MESH_TVERT 137 0.0000 1.0000 0.0000 + *MESH_TVERT 138 0.2500 -0.0000 0.0000 + *MESH_TVERT 139 0.7500 0.0000 0.0000 + *MESH_TVERT 140 0.7500 1.0000 0.0000 + *MESH_TVERT 141 0.2500 1.0000 0.0000 + *MESH_TVERT 142 0.5000 -0.0000 0.0000 + *MESH_TVERT 143 0.5000 0.0000 0.0000 + *MESH_TVERT 144 0.5000 1.0000 0.0000 + *MESH_TVERT 145 0.5000 1.0000 0.0000 + *MESH_TVERT 146 0.7500 -0.0000 0.0000 + *MESH_TVERT 147 0.2500 -0.0000 0.0000 + *MESH_TVERT 148 0.2500 1.0000 0.0000 + *MESH_TVERT 149 0.7500 1.0000 0.0000 + *MESH_TVERT 150 0.8750 0.1250 0.0000 + *MESH_TVERT 151 0.6250 0.1250 0.0000 + *MESH_TVERT 152 0.3750 0.1250 0.0000 + *MESH_TVERT 153 0.1250 0.1250 0.0000 + *MESH_TVERT 154 0.8750 0.3750 0.0000 + *MESH_TVERT 155 0.6250 0.3750 0.0000 + *MESH_TVERT 156 0.3750 0.3750 0.0000 + *MESH_TVERT 157 0.1250 0.3750 0.0000 + *MESH_TVERT 158 0.8750 0.6250 0.0000 + *MESH_TVERT 159 0.6250 0.6250 0.0000 + *MESH_TVERT 160 0.3750 0.6250 0.0000 + *MESH_TVERT 161 0.1250 0.6250 0.0000 + *MESH_TVERT 162 0.8750 0.8750 0.0000 + *MESH_TVERT 163 0.6250 0.8750 0.0000 + *MESH_TVERT 164 0.3750 0.8750 0.0000 + *MESH_TVERT 165 0.1250 0.8750 0.0000 + *MESH_TVERT 166 0.1250 0.1250 0.0000 + *MESH_TVERT 167 0.3750 0.1250 0.0000 + *MESH_TVERT 168 0.6250 0.1250 0.0000 + *MESH_TVERT 169 0.8750 0.1250 0.0000 + *MESH_TVERT 170 0.1250 0.3750 0.0000 + *MESH_TVERT 171 0.3750 0.3750 0.0000 + *MESH_TVERT 172 0.6250 0.3750 0.0000 + *MESH_TVERT 173 0.8750 0.3750 0.0000 + *MESH_TVERT 174 0.1250 0.6250 0.0000 + *MESH_TVERT 175 0.3750 0.6250 0.0000 + *MESH_TVERT 176 0.6250 0.6250 0.0000 + *MESH_TVERT 177 0.8750 0.6250 0.0000 + *MESH_TVERT 178 0.1250 0.8750 0.0000 + *MESH_TVERT 179 0.3750 0.8750 0.0000 + *MESH_TVERT 180 0.6250 0.8750 0.0000 + *MESH_TVERT 181 0.8750 0.8750 0.0000 + *MESH_TVERT 182 0.8750 0.8750 0.0000 + *MESH_TVERT 183 0.6250 0.8750 0.0000 + *MESH_TVERT 184 0.3750 0.8750 0.0000 + *MESH_TVERT 185 0.1250 0.8750 0.0000 + *MESH_TVERT 186 0.8750 0.6250 0.0000 + *MESH_TVERT 187 0.6250 0.6250 0.0000 + *MESH_TVERT 188 0.3750 0.6250 0.0000 + *MESH_TVERT 189 0.1250 0.6250 0.0000 + *MESH_TVERT 190 0.8750 0.3750 0.0000 + *MESH_TVERT 191 0.6250 0.3750 0.0000 + *MESH_TVERT 192 0.3750 0.3750 0.0000 + *MESH_TVERT 193 0.1250 0.3750 0.0000 + *MESH_TVERT 194 0.8750 0.1250 0.0000 + *MESH_TVERT 195 0.6250 0.1250 0.0000 + *MESH_TVERT 196 0.3750 0.1250 0.0000 + *MESH_TVERT 197 0.1250 0.1250 0.0000 + *MESH_TVERT 198 0.8750 0.1250 0.0000 + *MESH_TVERT 199 0.8750 0.3750 0.0000 + *MESH_TVERT 200 0.8750 0.6250 0.0000 + *MESH_TVERT 201 0.8750 0.8750 0.0000 + *MESH_TVERT 202 0.6250 0.1250 0.0000 + *MESH_TVERT 203 0.6250 0.3750 0.0000 + *MESH_TVERT 204 0.6250 0.6250 0.0000 + *MESH_TVERT 205 0.6250 0.8750 0.0000 + *MESH_TVERT 206 0.3750 0.1250 0.0000 + *MESH_TVERT 207 0.3750 0.3750 0.0000 + *MESH_TVERT 208 0.3750 0.6250 0.0000 + *MESH_TVERT 209 0.3750 0.8750 0.0000 + *MESH_TVERT 210 0.1250 0.1250 0.0000 + *MESH_TVERT 211 0.1250 0.3750 0.0000 + *MESH_TVERT 212 0.1250 0.6250 0.0000 + *MESH_TVERT 213 0.1250 0.8750 0.0000 + *MESH_TVERT 214 0.8750 0.8750 0.0000 + *MESH_TVERT 215 0.6250 0.8750 0.0000 + *MESH_TVERT 216 0.3750 0.8750 0.0000 + *MESH_TVERT 217 0.1250 0.8750 0.0000 + *MESH_TVERT 218 0.8750 0.6250 0.0000 + *MESH_TVERT 219 0.6250 0.6250 0.0000 + *MESH_TVERT 220 0.3750 0.6250 0.0000 + *MESH_TVERT 221 0.1250 0.6250 0.0000 + *MESH_TVERT 222 0.8750 0.3750 0.0000 + *MESH_TVERT 223 0.6250 0.3750 0.0000 + *MESH_TVERT 224 0.3750 0.3750 0.0000 + *MESH_TVERT 225 0.1250 0.3750 0.0000 + *MESH_TVERT 226 0.8750 0.1250 0.0000 + *MESH_TVERT 227 0.6250 0.1250 0.0000 + *MESH_TVERT 228 0.3750 0.1250 0.0000 + *MESH_TVERT 229 0.1250 0.1250 0.0000 + *MESH_TVERT 230 0.1250 0.8750 0.0000 + *MESH_TVERT 231 0.1250 0.6250 0.0000 + *MESH_TVERT 232 0.1250 0.3750 0.0000 + *MESH_TVERT 233 0.1250 0.1250 0.0000 + *MESH_TVERT 234 0.3750 0.8750 0.0000 + *MESH_TVERT 235 0.3750 0.6250 0.0000 + *MESH_TVERT 236 0.3750 0.3750 0.0000 + *MESH_TVERT 237 0.3750 0.1250 0.0000 + *MESH_TVERT 238 0.6250 0.8750 0.0000 + *MESH_TVERT 239 0.6250 0.6250 0.0000 + *MESH_TVERT 240 0.6250 0.3750 0.0000 + *MESH_TVERT 241 0.6250 0.1250 0.0000 + *MESH_TVERT 242 0.8750 0.8750 0.0000 + *MESH_TVERT 243 0.8750 0.6250 0.0000 + *MESH_TVERT 244 0.8750 0.3750 0.0000 + *MESH_TVERT 245 0.8750 0.1250 0.0000 + *MESH_TVERT 246 0.0000 0.1250 0.0000 + *MESH_TVERT 247 0.8750 0.2500 0.0000 + *MESH_TVERT 248 0.7500 0.1250 0.0000 + *MESH_TVERT 249 0.8750 1.0000 0.0000 + *MESH_TVERT 250 0.6250 0.2500 0.0000 + *MESH_TVERT 251 0.5000 0.1250 0.0000 + *MESH_TVERT 252 0.6250 1.0000 0.0000 + *MESH_TVERT 253 0.3750 0.2500 0.0000 + *MESH_TVERT 254 0.2500 0.1250 0.0000 + *MESH_TVERT 255 0.3750 1.0000 0.0000 + *MESH_TVERT 256 0.1250 0.2500 0.0000 + *MESH_TVERT 257 1.0000 0.1250 0.0000 + *MESH_TVERT 258 0.1250 1.0000 0.0000 + *MESH_TVERT 259 0.0000 0.3750 0.0000 + *MESH_TVERT 260 0.8750 0.5000 0.0000 + *MESH_TVERT 261 0.7500 0.3750 0.0000 + *MESH_TVERT 262 0.6250 0.5000 0.0000 + *MESH_TVERT 263 0.5000 0.3750 0.0000 + *MESH_TVERT 264 0.3750 0.5000 0.0000 + *MESH_TVERT 265 0.2500 0.3750 0.0000 + *MESH_TVERT 266 0.1250 0.5000 0.0000 + *MESH_TVERT 267 1.0000 0.3750 0.0000 + *MESH_TVERT 268 0.0000 0.6250 0.0000 + *MESH_TVERT 269 0.8750 0.7500 0.0000 + *MESH_TVERT 270 0.7500 0.6250 0.0000 + *MESH_TVERT 271 0.6250 0.7500 0.0000 + *MESH_TVERT 272 0.5000 0.6250 0.0000 + *MESH_TVERT 273 0.3750 0.7500 0.0000 + *MESH_TVERT 274 0.2500 0.6250 0.0000 + *MESH_TVERT 275 0.1250 0.7500 0.0000 + *MESH_TVERT 276 1.0000 0.6250 0.0000 + *MESH_TVERT 277 0.0000 0.8750 0.0000 + *MESH_TVERT 278 0.1250 1.0000 0.0000 + *MESH_TVERT 279 0.7500 0.8750 0.0000 + *MESH_TVERT 280 0.3750 1.0000 0.0000 + *MESH_TVERT 281 0.5000 0.8750 0.0000 + *MESH_TVERT 282 0.6250 1.0000 0.0000 + *MESH_TVERT 283 0.2500 0.8750 0.0000 + *MESH_TVERT 284 0.8750 1.0000 0.0000 + *MESH_TVERT 285 1.0000 0.8750 0.0000 + *MESH_TVERT 286 0.8750 -0.0000 0.0000 + *MESH_TVERT 287 0.2500 0.1250 0.0000 + *MESH_TVERT 288 0.1250 0.2500 0.0000 + *MESH_TVERT 289 1.0000 0.1250 0.0000 + *MESH_TVERT 290 0.6250 0.0000 0.0000 + *MESH_TVERT 291 0.5000 0.1250 0.0000 + *MESH_TVERT 292 0.3750 0.2500 0.0000 + *MESH_TVERT 293 0.3750 0.0000 0.0000 + *MESH_TVERT 294 0.7500 0.1250 0.0000 + *MESH_TVERT 295 0.6250 0.2500 0.0000 + *MESH_TVERT 296 0.1250 0.0000 0.0000 + *MESH_TVERT 297 0.0000 0.1250 0.0000 + *MESH_TVERT 298 0.8750 0.2500 0.0000 + *MESH_TVERT 299 0.2500 0.3750 0.0000 + *MESH_TVERT 300 0.1250 0.5000 0.0000 + *MESH_TVERT 301 1.0000 0.3750 0.0000 + *MESH_TVERT 302 0.5000 0.3750 0.0000 + *MESH_TVERT 303 0.3750 0.5000 0.0000 + *MESH_TVERT 304 0.7500 0.3750 0.0000 + *MESH_TVERT 305 0.6250 0.5000 0.0000 + *MESH_TVERT 306 0.0000 0.3750 0.0000 + *MESH_TVERT 307 0.8750 0.5000 0.0000 + *MESH_TVERT 308 0.2500 0.6250 0.0000 + *MESH_TVERT 309 0.1250 0.7500 0.0000 + *MESH_TVERT 310 1.0000 0.6250 0.0000 + *MESH_TVERT 311 0.5000 0.6250 0.0000 + *MESH_TVERT 312 0.3750 0.7500 0.0000 + *MESH_TVERT 313 0.7500 0.6250 0.0000 + *MESH_TVERT 314 0.6250 0.7500 0.0000 + *MESH_TVERT 315 0.0000 0.6250 0.0000 + *MESH_TVERT 316 0.8750 0.7500 0.0000 + *MESH_TVERT 317 0.2500 0.8750 0.0000 + *MESH_TVERT 318 0.1250 0.0000 0.0000 + *MESH_TVERT 319 1.0000 0.8750 0.0000 + *MESH_TVERT 320 0.5000 0.8750 0.0000 + *MESH_TVERT 321 0.3750 -0.0000 0.0000 + *MESH_TVERT 322 0.7500 0.8750 0.0000 + *MESH_TVERT 323 0.6250 -0.0000 0.0000 + *MESH_TVERT 324 0.0000 0.8750 0.0000 + *MESH_TVERT 325 0.8750 -0.0000 0.0000 + *MESH_TVERT 326 0.7500 0.8750 0.0000 + *MESH_TVERT 327 0.8750 0.7500 0.0000 + *MESH_TVERT 328 1.0000 0.8750 0.0000 + *MESH_TVERT 329 0.5000 0.8750 0.0000 + *MESH_TVERT 330 0.6250 0.7500 0.0000 + *MESH_TVERT 331 0.2500 0.8750 0.0000 + *MESH_TVERT 332 0.3750 0.7500 0.0000 + *MESH_TVERT 333 0.0000 0.8750 0.0000 + *MESH_TVERT 334 0.1250 0.7500 0.0000 + *MESH_TVERT 335 0.7500 0.6250 0.0000 + *MESH_TVERT 336 0.8750 0.5000 0.0000 + *MESH_TVERT 337 1.0000 0.6250 0.0000 + *MESH_TVERT 338 0.5000 0.6250 0.0000 + *MESH_TVERT 339 0.6250 0.5000 0.0000 + *MESH_TVERT 340 0.2500 0.6250 0.0000 + *MESH_TVERT 341 0.3750 0.5000 0.0000 + *MESH_TVERT 342 0.0000 0.6250 0.0000 + *MESH_TVERT 343 0.1250 0.5000 0.0000 + *MESH_TVERT 344 0.7500 0.3750 0.0000 + *MESH_TVERT 345 0.8750 0.2500 0.0000 + *MESH_TVERT 346 1.0000 0.3750 0.0000 + *MESH_TVERT 347 0.5000 0.3750 0.0000 + *MESH_TVERT 348 0.6250 0.2500 0.0000 + *MESH_TVERT 349 0.2500 0.3750 0.0000 + *MESH_TVERT 350 0.3750 0.2500 0.0000 + *MESH_TVERT 351 0.0000 0.3750 0.0000 + *MESH_TVERT 352 0.1250 0.2500 0.0000 + *MESH_TVERT 353 0.7500 0.1250 0.0000 + *MESH_TVERT 354 1.0000 0.1250 0.0000 + *MESH_TVERT 355 0.5000 0.1250 0.0000 + *MESH_TVERT 356 0.2500 0.1250 0.0000 + *MESH_TVERT 357 0.0000 0.1250 0.0000 + *MESH_TVERT 358 0.8750 0.2500 0.0000 + *MESH_TVERT 359 0.7500 0.1250 0.0000 + *MESH_TVERT 360 0.8750 0.5000 0.0000 + *MESH_TVERT 361 0.7500 0.3750 0.0000 + *MESH_TVERT 362 0.8750 0.7500 0.0000 + *MESH_TVERT 363 0.7500 0.6250 0.0000 + *MESH_TVERT 364 1.0000 0.8750 0.0000 + *MESH_TVERT 365 0.7500 0.8750 0.0000 + *MESH_TVERT 366 0.6250 0.2500 0.0000 + *MESH_TVERT 367 0.5000 0.1250 0.0000 + *MESH_TVERT 368 0.6250 0.5000 0.0000 + *MESH_TVERT 369 0.5000 0.3750 0.0000 + *MESH_TVERT 370 0.6250 0.7500 0.0000 + *MESH_TVERT 371 0.5000 0.6250 0.0000 + *MESH_TVERT 372 1.0000 0.6250 0.0000 + *MESH_TVERT 373 0.5000 0.8750 0.0000 + *MESH_TVERT 374 0.3750 0.2500 0.0000 + *MESH_TVERT 375 0.2500 0.1250 0.0000 + *MESH_TVERT 376 0.3750 0.5000 0.0000 + *MESH_TVERT 377 0.2500 0.3750 0.0000 + *MESH_TVERT 378 0.3750 0.7500 0.0000 + *MESH_TVERT 379 0.2500 0.6250 0.0000 + *MESH_TVERT 380 1.0000 0.3750 0.0000 + *MESH_TVERT 381 0.2500 0.8750 0.0000 + *MESH_TVERT 382 0.1250 0.2500 0.0000 + *MESH_TVERT 383 0.1250 0.5000 0.0000 + *MESH_TVERT 384 0.1250 0.7500 0.0000 + *MESH_TVERT 385 1.0000 0.1250 0.0000 + *MESH_TVERT 386 0.7500 0.8750 0.0000 + *MESH_TVERT 387 0.8750 0.7500 0.0000 + *MESH_TVERT 388 0.5000 0.8750 0.0000 + *MESH_TVERT 389 0.6250 0.7500 0.0000 + *MESH_TVERT 390 0.2500 0.8750 0.0000 + *MESH_TVERT 391 0.3750 0.7500 0.0000 + *MESH_TVERT 392 -0.0000 0.8750 0.0000 + *MESH_TVERT 393 0.1250 0.7500 0.0000 + *MESH_TVERT 394 0.7500 0.6250 0.0000 + *MESH_TVERT 395 0.8750 0.5000 0.0000 + *MESH_TVERT 396 0.5000 0.6250 0.0000 + *MESH_TVERT 397 0.6250 0.5000 0.0000 + *MESH_TVERT 398 0.2500 0.6250 0.0000 + *MESH_TVERT 399 0.3750 0.5000 0.0000 + *MESH_TVERT 400 -0.0000 0.6250 0.0000 + *MESH_TVERT 401 0.1250 0.5000 0.0000 + *MESH_TVERT 402 0.7500 0.3750 0.0000 + *MESH_TVERT 403 0.8750 0.2500 0.0000 + *MESH_TVERT 404 0.5000 0.3750 0.0000 + *MESH_TVERT 405 0.6250 0.2500 0.0000 + *MESH_TVERT 406 0.2500 0.3750 0.0000 + *MESH_TVERT 407 0.3750 0.2500 0.0000 + *MESH_TVERT 408 -0.0000 0.3750 0.0000 + *MESH_TVERT 409 0.1250 0.2500 0.0000 + *MESH_TVERT 410 0.7500 0.1250 0.0000 + *MESH_TVERT 411 0.5000 0.1250 0.0000 + *MESH_TVERT 412 0.2500 0.1250 0.0000 + *MESH_TVERT 413 -0.0000 0.1250 0.0000 + *MESH_TVERT 414 0.1250 0.7500 0.0000 + *MESH_TVERT 415 0.2500 0.8750 0.0000 + *MESH_TVERT 416 0.1250 0.5000 0.0000 + *MESH_TVERT 417 0.2500 0.6250 0.0000 + *MESH_TVERT 418 0.1250 0.2500 0.0000 + *MESH_TVERT 419 0.2500 0.3750 0.0000 + *MESH_TVERT 420 0.2500 0.1250 0.0000 + *MESH_TVERT 421 0.3750 0.7500 0.0000 + *MESH_TVERT 422 0.5000 0.8750 0.0000 + *MESH_TVERT 423 0.3750 0.5000 0.0000 + *MESH_TVERT 424 0.5000 0.6250 0.0000 + *MESH_TVERT 425 0.3750 0.2500 0.0000 + *MESH_TVERT 426 0.5000 0.3750 0.0000 + *MESH_TVERT 427 0.5000 0.1250 0.0000 + *MESH_TVERT 428 0.6250 0.7500 0.0000 + *MESH_TVERT 429 0.7500 0.8750 0.0000 + *MESH_TVERT 430 0.6250 0.5000 0.0000 + *MESH_TVERT 431 0.7500 0.6250 0.0000 + *MESH_TVERT 432 0.6250 0.2500 0.0000 + *MESH_TVERT 433 0.7500 0.3750 0.0000 + *MESH_TVERT 434 0.7500 0.1250 0.0000 + *MESH_TVERT 435 0.8750 0.7500 0.0000 + *MESH_TVERT 436 0.8750 0.5000 0.0000 + *MESH_TVERT 437 0.8750 0.2500 0.0000 + *MESH_TVERT 438 1.0000 0.1250 0.0000 + *MESH_TVERT 439 0.8750 0.0000 0.0000 + *MESH_TVERT 440 0.6250 0.0000 0.0000 + *MESH_TVERT 441 0.3750 0.0000 0.0000 + *MESH_TVERT 442 0.0000 0.1250 0.0000 + *MESH_TVERT 443 0.1250 0.0000 0.0000 + *MESH_TVERT 444 1.0000 0.3750 0.0000 + *MESH_TVERT 445 0.0000 0.3750 0.0000 + *MESH_TVERT 446 1.0000 0.6250 0.0000 + *MESH_TVERT 447 0.0000 0.6250 0.0000 + *MESH_TVERT 448 1.0000 0.8750 0.0000 + *MESH_TVERT 449 0.8750 1.0000 0.0000 + *MESH_TVERT 450 0.6250 1.0000 0.0000 + *MESH_TVERT 451 0.3750 1.0000 0.0000 + *MESH_TVERT 452 0.1250 1.0000 0.0000 + *MESH_TVERT 453 0.0000 0.8750 0.0000 + *MESH_TVERT 454 0.1250 0.0000 0.0000 + *MESH_TVERT 455 0.0000 0.1250 0.0000 + *MESH_TVERT 456 0.3750 0.0000 0.0000 + *MESH_TVERT 457 0.6250 0.0000 0.0000 + *MESH_TVERT 458 0.8750 0.0000 0.0000 + *MESH_TVERT 459 1.0000 0.1250 0.0000 + *MESH_TVERT 460 0.0000 0.3750 0.0000 + *MESH_TVERT 461 1.0000 0.3750 0.0000 + *MESH_TVERT 462 0.0000 0.6250 0.0000 + *MESH_TVERT 463 1.0000 0.6250 0.0000 + *MESH_TVERT 464 0.1250 1.0000 0.0000 + *MESH_TVERT 465 0.0000 0.8750 0.0000 + *MESH_TVERT 466 0.3750 1.0000 0.0000 + *MESH_TVERT 467 0.6250 1.0000 0.0000 + *MESH_TVERT 468 1.0000 0.8750 0.0000 + *MESH_TVERT 469 0.8750 1.0000 0.0000 + *MESH_TVERT 470 0.1250 0.0000 0.0000 + *MESH_TVERT 471 0.8750 0.0000 0.0000 + *MESH_TVERT 472 0.3750 -0.0000 0.0000 + *MESH_TVERT 473 0.6250 0.0000 0.0000 + *MESH_TVERT 474 0.6250 -0.0000 0.0000 + *MESH_TVERT 475 0.3750 0.0000 0.0000 + *MESH_TVERT 476 0.8750 -0.0000 0.0000 + *MESH_TVERT 477 0.1250 -0.0000 0.0000 + *MESH_TVERT 478 0.8750 1.0000 0.0000 + *MESH_TVERT 479 0.6250 1.0000 0.0000 + *MESH_TVERT 480 0.3750 1.0000 0.0000 + *MESH_TVERT 481 0.1250 1.0000 0.0000 + *MESH_TVERT 482 0.1250 1.0000 0.0000 + *MESH_TVERT 483 0.3750 1.0000 0.0000 + *MESH_TVERT 484 0.6250 1.0000 0.0000 + *MESH_TVERT 485 0.8750 1.0000 0.0000 + } + *MESH_NUMTVFACES 768 + *MESH_TFACELIST { + *MESH_TFACE 0 98 438 150 + *MESH_TFACE 1 150 439 98 + *MESH_TFACE 2 103 247 150 + *MESH_TFACE 3 150 438 103 + *MESH_TFACE 4 0 248 150 + *MESH_TFACE 5 150 247 0 + *MESH_TFACE 6 99 439 150 + *MESH_TFACE 7 150 248 99 + *MESH_TFACE 8 99 248 151 + *MESH_TFACE 9 151 440 99 + *MESH_TFACE 10 0 250 151 + *MESH_TFACE 11 151 248 0 + *MESH_TFACE 12 1 251 151 + *MESH_TFACE 13 151 250 1 + *MESH_TFACE 14 100 440 151 + *MESH_TFACE 15 151 251 100 + *MESH_TFACE 16 100 251 152 + *MESH_TFACE 17 152 441 100 + *MESH_TFACE 18 1 253 152 + *MESH_TFACE 19 152 251 1 + *MESH_TFACE 20 2 254 152 + *MESH_TFACE 21 152 253 2 + *MESH_TFACE 22 101 441 152 + *MESH_TFACE 23 152 254 101 + *MESH_TFACE 24 101 254 153 + *MESH_TFACE 25 153 443 101 + *MESH_TFACE 26 2 256 153 + *MESH_TFACE 27 153 254 2 + *MESH_TFACE 28 104 442 153 + *MESH_TFACE 29 153 256 104 + *MESH_TFACE 30 102 443 153 + *MESH_TFACE 31 153 442 102 + *MESH_TFACE 32 103 444 154 + *MESH_TFACE 33 154 247 103 + *MESH_TFACE 34 105 260 154 + *MESH_TFACE 35 154 444 105 + *MESH_TFACE 36 3 261 154 + *MESH_TFACE 37 154 260 3 + *MESH_TFACE 38 0 247 154 + *MESH_TFACE 39 154 261 0 + *MESH_TFACE 40 0 261 155 + *MESH_TFACE 41 155 250 0 + *MESH_TFACE 42 3 262 155 + *MESH_TFACE 43 155 261 3 + *MESH_TFACE 44 4 263 155 + *MESH_TFACE 45 155 262 4 + *MESH_TFACE 46 1 250 155 + *MESH_TFACE 47 155 263 1 + *MESH_TFACE 48 1 263 156 + *MESH_TFACE 49 156 253 1 + *MESH_TFACE 50 4 264 156 + *MESH_TFACE 51 156 263 4 + *MESH_TFACE 52 5 265 156 + *MESH_TFACE 53 156 264 5 + *MESH_TFACE 54 2 253 156 + *MESH_TFACE 55 156 265 2 + *MESH_TFACE 56 2 265 157 + *MESH_TFACE 57 157 256 2 + *MESH_TFACE 58 5 266 157 + *MESH_TFACE 59 157 265 5 + *MESH_TFACE 60 106 445 157 + *MESH_TFACE 61 157 266 106 + *MESH_TFACE 62 104 256 157 + *MESH_TFACE 63 157 445 104 + *MESH_TFACE 64 105 446 158 + *MESH_TFACE 65 158 260 105 + *MESH_TFACE 66 107 269 158 + *MESH_TFACE 67 158 446 107 + *MESH_TFACE 68 6 270 158 + *MESH_TFACE 69 158 269 6 + *MESH_TFACE 70 3 260 158 + *MESH_TFACE 71 158 270 3 + *MESH_TFACE 72 3 270 159 + *MESH_TFACE 73 159 262 3 + *MESH_TFACE 74 6 271 159 + *MESH_TFACE 75 159 270 6 + *MESH_TFACE 76 7 272 159 + *MESH_TFACE 77 159 271 7 + *MESH_TFACE 78 4 262 159 + *MESH_TFACE 79 159 272 4 + *MESH_TFACE 80 4 272 160 + *MESH_TFACE 81 160 264 4 + *MESH_TFACE 82 7 273 160 + *MESH_TFACE 83 160 272 7 + *MESH_TFACE 84 8 274 160 + *MESH_TFACE 85 160 273 8 + *MESH_TFACE 86 5 264 160 + *MESH_TFACE 87 160 274 5 + *MESH_TFACE 88 5 274 161 + *MESH_TFACE 89 161 266 5 + *MESH_TFACE 90 8 275 161 + *MESH_TFACE 91 161 274 8 + *MESH_TFACE 92 108 447 161 + *MESH_TFACE 93 161 275 108 + *MESH_TFACE 94 106 266 161 + *MESH_TFACE 95 161 447 106 + *MESH_TFACE 96 107 448 162 + *MESH_TFACE 97 162 269 107 + *MESH_TFACE 98 109 449 162 + *MESH_TFACE 99 162 448 109 + *MESH_TFACE 100 110 279 162 + *MESH_TFACE 101 162 449 110 + *MESH_TFACE 102 6 269 162 + *MESH_TFACE 103 162 279 6 + *MESH_TFACE 104 6 279 163 + *MESH_TFACE 105 163 271 6 + *MESH_TFACE 106 110 450 163 + *MESH_TFACE 107 163 279 110 + *MESH_TFACE 108 111 281 163 + *MESH_TFACE 109 163 450 111 + *MESH_TFACE 110 7 271 163 + *MESH_TFACE 111 163 281 7 + *MESH_TFACE 112 7 281 164 + *MESH_TFACE 113 164 273 7 + *MESH_TFACE 114 111 451 164 + *MESH_TFACE 115 164 281 111 + *MESH_TFACE 116 112 283 164 + *MESH_TFACE 117 164 451 112 + *MESH_TFACE 118 8 273 164 + *MESH_TFACE 119 164 283 8 + *MESH_TFACE 120 8 283 165 + *MESH_TFACE 121 165 275 8 + *MESH_TFACE 122 112 452 165 + *MESH_TFACE 123 165 283 112 + *MESH_TFACE 124 113 453 165 + *MESH_TFACE 125 165 452 113 + *MESH_TFACE 126 108 275 165 + *MESH_TFACE 127 165 453 108 + *MESH_TFACE 128 114 454 166 + *MESH_TFACE 129 166 455 114 + *MESH_TFACE 130 115 287 166 + *MESH_TFACE 131 166 454 115 + *MESH_TFACE 132 9 288 166 + *MESH_TFACE 133 166 287 9 + *MESH_TFACE 134 119 455 166 + *MESH_TFACE 135 166 288 119 + *MESH_TFACE 136 115 456 167 + *MESH_TFACE 137 167 287 115 + *MESH_TFACE 138 116 291 167 + *MESH_TFACE 139 167 456 116 + *MESH_TFACE 140 10 292 167 + *MESH_TFACE 141 167 291 10 + *MESH_TFACE 142 9 287 167 + *MESH_TFACE 143 167 292 9 + *MESH_TFACE 144 116 457 168 + *MESH_TFACE 145 168 291 116 + *MESH_TFACE 146 117 294 168 + *MESH_TFACE 147 168 457 117 + *MESH_TFACE 148 11 295 168 + *MESH_TFACE 149 168 294 11 + *MESH_TFACE 150 10 291 168 + *MESH_TFACE 151 168 295 10 + *MESH_TFACE 152 117 458 169 + *MESH_TFACE 153 169 294 117 + *MESH_TFACE 154 118 459 169 + *MESH_TFACE 155 169 458 118 + *MESH_TFACE 156 120 298 169 + *MESH_TFACE 157 169 459 120 + *MESH_TFACE 158 11 294 169 + *MESH_TFACE 159 169 298 11 + *MESH_TFACE 160 119 288 170 + *MESH_TFACE 161 170 460 119 + *MESH_TFACE 162 9 299 170 + *MESH_TFACE 163 170 288 9 + *MESH_TFACE 164 12 300 170 + *MESH_TFACE 165 170 299 12 + *MESH_TFACE 166 121 460 170 + *MESH_TFACE 167 170 300 121 + *MESH_TFACE 168 9 292 171 + *MESH_TFACE 169 171 299 9 + *MESH_TFACE 170 10 302 171 + *MESH_TFACE 171 171 292 10 + *MESH_TFACE 172 13 303 171 + *MESH_TFACE 173 171 302 13 + *MESH_TFACE 174 12 299 171 + *MESH_TFACE 175 171 303 12 + *MESH_TFACE 176 10 295 172 + *MESH_TFACE 177 172 302 10 + *MESH_TFACE 178 11 304 172 + *MESH_TFACE 179 172 295 11 + *MESH_TFACE 180 14 305 172 + *MESH_TFACE 181 172 304 14 + *MESH_TFACE 182 13 302 172 + *MESH_TFACE 183 172 305 13 + *MESH_TFACE 184 11 298 173 + *MESH_TFACE 185 173 304 11 + *MESH_TFACE 186 120 461 173 + *MESH_TFACE 187 173 298 120 + *MESH_TFACE 188 122 307 173 + *MESH_TFACE 189 173 461 122 + *MESH_TFACE 190 14 304 173 + *MESH_TFACE 191 173 307 14 + *MESH_TFACE 192 121 300 174 + *MESH_TFACE 193 174 462 121 + *MESH_TFACE 194 12 308 174 + *MESH_TFACE 195 174 300 12 + *MESH_TFACE 196 15 309 174 + *MESH_TFACE 197 174 308 15 + *MESH_TFACE 198 123 462 174 + *MESH_TFACE 199 174 309 123 + *MESH_TFACE 200 12 303 175 + *MESH_TFACE 201 175 308 12 + *MESH_TFACE 202 13 311 175 + *MESH_TFACE 203 175 303 13 + *MESH_TFACE 204 16 312 175 + *MESH_TFACE 205 175 311 16 + *MESH_TFACE 206 15 308 175 + *MESH_TFACE 207 175 312 15 + *MESH_TFACE 208 13 305 176 + *MESH_TFACE 209 176 311 13 + *MESH_TFACE 210 14 313 176 + *MESH_TFACE 211 176 305 14 + *MESH_TFACE 212 17 314 176 + *MESH_TFACE 213 176 313 17 + *MESH_TFACE 214 16 311 176 + *MESH_TFACE 215 176 314 16 + *MESH_TFACE 216 14 307 177 + *MESH_TFACE 217 177 313 14 + *MESH_TFACE 218 122 463 177 + *MESH_TFACE 219 177 307 122 + *MESH_TFACE 220 124 316 177 + *MESH_TFACE 221 177 463 124 + *MESH_TFACE 222 17 313 177 + *MESH_TFACE 223 177 316 17 + *MESH_TFACE 224 123 309 178 + *MESH_TFACE 225 178 465 123 + *MESH_TFACE 226 15 317 178 + *MESH_TFACE 227 178 309 15 + *MESH_TFACE 228 126 464 178 + *MESH_TFACE 229 178 317 126 + *MESH_TFACE 230 125 465 178 + *MESH_TFACE 231 178 464 125 + *MESH_TFACE 232 15 312 179 + *MESH_TFACE 233 179 317 15 + *MESH_TFACE 234 16 320 179 + *MESH_TFACE 235 179 312 16 + *MESH_TFACE 236 127 466 179 + *MESH_TFACE 237 179 320 127 + *MESH_TFACE 238 126 317 179 + *MESH_TFACE 239 179 466 126 + *MESH_TFACE 240 16 314 180 + *MESH_TFACE 241 180 320 16 + *MESH_TFACE 242 17 322 180 + *MESH_TFACE 243 180 314 17 + *MESH_TFACE 244 128 467 180 + *MESH_TFACE 245 180 322 128 + *MESH_TFACE 246 127 320 180 + *MESH_TFACE 247 180 467 127 + *MESH_TFACE 248 17 316 181 + *MESH_TFACE 249 181 322 17 + *MESH_TFACE 250 124 468 181 + *MESH_TFACE 251 181 316 124 + *MESH_TFACE 252 129 469 181 + *MESH_TFACE 253 181 468 129 + *MESH_TFACE 254 128 322 181 + *MESH_TFACE 255 181 469 128 + *MESH_TFACE 256 18 249 182 + *MESH_TFACE 257 182 328 18 + *MESH_TFACE 258 19 326 182 + *MESH_TFACE 259 182 249 19 + *MESH_TFACE 260 24 327 182 + *MESH_TFACE 261 182 326 24 + *MESH_TFACE 262 23 328 182 + *MESH_TFACE 263 182 327 23 + *MESH_TFACE 264 19 252 183 + *MESH_TFACE 265 183 326 19 + *MESH_TFACE 266 20 329 183 + *MESH_TFACE 267 183 252 20 + *MESH_TFACE 268 25 330 183 + *MESH_TFACE 269 183 329 25 + *MESH_TFACE 270 24 326 183 + *MESH_TFACE 271 183 330 24 + *MESH_TFACE 272 20 255 184 + *MESH_TFACE 273 184 329 20 + *MESH_TFACE 274 21 331 184 + *MESH_TFACE 275 184 255 21 + *MESH_TFACE 276 26 332 184 + *MESH_TFACE 277 184 331 26 + *MESH_TFACE 278 25 329 184 + *MESH_TFACE 279 184 332 25 + *MESH_TFACE 280 21 258 185 + *MESH_TFACE 281 185 331 21 + *MESH_TFACE 282 22 333 185 + *MESH_TFACE 283 185 258 22 + *MESH_TFACE 284 27 334 185 + *MESH_TFACE 285 185 333 27 + *MESH_TFACE 286 26 331 185 + *MESH_TFACE 287 185 334 26 + *MESH_TFACE 288 23 327 186 + *MESH_TFACE 289 186 337 23 + *MESH_TFACE 290 24 335 186 + *MESH_TFACE 291 186 327 24 + *MESH_TFACE 292 29 336 186 + *MESH_TFACE 293 186 335 29 + *MESH_TFACE 294 28 337 186 + *MESH_TFACE 295 186 336 28 + *MESH_TFACE 296 24 330 187 + *MESH_TFACE 297 187 335 24 + *MESH_TFACE 298 25 338 187 + *MESH_TFACE 299 187 330 25 + *MESH_TFACE 300 30 339 187 + *MESH_TFACE 301 187 338 30 + *MESH_TFACE 302 29 335 187 + *MESH_TFACE 303 187 339 29 + *MESH_TFACE 304 25 332 188 + *MESH_TFACE 305 188 338 25 + *MESH_TFACE 306 26 340 188 + *MESH_TFACE 307 188 332 26 + *MESH_TFACE 308 31 341 188 + *MESH_TFACE 309 188 340 31 + *MESH_TFACE 310 30 338 188 + *MESH_TFACE 311 188 341 30 + *MESH_TFACE 312 26 334 189 + *MESH_TFACE 313 189 340 26 + *MESH_TFACE 314 27 342 189 + *MESH_TFACE 315 189 334 27 + *MESH_TFACE 316 32 343 189 + *MESH_TFACE 317 189 342 32 + *MESH_TFACE 318 31 340 189 + *MESH_TFACE 319 189 343 31 + *MESH_TFACE 320 28 336 190 + *MESH_TFACE 321 190 346 28 + *MESH_TFACE 322 29 344 190 + *MESH_TFACE 323 190 336 29 + *MESH_TFACE 324 34 345 190 + *MESH_TFACE 325 190 344 34 + *MESH_TFACE 326 33 346 190 + *MESH_TFACE 327 190 345 33 + *MESH_TFACE 328 29 339 191 + *MESH_TFACE 329 191 344 29 + *MESH_TFACE 330 30 347 191 + *MESH_TFACE 331 191 339 30 + *MESH_TFACE 332 35 348 191 + *MESH_TFACE 333 191 347 35 + *MESH_TFACE 334 34 344 191 + *MESH_TFACE 335 191 348 34 + *MESH_TFACE 336 30 341 192 + *MESH_TFACE 337 192 347 30 + *MESH_TFACE 338 31 349 192 + *MESH_TFACE 339 192 341 31 + *MESH_TFACE 340 36 350 192 + *MESH_TFACE 341 192 349 36 + *MESH_TFACE 342 35 347 192 + *MESH_TFACE 343 192 350 35 + *MESH_TFACE 344 31 343 193 + *MESH_TFACE 345 193 349 31 + *MESH_TFACE 346 32 351 193 + *MESH_TFACE 347 193 343 32 + *MESH_TFACE 348 37 352 193 + *MESH_TFACE 349 193 351 37 + *MESH_TFACE 350 36 349 193 + *MESH_TFACE 351 193 352 36 + *MESH_TFACE 352 33 345 194 + *MESH_TFACE 353 194 354 33 + *MESH_TFACE 354 34 353 194 + *MESH_TFACE 355 194 345 34 + *MESH_TFACE 356 39 286 194 + *MESH_TFACE 357 194 353 39 + *MESH_TFACE 358 38 354 194 + *MESH_TFACE 359 194 286 38 + *MESH_TFACE 360 34 348 195 + *MESH_TFACE 361 195 353 34 + *MESH_TFACE 362 35 355 195 + *MESH_TFACE 363 195 348 35 + *MESH_TFACE 364 40 290 195 + *MESH_TFACE 365 195 355 40 + *MESH_TFACE 366 39 353 195 + *MESH_TFACE 367 195 290 39 + *MESH_TFACE 368 35 350 196 + *MESH_TFACE 369 196 355 35 + *MESH_TFACE 370 36 356 196 + *MESH_TFACE 371 196 350 36 + *MESH_TFACE 372 41 293 196 + *MESH_TFACE 373 196 356 41 + *MESH_TFACE 374 40 355 196 + *MESH_TFACE 375 196 293 40 + *MESH_TFACE 376 36 352 197 + *MESH_TFACE 377 197 356 36 + *MESH_TFACE 378 37 357 197 + *MESH_TFACE 379 197 352 37 + *MESH_TFACE 380 42 296 197 + *MESH_TFACE 381 197 357 42 + *MESH_TFACE 382 41 356 197 + *MESH_TFACE 383 197 296 41 + *MESH_TFACE 384 131 257 198 + *MESH_TFACE 385 198 471 131 + *MESH_TFACE 386 43 358 198 + *MESH_TFACE 387 198 257 43 + *MESH_TFACE 388 46 359 198 + *MESH_TFACE 389 198 358 46 + *MESH_TFACE 390 139 471 198 + *MESH_TFACE 391 198 359 139 + *MESH_TFACE 392 43 267 199 + *MESH_TFACE 393 199 358 43 + *MESH_TFACE 394 44 360 199 + *MESH_TFACE 395 199 267 44 + *MESH_TFACE 396 47 361 199 + *MESH_TFACE 397 199 360 47 + *MESH_TFACE 398 46 358 199 + *MESH_TFACE 399 199 361 46 + *MESH_TFACE 400 44 276 200 + *MESH_TFACE 401 200 360 44 + *MESH_TFACE 402 45 362 200 + *MESH_TFACE 403 200 276 45 + *MESH_TFACE 404 48 363 200 + *MESH_TFACE 405 200 362 48 + *MESH_TFACE 406 47 360 200 + *MESH_TFACE 407 200 363 47 + *MESH_TFACE 408 45 285 201 + *MESH_TFACE 409 201 362 45 + *MESH_TFACE 410 133 478 201 + *MESH_TFACE 411 201 285 133 + *MESH_TFACE 412 140 365 201 + *MESH_TFACE 413 201 478 140 + *MESH_TFACE 414 48 362 201 + *MESH_TFACE 415 201 365 48 + *MESH_TFACE 416 139 359 202 + *MESH_TFACE 417 202 473 139 + *MESH_TFACE 418 46 366 202 + *MESH_TFACE 419 202 359 46 + *MESH_TFACE 420 49 367 202 + *MESH_TFACE 421 202 366 49 + *MESH_TFACE 422 143 473 202 + *MESH_TFACE 423 202 367 143 + *MESH_TFACE 424 46 361 203 + *MESH_TFACE 425 203 366 46 + *MESH_TFACE 426 47 368 203 + *MESH_TFACE 427 203 361 47 + *MESH_TFACE 428 50 369 203 + *MESH_TFACE 429 203 368 50 + *MESH_TFACE 430 49 366 203 + *MESH_TFACE 431 203 369 49 + *MESH_TFACE 432 47 363 204 + *MESH_TFACE 433 204 368 47 + *MESH_TFACE 434 48 370 204 + *MESH_TFACE 435 204 363 48 + *MESH_TFACE 436 51 371 204 + *MESH_TFACE 437 204 370 51 + *MESH_TFACE 438 50 368 204 + *MESH_TFACE 439 204 371 50 + *MESH_TFACE 440 48 365 205 + *MESH_TFACE 441 205 370 48 + *MESH_TFACE 442 140 479 205 + *MESH_TFACE 443 205 365 140 + *MESH_TFACE 444 144 373 205 + *MESH_TFACE 445 205 479 144 + *MESH_TFACE 446 51 370 205 + *MESH_TFACE 447 205 373 51 + *MESH_TFACE 448 143 367 206 + *MESH_TFACE 449 206 475 143 + *MESH_TFACE 450 49 374 206 + *MESH_TFACE 451 206 367 49 + *MESH_TFACE 452 52 375 206 + *MESH_TFACE 453 206 374 52 + *MESH_TFACE 454 147 475 206 + *MESH_TFACE 455 206 375 147 + *MESH_TFACE 456 49 369 207 + *MESH_TFACE 457 207 374 49 + *MESH_TFACE 458 50 376 207 + *MESH_TFACE 459 207 369 50 + *MESH_TFACE 460 53 377 207 + *MESH_TFACE 461 207 376 53 + *MESH_TFACE 462 52 374 207 + *MESH_TFACE 463 207 377 52 + *MESH_TFACE 464 50 371 208 + *MESH_TFACE 465 208 376 50 + *MESH_TFACE 466 51 378 208 + *MESH_TFACE 467 208 371 51 + *MESH_TFACE 468 54 379 208 + *MESH_TFACE 469 208 378 54 + *MESH_TFACE 470 53 376 208 + *MESH_TFACE 471 208 379 53 + *MESH_TFACE 472 51 373 209 + *MESH_TFACE 473 209 378 51 + *MESH_TFACE 474 144 480 209 + *MESH_TFACE 475 209 373 144 + *MESH_TFACE 476 148 381 209 + *MESH_TFACE 477 209 480 148 + *MESH_TFACE 478 54 378 209 + *MESH_TFACE 479 209 381 54 + *MESH_TFACE 480 147 375 210 + *MESH_TFACE 481 210 477 147 + *MESH_TFACE 482 52 382 210 + *MESH_TFACE 483 210 375 52 + *MESH_TFACE 484 55 297 210 + *MESH_TFACE 485 210 382 55 + *MESH_TFACE 486 135 477 210 + *MESH_TFACE 487 210 297 135 + *MESH_TFACE 488 52 377 211 + *MESH_TFACE 489 211 382 52 + *MESH_TFACE 490 53 383 211 + *MESH_TFACE 491 211 377 53 + *MESH_TFACE 492 56 306 211 + *MESH_TFACE 493 211 383 56 + *MESH_TFACE 494 55 382 211 + *MESH_TFACE 495 211 306 55 + *MESH_TFACE 496 53 379 212 + *MESH_TFACE 497 212 383 53 + *MESH_TFACE 498 54 384 212 + *MESH_TFACE 499 212 379 54 + *MESH_TFACE 500 57 315 212 + *MESH_TFACE 501 212 384 57 + *MESH_TFACE 502 56 383 212 + *MESH_TFACE 503 212 315 56 + *MESH_TFACE 504 54 381 213 + *MESH_TFACE 505 213 384 54 + *MESH_TFACE 506 148 481 213 + *MESH_TFACE 507 213 381 148 + *MESH_TFACE 508 137 324 213 + *MESH_TFACE 509 213 481 137 + *MESH_TFACE 510 57 384 213 + *MESH_TFACE 511 213 324 57 + *MESH_TFACE 512 58 284 214 + *MESH_TFACE 513 214 364 58 + *MESH_TFACE 514 59 386 214 + *MESH_TFACE 515 214 284 59 + *MESH_TFACE 516 64 387 214 + *MESH_TFACE 517 214 386 64 + *MESH_TFACE 518 63 364 214 + *MESH_TFACE 519 214 387 63 + *MESH_TFACE 520 59 282 215 + *MESH_TFACE 521 215 386 59 + *MESH_TFACE 522 60 388 215 + *MESH_TFACE 523 215 282 60 + *MESH_TFACE 524 65 389 215 + *MESH_TFACE 525 215 388 65 + *MESH_TFACE 526 64 386 215 + *MESH_TFACE 527 215 389 64 + *MESH_TFACE 528 60 280 216 + *MESH_TFACE 529 216 388 60 + *MESH_TFACE 530 61 390 216 + *MESH_TFACE 531 216 280 61 + *MESH_TFACE 532 66 391 216 + *MESH_TFACE 533 216 390 66 + *MESH_TFACE 534 65 388 216 + *MESH_TFACE 535 216 391 65 + *MESH_TFACE 536 61 278 217 + *MESH_TFACE 537 217 390 61 + *MESH_TFACE 538 62 392 217 + *MESH_TFACE 539 217 278 62 + *MESH_TFACE 540 67 393 217 + *MESH_TFACE 541 217 392 67 + *MESH_TFACE 542 66 390 217 + *MESH_TFACE 543 217 393 66 + *MESH_TFACE 544 63 387 218 + *MESH_TFACE 545 218 372 63 + *MESH_TFACE 546 64 394 218 + *MESH_TFACE 547 218 387 64 + *MESH_TFACE 548 69 395 218 + *MESH_TFACE 549 218 394 69 + *MESH_TFACE 550 68 372 218 + *MESH_TFACE 551 218 395 68 + *MESH_TFACE 552 64 389 219 + *MESH_TFACE 553 219 394 64 + *MESH_TFACE 554 65 396 219 + *MESH_TFACE 555 219 389 65 + *MESH_TFACE 556 70 397 219 + *MESH_TFACE 557 219 396 70 + *MESH_TFACE 558 69 394 219 + *MESH_TFACE 559 219 397 69 + *MESH_TFACE 560 65 391 220 + *MESH_TFACE 561 220 396 65 + *MESH_TFACE 562 66 398 220 + *MESH_TFACE 563 220 391 66 + *MESH_TFACE 564 71 399 220 + *MESH_TFACE 565 220 398 71 + *MESH_TFACE 566 70 396 220 + *MESH_TFACE 567 220 399 70 + *MESH_TFACE 568 66 393 221 + *MESH_TFACE 569 221 398 66 + *MESH_TFACE 570 67 400 221 + *MESH_TFACE 571 221 393 67 + *MESH_TFACE 572 72 401 221 + *MESH_TFACE 573 221 400 72 + *MESH_TFACE 574 71 398 221 + *MESH_TFACE 575 221 401 71 + *MESH_TFACE 576 68 395 222 + *MESH_TFACE 577 222 380 68 + *MESH_TFACE 578 69 402 222 + *MESH_TFACE 579 222 395 69 + *MESH_TFACE 580 74 403 222 + *MESH_TFACE 581 222 402 74 + *MESH_TFACE 582 73 380 222 + *MESH_TFACE 583 222 403 73 + *MESH_TFACE 584 69 397 223 + *MESH_TFACE 585 223 402 69 + *MESH_TFACE 586 70 404 223 + *MESH_TFACE 587 223 397 70 + *MESH_TFACE 588 75 405 223 + *MESH_TFACE 589 223 404 75 + *MESH_TFACE 590 74 402 223 + *MESH_TFACE 591 223 405 74 + *MESH_TFACE 592 70 399 224 + *MESH_TFACE 593 224 404 70 + *MESH_TFACE 594 71 406 224 + *MESH_TFACE 595 224 399 71 + *MESH_TFACE 596 76 407 224 + *MESH_TFACE 597 224 406 76 + *MESH_TFACE 598 75 404 224 + *MESH_TFACE 599 224 407 75 + *MESH_TFACE 600 71 401 225 + *MESH_TFACE 601 225 406 71 + *MESH_TFACE 602 72 408 225 + *MESH_TFACE 603 225 401 72 + *MESH_TFACE 604 77 409 225 + *MESH_TFACE 605 225 408 77 + *MESH_TFACE 606 76 406 225 + *MESH_TFACE 607 225 409 76 + *MESH_TFACE 608 73 403 226 + *MESH_TFACE 609 226 385 73 + *MESH_TFACE 610 74 410 226 + *MESH_TFACE 611 226 403 74 + *MESH_TFACE 612 79 325 226 + *MESH_TFACE 613 226 410 79 + *MESH_TFACE 614 78 385 226 + *MESH_TFACE 615 226 325 78 + *MESH_TFACE 616 74 405 227 + *MESH_TFACE 617 227 410 74 + *MESH_TFACE 618 75 411 227 + *MESH_TFACE 619 227 405 75 + *MESH_TFACE 620 80 323 227 + *MESH_TFACE 621 227 411 80 + *MESH_TFACE 622 79 410 227 + *MESH_TFACE 623 227 323 79 + *MESH_TFACE 624 75 407 228 + *MESH_TFACE 625 228 411 75 + *MESH_TFACE 626 76 412 228 + *MESH_TFACE 627 228 407 76 + *MESH_TFACE 628 81 321 228 + *MESH_TFACE 629 228 412 81 + *MESH_TFACE 630 80 411 228 + *MESH_TFACE 631 228 321 80 + *MESH_TFACE 632 76 409 229 + *MESH_TFACE 633 229 412 76 + *MESH_TFACE 634 77 413 229 + *MESH_TFACE 635 229 409 77 + *MESH_TFACE 636 82 318 229 + *MESH_TFACE 637 229 413 82 + *MESH_TFACE 638 81 412 229 + *MESH_TFACE 639 229 318 81 + *MESH_TFACE 640 132 277 230 + *MESH_TFACE 641 230 482 132 + *MESH_TFACE 642 83 414 230 + *MESH_TFACE 643 230 277 83 + *MESH_TFACE 644 86 415 230 + *MESH_TFACE 645 230 414 86 + *MESH_TFACE 646 141 482 230 + *MESH_TFACE 647 230 415 141 + *MESH_TFACE 648 83 268 231 + *MESH_TFACE 649 231 414 83 + *MESH_TFACE 650 84 416 231 + *MESH_TFACE 651 231 268 84 + *MESH_TFACE 652 87 417 231 + *MESH_TFACE 653 231 416 87 + *MESH_TFACE 654 86 414 231 + *MESH_TFACE 655 231 417 86 + *MESH_TFACE 656 84 259 232 + *MESH_TFACE 657 232 416 84 + *MESH_TFACE 658 85 418 232 + *MESH_TFACE 659 232 259 85 + *MESH_TFACE 660 88 419 232 + *MESH_TFACE 661 232 418 88 + *MESH_TFACE 662 87 416 232 + *MESH_TFACE 663 232 419 87 + *MESH_TFACE 664 85 246 233 + *MESH_TFACE 665 233 418 85 + *MESH_TFACE 666 130 470 233 + *MESH_TFACE 667 233 246 130 + *MESH_TFACE 668 138 420 233 + *MESH_TFACE 669 233 470 138 + *MESH_TFACE 670 88 418 233 + *MESH_TFACE 671 233 420 88 + *MESH_TFACE 672 141 415 234 + *MESH_TFACE 673 234 483 141 + *MESH_TFACE 674 86 421 234 + *MESH_TFACE 675 234 415 86 + *MESH_TFACE 676 89 422 234 + *MESH_TFACE 677 234 421 89 + *MESH_TFACE 678 145 483 234 + *MESH_TFACE 679 234 422 145 + *MESH_TFACE 680 86 417 235 + *MESH_TFACE 681 235 421 86 + *MESH_TFACE 682 87 423 235 + *MESH_TFACE 683 235 417 87 + *MESH_TFACE 684 90 424 235 + *MESH_TFACE 685 235 423 90 + *MESH_TFACE 686 89 421 235 + *MESH_TFACE 687 235 424 89 + *MESH_TFACE 688 87 419 236 + *MESH_TFACE 689 236 423 87 + *MESH_TFACE 690 88 425 236 + *MESH_TFACE 691 236 419 88 + *MESH_TFACE 692 91 426 236 + *MESH_TFACE 693 236 425 91 + *MESH_TFACE 694 90 423 236 + *MESH_TFACE 695 236 426 90 + *MESH_TFACE 696 88 420 237 + *MESH_TFACE 697 237 425 88 + *MESH_TFACE 698 138 472 237 + *MESH_TFACE 699 237 420 138 + *MESH_TFACE 700 142 427 237 + *MESH_TFACE 701 237 472 142 + *MESH_TFACE 702 91 425 237 + *MESH_TFACE 703 237 427 91 + *MESH_TFACE 704 145 422 238 + *MESH_TFACE 705 238 484 145 + *MESH_TFACE 706 89 428 238 + *MESH_TFACE 707 238 422 89 + *MESH_TFACE 708 92 429 238 + *MESH_TFACE 709 238 428 92 + *MESH_TFACE 710 149 484 238 + *MESH_TFACE 711 238 429 149 + *MESH_TFACE 712 89 424 239 + *MESH_TFACE 713 239 428 89 + *MESH_TFACE 714 90 430 239 + *MESH_TFACE 715 239 424 90 + *MESH_TFACE 716 93 431 239 + *MESH_TFACE 717 239 430 93 + *MESH_TFACE 718 92 428 239 + *MESH_TFACE 719 239 431 92 + *MESH_TFACE 720 90 426 240 + *MESH_TFACE 721 240 430 90 + *MESH_TFACE 722 91 432 240 + *MESH_TFACE 723 240 426 91 + *MESH_TFACE 724 94 433 240 + *MESH_TFACE 725 240 432 94 + *MESH_TFACE 726 93 430 240 + *MESH_TFACE 727 240 433 93 + *MESH_TFACE 728 91 427 241 + *MESH_TFACE 729 241 432 91 + *MESH_TFACE 730 142 474 241 + *MESH_TFACE 731 241 427 142 + *MESH_TFACE 732 146 434 241 + *MESH_TFACE 733 241 474 146 + *MESH_TFACE 734 94 432 241 + *MESH_TFACE 735 241 434 94 + *MESH_TFACE 736 149 429 242 + *MESH_TFACE 737 242 485 149 + *MESH_TFACE 738 92 435 242 + *MESH_TFACE 739 242 429 92 + *MESH_TFACE 740 95 319 242 + *MESH_TFACE 741 242 435 95 + *MESH_TFACE 742 136 485 242 + *MESH_TFACE 743 242 319 136 + *MESH_TFACE 744 92 431 243 + *MESH_TFACE 745 243 435 92 + *MESH_TFACE 746 93 436 243 + *MESH_TFACE 747 243 431 93 + *MESH_TFACE 748 96 310 243 + *MESH_TFACE 749 243 436 96 + *MESH_TFACE 750 95 435 243 + *MESH_TFACE 751 243 310 95 + *MESH_TFACE 752 93 433 244 + *MESH_TFACE 753 244 436 93 + *MESH_TFACE 754 94 437 244 + *MESH_TFACE 755 244 433 94 + *MESH_TFACE 756 97 301 244 + *MESH_TFACE 757 244 437 97 + *MESH_TFACE 758 96 436 244 + *MESH_TFACE 759 244 301 96 + *MESH_TFACE 760 94 434 245 + *MESH_TFACE 761 245 437 94 + *MESH_TFACE 762 146 476 245 + *MESH_TFACE 763 245 434 146 + *MESH_TFACE 764 134 289 245 + *MESH_TFACE 765 245 476 134 + *MESH_TFACE 766 97 437 245 + *MESH_TFACE 767 245 289 97 + } + *MESH_NORMALS { + *MESH_FACENORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 98 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 438 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 150 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 1 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 150 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 439 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 98 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 2 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 103 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 247 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 150 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 3 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 150 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 438 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 103 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 4 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 248 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 150 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 5 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 150 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 247 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 99 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 439 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 150 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 150 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 248 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 99 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 99 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 248 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 151 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 9 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 151 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 440 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 99 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 10 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 250 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 151 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 151 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 248 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 251 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 151 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 151 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 250 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 14 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 100 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 440 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 151 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 15 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 151 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 251 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 100 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 100 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 251 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 152 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 152 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 441 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 100 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 253 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 152 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 19 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 152 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 251 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 20 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 254 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 152 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 21 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 152 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 253 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 22 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 101 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 441 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 152 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 23 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 152 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 254 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 101 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 24 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 101 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 254 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 153 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 25 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 153 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 443 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 101 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 26 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 256 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 153 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 27 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 153 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 254 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 28 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 104 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 442 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 153 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 29 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 153 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 256 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 104 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 30 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 102 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 443 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 153 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 31 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 153 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 442 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 102 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 32 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 103 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 444 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 154 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 33 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 154 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 247 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 103 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 34 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 105 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 260 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 154 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 35 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 154 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 444 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 105 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 36 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 261 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 154 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 37 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 154 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 260 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 38 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 247 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 154 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 39 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 154 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 261 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 40 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 261 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 155 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 41 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 155 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 250 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 42 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 262 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 155 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 43 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 155 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 261 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 44 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 263 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 155 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 45 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 155 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 262 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 46 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 250 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 155 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 47 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 155 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 263 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 48 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 263 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 156 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 49 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 156 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 253 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 50 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 264 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 156 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 51 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 156 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 263 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 52 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 265 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 156 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 53 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 156 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 264 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 54 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 253 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 156 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 55 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 156 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 265 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 56 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 265 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 157 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 57 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 157 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 256 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 58 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 266 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 157 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 59 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 157 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 265 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 60 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 106 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 445 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 157 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 61 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 157 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 266 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 106 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 62 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 104 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 256 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 157 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 63 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 157 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 445 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 104 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 64 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 105 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 446 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 158 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 65 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 158 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 260 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 105 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 66 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 107 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 269 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 158 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 67 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 158 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 446 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 107 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 68 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 270 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 158 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 69 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 158 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 269 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 70 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 260 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 158 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 71 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 158 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 270 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 72 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 270 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 159 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 73 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 159 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 262 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 74 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 271 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 159 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 75 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 159 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 270 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 76 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 272 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 159 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 77 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 159 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 271 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 78 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 262 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 159 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 79 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 159 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 272 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 80 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 272 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 160 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 81 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 160 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 264 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 82 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 273 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 160 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 83 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 160 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 272 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 84 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 274 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 160 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 85 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 160 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 273 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 86 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 264 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 160 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 87 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 160 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 274 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 88 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 274 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 161 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 89 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 161 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 266 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 90 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 275 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 161 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 91 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 161 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 274 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 92 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 108 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 447 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 161 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 93 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 161 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 275 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 108 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 94 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 106 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 266 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 161 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 95 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 161 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 447 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 106 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 96 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 107 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 448 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 162 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 97 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 162 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 269 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 107 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 98 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 109 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 449 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 162 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 99 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 162 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 448 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 109 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 100 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 110 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 279 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 162 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 101 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 162 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 449 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 110 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 102 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 269 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 162 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 103 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 162 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 279 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 104 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 279 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 163 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 105 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 163 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 271 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 106 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 110 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 450 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 163 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 107 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 163 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 279 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 110 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 108 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 111 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 281 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 163 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 109 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 163 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 450 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 111 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 110 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 271 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 163 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 111 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 163 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 281 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 112 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 281 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 164 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 113 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 164 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 273 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 114 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 111 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 451 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 164 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 115 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 164 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 281 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 111 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 116 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 112 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 283 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 164 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 117 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 164 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 451 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 112 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 118 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 273 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 164 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 119 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 164 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 283 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 120 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 283 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 165 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 121 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 165 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 275 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 122 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 112 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 452 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 165 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 123 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 165 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 283 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 112 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 124 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 113 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 453 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 165 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 125 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 165 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 452 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 113 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 126 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 108 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 275 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 165 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 127 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 165 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 453 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 108 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 128 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 114 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 454 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 166 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 129 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 166 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 455 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 114 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 130 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 115 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 287 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 166 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 131 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 166 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 454 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 115 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 132 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 288 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 166 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 133 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 166 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 287 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 134 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 119 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 455 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 166 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 135 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 166 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 288 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 119 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 136 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 115 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 456 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 167 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 137 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 167 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 287 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 115 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 138 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 116 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 291 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 167 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 139 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 167 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 456 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 116 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 140 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 292 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 167 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 141 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 167 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 291 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 142 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 287 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 167 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 143 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 167 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 292 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 144 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 116 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 457 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 168 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 145 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 168 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 291 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 116 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 146 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 117 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 294 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 168 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 147 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 168 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 457 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 117 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 148 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 295 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 168 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 149 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 168 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 294 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 150 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 291 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 168 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 151 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 168 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 295 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 152 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 117 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 458 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 169 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 153 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 169 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 294 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 117 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 154 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 118 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 459 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 169 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 155 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 169 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 458 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 118 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 156 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 120 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 298 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 169 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 157 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 169 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 459 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 120 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 158 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 294 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 169 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 159 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 169 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 298 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 160 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 119 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 288 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 170 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 161 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 170 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 460 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 119 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 162 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 299 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 170 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 163 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 170 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 288 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 164 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 300 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 170 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 165 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 170 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 299 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 166 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 121 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 460 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 170 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 167 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 170 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 300 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 121 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 168 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 292 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 171 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 169 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 171 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 299 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 170 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 302 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 171 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 171 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 171 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 292 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 172 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 303 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 171 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 173 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 171 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 302 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 174 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 299 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 171 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 175 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 171 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 303 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 176 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 295 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 172 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 177 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 172 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 302 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 178 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 304 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 172 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 179 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 172 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 295 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 180 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 305 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 172 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 181 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 172 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 304 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 182 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 302 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 172 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 183 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 172 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 305 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 184 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 298 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 173 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 185 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 173 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 304 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 186 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 120 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 461 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 173 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 187 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 173 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 298 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 120 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 188 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 122 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 307 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 173 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 189 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 173 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 461 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 122 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 190 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 304 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 173 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 191 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 173 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 307 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 192 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 121 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 300 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 174 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 193 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 174 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 462 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 121 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 194 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 308 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 174 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 195 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 174 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 300 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 196 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 309 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 174 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 197 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 174 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 308 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 198 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 123 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 462 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 174 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 199 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 174 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 309 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 123 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 200 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 303 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 175 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 201 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 175 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 308 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 202 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 311 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 175 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 203 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 175 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 303 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 204 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 312 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 175 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 205 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 175 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 311 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 206 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 308 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 175 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 207 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 175 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 312 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 208 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 305 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 176 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 209 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 176 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 311 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 210 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 313 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 176 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 211 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 176 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 305 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 212 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 314 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 176 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 213 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 176 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 313 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 214 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 311 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 176 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 215 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 176 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 314 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 216 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 307 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 177 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 217 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 177 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 313 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 218 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 122 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 463 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 177 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 219 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 177 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 307 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 122 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 220 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 124 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 316 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 177 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 221 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 177 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 463 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 124 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 222 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 313 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 177 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 223 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 177 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 316 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 224 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 123 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 309 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 178 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 225 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 178 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 465 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 123 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 226 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 317 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 178 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 227 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 178 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 309 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 228 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 126 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 464 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 178 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 229 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 178 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 317 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 126 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 230 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 125 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 465 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 178 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 231 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 178 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 464 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 125 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 232 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 312 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 179 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 233 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 179 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 317 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 234 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 320 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 179 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 235 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 179 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 312 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 236 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 127 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 466 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 179 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 237 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 179 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 320 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 127 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 238 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 126 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 317 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 179 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 239 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 179 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 466 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 126 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 240 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 314 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 180 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 241 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 180 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 320 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 242 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 322 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 180 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 243 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 180 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 314 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 244 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 128 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 467 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 180 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 245 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 180 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 322 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 128 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 246 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 127 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 320 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 180 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 247 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 180 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 467 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 127 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 248 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 316 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 181 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 249 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 181 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 322 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 250 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 124 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 468 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 181 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 251 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 181 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 316 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 124 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 252 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 129 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 469 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 181 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 253 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 181 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 468 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 129 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 254 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 128 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 322 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 181 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 255 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 181 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 469 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 128 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 256 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 0 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 249 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 182 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 257 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 182 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 328 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 0 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 258 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 326 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 182 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 259 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 182 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 249 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 260 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 327 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 182 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 261 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 182 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 326 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 262 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 50 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 328 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 182 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 263 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 182 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 327 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 50 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 264 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 252 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 183 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 265 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 183 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 326 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 266 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 329 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 183 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 267 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 183 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 252 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 268 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 330 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 183 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 269 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 183 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 329 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 270 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 326 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 183 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 271 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 183 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 330 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 272 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 255 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 184 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 273 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 184 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 329 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 274 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 331 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 184 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 275 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 184 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 255 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 276 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 332 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 184 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 277 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 184 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 331 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 278 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 329 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 184 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 279 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 184 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 332 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 280 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 258 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 185 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 281 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 185 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 331 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 282 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 4 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 333 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 185 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 283 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 185 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 258 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 4 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 284 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 54 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 334 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 185 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 285 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 185 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 333 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 54 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 286 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 331 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 185 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 287 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 185 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 334 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 288 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 50 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 327 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 186 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 289 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 186 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 337 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 50 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 290 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 335 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 186 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 291 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 186 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 327 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 292 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 336 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 186 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 293 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 186 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 335 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 294 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 66 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 337 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 186 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 295 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 186 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 336 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 66 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 296 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 330 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 187 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 297 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 187 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 335 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 298 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 338 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 187 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 299 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 187 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 330 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 300 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 339 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 187 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 301 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 187 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 338 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 302 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 335 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 187 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 303 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 187 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 339 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 304 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 332 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 188 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 305 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 188 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 338 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 306 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 340 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 188 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 307 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 188 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 332 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 308 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 341 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 188 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 309 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 188 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 340 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 310 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 338 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 188 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 311 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 188 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 341 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 312 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 334 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 189 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 313 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 189 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 340 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 314 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 54 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 342 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 189 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 315 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 189 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 334 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 54 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 316 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 70 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 343 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 189 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 317 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 189 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 342 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 70 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 318 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 340 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 189 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 319 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 189 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 343 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 320 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 66 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 336 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 190 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 321 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 190 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 346 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 66 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 322 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 344 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 190 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 323 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 190 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 336 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 324 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 345 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 190 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 325 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 190 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 344 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 326 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 82 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 346 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 190 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 327 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 190 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 345 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 82 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 328 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 339 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 191 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 329 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 191 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 344 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 330 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 347 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 191 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 331 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 191 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 339 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 332 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 348 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 191 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 333 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 191 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 347 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 334 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 344 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 191 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 335 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 191 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 348 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 336 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 341 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 192 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 337 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 192 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 347 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 338 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 349 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 192 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 339 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 192 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 341 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 340 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 350 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 192 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 341 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 192 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 349 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 342 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 347 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 192 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 343 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 192 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 350 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 344 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 343 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 193 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 345 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 193 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 349 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 346 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 70 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 351 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 193 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 347 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 193 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 343 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 70 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 348 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 86 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 352 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 193 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 349 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 193 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 351 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 86 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 350 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 349 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 193 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 351 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 193 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 352 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 352 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 82 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 345 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 194 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 353 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 194 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 354 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 82 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 354 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 353 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 194 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 355 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 194 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 345 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 356 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 26 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 286 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 194 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 357 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 194 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 353 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 26 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 358 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 25 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 354 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 194 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 359 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 194 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 286 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 25 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 360 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 348 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 195 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 361 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 195 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 353 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 362 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 355 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 195 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 363 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 195 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 348 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 364 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 27 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 290 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 195 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 365 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 195 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 355 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 27 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 366 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 26 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 353 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 195 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 367 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 195 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 290 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 26 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 368 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 350 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 196 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 369 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 196 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 355 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 370 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 356 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 196 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 371 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 196 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 350 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 372 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 28 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 293 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 196 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 373 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 196 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 356 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 28 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 374 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 27 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 355 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 196 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 375 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 196 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 293 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 27 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 376 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 352 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 197 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 377 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 197 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 356 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 378 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 86 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 357 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 197 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 379 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 197 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 352 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 86 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 380 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 29 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 296 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 197 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 381 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 197 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 357 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 29 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 382 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 28 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 356 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 197 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 383 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 197 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 296 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 28 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 384 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 131 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 257 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 198 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 385 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 198 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 471 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 131 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 386 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 9 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 358 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 198 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 387 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 198 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 257 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 9 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 388 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 359 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 198 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 389 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 198 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 358 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 390 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 139 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 471 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 198 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 391 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 198 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 359 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 139 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 392 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 9 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 267 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 199 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 393 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 199 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 358 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 9 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 394 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 14 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 360 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 199 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 395 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 199 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 267 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 14 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 396 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 361 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 199 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 397 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 199 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 360 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 398 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 358 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 199 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 399 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 199 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 361 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 400 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 14 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 276 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 200 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 401 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 200 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 360 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 14 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 402 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 19 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 362 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 200 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 403 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 200 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 276 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 19 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 404 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 363 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 200 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 405 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 200 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 362 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 406 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 360 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 200 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 407 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 200 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 363 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 408 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 19 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 285 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 201 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 409 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 201 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 362 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 19 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 410 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 133 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 478 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 201 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 411 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 201 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 285 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 133 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 412 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 140 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 365 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 201 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 413 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 201 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 478 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 140 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 414 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 362 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 201 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 415 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 201 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 365 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 416 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 139 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 359 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 202 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 417 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 202 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 473 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 139 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 418 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 366 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 202 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 419 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 202 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 359 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 420 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 367 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 202 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 421 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 202 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 366 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 422 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 143 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 473 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 202 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 423 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 202 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 367 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 143 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 424 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 361 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 203 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 425 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 203 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 366 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 426 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 368 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 203 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 427 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 203 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 361 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 428 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 369 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 203 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 429 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 203 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 368 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 430 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 366 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 203 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 431 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 203 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 369 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 432 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 363 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 204 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 433 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 204 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 368 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 434 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 370 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 204 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 435 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 204 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 363 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 436 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 371 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 204 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 437 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 204 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 370 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 438 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 368 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 204 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 439 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 204 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 371 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 440 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 365 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 205 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 441 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 205 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 370 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 442 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 140 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 479 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 205 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 443 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 205 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 365 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 140 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 444 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 144 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 373 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 205 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 445 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 205 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 479 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 144 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 446 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 370 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 205 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 447 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 205 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 373 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 448 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 143 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 367 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 206 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 449 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 206 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 475 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 143 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 450 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 374 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 206 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 451 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 206 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 367 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 452 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 375 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 206 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 453 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 206 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 374 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 454 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 147 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 475 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 206 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 455 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 206 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 375 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 147 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 456 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 369 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 207 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 457 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 207 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 374 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 458 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 376 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 207 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 459 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 207 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 369 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 460 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 377 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 207 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 461 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 207 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 376 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 462 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 374 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 207 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 463 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 207 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 377 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 464 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 371 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 208 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 465 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 208 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 376 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 466 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 378 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 208 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 467 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 208 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 371 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 468 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 379 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 208 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 469 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 208 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 378 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 470 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 376 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 208 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 471 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 208 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 379 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 472 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 373 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 209 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 473 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 209 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 378 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 474 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 144 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 480 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 209 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 475 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 209 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 373 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 144 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 476 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 148 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 381 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 209 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 477 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 209 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 480 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 148 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 478 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 378 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 209 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 479 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 209 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 381 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 480 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 147 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 375 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 210 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 481 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 210 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 477 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 147 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 482 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 382 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 210 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 483 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 210 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 375 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 484 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 34 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 297 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 210 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 485 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 210 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 382 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 34 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 486 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 135 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 477 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 210 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 487 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 210 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 297 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 135 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 488 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 377 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 211 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 489 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 211 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 382 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 490 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 383 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 211 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 491 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 211 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 377 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 492 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 39 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 306 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 211 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 493 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 211 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 383 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 39 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 494 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 34 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 382 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 211 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 495 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 211 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 306 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 34 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 496 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 379 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 212 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 497 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 212 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 383 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 498 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 384 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 212 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 499 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 212 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 379 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 500 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 44 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 315 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 212 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 501 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 212 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 384 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 44 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 502 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 39 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 383 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 212 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 503 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 212 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 315 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 39 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 504 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 381 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 213 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 505 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 213 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 384 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 506 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 148 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 481 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 213 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 507 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 213 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 381 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 148 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 508 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 137 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 324 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 213 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 509 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 213 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 481 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 137 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 510 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 44 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 384 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 213 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 511 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 213 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 324 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 44 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 512 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 24 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 284 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 214 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 513 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 214 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 364 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 24 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 514 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 386 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 214 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 515 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 214 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 284 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 516 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 387 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 214 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 517 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 214 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 386 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 518 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 58 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 364 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 214 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 519 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 214 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 387 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 58 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 520 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 282 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 215 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 521 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 215 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 386 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 522 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 388 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 215 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 523 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 215 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 282 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 524 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 389 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 215 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 525 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 215 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 388 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 526 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 386 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 215 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 527 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 215 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 389 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 528 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 280 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 216 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 529 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 216 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 388 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 530 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 21 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 390 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 216 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 531 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 216 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 280 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 21 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 532 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 391 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 216 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 533 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 216 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 390 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 534 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 388 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 216 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 535 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 216 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 391 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 536 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 21 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 278 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 217 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 537 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 217 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 390 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 21 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 538 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 20 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 392 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 217 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 539 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 217 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 278 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 20 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 540 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 62 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 393 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 217 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 541 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 217 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 392 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 62 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 542 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 390 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 217 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 543 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 217 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 393 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 544 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 58 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 387 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 218 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 545 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 218 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 372 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 58 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 546 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 394 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 218 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 547 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 218 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 387 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 548 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 395 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 218 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 549 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 218 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 394 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 550 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 74 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 372 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 218 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 551 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 218 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 395 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 74 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 552 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 389 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 219 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 553 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 219 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 394 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 554 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 396 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 219 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 555 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 219 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 389 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 556 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 397 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 219 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 557 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 219 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 396 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 558 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 394 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 219 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 559 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 219 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 397 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 560 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 391 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 220 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 561 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 220 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 396 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 562 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 398 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 220 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 563 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 220 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 391 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 564 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 399 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 220 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 565 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 220 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 398 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 566 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 396 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 220 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 567 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 220 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 399 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 568 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 393 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 221 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 569 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 221 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 398 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 570 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 62 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 400 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 221 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 571 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 221 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 393 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 62 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 572 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 78 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 401 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 221 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 573 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 221 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 400 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 78 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 574 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 398 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 221 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 575 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 221 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 401 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 576 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 74 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 395 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 222 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 577 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 222 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 380 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 74 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 578 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 402 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 222 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 579 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 222 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 395 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 580 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 403 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 222 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 581 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 222 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 402 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 582 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 90 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 380 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 222 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 583 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 222 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 403 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 90 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 584 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 397 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 223 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 585 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 223 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 402 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 586 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 404 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 223 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 587 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 223 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 397 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 588 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 405 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 223 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 589 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 223 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 404 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 590 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 402 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 223 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 591 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 223 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 405 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 592 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 399 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 224 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 593 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 224 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 404 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 594 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 406 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 224 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 595 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 224 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 399 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 596 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 407 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 224 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 597 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 224 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 406 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 598 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 404 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 224 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 599 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 224 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 407 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 600 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 401 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 225 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 601 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 225 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 406 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 602 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 78 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 408 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 225 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 603 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 225 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 401 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 78 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 604 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 94 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 409 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 225 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 605 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 225 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 408 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 94 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 606 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 406 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 225 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 607 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 225 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 409 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 608 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 90 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 403 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 226 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 609 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 226 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 385 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 90 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 610 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 410 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 226 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 611 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 226 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 403 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 612 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 48 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 325 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 226 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 613 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 226 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 410 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 48 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 614 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 49 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 385 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 226 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 615 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 226 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 325 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 49 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 616 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 405 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 227 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 617 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 227 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 410 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 618 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 411 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 227 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 619 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 227 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 405 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 620 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 47 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 323 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 227 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 621 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 227 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 411 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 47 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 622 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 48 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 410 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 227 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 623 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 227 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 323 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 48 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 624 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 407 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 228 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 625 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 228 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 411 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 626 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 412 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 228 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 627 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 228 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 407 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 628 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 46 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 321 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 228 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 629 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 228 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 412 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 46 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 630 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 47 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 411 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 228 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 631 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 228 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 321 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 47 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 632 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 409 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 229 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 633 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 229 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 412 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 634 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 94 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 413 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 229 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 635 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 229 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 409 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 94 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 636 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 45 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 318 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 229 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 637 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 229 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 413 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 45 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 638 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 46 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 412 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 229 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 639 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 229 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 318 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 46 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 640 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 132 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 277 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 230 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 641 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 230 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 482 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 132 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 642 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 15 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 414 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 230 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 643 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 230 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 277 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 15 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 644 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 415 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 230 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 645 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 230 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 414 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 646 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 141 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 482 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 230 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 647 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 230 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 415 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 141 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 648 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 15 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 268 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 231 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 649 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 231 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 414 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 15 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 650 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 10 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 416 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 231 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 651 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 231 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 268 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 10 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 652 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 417 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 231 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 653 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 231 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 416 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 654 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 414 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 231 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 655 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 231 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 417 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 656 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 10 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 259 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 232 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 657 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 232 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 416 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 10 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 658 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 5 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 418 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 232 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 659 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 232 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 259 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 5 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 660 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 419 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 232 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 661 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 232 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 418 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 662 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 416 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 232 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 663 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 232 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 419 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 664 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 5 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 246 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 233 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 665 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 233 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 418 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 5 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 666 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 130 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 470 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 233 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 667 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 233 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 246 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 130 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 668 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 138 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 420 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 233 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 669 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 233 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 470 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 138 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 670 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 418 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 233 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 671 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 233 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 420 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 672 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 141 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 415 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 234 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 673 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 234 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 483 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 141 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 674 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 421 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 234 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 675 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 234 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 415 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 676 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 422 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 234 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 677 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 234 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 421 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 678 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 145 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 483 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 234 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 679 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 234 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 422 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 145 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 680 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 417 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 235 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 681 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 235 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 421 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 682 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 423 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 235 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 683 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 235 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 417 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 684 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 424 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 235 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 685 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 235 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 423 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 686 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 421 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 235 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 687 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 235 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 424 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 688 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 419 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 236 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 689 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 236 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 423 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 690 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 425 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 236 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 691 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 236 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 419 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 692 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 426 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 236 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 693 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 236 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 425 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 694 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 423 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 236 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 695 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 236 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 426 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 696 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 420 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 237 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 697 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 237 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 425 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 698 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 138 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 472 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 237 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 699 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 237 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 420 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 138 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 700 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 142 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 427 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 237 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 701 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 237 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 472 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 142 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 702 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 425 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 237 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 703 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 237 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 427 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 704 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 145 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 422 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 238 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 705 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 238 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 484 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 145 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 706 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 428 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 238 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 707 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 238 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 422 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 708 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 429 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 238 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 709 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 238 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 428 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 710 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 149 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 484 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 238 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 711 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 238 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 429 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 149 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 712 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 424 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 239 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 713 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 239 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 428 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 714 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 430 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 239 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 715 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 239 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 424 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 716 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 431 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 239 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 717 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 239 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 430 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 718 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 428 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 239 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 719 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 239 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 431 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 720 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 426 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 240 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 721 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 240 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 430 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 722 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 432 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 240 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 723 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 240 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 426 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 724 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 433 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 240 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 725 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 240 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 432 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 726 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 430 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 240 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 727 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 240 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 433 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 728 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 427 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 241 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 729 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 241 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 432 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 730 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 142 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 474 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 241 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 731 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 241 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 427 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 142 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 732 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 146 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 434 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 241 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 733 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 241 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 474 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 146 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 734 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 432 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 241 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 735 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 241 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 434 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 736 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 149 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 429 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 242 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 737 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 242 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 485 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 149 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 738 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 435 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 242 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 739 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 242 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 429 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 740 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 40 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 319 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 242 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 741 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 242 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 435 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 40 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 742 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 136 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 485 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 242 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 743 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 242 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 319 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 136 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 744 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 431 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 243 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 745 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 243 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 435 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 746 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 436 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 243 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 747 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 243 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 431 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 748 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 35 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 310 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 243 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 749 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 243 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 436 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 35 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 750 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 40 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 435 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 243 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 751 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 243 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 310 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 40 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 752 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 433 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 244 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 753 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 244 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 436 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 754 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 437 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 244 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 755 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 244 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 433 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 756 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 30 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 301 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 244 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 757 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 244 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 437 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 30 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 758 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 35 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 436 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 244 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 759 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 244 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 301 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 35 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 760 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 434 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 245 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 761 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 245 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 437 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 762 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 146 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 476 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 245 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 763 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 245 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 434 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 146 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 764 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 134 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 289 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 245 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 765 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 245 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 476 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 134 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 766 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 30 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 437 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 245 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 767 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 245 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 289 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 30 -1.0000 0.0000 0.0000 + } + } + *PROP_MOTIONBLUR 0 + *PROP_CASTSHADOW 1 + *PROP_RECVSHADOW 1 + *MATERIAL_REF 1 +} diff --git a/base/models/materialeditor/box32x128.ase b/base/models/materialeditor/box32x128.ase new file mode 100644 index 000000000..d947af481 --- /dev/null +++ b/base/models/materialeditor/box32x128.ase @@ -0,0 +1,1650 @@ +*3DSMAX_ASCIIEXPORT 200 +*COMMENT "AsciiExport Version 2.00 - Tue Dec 09 14:02:46 2003" +*SCENE { + *SCENE_FILENAME "t.max" + *SCENE_FIRSTFRAME 0 + *SCENE_LASTFRAME 300 + *SCENE_FRAMESPEED 30 + *SCENE_TICKSPERFRAME 160 + *SCENE_BACKGROUND_STATIC 0.0000 0.0000 0.0000 + *SCENE_AMBIENT_STATIC 0.0000 0.0000 0.0000 +} +*MATERIAL_LIST { + *MATERIAL_COUNT 6 + *MATERIAL 0 { + *MATERIAL_NAME "1 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4706 0.4706 0.4706 + *MATERIAL_DIFFUSE 0.4706 0.4706 0.4706 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 1 { + *MATERIAL_NAME "9 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5882 0.5882 0.5882 + *MATERIAL_DIFFUSE 0.5882 0.5882 0.5882 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + *MAP_DIFFUSE { + *MAP_NAME "Map #1" + *MAP_CLASS "Bitmap" + *MAP_SUBNO 1 + *MAP_AMOUNT 1.0000 + *BITMAP "C:\Documents and Settings\Jay Brushwood\Desktop\base\Newguy\t.jpg" + *MAP_TYPE Screen + *UVW_U_OFFSET 0.0000 + *UVW_V_OFFSET 0.0000 + *UVW_U_TILING 1.0000 + *UVW_V_TILING 1.0000 + *UVW_ANGLE 0.0000 + *UVW_BLUR 1.0000 + *UVW_BLUR_OFFSET 0.0000 + *UVW_NOUSE_AMT 1.0000 + *UVW_NOISE_SIZE 1.0000 + *UVW_NOISE_LEVEL 1 + *UVW_NOISE_PHASE 0.0000 + *BITMAP_FILTER Pyramidal + } + } + *MATERIAL 2 { + *MATERIAL_NAME "Material #12" + *MATERIAL_CLASS "Multi/Sub-Object" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *NUMSUBMTLS 2 + *SUBMATERIAL 0 { + *MATERIAL_NAME "1 - Defaultas" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *SUBMATERIAL 1 { + *MATERIAL_NAME "1 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4706 0.4706 0.4706 + *MATERIAL_DIFFUSE 0.4706 0.4706 0.4706 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + } + *MATERIAL 3 { + *MATERIAL_NAME "1 - Defaultas" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 4 { + *MATERIAL_NAME "14 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5882 0.5882 0.5882 + *MATERIAL_DIFFUSE 0.5882 0.5882 0.5882 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 5 { + *MATERIAL_NAME "7 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4353 0.4471 0.6157 + *MATERIAL_DIFFUSE 0.4353 0.4471 0.6157 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } +} +*GEOMOBJECT { + *NODE_NAME "Box03_32x128" + *NODE_TM { + *NODE_NAME "Box03_32x128" + *INHERIT_POS 0 0 0 + *INHERIT_ROT 0 0 0 + *INHERIT_SCL 0 0 0 + *TM_ROW0 -0.0000 0.0000 -1.0000 + *TM_ROW1 -0.0000 -1.0000 -0.0000 + *TM_ROW2 -0.2500 0.0000 0.0000 + *TM_ROW3 -0.0000 0.0000 0.0000 + *TM_POS -0.0000 0.0000 0.0000 + *TM_ROTAXIS 0.7071 -0.0000 -0.7071 + *TM_ROTANGLE 3.1416 + *TM_SCALE 1.0000 1.0000 0.2500 + *TM_SCALEAXIS 0.0000 0.0000 0.0000 + *TM_SCALEAXISANG 0.0000 + } + *MESH { + *TIMEVALUE 0 + *MESH_NUMVERTEX 150 + *MESH_NUMFACES 192 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 16.0000 16.0000 64.0000 + *MESH_VERTEX 1 16.0000 16.0000 32.0000 + *MESH_VERTEX 2 16.0000 16.0000 -0.0000 + *MESH_VERTEX 3 16.0000 16.0000 -32.0000 + *MESH_VERTEX 4 16.0000 16.0000 -64.0000 + *MESH_VERTEX 5 16.0000 8.0000 64.0000 + *MESH_VERTEX 6 16.0000 8.0000 32.0000 + *MESH_VERTEX 7 16.0000 8.0000 -0.0000 + *MESH_VERTEX 8 16.0000 8.0000 -32.0000 + *MESH_VERTEX 9 16.0000 8.0000 -64.0000 + *MESH_VERTEX 10 16.0000 -0.0000 64.0000 + *MESH_VERTEX 11 16.0000 -0.0000 32.0000 + *MESH_VERTEX 12 16.0000 -0.0000 -0.0000 + *MESH_VERTEX 13 16.0000 -0.0000 -32.0000 + *MESH_VERTEX 14 16.0000 -0.0000 -64.0000 + *MESH_VERTEX 15 16.0000 -8.0000 64.0000 + *MESH_VERTEX 16 16.0000 -8.0000 32.0000 + *MESH_VERTEX 17 16.0000 -8.0000 -0.0000 + *MESH_VERTEX 18 16.0000 -8.0000 -32.0000 + *MESH_VERTEX 19 16.0000 -8.0000 -64.0000 + *MESH_VERTEX 20 16.0000 -16.0000 64.0000 + *MESH_VERTEX 21 16.0000 -16.0000 32.0000 + *MESH_VERTEX 22 16.0000 -16.0000 -0.0000 + *MESH_VERTEX 23 16.0000 -16.0000 -32.0000 + *MESH_VERTEX 24 16.0000 -16.0000 -64.0000 + *MESH_VERTEX 25 -16.0000 16.0000 64.0000 + *MESH_VERTEX 26 -16.0000 16.0000 32.0000 + *MESH_VERTEX 27 -16.0000 16.0000 0.0000 + *MESH_VERTEX 28 -16.0000 16.0000 -32.0000 + *MESH_VERTEX 29 -16.0000 16.0000 -64.0000 + *MESH_VERTEX 30 -16.0000 8.0000 64.0000 + *MESH_VERTEX 31 -16.0000 8.0000 32.0000 + *MESH_VERTEX 32 -16.0000 8.0000 0.0000 + *MESH_VERTEX 33 -16.0000 8.0000 -32.0000 + *MESH_VERTEX 34 -16.0000 8.0000 -64.0000 + *MESH_VERTEX 35 -16.0000 0.0000 64.0000 + *MESH_VERTEX 36 -16.0000 0.0000 32.0000 + *MESH_VERTEX 37 -16.0000 0.0000 0.0000 + *MESH_VERTEX 38 -16.0000 0.0000 -32.0000 + *MESH_VERTEX 39 -16.0000 0.0000 -64.0000 + *MESH_VERTEX 40 -16.0000 -8.0000 64.0000 + *MESH_VERTEX 41 -16.0000 -8.0000 32.0000 + *MESH_VERTEX 42 -16.0000 -8.0000 0.0000 + *MESH_VERTEX 43 -16.0000 -8.0000 -32.0000 + *MESH_VERTEX 44 -16.0000 -8.0000 -64.0000 + *MESH_VERTEX 45 -16.0000 -16.0000 64.0000 + *MESH_VERTEX 46 -16.0000 -16.0000 32.0000 + *MESH_VERTEX 47 -16.0000 -16.0000 0.0000 + *MESH_VERTEX 48 -16.0000 -16.0000 -32.0000 + *MESH_VERTEX 49 -16.0000 -16.0000 -64.0000 + *MESH_VERTEX 50 8.0000 16.0000 64.0000 + *MESH_VERTEX 51 8.0000 16.0000 32.0000 + *MESH_VERTEX 52 8.0000 16.0000 -0.0000 + *MESH_VERTEX 53 8.0000 16.0000 -32.0000 + *MESH_VERTEX 54 8.0000 16.0000 -64.0000 + *MESH_VERTEX 55 8.0000 8.0000 -64.0000 + *MESH_VERTEX 56 8.0000 -0.0000 -64.0000 + *MESH_VERTEX 57 8.0000 -8.0000 -64.0000 + *MESH_VERTEX 58 8.0000 -16.0000 -64.0000 + *MESH_VERTEX 59 8.0000 -16.0000 -32.0000 + *MESH_VERTEX 60 8.0000 -16.0000 -0.0000 + *MESH_VERTEX 61 8.0000 -16.0000 32.0000 + *MESH_VERTEX 62 8.0000 -16.0000 64.0000 + *MESH_VERTEX 63 8.0000 -8.0000 64.0000 + *MESH_VERTEX 64 8.0000 -0.0000 64.0000 + *MESH_VERTEX 65 8.0000 8.0000 64.0000 + *MESH_VERTEX 66 0.0000 16.0000 64.0000 + *MESH_VERTEX 67 -0.0000 16.0000 32.0000 + *MESH_VERTEX 68 -0.0000 16.0000 0.0000 + *MESH_VERTEX 69 -0.0000 16.0000 -32.0000 + *MESH_VERTEX 70 -0.0000 16.0000 -64.0000 + *MESH_VERTEX 71 -0.0000 8.0000 -64.0000 + *MESH_VERTEX 72 -0.0000 0.0000 -64.0000 + *MESH_VERTEX 73 -0.0000 -8.0000 -64.0000 + *MESH_VERTEX 74 -0.0000 -16.0000 -64.0000 + *MESH_VERTEX 75 -0.0000 -16.0000 -32.0000 + *MESH_VERTEX 76 -0.0000 -16.0000 0.0000 + *MESH_VERTEX 77 -0.0000 -16.0000 32.0000 + *MESH_VERTEX 78 -0.0000 -16.0000 64.0000 + *MESH_VERTEX 79 -0.0000 -8.0000 64.0000 + *MESH_VERTEX 80 -0.0000 0.0000 64.0000 + *MESH_VERTEX 81 -0.0000 8.0000 64.0000 + *MESH_VERTEX 82 -8.0000 16.0000 64.0000 + *MESH_VERTEX 83 -8.0000 16.0000 32.0000 + *MESH_VERTEX 84 -8.0000 16.0000 0.0000 + *MESH_VERTEX 85 -8.0000 16.0000 -32.0000 + *MESH_VERTEX 86 -8.0000 16.0000 -64.0000 + *MESH_VERTEX 87 -8.0000 8.0000 -64.0000 + *MESH_VERTEX 88 -8.0000 0.0000 -64.0000 + *MESH_VERTEX 89 -8.0000 -8.0000 -64.0000 + *MESH_VERTEX 90 -8.0000 -16.0000 -64.0000 + *MESH_VERTEX 91 -8.0000 -16.0000 -32.0000 + *MESH_VERTEX 92 -8.0000 -16.0000 0.0000 + *MESH_VERTEX 93 -8.0000 -16.0000 32.0000 + *MESH_VERTEX 94 -8.0000 -16.0000 64.0000 + *MESH_VERTEX 95 -8.0000 -8.0000 64.0000 + *MESH_VERTEX 96 -8.0000 0.0000 64.0000 + *MESH_VERTEX 97 -8.0000 8.0000 64.0000 + *MESH_VERTEX 98 16.0000 16.0000 64.0000 + *MESH_VERTEX 99 16.0000 16.0000 -64.0000 + *MESH_VERTEX 100 16.0000 8.0000 64.0000 + *MESH_VERTEX 101 16.0000 8.0000 -64.0000 + *MESH_VERTEX 102 16.0000 -0.0000 64.0000 + *MESH_VERTEX 103 16.0000 -0.0000 -64.0000 + *MESH_VERTEX 104 16.0000 -8.0000 64.0000 + *MESH_VERTEX 105 16.0000 -8.0000 -64.0000 + *MESH_VERTEX 106 16.0000 -16.0000 64.0000 + *MESH_VERTEX 107 16.0000 -16.0000 -64.0000 + *MESH_VERTEX 108 -16.0000 16.0000 64.0000 + *MESH_VERTEX 109 -16.0000 16.0000 -64.0000 + *MESH_VERTEX 110 -16.0000 8.0000 64.0000 + *MESH_VERTEX 111 -16.0000 8.0000 -64.0000 + *MESH_VERTEX 112 -16.0000 0.0000 64.0000 + *MESH_VERTEX 113 -16.0000 0.0000 -64.0000 + *MESH_VERTEX 114 -16.0000 -8.0000 64.0000 + *MESH_VERTEX 115 -16.0000 -8.0000 -64.0000 + *MESH_VERTEX 116 -16.0000 -16.0000 64.0000 + *MESH_VERTEX 117 -16.0000 -16.0000 -64.0000 + *MESH_VERTEX 118 8.0000 16.0000 64.0000 + *MESH_VERTEX 119 8.0000 16.0000 -64.0000 + *MESH_VERTEX 120 8.0000 -16.0000 -64.0000 + *MESH_VERTEX 121 8.0000 -16.0000 64.0000 + *MESH_VERTEX 122 0.0000 16.0000 64.0000 + *MESH_VERTEX 123 -0.0000 16.0000 -64.0000 + *MESH_VERTEX 124 -0.0000 -16.0000 -64.0000 + *MESH_VERTEX 125 -0.0000 -16.0000 64.0000 + *MESH_VERTEX 126 -8.0000 16.0000 64.0000 + *MESH_VERTEX 127 -8.0000 16.0000 -64.0000 + *MESH_VERTEX 128 -8.0000 -16.0000 -64.0000 + *MESH_VERTEX 129 -8.0000 -16.0000 64.0000 + *MESH_VERTEX 130 16.0000 16.0000 64.0000 + *MESH_VERTEX 131 16.0000 16.0000 32.0000 + *MESH_VERTEX 132 16.0000 16.0000 -0.0000 + *MESH_VERTEX 133 16.0000 16.0000 -32.0000 + *MESH_VERTEX 134 16.0000 16.0000 -64.0000 + *MESH_VERTEX 135 16.0000 -16.0000 64.0000 + *MESH_VERTEX 136 16.0000 -16.0000 32.0000 + *MESH_VERTEX 137 16.0000 -16.0000 -0.0000 + *MESH_VERTEX 138 16.0000 -16.0000 -32.0000 + *MESH_VERTEX 139 16.0000 -16.0000 -64.0000 + *MESH_VERTEX 140 -16.0000 16.0000 64.0000 + *MESH_VERTEX 141 -16.0000 16.0000 32.0000 + *MESH_VERTEX 142 -16.0000 16.0000 0.0000 + *MESH_VERTEX 143 -16.0000 16.0000 -32.0000 + *MESH_VERTEX 144 -16.0000 16.0000 -64.0000 + *MESH_VERTEX 145 -16.0000 -16.0000 64.0000 + *MESH_VERTEX 146 -16.0000 -16.0000 32.0000 + *MESH_VERTEX 147 -16.0000 -16.0000 0.0000 + *MESH_VERTEX 148 -16.0000 -16.0000 -32.0000 + *MESH_VERTEX 149 -16.0000 -16.0000 -64.0000 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 130 B: 5 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 1: A: 6 B: 131 C: 130 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 2: A: 131 B: 6 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 3: A: 7 B: 132 C: 131 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 4: A: 132 B: 7 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 5: A: 8 B: 133 C: 132 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 6: A: 133 B: 8 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 7: A: 9 B: 134 C: 133 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 8: A: 5 B: 10 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 9: A: 11 B: 6 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 10: A: 6 B: 11 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 11: A: 12 B: 7 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 12: A: 7 B: 12 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 13: A: 13 B: 8 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 14: A: 8 B: 13 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 15: A: 14 B: 9 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 16: A: 10 B: 15 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 17: A: 16 B: 11 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 18: A: 11 B: 16 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 19: A: 17 B: 12 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 20: A: 12 B: 17 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 21: A: 18 B: 13 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 22: A: 13 B: 18 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 23: A: 19 B: 14 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 24: A: 15 B: 135 C: 136 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 25: A: 136 B: 16 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 26: A: 16 B: 136 C: 137 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 27: A: 137 B: 17 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 28: A: 17 B: 137 C: 138 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 29: A: 138 B: 18 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 30: A: 18 B: 138 C: 139 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 31: A: 139 B: 19 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 32: A: 140 B: 141 C: 31 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 33: A: 31 B: 30 C: 140 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 34: A: 141 B: 142 C: 32 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 35: A: 32 B: 31 C: 141 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 36: A: 142 B: 143 C: 33 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 37: A: 33 B: 32 C: 142 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 38: A: 143 B: 144 C: 34 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 39: A: 34 B: 33 C: 143 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 40: A: 30 B: 31 C: 36 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 41: A: 36 B: 35 C: 30 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 42: A: 31 B: 32 C: 37 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 43: A: 37 B: 36 C: 31 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 44: A: 32 B: 33 C: 38 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 45: A: 38 B: 37 C: 32 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 46: A: 33 B: 34 C: 39 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 47: A: 39 B: 38 C: 33 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 48: A: 35 B: 36 C: 41 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 49: A: 41 B: 40 C: 35 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 50: A: 36 B: 37 C: 42 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 51: A: 42 B: 41 C: 36 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 52: A: 37 B: 38 C: 43 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 53: A: 43 B: 42 C: 37 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 54: A: 38 B: 39 C: 44 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 55: A: 44 B: 43 C: 38 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 56: A: 40 B: 41 C: 146 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 57: A: 146 B: 145 C: 40 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 58: A: 41 B: 42 C: 147 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 59: A: 147 B: 146 C: 41 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 60: A: 42 B: 43 C: 148 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 61: A: 148 B: 147 C: 42 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 62: A: 43 B: 44 C: 149 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 63: A: 149 B: 148 C: 43 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 64: A: 0 B: 1 C: 51 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 65: A: 51 B: 50 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 66: A: 1 B: 2 C: 52 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 67: A: 52 B: 51 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 68: A: 2 B: 3 C: 53 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 69: A: 53 B: 52 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 70: A: 3 B: 4 C: 54 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 71: A: 54 B: 53 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 72: A: 50 B: 51 C: 67 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 73: A: 67 B: 66 C: 50 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 74: A: 51 B: 52 C: 68 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 75: A: 68 B: 67 C: 51 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 76: A: 52 B: 53 C: 69 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 77: A: 69 B: 68 C: 52 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 78: A: 53 B: 54 C: 70 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 79: A: 70 B: 69 C: 53 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 80: A: 66 B: 67 C: 83 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 81: A: 83 B: 82 C: 66 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 82: A: 67 B: 68 C: 84 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 83: A: 84 B: 83 C: 67 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 84: A: 68 B: 69 C: 85 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 85: A: 85 B: 84 C: 68 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 86: A: 69 B: 70 C: 86 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 87: A: 86 B: 85 C: 69 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 88: A: 82 B: 83 C: 26 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 89: A: 26 B: 25 C: 82 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 90: A: 83 B: 84 C: 27 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 91: A: 27 B: 26 C: 83 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 92: A: 84 B: 85 C: 28 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 93: A: 28 B: 27 C: 84 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 94: A: 85 B: 86 C: 29 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 95: A: 29 B: 28 C: 85 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 96: A: 99 B: 101 C: 55 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 97: A: 55 B: 119 C: 99 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 98: A: 101 B: 103 C: 56 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 99: A: 56 B: 55 C: 101 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 100: A: 103 B: 105 C: 57 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 101: A: 57 B: 56 C: 103 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 102: A: 105 B: 107 C: 120 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 103: A: 120 B: 57 C: 105 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 104: A: 119 B: 55 C: 71 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 105: A: 71 B: 123 C: 119 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 106: A: 55 B: 56 C: 72 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 107: A: 72 B: 71 C: 55 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 108: A: 56 B: 57 C: 73 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 109: A: 73 B: 72 C: 56 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 110: A: 57 B: 120 C: 124 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 111: A: 124 B: 73 C: 57 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 112: A: 123 B: 71 C: 87 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 113: A: 87 B: 127 C: 123 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 114: A: 71 B: 72 C: 88 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 115: A: 88 B: 87 C: 71 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 116: A: 72 B: 73 C: 89 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 117: A: 89 B: 88 C: 72 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 118: A: 73 B: 124 C: 128 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 119: A: 128 B: 89 C: 73 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 120: A: 127 B: 87 C: 111 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 121: A: 111 B: 109 C: 127 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 122: A: 87 B: 88 C: 113 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 123: A: 113 B: 111 C: 87 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 124: A: 88 B: 89 C: 115 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 125: A: 115 B: 113 C: 88 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 126: A: 89 B: 128 C: 117 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 127: A: 117 B: 115 C: 89 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 128: A: 24 B: 23 C: 59 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 129: A: 59 B: 58 C: 24 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 130: A: 23 B: 22 C: 60 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 131: A: 60 B: 59 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 132: A: 22 B: 21 C: 61 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 133: A: 61 B: 60 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 134: A: 21 B: 20 C: 62 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 135: A: 62 B: 61 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 136: A: 58 B: 59 C: 75 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 137: A: 75 B: 74 C: 58 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 138: A: 59 B: 60 C: 76 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 139: A: 76 B: 75 C: 59 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 140: A: 60 B: 61 C: 77 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 141: A: 77 B: 76 C: 60 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 142: A: 61 B: 62 C: 78 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 143: A: 78 B: 77 C: 61 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 144: A: 74 B: 75 C: 91 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 145: A: 91 B: 90 C: 74 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 146: A: 75 B: 76 C: 92 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 147: A: 92 B: 91 C: 75 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 148: A: 76 B: 77 C: 93 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 149: A: 93 B: 92 C: 76 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 150: A: 77 B: 78 C: 94 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 151: A: 94 B: 93 C: 77 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 152: A: 90 B: 91 C: 48 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 153: A: 48 B: 49 C: 90 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 154: A: 91 B: 92 C: 47 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 155: A: 47 B: 48 C: 91 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 156: A: 92 B: 93 C: 46 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 157: A: 46 B: 47 C: 92 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 158: A: 93 B: 94 C: 45 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 159: A: 45 B: 46 C: 93 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 160: A: 106 B: 104 C: 63 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 161: A: 63 B: 121 C: 106 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 162: A: 104 B: 102 C: 64 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 163: A: 64 B: 63 C: 104 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 164: A: 102 B: 100 C: 65 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 165: A: 65 B: 64 C: 102 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 166: A: 100 B: 98 C: 118 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 167: A: 118 B: 65 C: 100 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 168: A: 121 B: 63 C: 79 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 169: A: 79 B: 125 C: 121 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 170: A: 63 B: 64 C: 80 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 171: A: 80 B: 79 C: 63 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 172: A: 64 B: 65 C: 81 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 173: A: 81 B: 80 C: 64 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 174: A: 65 B: 118 C: 122 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 175: A: 122 B: 81 C: 65 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 176: A: 125 B: 79 C: 95 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 177: A: 95 B: 129 C: 125 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 178: A: 79 B: 80 C: 96 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 179: A: 96 B: 95 C: 79 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 180: A: 80 B: 81 C: 97 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 181: A: 97 B: 96 C: 80 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 182: A: 81 B: 122 C: 126 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 183: A: 126 B: 97 C: 81 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 184: A: 129 B: 95 C: 114 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 185: A: 114 B: 116 C: 129 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 186: A: 95 B: 96 C: 112 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 187: A: 112 B: 114 C: 95 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 188: A: 96 B: 97 C: 110 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 189: A: 110 B: 112 C: 96 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 190: A: 97 B: 126 C: 108 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 191: A: 108 B: 110 C: 97 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 150 + *MESH_TVERTLIST { + *MESH_TVERT 0 0.7500 1.0000 0.0000 + *MESH_TVERT 1 0.7500 0.7500 0.0000 + *MESH_TVERT 2 0.7500 0.5000 0.0000 + *MESH_TVERT 3 0.7500 0.2500 0.0000 + *MESH_TVERT 4 0.7500 -0.0000 0.0000 + *MESH_TVERT 5 0.5000 1.0000 0.0000 + *MESH_TVERT 6 0.5000 0.7500 0.0000 + *MESH_TVERT 7 0.5000 0.5000 0.0000 + *MESH_TVERT 8 0.5000 0.2500 0.0000 + *MESH_TVERT 9 0.5000 -0.0000 0.0000 + *MESH_TVERT 10 0.2500 1.0000 0.0000 + *MESH_TVERT 11 0.2500 0.7500 0.0000 + *MESH_TVERT 12 0.2500 0.5000 0.0000 + *MESH_TVERT 13 0.2500 0.2500 0.0000 + *MESH_TVERT 14 0.2500 0.0000 0.0000 + *MESH_TVERT 15 0.2500 1.0000 0.0000 + *MESH_TVERT 16 0.2500 0.7500 0.0000 + *MESH_TVERT 17 0.2500 0.5000 0.0000 + *MESH_TVERT 18 0.2500 0.2500 0.0000 + *MESH_TVERT 19 0.2500 0.0000 0.0000 + *MESH_TVERT 20 0.5000 1.0000 0.0000 + *MESH_TVERT 21 0.5000 0.7500 0.0000 + *MESH_TVERT 22 0.5000 0.5000 0.0000 + *MESH_TVERT 23 0.5000 0.2500 0.0000 + *MESH_TVERT 24 0.5000 -0.0000 0.0000 + *MESH_TVERT 25 0.7500 1.0000 0.0000 + *MESH_TVERT 26 0.7500 0.7500 0.0000 + *MESH_TVERT 27 0.7500 0.5000 0.0000 + *MESH_TVERT 28 0.7500 0.2500 0.0000 + *MESH_TVERT 29 0.7500 -0.0000 0.0000 + *MESH_TVERT 30 0.0000 1.0000 0.0000 + *MESH_TVERT 31 -0.0000 0.7500 0.0000 + *MESH_TVERT 32 -0.0000 0.5000 0.0000 + *MESH_TVERT 33 -0.0000 0.2500 0.0000 + *MESH_TVERT 34 -0.0000 0.0000 0.0000 + *MESH_TVERT 35 0.2500 1.0000 0.0000 + *MESH_TVERT 36 0.2500 0.7500 0.0000 + *MESH_TVERT 37 0.2500 0.5000 0.0000 + *MESH_TVERT 38 0.2500 0.2500 0.0000 + *MESH_TVERT 39 0.2500 -0.0000 0.0000 + *MESH_TVERT 40 0.5000 1.0000 0.0000 + *MESH_TVERT 41 0.5000 0.7500 0.0000 + *MESH_TVERT 42 0.5000 0.5000 0.0000 + *MESH_TVERT 43 0.5000 0.2500 0.0000 + *MESH_TVERT 44 0.5000 -0.0000 0.0000 + *MESH_TVERT 45 0.7500 1.0000 0.0000 + *MESH_TVERT 46 0.7500 0.7500 0.0000 + *MESH_TVERT 47 0.7500 0.5000 0.0000 + *MESH_TVERT 48 0.7500 0.2500 0.0000 + *MESH_TVERT 49 0.7500 -0.0000 0.0000 + *MESH_TVERT 50 1.0000 1.0000 0.0000 + *MESH_TVERT 51 1.0000 0.7500 0.0000 + *MESH_TVERT 52 1.0000 0.5000 0.0000 + *MESH_TVERT 53 1.0000 0.2500 0.0000 + *MESH_TVERT 54 1.0000 -0.0000 0.0000 + *MESH_TVERT 55 0.2500 0.2500 0.0000 + *MESH_TVERT 56 0.5000 0.2500 0.0000 + *MESH_TVERT 57 0.7500 0.2500 0.0000 + *MESH_TVERT 58 0.2500 0.5000 0.0000 + *MESH_TVERT 59 0.5000 0.5000 0.0000 + *MESH_TVERT 60 0.7500 0.5000 0.0000 + *MESH_TVERT 61 0.2500 0.7500 0.0000 + *MESH_TVERT 62 0.5000 0.7500 0.0000 + *MESH_TVERT 63 0.7500 0.7500 0.0000 + *MESH_TVERT 64 1.0000 -0.0000 0.0000 + *MESH_TVERT 65 1.0000 0.2500 0.0000 + *MESH_TVERT 66 1.0000 0.5000 0.0000 + *MESH_TVERT 67 1.0000 0.7500 0.0000 + *MESH_TVERT 68 1.0000 1.0000 0.0000 + *MESH_TVERT 69 0.7500 -0.0000 0.0000 + *MESH_TVERT 70 0.7500 0.2500 0.0000 + *MESH_TVERT 71 0.7500 0.5000 0.0000 + *MESH_TVERT 72 0.7500 0.7500 0.0000 + *MESH_TVERT 73 0.7500 1.0000 0.0000 + *MESH_TVERT 74 0.5000 -0.0000 0.0000 + *MESH_TVERT 75 0.5000 0.2500 0.0000 + *MESH_TVERT 76 0.5000 0.5000 0.0000 + *MESH_TVERT 77 0.5000 0.7500 0.0000 + *MESH_TVERT 78 0.5000 1.0000 0.0000 + *MESH_TVERT 79 0.2500 -0.0000 0.0000 + *MESH_TVERT 80 0.2500 0.2500 0.0000 + *MESH_TVERT 81 0.2500 0.5000 0.0000 + *MESH_TVERT 82 0.2500 0.7500 0.0000 + *MESH_TVERT 83 0.2500 1.0000 0.0000 + *MESH_TVERT 84 -0.0000 -0.0000 0.0000 + *MESH_TVERT 85 -0.0000 0.2500 0.0000 + *MESH_TVERT 86 -0.0000 0.5000 0.0000 + *MESH_TVERT 87 -0.0000 0.7500 0.0000 + *MESH_TVERT 88 -0.0000 1.0000 0.0000 + *MESH_TVERT 89 0.7500 0.7500 0.0000 + *MESH_TVERT 90 0.5000 0.7500 0.0000 + *MESH_TVERT 91 0.2500 0.7500 0.0000 + *MESH_TVERT 92 0.7500 0.5000 0.0000 + *MESH_TVERT 93 0.5000 0.5000 0.0000 + *MESH_TVERT 94 0.2500 0.5000 0.0000 + *MESH_TVERT 95 0.7500 0.2500 0.0000 + *MESH_TVERT 96 0.5000 0.2500 0.0000 + *MESH_TVERT 97 0.2500 0.2500 0.0000 + *MESH_TVERT 98 0.0000 1.0000 0.0000 + *MESH_TVERT 99 -0.0000 0.0000 0.0000 + *MESH_TVERT 100 0.2500 1.0000 0.0000 + *MESH_TVERT 101 0.2500 0.0000 0.0000 + *MESH_TVERT 102 0.5000 1.0000 0.0000 + *MESH_TVERT 103 0.5000 0.0000 0.0000 + *MESH_TVERT 104 0.7500 1.0000 0.0000 + *MESH_TVERT 105 0.7500 0.0000 0.0000 + *MESH_TVERT 106 1.0000 1.0000 0.0000 + *MESH_TVERT 107 1.0000 -0.0000 0.0000 + *MESH_TVERT 108 -0.0000 0.0000 0.0000 + *MESH_TVERT 109 0.0000 1.0000 0.0000 + *MESH_TVERT 110 0.2500 -0.0000 0.0000 + *MESH_TVERT 111 0.2500 1.0000 0.0000 + *MESH_TVERT 112 0.5000 -0.0000 0.0000 + *MESH_TVERT 113 0.5000 1.0000 0.0000 + *MESH_TVERT 114 0.7500 -0.0000 0.0000 + *MESH_TVERT 115 0.7500 1.0000 0.0000 + *MESH_TVERT 116 1.0000 -0.0000 0.0000 + *MESH_TVERT 117 1.0000 1.0000 0.0000 + *MESH_TVERT 118 0.0000 0.7500 0.0000 + *MESH_TVERT 119 -0.0000 0.2500 0.0000 + *MESH_TVERT 120 1.0000 0.2500 0.0000 + *MESH_TVERT 121 1.0000 0.7500 0.0000 + *MESH_TVERT 122 -0.0000 0.5000 0.0000 + *MESH_TVERT 123 -0.0000 0.5000 0.0000 + *MESH_TVERT 124 1.0000 0.5000 0.0000 + *MESH_TVERT 125 1.0000 0.5000 0.0000 + *MESH_TVERT 126 -0.0000 0.2500 0.0000 + *MESH_TVERT 127 0.0000 0.7500 0.0000 + *MESH_TVERT 128 1.0000 0.7500 0.0000 + *MESH_TVERT 129 1.0000 0.2500 0.0000 + *MESH_TVERT 130 1.0000 1.0000 0.0000 + *MESH_TVERT 131 1.0000 0.7500 0.0000 + *MESH_TVERT 132 1.0000 0.5000 0.0000 + *MESH_TVERT 133 1.0000 0.2500 0.0000 + *MESH_TVERT 134 1.0000 -0.0000 0.0000 + *MESH_TVERT 135 0.0000 1.0000 0.0000 + *MESH_TVERT 136 0.0000 0.7500 0.0000 + *MESH_TVERT 137 0.0000 0.5000 0.0000 + *MESH_TVERT 138 -0.0000 0.2500 0.0000 + *MESH_TVERT 139 -0.0000 0.0000 0.0000 + *MESH_TVERT 140 0.0000 1.0000 0.0000 + *MESH_TVERT 141 0.0000 0.7500 0.0000 + *MESH_TVERT 142 0.0000 0.5000 0.0000 + *MESH_TVERT 143 0.0000 0.2500 0.0000 + *MESH_TVERT 144 -0.0000 0.0000 0.0000 + *MESH_TVERT 145 1.0000 1.0000 0.0000 + *MESH_TVERT 146 1.0000 0.7500 0.0000 + *MESH_TVERT 147 1.0000 0.5000 0.0000 + *MESH_TVERT 148 1.0000 0.2500 0.0000 + *MESH_TVERT 149 1.0000 -0.0000 0.0000 + } + *MESH_NUMTVFACES 192 + *MESH_TFACELIST { + *MESH_TFACE 0 130 0 1 + *MESH_TFACE 1 1 131 130 + *MESH_TFACE 2 131 1 2 + *MESH_TFACE 3 2 132 131 + *MESH_TFACE 4 132 2 3 + *MESH_TFACE 5 3 133 132 + *MESH_TFACE 6 133 3 4 + *MESH_TFACE 7 4 134 133 + *MESH_TFACE 8 0 5 6 + *MESH_TFACE 9 6 1 0 + *MESH_TFACE 10 1 6 7 + *MESH_TFACE 11 7 2 1 + *MESH_TFACE 12 2 7 8 + *MESH_TFACE 13 8 3 2 + *MESH_TFACE 14 3 8 9 + *MESH_TFACE 15 9 4 3 + *MESH_TFACE 16 5 10 11 + *MESH_TFACE 17 11 6 5 + *MESH_TFACE 18 6 11 12 + *MESH_TFACE 19 12 7 6 + *MESH_TFACE 20 7 12 13 + *MESH_TFACE 21 13 8 7 + *MESH_TFACE 22 8 13 14 + *MESH_TFACE 23 14 9 8 + *MESH_TFACE 24 10 135 136 + *MESH_TFACE 25 136 11 10 + *MESH_TFACE 26 11 136 137 + *MESH_TFACE 27 137 12 11 + *MESH_TFACE 28 12 137 138 + *MESH_TFACE 29 138 13 12 + *MESH_TFACE 30 13 138 139 + *MESH_TFACE 31 139 14 13 + *MESH_TFACE 32 140 141 16 + *MESH_TFACE 33 16 15 140 + *MESH_TFACE 34 141 142 17 + *MESH_TFACE 35 17 16 141 + *MESH_TFACE 36 142 143 18 + *MESH_TFACE 37 18 17 142 + *MESH_TFACE 38 143 144 19 + *MESH_TFACE 39 19 18 143 + *MESH_TFACE 40 15 16 21 + *MESH_TFACE 41 21 20 15 + *MESH_TFACE 42 16 17 22 + *MESH_TFACE 43 22 21 16 + *MESH_TFACE 44 17 18 23 + *MESH_TFACE 45 23 22 17 + *MESH_TFACE 46 18 19 24 + *MESH_TFACE 47 24 23 18 + *MESH_TFACE 48 20 21 26 + *MESH_TFACE 49 26 25 20 + *MESH_TFACE 50 21 22 27 + *MESH_TFACE 51 27 26 21 + *MESH_TFACE 52 22 23 28 + *MESH_TFACE 53 28 27 22 + *MESH_TFACE 54 23 24 29 + *MESH_TFACE 55 29 28 23 + *MESH_TFACE 56 25 26 146 + *MESH_TFACE 57 146 145 25 + *MESH_TFACE 58 26 27 147 + *MESH_TFACE 59 147 146 26 + *MESH_TFACE 60 27 28 148 + *MESH_TFACE 61 148 147 27 + *MESH_TFACE 62 28 29 149 + *MESH_TFACE 63 149 148 28 + *MESH_TFACE 64 30 31 36 + *MESH_TFACE 65 36 35 30 + *MESH_TFACE 66 31 32 37 + *MESH_TFACE 67 37 36 31 + *MESH_TFACE 68 32 33 38 + *MESH_TFACE 69 38 37 32 + *MESH_TFACE 70 33 34 39 + *MESH_TFACE 71 39 38 33 + *MESH_TFACE 72 35 36 41 + *MESH_TFACE 73 41 40 35 + *MESH_TFACE 74 36 37 42 + *MESH_TFACE 75 42 41 36 + *MESH_TFACE 76 37 38 43 + *MESH_TFACE 77 43 42 37 + *MESH_TFACE 78 38 39 44 + *MESH_TFACE 79 44 43 38 + *MESH_TFACE 80 40 41 46 + *MESH_TFACE 81 46 45 40 + *MESH_TFACE 82 41 42 47 + *MESH_TFACE 83 47 46 41 + *MESH_TFACE 84 42 43 48 + *MESH_TFACE 85 48 47 42 + *MESH_TFACE 86 43 44 49 + *MESH_TFACE 87 49 48 43 + *MESH_TFACE 88 45 46 51 + *MESH_TFACE 89 51 50 45 + *MESH_TFACE 90 46 47 52 + *MESH_TFACE 91 52 51 46 + *MESH_TFACE 92 47 48 53 + *MESH_TFACE 93 53 52 47 + *MESH_TFACE 94 48 49 54 + *MESH_TFACE 95 54 53 48 + *MESH_TFACE 96 99 101 55 + *MESH_TFACE 97 55 119 99 + *MESH_TFACE 98 101 103 56 + *MESH_TFACE 99 56 55 101 + *MESH_TFACE 100 103 105 57 + *MESH_TFACE 101 57 56 103 + *MESH_TFACE 102 105 107 120 + *MESH_TFACE 103 120 57 105 + *MESH_TFACE 104 119 55 58 + *MESH_TFACE 105 58 123 119 + *MESH_TFACE 106 55 56 59 + *MESH_TFACE 107 59 58 55 + *MESH_TFACE 108 56 57 60 + *MESH_TFACE 109 60 59 56 + *MESH_TFACE 110 57 120 124 + *MESH_TFACE 111 124 60 57 + *MESH_TFACE 112 123 58 61 + *MESH_TFACE 113 61 127 123 + *MESH_TFACE 114 58 59 62 + *MESH_TFACE 115 62 61 58 + *MESH_TFACE 116 59 60 63 + *MESH_TFACE 117 63 62 59 + *MESH_TFACE 118 60 124 128 + *MESH_TFACE 119 128 63 60 + *MESH_TFACE 120 127 61 111 + *MESH_TFACE 121 111 109 127 + *MESH_TFACE 122 61 62 113 + *MESH_TFACE 123 113 111 61 + *MESH_TFACE 124 62 63 115 + *MESH_TFACE 125 115 113 62 + *MESH_TFACE 126 63 128 117 + *MESH_TFACE 127 117 115 63 + *MESH_TFACE 128 64 65 70 + *MESH_TFACE 129 70 69 64 + *MESH_TFACE 130 65 66 71 + *MESH_TFACE 131 71 70 65 + *MESH_TFACE 132 66 67 72 + *MESH_TFACE 133 72 71 66 + *MESH_TFACE 134 67 68 73 + *MESH_TFACE 135 73 72 67 + *MESH_TFACE 136 69 70 75 + *MESH_TFACE 137 75 74 69 + *MESH_TFACE 138 70 71 76 + *MESH_TFACE 139 76 75 70 + *MESH_TFACE 140 71 72 77 + *MESH_TFACE 141 77 76 71 + *MESH_TFACE 142 72 73 78 + *MESH_TFACE 143 78 77 72 + *MESH_TFACE 144 74 75 80 + *MESH_TFACE 145 80 79 74 + *MESH_TFACE 146 75 76 81 + *MESH_TFACE 147 81 80 75 + *MESH_TFACE 148 76 77 82 + *MESH_TFACE 149 82 81 76 + *MESH_TFACE 150 77 78 83 + *MESH_TFACE 151 83 82 77 + *MESH_TFACE 152 79 80 85 + *MESH_TFACE 153 85 84 79 + *MESH_TFACE 154 80 81 86 + *MESH_TFACE 155 86 85 80 + *MESH_TFACE 156 81 82 87 + *MESH_TFACE 157 87 86 81 + *MESH_TFACE 158 82 83 88 + *MESH_TFACE 159 88 87 82 + *MESH_TFACE 160 106 104 89 + *MESH_TFACE 161 89 121 106 + *MESH_TFACE 162 104 102 90 + *MESH_TFACE 163 90 89 104 + *MESH_TFACE 164 102 100 91 + *MESH_TFACE 165 91 90 102 + *MESH_TFACE 166 100 98 118 + *MESH_TFACE 167 118 91 100 + *MESH_TFACE 168 121 89 92 + *MESH_TFACE 169 92 125 121 + *MESH_TFACE 170 89 90 93 + *MESH_TFACE 171 93 92 89 + *MESH_TFACE 172 90 91 94 + *MESH_TFACE 173 94 93 90 + *MESH_TFACE 174 91 118 122 + *MESH_TFACE 175 122 94 91 + *MESH_TFACE 176 125 92 95 + *MESH_TFACE 177 95 129 125 + *MESH_TFACE 178 92 93 96 + *MESH_TFACE 179 96 95 92 + *MESH_TFACE 180 93 94 97 + *MESH_TFACE 181 97 96 93 + *MESH_TFACE 182 94 122 126 + *MESH_TFACE 183 126 97 94 + *MESH_TFACE 184 129 95 114 + *MESH_TFACE 185 114 116 129 + *MESH_TFACE 186 95 96 112 + *MESH_TFACE 187 112 114 95 + *MESH_TFACE 188 96 97 110 + *MESH_TFACE 189 110 112 96 + *MESH_TFACE 190 97 126 108 + *MESH_TFACE 191 108 110 97 + } + *MESH_NORMALS { + *MESH_FACENORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 130 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 5 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 1 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 131 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 130 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 2 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 131 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 3 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 132 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 131 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 4 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 132 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 5 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 133 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 132 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 133 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 9 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 9 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 134 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 133 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 5 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 10 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 9 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 5 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 10 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 14 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 14 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 15 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 14 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 9 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 10 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 15 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 10 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 19 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 20 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 21 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 22 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 19 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 23 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 19 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 14 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 24 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 15 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 135 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 136 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 25 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 136 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 15 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 26 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 136 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 137 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 27 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 137 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 28 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 137 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 138 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 29 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 138 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 30 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 138 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 139 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 31 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 139 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 19 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 140 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 141 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 30 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 140 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 34 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 141 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 142 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 35 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 141 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 142 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 143 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 142 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 143 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 144 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 34 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 39 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 34 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 143 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 40 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 30 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 35 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 30 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 44 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 45 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 46 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 34 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 39 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 47 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 39 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 48 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 35 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 49 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 40 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 35 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 50 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 51 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 52 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 53 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 54 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 39 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 44 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 55 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 44 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 56 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 40 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 146 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 57 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 146 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 145 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 40 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 58 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 147 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 59 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 147 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 146 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 60 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 148 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 61 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 148 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 147 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 62 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 44 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 149 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 63 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 149 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 148 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 64 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 0 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 65 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 50 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 0 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 66 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 70 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 4 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 54 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 71 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 54 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 72 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 50 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 73 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 66 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 50 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 74 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 75 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 76 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 77 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 78 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 54 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 70 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 79 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 70 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 80 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 66 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 81 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 82 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 66 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 82 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 86 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 70 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 86 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 87 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 86 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 88 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 82 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 26 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 89 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 26 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 25 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 82 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 90 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 27 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 91 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 27 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 26 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 92 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 28 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 93 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 28 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 27 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 94 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 86 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 29 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 95 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 29 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 28 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 96 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 99 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 101 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 97 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 119 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 99 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 98 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 101 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 103 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 99 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 101 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 100 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 103 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 105 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 101 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 103 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 102 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 105 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 107 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 120 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 103 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 120 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 105 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 104 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 119 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 105 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 123 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 119 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 106 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 107 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 108 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 109 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 110 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 120 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 124 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 111 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 124 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 112 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 123 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 113 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 127 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 123 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 114 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 115 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 116 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 117 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 118 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 124 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 128 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 119 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 128 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 120 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 127 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 111 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 121 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 111 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 109 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 127 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 122 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 113 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 123 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 113 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 111 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 124 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 115 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 125 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 115 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 113 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 126 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 128 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 117 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 127 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 117 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 115 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 128 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 24 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 129 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 58 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 24 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 130 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 131 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 132 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 21 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 133 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 134 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 21 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 20 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 62 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 135 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 62 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 21 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 136 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 58 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 137 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 74 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 58 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 138 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 139 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 140 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 141 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 142 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 62 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 78 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 143 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 78 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 144 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 74 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 145 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 90 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 74 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 146 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 147 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 148 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 149 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 150 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 78 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 94 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 151 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 94 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 152 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 90 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 48 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 153 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 48 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 49 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 90 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 154 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 47 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 155 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 47 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 48 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 156 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 46 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 157 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 46 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 47 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 158 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 94 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 45 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 159 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 45 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 46 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 160 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 106 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 104 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 161 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 121 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 106 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 162 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 104 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 102 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 163 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 104 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 164 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 102 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 100 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 165 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 102 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 166 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 100 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 98 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 118 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 167 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 118 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 100 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 168 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 121 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 169 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 125 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 121 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 170 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 171 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 172 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 173 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 174 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 118 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 122 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 175 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 122 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 176 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 125 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 177 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 129 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 125 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 178 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 179 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 180 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 181 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 182 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 122 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 126 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 183 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 126 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 184 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 129 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 114 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 185 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 114 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 116 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 129 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 186 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 112 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 187 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 112 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 114 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 188 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 110 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 189 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 110 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 112 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 190 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 126 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 108 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 191 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 108 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 110 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + } + } + *PROP_MOTIONBLUR 0 + *PROP_CASTSHADOW 1 + *PROP_RECVSHADOW 1 + *MATERIAL_REF 1 +} diff --git a/base/models/materialeditor/box64x128.ase b/base/models/materialeditor/box64x128.ase new file mode 100644 index 000000000..51c416d4c --- /dev/null +++ b/base/models/materialeditor/box64x128.ase @@ -0,0 +1,1650 @@ +*3DSMAX_ASCIIEXPORT 200 +*COMMENT "AsciiExport Version 2.00 - Tue Dec 09 14:09:21 2003" +*SCENE { + *SCENE_FILENAME "t.max" + *SCENE_FIRSTFRAME 0 + *SCENE_LASTFRAME 300 + *SCENE_FRAMESPEED 30 + *SCENE_TICKSPERFRAME 160 + *SCENE_BACKGROUND_STATIC 0.0000 0.0000 0.0000 + *SCENE_AMBIENT_STATIC 0.0000 0.0000 0.0000 +} +*MATERIAL_LIST { + *MATERIAL_COUNT 6 + *MATERIAL 0 { + *MATERIAL_NAME "1 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4706 0.4706 0.4706 + *MATERIAL_DIFFUSE 0.4706 0.4706 0.4706 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 1 { + *MATERIAL_NAME "9 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5882 0.5882 0.5882 + *MATERIAL_DIFFUSE 0.5882 0.5882 0.5882 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + *MAP_DIFFUSE { + *MAP_NAME "Map #1" + *MAP_CLASS "Bitmap" + *MAP_SUBNO 1 + *MAP_AMOUNT 1.0000 + *BITMAP "C:\Documents and Settings\Jay Brushwood\Desktop\base\Newguy\t.jpg" + *MAP_TYPE Screen + *UVW_U_OFFSET 0.0000 + *UVW_V_OFFSET 0.0000 + *UVW_U_TILING 1.0000 + *UVW_V_TILING 1.0000 + *UVW_ANGLE 0.0000 + *UVW_BLUR 1.0000 + *UVW_BLUR_OFFSET 0.0000 + *UVW_NOUSE_AMT 1.0000 + *UVW_NOISE_SIZE 1.0000 + *UVW_NOISE_LEVEL 1 + *UVW_NOISE_PHASE 0.0000 + *BITMAP_FILTER Pyramidal + } + } + *MATERIAL 2 { + *MATERIAL_NAME "Material #12" + *MATERIAL_CLASS "Multi/Sub-Object" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *NUMSUBMTLS 2 + *SUBMATERIAL 0 { + *MATERIAL_NAME "1 - Defaultas" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *SUBMATERIAL 1 { + *MATERIAL_NAME "1 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4706 0.4706 0.4706 + *MATERIAL_DIFFUSE 0.4706 0.4706 0.4706 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + } + *MATERIAL 3 { + *MATERIAL_NAME "1 - Defaultas" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 4 { + *MATERIAL_NAME "14 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5882 0.5882 0.5882 + *MATERIAL_DIFFUSE 0.5882 0.5882 0.5882 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 5 { + *MATERIAL_NAME "7 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4353 0.4471 0.6157 + *MATERIAL_DIFFUSE 0.4353 0.4471 0.6157 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } +} +*GEOMOBJECT { + *NODE_NAME "Box02_64x128" + *NODE_TM { + *NODE_NAME "Box02_64x128" + *INHERIT_POS 0 0 0 + *INHERIT_ROT 0 0 0 + *INHERIT_SCL 0 0 0 + *TM_ROW0 -0.0000 -0.0000 -1.0000 + *TM_ROW1 -0.0000 -1.0000 -0.0000 + *TM_ROW2 -0.5000 0.0000 0.0000 + *TM_ROW3 -0.0000 0.0000 0.0000 + *TM_POS -0.0000 0.0000 0.0000 + *TM_ROTAXIS 0.7071 -0.0000 -0.7071 + *TM_ROTANGLE 3.1416 + *TM_SCALE 1.0000 1.0000 0.5000 + *TM_SCALEAXIS 0.0000 0.0000 0.0000 + *TM_SCALEAXISANG 0.0000 + } + *MESH { + *TIMEVALUE 0 + *MESH_NUMVERTEX 150 + *MESH_NUMFACES 192 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 32.0000 32.0000 64.0000 + *MESH_VERTEX 1 32.0000 32.0000 32.0000 + *MESH_VERTEX 2 32.0000 32.0000 -0.0000 + *MESH_VERTEX 3 32.0000 32.0000 -32.0000 + *MESH_VERTEX 4 32.0000 32.0000 -64.0000 + *MESH_VERTEX 5 32.0000 16.0000 64.0000 + *MESH_VERTEX 6 32.0000 16.0000 32.0000 + *MESH_VERTEX 7 32.0000 16.0000 -0.0000 + *MESH_VERTEX 8 32.0000 16.0000 -32.0000 + *MESH_VERTEX 9 32.0000 16.0000 -64.0000 + *MESH_VERTEX 10 32.0000 -0.0000 64.0000 + *MESH_VERTEX 11 32.0000 -0.0000 32.0000 + *MESH_VERTEX 12 32.0000 -0.0000 -0.0000 + *MESH_VERTEX 13 32.0000 -0.0000 -32.0000 + *MESH_VERTEX 14 32.0000 -0.0000 -64.0000 + *MESH_VERTEX 15 32.0000 -16.0000 64.0000 + *MESH_VERTEX 16 32.0000 -16.0000 32.0000 + *MESH_VERTEX 17 32.0000 -16.0000 -0.0000 + *MESH_VERTEX 18 32.0000 -16.0000 -32.0000 + *MESH_VERTEX 19 32.0000 -16.0000 -64.0000 + *MESH_VERTEX 20 32.0000 -32.0000 64.0000 + *MESH_VERTEX 21 32.0000 -32.0000 32.0000 + *MESH_VERTEX 22 32.0000 -32.0000 -0.0000 + *MESH_VERTEX 23 32.0000 -32.0000 -32.0000 + *MESH_VERTEX 24 32.0000 -32.0000 -64.0000 + *MESH_VERTEX 25 -32.0000 32.0000 64.0000 + *MESH_VERTEX 26 -32.0000 32.0000 32.0000 + *MESH_VERTEX 27 -32.0000 32.0000 0.0000 + *MESH_VERTEX 28 -32.0000 32.0000 -32.0000 + *MESH_VERTEX 29 -32.0000 32.0000 -64.0000 + *MESH_VERTEX 30 -32.0000 16.0000 64.0000 + *MESH_VERTEX 31 -32.0000 16.0000 32.0000 + *MESH_VERTEX 32 -32.0000 16.0000 0.0000 + *MESH_VERTEX 33 -32.0000 16.0000 -32.0000 + *MESH_VERTEX 34 -32.0000 16.0000 -64.0000 + *MESH_VERTEX 35 -32.0000 0.0000 64.0000 + *MESH_VERTEX 36 -32.0000 0.0000 32.0000 + *MESH_VERTEX 37 -32.0000 0.0000 0.0000 + *MESH_VERTEX 38 -32.0000 0.0000 -32.0000 + *MESH_VERTEX 39 -32.0000 0.0000 -64.0000 + *MESH_VERTEX 40 -32.0000 -16.0000 64.0000 + *MESH_VERTEX 41 -32.0000 -16.0000 32.0000 + *MESH_VERTEX 42 -32.0000 -16.0000 0.0000 + *MESH_VERTEX 43 -32.0000 -16.0000 -32.0000 + *MESH_VERTEX 44 -32.0000 -16.0000 -64.0000 + *MESH_VERTEX 45 -32.0000 -32.0000 64.0000 + *MESH_VERTEX 46 -32.0000 -32.0000 32.0000 + *MESH_VERTEX 47 -32.0000 -32.0000 0.0000 + *MESH_VERTEX 48 -32.0000 -32.0000 -32.0000 + *MESH_VERTEX 49 -32.0000 -32.0000 -64.0000 + *MESH_VERTEX 50 16.0000 32.0000 64.0000 + *MESH_VERTEX 51 16.0000 32.0000 32.0000 + *MESH_VERTEX 52 16.0000 32.0000 -0.0000 + *MESH_VERTEX 53 16.0000 32.0000 -32.0000 + *MESH_VERTEX 54 16.0000 32.0000 -64.0000 + *MESH_VERTEX 55 16.0000 16.0000 -64.0000 + *MESH_VERTEX 56 16.0000 -0.0000 -64.0000 + *MESH_VERTEX 57 16.0000 -16.0000 -64.0000 + *MESH_VERTEX 58 16.0000 -32.0000 -64.0000 + *MESH_VERTEX 59 16.0000 -32.0000 -32.0000 + *MESH_VERTEX 60 16.0000 -32.0000 -0.0000 + *MESH_VERTEX 61 16.0000 -32.0000 32.0000 + *MESH_VERTEX 62 16.0000 -32.0000 64.0000 + *MESH_VERTEX 63 16.0000 -16.0000 64.0000 + *MESH_VERTEX 64 16.0000 -0.0000 64.0000 + *MESH_VERTEX 65 16.0000 16.0000 64.0000 + *MESH_VERTEX 66 0.0000 32.0000 64.0000 + *MESH_VERTEX 67 0.0000 32.0000 32.0000 + *MESH_VERTEX 68 0.0000 32.0000 0.0000 + *MESH_VERTEX 69 0.0000 32.0000 -32.0000 + *MESH_VERTEX 70 0.0000 32.0000 -64.0000 + *MESH_VERTEX 71 -0.0000 16.0000 -64.0000 + *MESH_VERTEX 72 -0.0000 0.0000 -64.0000 + *MESH_VERTEX 73 -0.0000 -16.0000 -64.0000 + *MESH_VERTEX 74 -0.0000 -32.0000 -64.0000 + *MESH_VERTEX 75 -0.0000 -32.0000 -32.0000 + *MESH_VERTEX 76 -0.0000 -32.0000 0.0000 + *MESH_VERTEX 77 -0.0000 -32.0000 32.0000 + *MESH_VERTEX 78 -0.0000 -32.0000 64.0000 + *MESH_VERTEX 79 0.0000 -16.0000 64.0000 + *MESH_VERTEX 80 0.0000 0.0000 64.0000 + *MESH_VERTEX 81 0.0000 16.0000 64.0000 + *MESH_VERTEX 82 -16.0000 32.0000 64.0000 + *MESH_VERTEX 83 -16.0000 32.0000 32.0000 + *MESH_VERTEX 84 -16.0000 32.0000 0.0000 + *MESH_VERTEX 85 -16.0000 32.0000 -32.0000 + *MESH_VERTEX 86 -16.0000 32.0000 -64.0000 + *MESH_VERTEX 87 -16.0000 16.0000 -64.0000 + *MESH_VERTEX 88 -16.0000 0.0000 -64.0000 + *MESH_VERTEX 89 -16.0000 -16.0000 -64.0000 + *MESH_VERTEX 90 -16.0000 -32.0000 -64.0000 + *MESH_VERTEX 91 -16.0000 -32.0000 -32.0000 + *MESH_VERTEX 92 -16.0000 -32.0000 0.0000 + *MESH_VERTEX 93 -16.0000 -32.0000 32.0000 + *MESH_VERTEX 94 -16.0000 -32.0000 64.0000 + *MESH_VERTEX 95 -16.0000 -16.0000 64.0000 + *MESH_VERTEX 96 -16.0000 0.0000 64.0000 + *MESH_VERTEX 97 -16.0000 16.0000 64.0000 + *MESH_VERTEX 98 32.0000 32.0000 64.0000 + *MESH_VERTEX 99 32.0000 32.0000 32.0000 + *MESH_VERTEX 100 32.0000 32.0000 -0.0000 + *MESH_VERTEX 101 32.0000 32.0000 -32.0000 + *MESH_VERTEX 102 32.0000 32.0000 -64.0000 + *MESH_VERTEX 103 32.0000 16.0000 64.0000 + *MESH_VERTEX 104 32.0000 16.0000 -64.0000 + *MESH_VERTEX 105 32.0000 -0.0000 64.0000 + *MESH_VERTEX 106 32.0000 -0.0000 -64.0000 + *MESH_VERTEX 107 32.0000 -16.0000 64.0000 + *MESH_VERTEX 108 32.0000 -16.0000 -64.0000 + *MESH_VERTEX 109 32.0000 -32.0000 64.0000 + *MESH_VERTEX 110 32.0000 -32.0000 32.0000 + *MESH_VERTEX 111 32.0000 -32.0000 -0.0000 + *MESH_VERTEX 112 32.0000 -32.0000 -32.0000 + *MESH_VERTEX 113 32.0000 -32.0000 -64.0000 + *MESH_VERTEX 114 -32.0000 32.0000 64.0000 + *MESH_VERTEX 115 -32.0000 32.0000 32.0000 + *MESH_VERTEX 116 -32.0000 32.0000 0.0000 + *MESH_VERTEX 117 -32.0000 32.0000 -32.0000 + *MESH_VERTEX 118 -32.0000 32.0000 -64.0000 + *MESH_VERTEX 119 -32.0000 16.0000 64.0000 + *MESH_VERTEX 120 -32.0000 16.0000 -64.0000 + *MESH_VERTEX 121 -32.0000 0.0000 64.0000 + *MESH_VERTEX 122 -32.0000 0.0000 -64.0000 + *MESH_VERTEX 123 -32.0000 -16.0000 64.0000 + *MESH_VERTEX 124 -32.0000 -16.0000 -64.0000 + *MESH_VERTEX 125 -32.0000 -32.0000 64.0000 + *MESH_VERTEX 126 -32.0000 -32.0000 32.0000 + *MESH_VERTEX 127 -32.0000 -32.0000 0.0000 + *MESH_VERTEX 128 -32.0000 -32.0000 -32.0000 + *MESH_VERTEX 129 -32.0000 -32.0000 -64.0000 + *MESH_VERTEX 130 32.0000 32.0000 64.0000 + *MESH_VERTEX 131 32.0000 32.0000 -64.0000 + *MESH_VERTEX 132 32.0000 -32.0000 64.0000 + *MESH_VERTEX 133 32.0000 -32.0000 -64.0000 + *MESH_VERTEX 134 -32.0000 32.0000 64.0000 + *MESH_VERTEX 135 -32.0000 32.0000 -64.0000 + *MESH_VERTEX 136 -32.0000 -32.0000 64.0000 + *MESH_VERTEX 137 -32.0000 -32.0000 -64.0000 + *MESH_VERTEX 138 16.0000 32.0000 64.0000 + *MESH_VERTEX 139 16.0000 32.0000 -64.0000 + *MESH_VERTEX 140 16.0000 -32.0000 -64.0000 + *MESH_VERTEX 141 16.0000 -32.0000 64.0000 + *MESH_VERTEX 142 0.0000 32.0000 64.0000 + *MESH_VERTEX 143 0.0000 32.0000 -64.0000 + *MESH_VERTEX 144 -0.0000 -32.0000 -64.0000 + *MESH_VERTEX 145 -0.0000 -32.0000 64.0000 + *MESH_VERTEX 146 -16.0000 32.0000 64.0000 + *MESH_VERTEX 147 -16.0000 32.0000 -64.0000 + *MESH_VERTEX 148 -16.0000 -32.0000 -64.0000 + *MESH_VERTEX 149 -16.0000 -32.0000 64.0000 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 98 B: 103 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 1: A: 6 B: 99 C: 98 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 2: A: 99 B: 6 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 3: A: 7 B: 100 C: 99 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 4: A: 100 B: 7 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 5: A: 8 B: 101 C: 100 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 6: A: 101 B: 8 C: 104 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 7: A: 104 B: 102 C: 101 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 8: A: 103 B: 105 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 9: A: 11 B: 6 C: 103 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 10: A: 6 B: 11 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 11: A: 12 B: 7 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 12: A: 7 B: 12 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 13: A: 13 B: 8 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 14: A: 8 B: 13 C: 106 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 15: A: 106 B: 104 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 16: A: 105 B: 107 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 17: A: 16 B: 11 C: 105 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 18: A: 11 B: 16 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 19: A: 17 B: 12 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 20: A: 12 B: 17 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 21: A: 18 B: 13 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 22: A: 13 B: 18 C: 108 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 23: A: 108 B: 106 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 24: A: 107 B: 109 C: 110 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 25: A: 110 B: 16 C: 107 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 26: A: 16 B: 110 C: 111 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 27: A: 111 B: 17 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 28: A: 17 B: 111 C: 112 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 29: A: 112 B: 18 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 30: A: 18 B: 112 C: 113 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 31: A: 113 B: 108 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 1 + *MESH_FACE 32: A: 114 B: 115 C: 31 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 33: A: 31 B: 119 C: 114 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 34: A: 115 B: 116 C: 32 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 35: A: 32 B: 31 C: 115 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 36: A: 116 B: 117 C: 33 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 37: A: 33 B: 32 C: 116 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 38: A: 117 B: 118 C: 120 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 39: A: 120 B: 33 C: 117 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 40: A: 119 B: 31 C: 36 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 41: A: 36 B: 121 C: 119 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 42: A: 31 B: 32 C: 37 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 43: A: 37 B: 36 C: 31 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 44: A: 32 B: 33 C: 38 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 45: A: 38 B: 37 C: 32 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 46: A: 33 B: 120 C: 122 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 47: A: 122 B: 38 C: 33 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 48: A: 121 B: 36 C: 41 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 49: A: 41 B: 123 C: 121 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 50: A: 36 B: 37 C: 42 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 51: A: 42 B: 41 C: 36 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 52: A: 37 B: 38 C: 43 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 53: A: 43 B: 42 C: 37 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 54: A: 38 B: 122 C: 124 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 55: A: 124 B: 43 C: 38 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 56: A: 123 B: 41 C: 126 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 57: A: 126 B: 125 C: 123 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 58: A: 41 B: 42 C: 127 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 59: A: 127 B: 126 C: 41 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 60: A: 42 B: 43 C: 128 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 61: A: 128 B: 127 C: 42 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 62: A: 43 B: 124 C: 129 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 63: A: 129 B: 128 C: 43 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 64: A: 0 B: 1 C: 51 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 65: A: 51 B: 50 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 66: A: 1 B: 2 C: 52 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 67: A: 52 B: 51 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 68: A: 2 B: 3 C: 53 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 69: A: 53 B: 52 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 70: A: 3 B: 4 C: 54 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 71: A: 54 B: 53 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 72: A: 50 B: 51 C: 67 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 73: A: 67 B: 66 C: 50 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 74: A: 51 B: 52 C: 68 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 75: A: 68 B: 67 C: 51 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 76: A: 52 B: 53 C: 69 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 77: A: 69 B: 68 C: 52 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 78: A: 53 B: 54 C: 70 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 79: A: 70 B: 69 C: 53 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 80: A: 66 B: 67 C: 83 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 81: A: 83 B: 82 C: 66 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 82: A: 67 B: 68 C: 84 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 83: A: 84 B: 83 C: 67 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 84: A: 68 B: 69 C: 85 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 85: A: 85 B: 84 C: 68 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 86: A: 69 B: 70 C: 86 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 87: A: 86 B: 85 C: 69 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 88: A: 82 B: 83 C: 26 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 89: A: 26 B: 25 C: 82 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 90: A: 83 B: 84 C: 27 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 91: A: 27 B: 26 C: 83 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 92: A: 84 B: 85 C: 28 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 93: A: 28 B: 27 C: 84 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 94: A: 85 B: 86 C: 29 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 95: A: 29 B: 28 C: 85 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 4 + *MESH_FACE 96: A: 131 B: 9 C: 55 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 97: A: 55 B: 139 C: 131 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 98: A: 9 B: 14 C: 56 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 99: A: 56 B: 55 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 100: A: 14 B: 19 C: 57 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 101: A: 57 B: 56 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 102: A: 19 B: 133 C: 140 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 103: A: 140 B: 57 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 104: A: 139 B: 55 C: 71 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 105: A: 71 B: 143 C: 139 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 106: A: 55 B: 56 C: 72 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 107: A: 72 B: 71 C: 55 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 108: A: 56 B: 57 C: 73 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 109: A: 73 B: 72 C: 56 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 110: A: 57 B: 140 C: 144 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 111: A: 144 B: 73 C: 57 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 112: A: 143 B: 71 C: 87 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 113: A: 87 B: 147 C: 143 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 114: A: 71 B: 72 C: 88 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 115: A: 88 B: 87 C: 71 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 116: A: 72 B: 73 C: 89 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 117: A: 89 B: 88 C: 72 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 118: A: 73 B: 144 C: 148 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 119: A: 148 B: 89 C: 73 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 120: A: 147 B: 87 C: 34 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 121: A: 34 B: 135 C: 147 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 122: A: 87 B: 88 C: 39 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 123: A: 39 B: 34 C: 87 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 124: A: 88 B: 89 C: 44 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 125: A: 44 B: 39 C: 88 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 126: A: 89 B: 148 C: 137 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 127: A: 137 B: 44 C: 89 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 5 *MESH_MTLID 3 + *MESH_FACE 128: A: 24 B: 23 C: 59 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 129: A: 59 B: 58 C: 24 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 130: A: 23 B: 22 C: 60 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 131: A: 60 B: 59 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 132: A: 22 B: 21 C: 61 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 133: A: 61 B: 60 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 134: A: 21 B: 20 C: 62 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 135: A: 62 B: 61 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 136: A: 58 B: 59 C: 75 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 137: A: 75 B: 74 C: 58 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 138: A: 59 B: 60 C: 76 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 139: A: 76 B: 75 C: 59 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 140: A: 60 B: 61 C: 77 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 141: A: 77 B: 76 C: 60 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 142: A: 61 B: 62 C: 78 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 143: A: 78 B: 77 C: 61 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 144: A: 74 B: 75 C: 91 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 145: A: 91 B: 90 C: 74 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 146: A: 75 B: 76 C: 92 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 147: A: 92 B: 91 C: 75 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 148: A: 76 B: 77 C: 93 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 149: A: 93 B: 92 C: 76 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 150: A: 77 B: 78 C: 94 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 151: A: 94 B: 93 C: 77 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 152: A: 90 B: 91 C: 48 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 153: A: 48 B: 49 C: 90 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 154: A: 91 B: 92 C: 47 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 155: A: 47 B: 48 C: 91 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 156: A: 92 B: 93 C: 46 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 157: A: 46 B: 47 C: 92 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 158: A: 93 B: 94 C: 45 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 159: A: 45 B: 46 C: 93 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 6 *MESH_MTLID 5 + *MESH_FACE 160: A: 132 B: 15 C: 63 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 161: A: 63 B: 141 C: 132 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 162: A: 15 B: 10 C: 64 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 163: A: 64 B: 63 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 164: A: 10 B: 5 C: 65 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 165: A: 65 B: 64 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 166: A: 5 B: 130 C: 138 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 167: A: 138 B: 65 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 168: A: 141 B: 63 C: 79 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 169: A: 79 B: 145 C: 141 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 170: A: 63 B: 64 C: 80 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 171: A: 80 B: 79 C: 63 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 172: A: 64 B: 65 C: 81 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 173: A: 81 B: 80 C: 64 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 174: A: 65 B: 138 C: 142 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 175: A: 142 B: 81 C: 65 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 176: A: 145 B: 79 C: 95 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 177: A: 95 B: 149 C: 145 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 178: A: 79 B: 80 C: 96 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 179: A: 96 B: 95 C: 79 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 180: A: 80 B: 81 C: 97 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 181: A: 97 B: 96 C: 80 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 182: A: 81 B: 142 C: 146 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 183: A: 146 B: 97 C: 81 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 184: A: 149 B: 95 C: 40 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 185: A: 40 B: 136 C: 149 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 186: A: 95 B: 96 C: 35 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 187: A: 35 B: 40 C: 95 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 188: A: 96 B: 97 C: 30 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 189: A: 30 B: 35 C: 96 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 190: A: 97 B: 146 C: 134 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + *MESH_FACE 191: A: 134 B: 30 C: 97 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 7 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 150 + *MESH_TVERTLIST { + *MESH_TVERT 0 0.7500 0.7500 0.0000 + *MESH_TVERT 1 0.7500 0.5000 0.0000 + *MESH_TVERT 2 0.7500 0.2500 0.0000 + *MESH_TVERT 3 0.5000 0.7500 0.0000 + *MESH_TVERT 4 0.5000 0.5000 0.0000 + *MESH_TVERT 5 0.5000 0.2500 0.0000 + *MESH_TVERT 6 0.2500 0.7500 0.0000 + *MESH_TVERT 7 0.2500 0.5000 0.0000 + *MESH_TVERT 8 0.2500 0.2500 0.0000 + *MESH_TVERT 9 0.2500 0.7500 0.0000 + *MESH_TVERT 10 0.2500 0.5000 0.0000 + *MESH_TVERT 11 0.2500 0.2500 0.0000 + *MESH_TVERT 12 0.5000 0.7500 0.0000 + *MESH_TVERT 13 0.5000 0.5000 0.0000 + *MESH_TVERT 14 0.5000 0.2500 0.0000 + *MESH_TVERT 15 0.7500 0.7500 0.0000 + *MESH_TVERT 16 0.7500 0.5000 0.0000 + *MESH_TVERT 17 0.7500 0.2500 0.0000 + *MESH_TVERT 18 0.0000 1.0000 0.0000 + *MESH_TVERT 19 -0.0000 0.7500 0.0000 + *MESH_TVERT 20 -0.0000 0.5000 0.0000 + *MESH_TVERT 21 -0.0000 0.2500 0.0000 + *MESH_TVERT 22 -0.0000 0.0000 0.0000 + *MESH_TVERT 23 0.2500 1.0000 0.0000 + *MESH_TVERT 24 0.2500 0.7500 0.0000 + *MESH_TVERT 25 0.2500 0.5000 0.0000 + *MESH_TVERT 26 0.2500 0.2500 0.0000 + *MESH_TVERT 27 0.2500 -0.0000 0.0000 + *MESH_TVERT 28 0.5000 1.0000 0.0000 + *MESH_TVERT 29 0.5000 0.7500 0.0000 + *MESH_TVERT 30 0.5000 0.5000 0.0000 + *MESH_TVERT 31 0.5000 0.2500 0.0000 + *MESH_TVERT 32 0.5000 -0.0000 0.0000 + *MESH_TVERT 33 0.7500 1.0000 0.0000 + *MESH_TVERT 34 0.7500 0.7500 0.0000 + *MESH_TVERT 35 0.7500 0.5000 0.0000 + *MESH_TVERT 36 0.7500 0.2500 0.0000 + *MESH_TVERT 37 0.7500 -0.0000 0.0000 + *MESH_TVERT 38 1.0000 1.0000 0.0000 + *MESH_TVERT 39 1.0000 0.7500 0.0000 + *MESH_TVERT 40 1.0000 0.5000 0.0000 + *MESH_TVERT 41 1.0000 0.2500 0.0000 + *MESH_TVERT 42 1.0000 -0.0000 0.0000 + *MESH_TVERT 43 0.7500 -0.0000 0.0000 + *MESH_TVERT 44 0.5000 -0.0000 0.0000 + *MESH_TVERT 45 0.2500 -0.0000 0.0000 + *MESH_TVERT 46 0.7500 0.2500 0.0000 + *MESH_TVERT 47 0.5000 0.2500 0.0000 + *MESH_TVERT 48 0.2500 0.2500 0.0000 + *MESH_TVERT 49 0.7500 0.5000 0.0000 + *MESH_TVERT 50 0.5000 0.5000 0.0000 + *MESH_TVERT 51 0.2500 0.5000 0.0000 + *MESH_TVERT 52 0.7500 0.7500 0.0000 + *MESH_TVERT 53 0.5000 0.7500 0.0000 + *MESH_TVERT 54 0.2500 0.7500 0.0000 + *MESH_TVERT 55 0.7500 1.0000 0.0000 + *MESH_TVERT 56 0.5000 1.0000 0.0000 + *MESH_TVERT 57 0.2500 1.0000 0.0000 + *MESH_TVERT 58 1.0000 -0.0000 0.0000 + *MESH_TVERT 59 1.0000 0.2500 0.0000 + *MESH_TVERT 60 1.0000 0.5000 0.0000 + *MESH_TVERT 61 1.0000 0.7500 0.0000 + *MESH_TVERT 62 1.0000 1.0000 0.0000 + *MESH_TVERT 63 0.7500 0.0000 0.0000 + *MESH_TVERT 64 0.7500 0.2500 0.0000 + *MESH_TVERT 65 0.7500 0.5000 0.0000 + *MESH_TVERT 66 0.7500 0.7500 0.0000 + *MESH_TVERT 67 0.7500 1.0000 0.0000 + *MESH_TVERT 68 0.5000 0.0000 0.0000 + *MESH_TVERT 69 0.5000 0.2500 0.0000 + *MESH_TVERT 70 0.5000 0.5000 0.0000 + *MESH_TVERT 71 0.5000 0.7500 0.0000 + *MESH_TVERT 72 0.5000 1.0000 0.0000 + *MESH_TVERT 73 0.2500 0.0000 0.0000 + *MESH_TVERT 74 0.2500 0.2500 0.0000 + *MESH_TVERT 75 0.2500 0.5000 0.0000 + *MESH_TVERT 76 0.2500 0.7500 0.0000 + *MESH_TVERT 77 0.2500 1.0000 0.0000 + *MESH_TVERT 78 -0.0000 0.0000 0.0000 + *MESH_TVERT 79 -0.0000 0.2500 0.0000 + *MESH_TVERT 80 -0.0000 0.5000 0.0000 + *MESH_TVERT 81 0.0000 0.7500 0.0000 + *MESH_TVERT 82 0.0000 1.0000 0.0000 + *MESH_TVERT 83 0.7500 1.0000 0.0000 + *MESH_TVERT 84 0.5000 1.0000 0.0000 + *MESH_TVERT 85 0.2500 1.0000 0.0000 + *MESH_TVERT 86 0.7500 0.7500 0.0000 + *MESH_TVERT 87 0.5000 0.7500 0.0000 + *MESH_TVERT 88 0.2500 0.7500 0.0000 + *MESH_TVERT 89 0.7500 0.5000 0.0000 + *MESH_TVERT 90 0.5000 0.5000 0.0000 + *MESH_TVERT 91 0.2500 0.5000 0.0000 + *MESH_TVERT 92 0.7500 0.2500 0.0000 + *MESH_TVERT 93 0.5000 0.2500 0.0000 + *MESH_TVERT 94 0.2500 0.2500 0.0000 + *MESH_TVERT 95 0.7500 -0.0000 0.0000 + *MESH_TVERT 96 0.5000 -0.0000 0.0000 + *MESH_TVERT 97 0.2500 -0.0000 0.0000 + *MESH_TVERT 98 1.0000 1.0000 0.0000 + *MESH_TVERT 99 1.0000 0.7500 0.0000 + *MESH_TVERT 100 1.0000 0.5000 0.0000 + *MESH_TVERT 101 1.0000 0.2500 0.0000 + *MESH_TVERT 102 1.0000 -0.0000 0.0000 + *MESH_TVERT 103 0.7500 1.0000 0.0000 + *MESH_TVERT 104 0.7500 -0.0000 0.0000 + *MESH_TVERT 105 0.5000 1.0000 0.0000 + *MESH_TVERT 106 0.5000 -0.0000 0.0000 + *MESH_TVERT 107 0.2500 1.0000 0.0000 + *MESH_TVERT 108 0.2500 -0.0000 0.0000 + *MESH_TVERT 109 0.0000 1.0000 0.0000 + *MESH_TVERT 110 0.0000 0.7500 0.0000 + *MESH_TVERT 111 0.0000 0.5000 0.0000 + *MESH_TVERT 112 0.0000 0.2500 0.0000 + *MESH_TVERT 113 0.0000 0.0000 0.0000 + *MESH_TVERT 114 0.0000 1.0000 0.0000 + *MESH_TVERT 115 0.0000 0.7500 0.0000 + *MESH_TVERT 116 0.0000 0.5000 0.0000 + *MESH_TVERT 117 0.0000 0.2500 0.0000 + *MESH_TVERT 118 -0.0000 0.0000 0.0000 + *MESH_TVERT 119 0.2500 1.0000 0.0000 + *MESH_TVERT 120 0.2500 0.0000 0.0000 + *MESH_TVERT 121 0.5000 1.0000 0.0000 + *MESH_TVERT 122 0.5000 -0.0000 0.0000 + *MESH_TVERT 123 0.7500 1.0000 0.0000 + *MESH_TVERT 124 0.7500 -0.0000 0.0000 + *MESH_TVERT 125 1.0000 1.0000 0.0000 + *MESH_TVERT 126 1.0000 0.7500 0.0000 + *MESH_TVERT 127 1.0000 0.5000 0.0000 + *MESH_TVERT 128 1.0000 0.2500 0.0000 + *MESH_TVERT 129 1.0000 -0.0000 0.0000 + *MESH_TVERT 130 0.0000 1.0000 0.0000 + *MESH_TVERT 131 1.0000 -0.0000 0.0000 + *MESH_TVERT 132 1.0000 1.0000 0.0000 + *MESH_TVERT 133 -0.0000 0.0000 0.0000 + *MESH_TVERT 134 -0.0000 -0.0000 0.0000 + *MESH_TVERT 135 1.0000 1.0000 0.0000 + *MESH_TVERT 136 1.0000 -0.0000 0.0000 + *MESH_TVERT 137 0.0000 1.0000 0.0000 + *MESH_TVERT 138 0.0000 0.7500 0.0000 + *MESH_TVERT 139 1.0000 0.2500 0.0000 + *MESH_TVERT 140 -0.0000 0.2500 0.0000 + *MESH_TVERT 141 1.0000 0.7500 0.0000 + *MESH_TVERT 142 0.0000 0.5000 0.0000 + *MESH_TVERT 143 1.0000 0.5000 0.0000 + *MESH_TVERT 144 0.0000 0.5000 0.0000 + *MESH_TVERT 145 1.0000 0.5000 0.0000 + *MESH_TVERT 146 -0.0000 0.2500 0.0000 + *MESH_TVERT 147 1.0000 0.7500 0.0000 + *MESH_TVERT 148 0.0000 0.7500 0.0000 + *MESH_TVERT 149 1.0000 0.2500 0.0000 + } + *MESH_NUMTVFACES 192 + *MESH_TFACELIST { + *MESH_TFACE 0 98 103 0 + *MESH_TFACE 1 0 99 98 + *MESH_TFACE 2 99 0 1 + *MESH_TFACE 3 1 100 99 + *MESH_TFACE 4 100 1 2 + *MESH_TFACE 5 2 101 100 + *MESH_TFACE 6 101 2 104 + *MESH_TFACE 7 104 102 101 + *MESH_TFACE 8 103 105 3 + *MESH_TFACE 9 3 0 103 + *MESH_TFACE 10 0 3 4 + *MESH_TFACE 11 4 1 0 + *MESH_TFACE 12 1 4 5 + *MESH_TFACE 13 5 2 1 + *MESH_TFACE 14 2 5 106 + *MESH_TFACE 15 106 104 2 + *MESH_TFACE 16 105 107 6 + *MESH_TFACE 17 6 3 105 + *MESH_TFACE 18 3 6 7 + *MESH_TFACE 19 7 4 3 + *MESH_TFACE 20 4 7 8 + *MESH_TFACE 21 8 5 4 + *MESH_TFACE 22 5 8 108 + *MESH_TFACE 23 108 106 5 + *MESH_TFACE 24 107 109 110 + *MESH_TFACE 25 110 6 107 + *MESH_TFACE 26 6 110 111 + *MESH_TFACE 27 111 7 6 + *MESH_TFACE 28 7 111 112 + *MESH_TFACE 29 112 8 7 + *MESH_TFACE 30 8 112 113 + *MESH_TFACE 31 113 108 8 + *MESH_TFACE 32 114 115 9 + *MESH_TFACE 33 9 119 114 + *MESH_TFACE 34 115 116 10 + *MESH_TFACE 35 10 9 115 + *MESH_TFACE 36 116 117 11 + *MESH_TFACE 37 11 10 116 + *MESH_TFACE 38 117 118 120 + *MESH_TFACE 39 120 11 117 + *MESH_TFACE 40 119 9 12 + *MESH_TFACE 41 12 121 119 + *MESH_TFACE 42 9 10 13 + *MESH_TFACE 43 13 12 9 + *MESH_TFACE 44 10 11 14 + *MESH_TFACE 45 14 13 10 + *MESH_TFACE 46 11 120 122 + *MESH_TFACE 47 122 14 11 + *MESH_TFACE 48 121 12 15 + *MESH_TFACE 49 15 123 121 + *MESH_TFACE 50 12 13 16 + *MESH_TFACE 51 16 15 12 + *MESH_TFACE 52 13 14 17 + *MESH_TFACE 53 17 16 13 + *MESH_TFACE 54 14 122 124 + *MESH_TFACE 55 124 17 14 + *MESH_TFACE 56 123 15 126 + *MESH_TFACE 57 126 125 123 + *MESH_TFACE 58 15 16 127 + *MESH_TFACE 59 127 126 15 + *MESH_TFACE 60 16 17 128 + *MESH_TFACE 61 128 127 16 + *MESH_TFACE 62 17 124 129 + *MESH_TFACE 63 129 128 17 + *MESH_TFACE 64 18 19 24 + *MESH_TFACE 65 24 23 18 + *MESH_TFACE 66 19 20 25 + *MESH_TFACE 67 25 24 19 + *MESH_TFACE 68 20 21 26 + *MESH_TFACE 69 26 25 20 + *MESH_TFACE 70 21 22 27 + *MESH_TFACE 71 27 26 21 + *MESH_TFACE 72 23 24 29 + *MESH_TFACE 73 29 28 23 + *MESH_TFACE 74 24 25 30 + *MESH_TFACE 75 30 29 24 + *MESH_TFACE 76 25 26 31 + *MESH_TFACE 77 31 30 25 + *MESH_TFACE 78 26 27 32 + *MESH_TFACE 79 32 31 26 + *MESH_TFACE 80 28 29 34 + *MESH_TFACE 81 34 33 28 + *MESH_TFACE 82 29 30 35 + *MESH_TFACE 83 35 34 29 + *MESH_TFACE 84 30 31 36 + *MESH_TFACE 85 36 35 30 + *MESH_TFACE 86 31 32 37 + *MESH_TFACE 87 37 36 31 + *MESH_TFACE 88 33 34 39 + *MESH_TFACE 89 39 38 33 + *MESH_TFACE 90 34 35 40 + *MESH_TFACE 91 40 39 34 + *MESH_TFACE 92 35 36 41 + *MESH_TFACE 93 41 40 35 + *MESH_TFACE 94 36 37 42 + *MESH_TFACE 95 42 41 36 + *MESH_TFACE 96 131 43 46 + *MESH_TFACE 97 46 139 131 + *MESH_TFACE 98 43 44 47 + *MESH_TFACE 99 47 46 43 + *MESH_TFACE 100 44 45 48 + *MESH_TFACE 101 48 47 44 + *MESH_TFACE 102 45 133 140 + *MESH_TFACE 103 140 48 45 + *MESH_TFACE 104 139 46 49 + *MESH_TFACE 105 49 143 139 + *MESH_TFACE 106 46 47 50 + *MESH_TFACE 107 50 49 46 + *MESH_TFACE 108 47 48 51 + *MESH_TFACE 109 51 50 47 + *MESH_TFACE 110 48 140 144 + *MESH_TFACE 111 144 51 48 + *MESH_TFACE 112 143 49 52 + *MESH_TFACE 113 52 147 143 + *MESH_TFACE 114 49 50 53 + *MESH_TFACE 115 53 52 49 + *MESH_TFACE 116 50 51 54 + *MESH_TFACE 117 54 53 50 + *MESH_TFACE 118 51 144 148 + *MESH_TFACE 119 148 54 51 + *MESH_TFACE 120 147 52 55 + *MESH_TFACE 121 55 135 147 + *MESH_TFACE 122 52 53 56 + *MESH_TFACE 123 56 55 52 + *MESH_TFACE 124 53 54 57 + *MESH_TFACE 125 57 56 53 + *MESH_TFACE 126 54 148 137 + *MESH_TFACE 127 137 57 54 + *MESH_TFACE 128 58 59 64 + *MESH_TFACE 129 64 63 58 + *MESH_TFACE 130 59 60 65 + *MESH_TFACE 131 65 64 59 + *MESH_TFACE 132 60 61 66 + *MESH_TFACE 133 66 65 60 + *MESH_TFACE 134 61 62 67 + *MESH_TFACE 135 67 66 61 + *MESH_TFACE 136 63 64 69 + *MESH_TFACE 137 69 68 63 + *MESH_TFACE 138 64 65 70 + *MESH_TFACE 139 70 69 64 + *MESH_TFACE 140 65 66 71 + *MESH_TFACE 141 71 70 65 + *MESH_TFACE 142 66 67 72 + *MESH_TFACE 143 72 71 66 + *MESH_TFACE 144 68 69 74 + *MESH_TFACE 145 74 73 68 + *MESH_TFACE 146 69 70 75 + *MESH_TFACE 147 75 74 69 + *MESH_TFACE 148 70 71 76 + *MESH_TFACE 149 76 75 70 + *MESH_TFACE 150 71 72 77 + *MESH_TFACE 151 77 76 71 + *MESH_TFACE 152 73 74 79 + *MESH_TFACE 153 79 78 73 + *MESH_TFACE 154 74 75 80 + *MESH_TFACE 155 80 79 74 + *MESH_TFACE 156 75 76 81 + *MESH_TFACE 157 81 80 75 + *MESH_TFACE 158 76 77 82 + *MESH_TFACE 159 82 81 76 + *MESH_TFACE 160 132 83 86 + *MESH_TFACE 161 86 141 132 + *MESH_TFACE 162 83 84 87 + *MESH_TFACE 163 87 86 83 + *MESH_TFACE 164 84 85 88 + *MESH_TFACE 165 88 87 84 + *MESH_TFACE 166 85 130 138 + *MESH_TFACE 167 138 88 85 + *MESH_TFACE 168 141 86 89 + *MESH_TFACE 169 89 145 141 + *MESH_TFACE 170 86 87 90 + *MESH_TFACE 171 90 89 86 + *MESH_TFACE 172 87 88 91 + *MESH_TFACE 173 91 90 87 + *MESH_TFACE 174 88 138 142 + *MESH_TFACE 175 142 91 88 + *MESH_TFACE 176 145 89 92 + *MESH_TFACE 177 92 149 145 + *MESH_TFACE 178 89 90 93 + *MESH_TFACE 179 93 92 89 + *MESH_TFACE 180 90 91 94 + *MESH_TFACE 181 94 93 90 + *MESH_TFACE 182 91 142 146 + *MESH_TFACE 183 146 94 91 + *MESH_TFACE 184 149 92 95 + *MESH_TFACE 185 95 136 149 + *MESH_TFACE 186 92 93 96 + *MESH_TFACE 187 96 95 92 + *MESH_TFACE 188 93 94 97 + *MESH_TFACE 189 97 96 93 + *MESH_TFACE 190 94 146 134 + *MESH_TFACE 191 134 97 94 + } + *MESH_NORMALS { + *MESH_FACENORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 98 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 103 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 1 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 99 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 98 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 2 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 99 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 3 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 100 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 99 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 4 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 100 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 5 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 101 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 100 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 101 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 104 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 104 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 102 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 101 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 103 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 105 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 9 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 103 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 10 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 14 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 106 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 15 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 106 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 104 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 105 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 107 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 105 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 19 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 20 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 21 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 22 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 108 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 23 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 108 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 106 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 24 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 107 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 109 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 110 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 25 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 110 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 107 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 26 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 110 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 111 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 27 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 111 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 28 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 111 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 112 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 29 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 112 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 30 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 112 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 113 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 31 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 113 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 108 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 114 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 115 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 119 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 114 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 34 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 115 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 116 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 35 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 115 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 116 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 117 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 116 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 117 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 118 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 120 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 39 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 120 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 117 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 40 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 119 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 121 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 119 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 44 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 45 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 46 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 120 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 122 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 47 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 122 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 48 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 121 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 49 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 123 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 121 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 50 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 51 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 52 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 53 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 54 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 122 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 124 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 55 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 124 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 56 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 123 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 126 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 57 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 126 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 125 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 123 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 58 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 127 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 59 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 127 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 126 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 60 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 128 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 61 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 128 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 127 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 62 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 124 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 129 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 63 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 129 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 128 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 64 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 0 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 65 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 50 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 0 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 66 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 70 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 4 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 54 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 71 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 54 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 72 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 50 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 73 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 66 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 50 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 74 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 75 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 76 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 77 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 78 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 54 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 70 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 79 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 70 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 80 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 66 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 81 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 82 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 66 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 82 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 86 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 70 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 86 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 87 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 86 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 88 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 82 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 26 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 89 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 26 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 25 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 82 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 90 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 27 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 91 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 27 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 26 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 92 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 28 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 93 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 28 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 27 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 94 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 86 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 29 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 95 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 29 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 28 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 96 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 131 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 9 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 97 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 139 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 131 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 98 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 9 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 14 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 99 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 9 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 100 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 14 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 19 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 101 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 14 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 102 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 19 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 133 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 140 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 103 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 140 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 19 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 104 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 139 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 105 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 143 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 139 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 106 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 107 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 108 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 109 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 110 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 140 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 144 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 111 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 144 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 112 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 143 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 113 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 147 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 143 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 114 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 115 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 116 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 117 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 118 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 144 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 148 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 119 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 148 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 120 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 147 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 34 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 121 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 34 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 135 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 147 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 122 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 39 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 123 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 39 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 34 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 124 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 44 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 125 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 44 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 39 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 126 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 148 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 137 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 127 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 137 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 44 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 128 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 24 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 129 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 58 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 24 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 130 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 131 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 132 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 21 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 133 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 134 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 21 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 20 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 62 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 135 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 62 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 21 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 136 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 58 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 137 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 74 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 58 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 138 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 139 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 140 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 141 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 142 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 62 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 78 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 143 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 78 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 144 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 74 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 145 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 90 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 74 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 146 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 147 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 148 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 149 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 150 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 78 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 94 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 151 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 94 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 152 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 90 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 48 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 153 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 48 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 49 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 90 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 154 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 47 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 155 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 47 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 48 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 156 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 46 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 157 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 46 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 47 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 158 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 94 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 45 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 159 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 45 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 46 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 160 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 132 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 15 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 161 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 141 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 132 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 162 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 15 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 10 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 163 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 15 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 164 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 10 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 5 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 165 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 10 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 166 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 5 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 130 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 138 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 167 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 138 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 5 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 168 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 141 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 169 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 145 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 141 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 170 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 171 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 172 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 173 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 174 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 138 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 142 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 175 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 142 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 176 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 145 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 177 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 149 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 145 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 178 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 179 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 180 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 181 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 182 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 142 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 146 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 183 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 146 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 184 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 149 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 40 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 185 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 40 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 136 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 149 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 186 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 35 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 187 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 35 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 40 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 188 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 30 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 189 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 30 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 35 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 190 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 146 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 134 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 191 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 134 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 30 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + } + } + *PROP_MOTIONBLUR 0 + *PROP_CASTSHADOW 1 + *PROP_RECVSHADOW 1 + *MATERIAL_REF 1 +} diff --git a/base/models/materialeditor/cube128.ase b/base/models/materialeditor/cube128.ase new file mode 100644 index 000000000..9e8db157e --- /dev/null +++ b/base/models/materialeditor/cube128.ase @@ -0,0 +1,5850 @@ +*3DSMAX_ASCIIEXPORT 200 +*COMMENT "AsciiExport Version 2.00 - Tue Dec 09 13:55:17 2003" +*SCENE { + *SCENE_FILENAME "t.max" + *SCENE_FIRSTFRAME 0 + *SCENE_LASTFRAME 300 + *SCENE_FRAMESPEED 30 + *SCENE_TICKSPERFRAME 160 + *SCENE_BACKGROUND_STATIC 0.0000 0.0000 0.0000 + *SCENE_AMBIENT_STATIC 0.0000 0.0000 0.0000 +} +*MATERIAL_LIST { + *MATERIAL_COUNT 6 + *MATERIAL 0 { + *MATERIAL_NAME "1 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4706 0.4706 0.4706 + *MATERIAL_DIFFUSE 0.4706 0.4706 0.4706 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 1 { + *MATERIAL_NAME "9 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5882 0.5882 0.5882 + *MATERIAL_DIFFUSE 0.5882 0.5882 0.5882 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + *MAP_DIFFUSE { + *MAP_NAME "Map #1" + *MAP_CLASS "Bitmap" + *MAP_SUBNO 1 + *MAP_AMOUNT 1.0000 + *BITMAP "C:\Documents and Settings\Jay Brushwood\Desktop\base\Newguy\t.jpg" + *MAP_TYPE Screen + *UVW_U_OFFSET 0.0000 + *UVW_V_OFFSET 0.0000 + *UVW_U_TILING 1.0000 + *UVW_V_TILING 1.0000 + *UVW_ANGLE 0.0000 + *UVW_BLUR 1.0000 + *UVW_BLUR_OFFSET 0.0000 + *UVW_NOUSE_AMT 1.0000 + *UVW_NOISE_SIZE 1.0000 + *UVW_NOISE_LEVEL 1 + *UVW_NOISE_PHASE 0.0000 + *BITMAP_FILTER Pyramidal + } + } + *MATERIAL 2 { + *MATERIAL_NAME "Material #12" + *MATERIAL_CLASS "Multi/Sub-Object" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *NUMSUBMTLS 2 + *SUBMATERIAL 0 { + *MATERIAL_NAME "1 - Defaultas" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *SUBMATERIAL 1 { + *MATERIAL_NAME "1 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4706 0.4706 0.4706 + *MATERIAL_DIFFUSE 0.4706 0.4706 0.4706 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + } + *MATERIAL 3 { + *MATERIAL_NAME "1 - Defaultas" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 4 { + *MATERIAL_NAME "14 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5882 0.5882 0.5882 + *MATERIAL_DIFFUSE 0.5882 0.5882 0.5882 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 5 { + *MATERIAL_NAME "7 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4353 0.4471 0.6157 + *MATERIAL_DIFFUSE 0.4353 0.4471 0.6157 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } +} +*GEOMOBJECT { + *NODE_NAME "Box01_cube" + *NODE_TM { + *NODE_NAME "Box01_cube" + *INHERIT_POS 0 0 0 + *INHERIT_ROT 0 0 0 + *INHERIT_SCL 0 0 0 + *TM_ROW0 -0.0000 -1.0000 0.0000 + *TM_ROW1 -0.0000 0.0000 1.0000 + *TM_ROW2 -1.0000 0.0000 -0.0000 + *TM_ROW3 -0.0000 -0.0000 -0.0000 + *TM_POS -0.0000 -0.0000 -0.0000 + *TM_ROTAXIS 0.5774 -0.5774 -0.5774 + *TM_ROTANGLE 4.1888 + *TM_SCALE 1.0000 1.0000 1.0000 + *TM_SCALEAXIS 0.0000 0.0000 0.0000 + *TM_SCALEAXISANG 0.0000 + } + *MESH { + *TIMEVALUE 0 + *MESH_NUMVERTEX 486 + *MESH_NUMFACES 768 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 64.0000 64.0000 -64.0000 + *MESH_VERTEX 1 64.0000 32.0000 -64.0000 + *MESH_VERTEX 2 64.0000 -0.0000 -64.0000 + *MESH_VERTEX 3 64.0000 -32.0000 -64.0000 + *MESH_VERTEX 4 64.0000 -64.0000 -64.0000 + *MESH_VERTEX 5 64.0000 64.0000 -32.0000 + *MESH_VERTEX 6 64.0000 32.0000 -32.0000 + *MESH_VERTEX 7 64.0000 -0.0000 -32.0000 + *MESH_VERTEX 8 64.0000 -32.0000 -32.0000 + *MESH_VERTEX 9 64.0000 -64.0000 -32.0000 + *MESH_VERTEX 10 64.0000 64.0000 0.0000 + *MESH_VERTEX 11 64.0000 32.0000 0.0000 + *MESH_VERTEX 12 64.0000 -0.0000 0.0000 + *MESH_VERTEX 13 64.0000 -32.0000 0.0000 + *MESH_VERTEX 14 64.0000 -64.0000 0.0000 + *MESH_VERTEX 15 64.0000 64.0000 32.0000 + *MESH_VERTEX 16 64.0000 32.0000 32.0000 + *MESH_VERTEX 17 64.0000 -0.0000 32.0000 + *MESH_VERTEX 18 64.0000 -32.0000 32.0000 + *MESH_VERTEX 19 64.0000 -64.0000 32.0000 + *MESH_VERTEX 20 64.0000 64.0000 64.0000 + *MESH_VERTEX 21 64.0000 32.0000 64.0000 + *MESH_VERTEX 22 64.0000 -0.0000 64.0000 + *MESH_VERTEX 23 64.0000 -32.0000 64.0000 + *MESH_VERTEX 24 64.0000 -64.0000 64.0000 + *MESH_VERTEX 25 -64.0000 64.0000 -64.0000 + *MESH_VERTEX 26 -64.0000 32.0000 -64.0000 + *MESH_VERTEX 27 -64.0000 0.0000 -64.0000 + *MESH_VERTEX 28 -64.0000 -32.0000 -64.0000 + *MESH_VERTEX 29 -64.0000 -64.0000 -64.0000 + *MESH_VERTEX 30 -64.0000 64.0000 -32.0000 + *MESH_VERTEX 31 -64.0000 32.0000 -32.0000 + *MESH_VERTEX 32 -64.0000 0.0000 -32.0000 + *MESH_VERTEX 33 -64.0000 -32.0000 -32.0000 + *MESH_VERTEX 34 -64.0000 -64.0000 -32.0000 + *MESH_VERTEX 35 -64.0000 64.0000 -0.0000 + *MESH_VERTEX 36 -64.0000 32.0000 -0.0000 + *MESH_VERTEX 37 -64.0000 0.0000 -0.0000 + *MESH_VERTEX 38 -64.0000 -32.0000 -0.0000 + *MESH_VERTEX 39 -64.0000 -64.0000 -0.0000 + *MESH_VERTEX 40 -64.0000 64.0000 32.0000 + *MESH_VERTEX 41 -64.0000 32.0000 32.0000 + *MESH_VERTEX 42 -64.0000 0.0000 32.0000 + *MESH_VERTEX 43 -64.0000 -32.0000 32.0000 + *MESH_VERTEX 44 -64.0000 -64.0000 32.0000 + *MESH_VERTEX 45 -64.0000 64.0000 64.0000 + *MESH_VERTEX 46 -64.0000 32.0000 64.0000 + *MESH_VERTEX 47 -64.0000 0.0000 64.0000 + *MESH_VERTEX 48 -64.0000 -32.0000 64.0000 + *MESH_VERTEX 49 -64.0000 -64.0000 64.0000 + *MESH_VERTEX 50 32.0000 64.0000 -64.0000 + *MESH_VERTEX 51 32.0000 32.0000 -64.0000 + *MESH_VERTEX 52 32.0000 -0.0000 -64.0000 + *MESH_VERTEX 53 32.0000 -32.0000 -64.0000 + *MESH_VERTEX 54 32.0000 -64.0000 -64.0000 + *MESH_VERTEX 55 32.0000 -64.0000 -32.0000 + *MESH_VERTEX 56 32.0000 -64.0000 -0.0000 + *MESH_VERTEX 57 32.0000 -64.0000 32.0000 + *MESH_VERTEX 58 32.0000 -64.0000 64.0000 + *MESH_VERTEX 59 32.0000 -32.0000 64.0000 + *MESH_VERTEX 60 32.0000 -0.0000 64.0000 + *MESH_VERTEX 61 32.0000 32.0000 64.0000 + *MESH_VERTEX 62 32.0000 64.0000 64.0000 + *MESH_VERTEX 63 32.0000 64.0000 32.0000 + *MESH_VERTEX 64 32.0000 64.0000 -0.0000 + *MESH_VERTEX 65 32.0000 64.0000 -32.0000 + *MESH_VERTEX 66 0.0000 64.0000 -64.0000 + *MESH_VERTEX 67 0.0000 32.0000 -64.0000 + *MESH_VERTEX 68 0.0000 -0.0000 -64.0000 + *MESH_VERTEX 69 0.0000 -32.0000 -64.0000 + *MESH_VERTEX 70 0.0000 -64.0000 -64.0000 + *MESH_VERTEX 71 -0.0000 -64.0000 -32.0000 + *MESH_VERTEX 72 -0.0000 -64.0000 -0.0000 + *MESH_VERTEX 73 -0.0000 -64.0000 32.0000 + *MESH_VERTEX 74 -0.0000 -64.0000 64.0000 + *MESH_VERTEX 75 -0.0000 -32.0000 64.0000 + *MESH_VERTEX 76 -0.0000 -0.0000 64.0000 + *MESH_VERTEX 77 -0.0000 32.0000 64.0000 + *MESH_VERTEX 78 0.0000 64.0000 64.0000 + *MESH_VERTEX 79 0.0000 64.0000 32.0000 + *MESH_VERTEX 80 0.0000 64.0000 -0.0000 + *MESH_VERTEX 81 0.0000 64.0000 -32.0000 + *MESH_VERTEX 82 -32.0000 64.0000 -64.0000 + *MESH_VERTEX 83 -32.0000 32.0000 -64.0000 + *MESH_VERTEX 84 -32.0000 0.0000 -64.0000 + *MESH_VERTEX 85 -32.0000 -32.0000 -64.0000 + *MESH_VERTEX 86 -32.0000 -64.0000 -64.0000 + *MESH_VERTEX 87 -32.0000 -64.0000 -32.0000 + *MESH_VERTEX 88 -32.0000 -64.0000 -0.0000 + *MESH_VERTEX 89 -32.0000 -64.0000 32.0000 + *MESH_VERTEX 90 -32.0000 -64.0000 64.0000 + *MESH_VERTEX 91 -32.0000 -32.0000 64.0000 + *MESH_VERTEX 92 -32.0000 0.0000 64.0000 + *MESH_VERTEX 93 -32.0000 32.0000 64.0000 + *MESH_VERTEX 94 -32.0000 64.0000 64.0000 + *MESH_VERTEX 95 -32.0000 64.0000 32.0000 + *MESH_VERTEX 96 -32.0000 64.0000 -0.0000 + *MESH_VERTEX 97 -32.0000 64.0000 -32.0000 + *MESH_VERTEX 98 64.0000 64.0000 -64.0000 + *MESH_VERTEX 99 64.0000 32.0000 -64.0000 + *MESH_VERTEX 100 64.0000 -0.0000 -64.0000 + *MESH_VERTEX 101 64.0000 -32.0000 -64.0000 + *MESH_VERTEX 102 64.0000 -64.0000 -64.0000 + *MESH_VERTEX 103 64.0000 64.0000 -32.0000 + *MESH_VERTEX 104 64.0000 -64.0000 -32.0000 + *MESH_VERTEX 105 64.0000 64.0000 0.0000 + *MESH_VERTEX 106 64.0000 -64.0000 0.0000 + *MESH_VERTEX 107 64.0000 64.0000 32.0000 + *MESH_VERTEX 108 64.0000 -64.0000 32.0000 + *MESH_VERTEX 109 64.0000 64.0000 64.0000 + *MESH_VERTEX 110 64.0000 32.0000 64.0000 + *MESH_VERTEX 111 64.0000 -0.0000 64.0000 + *MESH_VERTEX 112 64.0000 -32.0000 64.0000 + *MESH_VERTEX 113 64.0000 -64.0000 64.0000 + *MESH_VERTEX 114 -64.0000 64.0000 -64.0000 + *MESH_VERTEX 115 -64.0000 32.0000 -64.0000 + *MESH_VERTEX 116 -64.0000 0.0000 -64.0000 + *MESH_VERTEX 117 -64.0000 -32.0000 -64.0000 + *MESH_VERTEX 118 -64.0000 -64.0000 -64.0000 + *MESH_VERTEX 119 -64.0000 64.0000 -32.0000 + *MESH_VERTEX 120 -64.0000 -64.0000 -32.0000 + *MESH_VERTEX 121 -64.0000 64.0000 -0.0000 + *MESH_VERTEX 122 -64.0000 -64.0000 -0.0000 + *MESH_VERTEX 123 -64.0000 64.0000 32.0000 + *MESH_VERTEX 124 -64.0000 -64.0000 32.0000 + *MESH_VERTEX 125 -64.0000 64.0000 64.0000 + *MESH_VERTEX 126 -64.0000 32.0000 64.0000 + *MESH_VERTEX 127 -64.0000 0.0000 64.0000 + *MESH_VERTEX 128 -64.0000 -32.0000 64.0000 + *MESH_VERTEX 129 -64.0000 -64.0000 64.0000 + *MESH_VERTEX 130 64.0000 64.0000 -64.0000 + *MESH_VERTEX 131 64.0000 -64.0000 -64.0000 + *MESH_VERTEX 132 64.0000 64.0000 64.0000 + *MESH_VERTEX 133 64.0000 -64.0000 64.0000 + *MESH_VERTEX 134 -64.0000 64.0000 -64.0000 + *MESH_VERTEX 135 -64.0000 -64.0000 -64.0000 + *MESH_VERTEX 136 -64.0000 64.0000 64.0000 + *MESH_VERTEX 137 -64.0000 -64.0000 64.0000 + *MESH_VERTEX 138 32.0000 64.0000 -64.0000 + *MESH_VERTEX 139 32.0000 -64.0000 -64.0000 + *MESH_VERTEX 140 32.0000 -64.0000 64.0000 + *MESH_VERTEX 141 32.0000 64.0000 64.0000 + *MESH_VERTEX 142 0.0000 64.0000 -64.0000 + *MESH_VERTEX 143 0.0000 -64.0000 -64.0000 + *MESH_VERTEX 144 -0.0000 -64.0000 64.0000 + *MESH_VERTEX 145 0.0000 64.0000 64.0000 + *MESH_VERTEX 146 -32.0000 64.0000 -64.0000 + *MESH_VERTEX 147 -32.0000 -64.0000 -64.0000 + *MESH_VERTEX 148 -32.0000 -64.0000 64.0000 + *MESH_VERTEX 149 -32.0000 64.0000 64.0000 + *MESH_VERTEX 150 64.0000 48.0000 -48.0000 + *MESH_VERTEX 151 64.0000 16.0000 -48.0000 + *MESH_VERTEX 152 64.0000 -16.0000 -48.0000 + *MESH_VERTEX 153 64.0000 -48.0000 -48.0000 + *MESH_VERTEX 154 64.0000 48.0000 -16.0000 + *MESH_VERTEX 155 64.0000 16.0000 -16.0000 + *MESH_VERTEX 156 64.0000 -16.0000 -16.0000 + *MESH_VERTEX 157 64.0000 -48.0000 -16.0000 + *MESH_VERTEX 158 64.0000 48.0000 16.0000 + *MESH_VERTEX 159 64.0000 16.0000 16.0000 + *MESH_VERTEX 160 64.0000 -16.0000 16.0000 + *MESH_VERTEX 161 64.0000 -48.0000 16.0000 + *MESH_VERTEX 162 64.0000 48.0000 48.0000 + *MESH_VERTEX 163 64.0000 16.0000 48.0000 + *MESH_VERTEX 164 64.0000 -16.0000 48.0000 + *MESH_VERTEX 165 64.0000 -48.0000 48.0000 + *MESH_VERTEX 166 -64.0000 48.0000 -48.0000 + *MESH_VERTEX 167 -64.0000 16.0000 -48.0000 + *MESH_VERTEX 168 -64.0000 -16.0000 -48.0000 + *MESH_VERTEX 169 -64.0000 -48.0000 -48.0000 + *MESH_VERTEX 170 -64.0000 48.0000 -16.0000 + *MESH_VERTEX 171 -64.0000 16.0000 -16.0000 + *MESH_VERTEX 172 -64.0000 -16.0000 -16.0000 + *MESH_VERTEX 173 -64.0000 -48.0000 -16.0000 + *MESH_VERTEX 174 -64.0000 48.0000 16.0000 + *MESH_VERTEX 175 -64.0000 16.0000 16.0000 + *MESH_VERTEX 176 -64.0000 -16.0000 16.0000 + *MESH_VERTEX 177 -64.0000 -48.0000 16.0000 + *MESH_VERTEX 178 -64.0000 48.0000 48.0000 + *MESH_VERTEX 179 -64.0000 16.0000 48.0000 + *MESH_VERTEX 180 -64.0000 -16.0000 48.0000 + *MESH_VERTEX 181 -64.0000 -48.0000 48.0000 + *MESH_VERTEX 182 48.0000 48.0000 -64.0000 + *MESH_VERTEX 183 48.0000 16.0000 -64.0000 + *MESH_VERTEX 184 48.0000 -16.0000 -64.0000 + *MESH_VERTEX 185 48.0000 -48.0000 -64.0000 + *MESH_VERTEX 186 16.0000 48.0000 -64.0000 + *MESH_VERTEX 187 16.0000 16.0000 -64.0000 + *MESH_VERTEX 188 16.0000 -16.0000 -64.0000 + *MESH_VERTEX 189 16.0000 -48.0000 -64.0000 + *MESH_VERTEX 190 -16.0000 48.0000 -64.0000 + *MESH_VERTEX 191 -16.0000 16.0000 -64.0000 + *MESH_VERTEX 192 -16.0000 -16.0000 -64.0000 + *MESH_VERTEX 193 -16.0000 -48.0000 -64.0000 + *MESH_VERTEX 194 -48.0000 48.0000 -64.0000 + *MESH_VERTEX 195 -48.0000 16.0000 -64.0000 + *MESH_VERTEX 196 -48.0000 -16.0000 -64.0000 + *MESH_VERTEX 197 -48.0000 -48.0000 -64.0000 + *MESH_VERTEX 198 48.0000 -64.0000 -48.0000 + *MESH_VERTEX 199 48.0000 -64.0000 -16.0000 + *MESH_VERTEX 200 48.0000 -64.0000 16.0000 + *MESH_VERTEX 201 48.0000 -64.0000 48.0000 + *MESH_VERTEX 202 16.0000 -64.0000 -48.0000 + *MESH_VERTEX 203 16.0000 -64.0000 -16.0000 + *MESH_VERTEX 204 16.0000 -64.0000 16.0000 + *MESH_VERTEX 205 16.0000 -64.0000 48.0000 + *MESH_VERTEX 206 -16.0000 -64.0000 -48.0000 + *MESH_VERTEX 207 -16.0000 -64.0000 -16.0000 + *MESH_VERTEX 208 -16.0000 -64.0000 16.0000 + *MESH_VERTEX 209 -16.0000 -64.0000 48.0000 + *MESH_VERTEX 210 -48.0000 -64.0000 -48.0000 + *MESH_VERTEX 211 -48.0000 -64.0000 -16.0000 + *MESH_VERTEX 212 -48.0000 -64.0000 16.0000 + *MESH_VERTEX 213 -48.0000 -64.0000 48.0000 + *MESH_VERTEX 214 48.0000 -48.0000 64.0000 + *MESH_VERTEX 215 48.0000 -16.0000 64.0000 + *MESH_VERTEX 216 48.0000 16.0000 64.0000 + *MESH_VERTEX 217 48.0000 48.0000 64.0000 + *MESH_VERTEX 218 16.0000 -48.0000 64.0000 + *MESH_VERTEX 219 16.0000 -16.0000 64.0000 + *MESH_VERTEX 220 16.0000 16.0000 64.0000 + *MESH_VERTEX 221 16.0000 48.0000 64.0000 + *MESH_VERTEX 222 -16.0000 -48.0000 64.0000 + *MESH_VERTEX 223 -16.0000 -16.0000 64.0000 + *MESH_VERTEX 224 -16.0000 16.0000 64.0000 + *MESH_VERTEX 225 -16.0000 48.0000 64.0000 + *MESH_VERTEX 226 -48.0000 -48.0000 64.0000 + *MESH_VERTEX 227 -48.0000 -16.0000 64.0000 + *MESH_VERTEX 228 -48.0000 16.0000 64.0000 + *MESH_VERTEX 229 -48.0000 48.0000 64.0000 + *MESH_VERTEX 230 48.0000 64.0000 48.0000 + *MESH_VERTEX 231 48.0000 64.0000 16.0000 + *MESH_VERTEX 232 48.0000 64.0000 -16.0000 + *MESH_VERTEX 233 48.0000 64.0000 -48.0000 + *MESH_VERTEX 234 16.0000 64.0000 48.0000 + *MESH_VERTEX 235 16.0000 64.0000 16.0000 + *MESH_VERTEX 236 16.0000 64.0000 -16.0000 + *MESH_VERTEX 237 16.0000 64.0000 -48.0000 + *MESH_VERTEX 238 -16.0000 64.0000 48.0000 + *MESH_VERTEX 239 -16.0000 64.0000 16.0000 + *MESH_VERTEX 240 -16.0000 64.0000 -16.0000 + *MESH_VERTEX 241 -16.0000 64.0000 -48.0000 + *MESH_VERTEX 242 -48.0000 64.0000 48.0000 + *MESH_VERTEX 243 -48.0000 64.0000 16.0000 + *MESH_VERTEX 244 -48.0000 64.0000 -16.0000 + *MESH_VERTEX 245 -48.0000 64.0000 -48.0000 + *MESH_VERTEX 246 64.0000 64.0000 -48.0000 + *MESH_VERTEX 247 64.0000 48.0000 -32.0000 + *MESH_VERTEX 248 64.0000 32.0000 -48.0000 + *MESH_VERTEX 249 64.0000 48.0000 -64.0000 + *MESH_VERTEX 250 64.0000 16.0000 -32.0000 + *MESH_VERTEX 251 64.0000 -0.0000 -48.0000 + *MESH_VERTEX 252 64.0000 16.0000 -64.0000 + *MESH_VERTEX 253 64.0000 -16.0000 -32.0000 + *MESH_VERTEX 254 64.0000 -32.0000 -48.0000 + *MESH_VERTEX 255 64.0000 -16.0000 -64.0000 + *MESH_VERTEX 256 64.0000 -48.0000 -32.0000 + *MESH_VERTEX 257 64.0000 -64.0000 -48.0000 + *MESH_VERTEX 258 64.0000 -48.0000 -64.0000 + *MESH_VERTEX 259 64.0000 64.0000 -16.0000 + *MESH_VERTEX 260 64.0000 48.0000 0.0000 + *MESH_VERTEX 261 64.0000 32.0000 -16.0000 + *MESH_VERTEX 262 64.0000 16.0000 0.0000 + *MESH_VERTEX 263 64.0000 -0.0000 -16.0000 + *MESH_VERTEX 264 64.0000 -16.0000 0.0000 + *MESH_VERTEX 265 64.0000 -32.0000 -16.0000 + *MESH_VERTEX 266 64.0000 -48.0000 0.0000 + *MESH_VERTEX 267 64.0000 -64.0000 -16.0000 + *MESH_VERTEX 268 64.0000 64.0000 16.0000 + *MESH_VERTEX 269 64.0000 48.0000 32.0000 + *MESH_VERTEX 270 64.0000 32.0000 16.0000 + *MESH_VERTEX 271 64.0000 16.0000 32.0000 + *MESH_VERTEX 272 64.0000 -0.0000 16.0000 + *MESH_VERTEX 273 64.0000 -16.0000 32.0000 + *MESH_VERTEX 274 64.0000 -32.0000 16.0000 + *MESH_VERTEX 275 64.0000 -48.0000 32.0000 + *MESH_VERTEX 276 64.0000 -64.0000 16.0000 + *MESH_VERTEX 277 64.0000 64.0000 48.0000 + *MESH_VERTEX 278 64.0000 48.0000 64.0000 + *MESH_VERTEX 279 64.0000 32.0000 48.0000 + *MESH_VERTEX 280 64.0000 16.0000 64.0000 + *MESH_VERTEX 281 64.0000 -0.0000 48.0000 + *MESH_VERTEX 282 64.0000 -16.0000 64.0000 + *MESH_VERTEX 283 64.0000 -32.0000 48.0000 + *MESH_VERTEX 284 64.0000 -48.0000 64.0000 + *MESH_VERTEX 285 64.0000 -64.0000 48.0000 + *MESH_VERTEX 286 -64.0000 48.0000 -64.0000 + *MESH_VERTEX 287 -64.0000 32.0000 -48.0000 + *MESH_VERTEX 288 -64.0000 48.0000 -32.0000 + *MESH_VERTEX 289 -64.0000 64.0000 -48.0000 + *MESH_VERTEX 290 -64.0000 16.0000 -64.0000 + *MESH_VERTEX 291 -64.0000 0.0000 -48.0000 + *MESH_VERTEX 292 -64.0000 16.0000 -32.0000 + *MESH_VERTEX 293 -64.0000 -16.0000 -64.0000 + *MESH_VERTEX 294 -64.0000 -32.0000 -48.0000 + *MESH_VERTEX 295 -64.0000 -16.0000 -32.0000 + *MESH_VERTEX 296 -64.0000 -48.0000 -64.0000 + *MESH_VERTEX 297 -64.0000 -64.0000 -48.0000 + *MESH_VERTEX 298 -64.0000 -48.0000 -32.0000 + *MESH_VERTEX 299 -64.0000 32.0000 -16.0000 + *MESH_VERTEX 300 -64.0000 48.0000 -0.0000 + *MESH_VERTEX 301 -64.0000 64.0000 -16.0000 + *MESH_VERTEX 302 -64.0000 0.0000 -16.0000 + *MESH_VERTEX 303 -64.0000 16.0000 -0.0000 + *MESH_VERTEX 304 -64.0000 -32.0000 -16.0000 + *MESH_VERTEX 305 -64.0000 -16.0000 -0.0000 + *MESH_VERTEX 306 -64.0000 -64.0000 -16.0000 + *MESH_VERTEX 307 -64.0000 -48.0000 -0.0000 + *MESH_VERTEX 308 -64.0000 32.0000 16.0000 + *MESH_VERTEX 309 -64.0000 48.0000 32.0000 + *MESH_VERTEX 310 -64.0000 64.0000 16.0000 + *MESH_VERTEX 311 -64.0000 0.0000 16.0000 + *MESH_VERTEX 312 -64.0000 16.0000 32.0000 + *MESH_VERTEX 313 -64.0000 -32.0000 16.0000 + *MESH_VERTEX 314 -64.0000 -16.0000 32.0000 + *MESH_VERTEX 315 -64.0000 -64.0000 16.0000 + *MESH_VERTEX 316 -64.0000 -48.0000 32.0000 + *MESH_VERTEX 317 -64.0000 32.0000 48.0000 + *MESH_VERTEX 318 -64.0000 48.0000 64.0000 + *MESH_VERTEX 319 -64.0000 64.0000 48.0000 + *MESH_VERTEX 320 -64.0000 0.0000 48.0000 + *MESH_VERTEX 321 -64.0000 16.0000 64.0000 + *MESH_VERTEX 322 -64.0000 -32.0000 48.0000 + *MESH_VERTEX 323 -64.0000 -16.0000 64.0000 + *MESH_VERTEX 324 -64.0000 -64.0000 48.0000 + *MESH_VERTEX 325 -64.0000 -48.0000 64.0000 + *MESH_VERTEX 326 48.0000 32.0000 -64.0000 + *MESH_VERTEX 327 32.0000 48.0000 -64.0000 + *MESH_VERTEX 328 48.0000 64.0000 -64.0000 + *MESH_VERTEX 329 48.0000 -0.0000 -64.0000 + *MESH_VERTEX 330 32.0000 16.0000 -64.0000 + *MESH_VERTEX 331 48.0000 -32.0000 -64.0000 + *MESH_VERTEX 332 32.0000 -16.0000 -64.0000 + *MESH_VERTEX 333 48.0000 -64.0000 -64.0000 + *MESH_VERTEX 334 32.0000 -48.0000 -64.0000 + *MESH_VERTEX 335 16.0000 32.0000 -64.0000 + *MESH_VERTEX 336 0.0000 48.0000 -64.0000 + *MESH_VERTEX 337 16.0000 64.0000 -64.0000 + *MESH_VERTEX 338 16.0000 -0.0000 -64.0000 + *MESH_VERTEX 339 0.0000 16.0000 -64.0000 + *MESH_VERTEX 340 16.0000 -32.0000 -64.0000 + *MESH_VERTEX 341 0.0000 -16.0000 -64.0000 + *MESH_VERTEX 342 16.0000 -64.0000 -64.0000 + *MESH_VERTEX 343 0.0000 -48.0000 -64.0000 + *MESH_VERTEX 344 -16.0000 32.0000 -64.0000 + *MESH_VERTEX 345 -32.0000 48.0000 -64.0000 + *MESH_VERTEX 346 -16.0000 64.0000 -64.0000 + *MESH_VERTEX 347 -16.0000 -0.0000 -64.0000 + *MESH_VERTEX 348 -32.0000 16.0000 -64.0000 + *MESH_VERTEX 349 -16.0000 -32.0000 -64.0000 + *MESH_VERTEX 350 -32.0000 -16.0000 -64.0000 + *MESH_VERTEX 351 -16.0000 -64.0000 -64.0000 + *MESH_VERTEX 352 -32.0000 -48.0000 -64.0000 + *MESH_VERTEX 353 -48.0000 32.0000 -64.0000 + *MESH_VERTEX 354 -48.0000 64.0000 -64.0000 + *MESH_VERTEX 355 -48.0000 0.0000 -64.0000 + *MESH_VERTEX 356 -48.0000 -32.0000 -64.0000 + *MESH_VERTEX 357 -48.0000 -64.0000 -64.0000 + *MESH_VERTEX 358 48.0000 -64.0000 -32.0000 + *MESH_VERTEX 359 32.0000 -64.0000 -48.0000 + *MESH_VERTEX 360 48.0000 -64.0000 -0.0000 + *MESH_VERTEX 361 32.0000 -64.0000 -16.0000 + *MESH_VERTEX 362 48.0000 -64.0000 32.0000 + *MESH_VERTEX 363 32.0000 -64.0000 16.0000 + *MESH_VERTEX 364 48.0000 -64.0000 64.0000 + *MESH_VERTEX 365 32.0000 -64.0000 48.0000 + *MESH_VERTEX 366 16.0000 -64.0000 -32.0000 + *MESH_VERTEX 367 -0.0000 -64.0000 -48.0000 + *MESH_VERTEX 368 16.0000 -64.0000 -0.0000 + *MESH_VERTEX 369 -0.0000 -64.0000 -16.0000 + *MESH_VERTEX 370 16.0000 -64.0000 32.0000 + *MESH_VERTEX 371 -0.0000 -64.0000 16.0000 + *MESH_VERTEX 372 16.0000 -64.0000 64.0000 + *MESH_VERTEX 373 -0.0000 -64.0000 48.0000 + *MESH_VERTEX 374 -16.0000 -64.0000 -32.0000 + *MESH_VERTEX 375 -32.0000 -64.0000 -48.0000 + *MESH_VERTEX 376 -16.0000 -64.0000 -0.0000 + *MESH_VERTEX 377 -32.0000 -64.0000 -16.0000 + *MESH_VERTEX 378 -16.0000 -64.0000 32.0000 + *MESH_VERTEX 379 -32.0000 -64.0000 16.0000 + *MESH_VERTEX 380 -16.0000 -64.0000 64.0000 + *MESH_VERTEX 381 -32.0000 -64.0000 48.0000 + *MESH_VERTEX 382 -48.0000 -64.0000 -32.0000 + *MESH_VERTEX 383 -48.0000 -64.0000 -0.0000 + *MESH_VERTEX 384 -48.0000 -64.0000 32.0000 + *MESH_VERTEX 385 -48.0000 -64.0000 64.0000 + *MESH_VERTEX 386 48.0000 -32.0000 64.0000 + *MESH_VERTEX 387 32.0000 -48.0000 64.0000 + *MESH_VERTEX 388 48.0000 -0.0000 64.0000 + *MESH_VERTEX 389 32.0000 -16.0000 64.0000 + *MESH_VERTEX 390 48.0000 32.0000 64.0000 + *MESH_VERTEX 391 32.0000 16.0000 64.0000 + *MESH_VERTEX 392 48.0000 64.0000 64.0000 + *MESH_VERTEX 393 32.0000 48.0000 64.0000 + *MESH_VERTEX 394 16.0000 -32.0000 64.0000 + *MESH_VERTEX 395 -0.0000 -48.0000 64.0000 + *MESH_VERTEX 396 16.0000 -0.0000 64.0000 + *MESH_VERTEX 397 -0.0000 -16.0000 64.0000 + *MESH_VERTEX 398 16.0000 32.0000 64.0000 + *MESH_VERTEX 399 -0.0000 16.0000 64.0000 + *MESH_VERTEX 400 16.0000 64.0000 64.0000 + *MESH_VERTEX 401 -0.0000 48.0000 64.0000 + *MESH_VERTEX 402 -16.0000 -32.0000 64.0000 + *MESH_VERTEX 403 -32.0000 -48.0000 64.0000 + *MESH_VERTEX 404 -16.0000 -0.0000 64.0000 + *MESH_VERTEX 405 -32.0000 -16.0000 64.0000 + *MESH_VERTEX 406 -16.0000 32.0000 64.0000 + *MESH_VERTEX 407 -32.0000 16.0000 64.0000 + *MESH_VERTEX 408 -16.0000 64.0000 64.0000 + *MESH_VERTEX 409 -32.0000 48.0000 64.0000 + *MESH_VERTEX 410 -48.0000 -32.0000 64.0000 + *MESH_VERTEX 411 -48.0000 0.0000 64.0000 + *MESH_VERTEX 412 -48.0000 32.0000 64.0000 + *MESH_VERTEX 413 -48.0000 64.0000 64.0000 + *MESH_VERTEX 414 48.0000 64.0000 32.0000 + *MESH_VERTEX 415 32.0000 64.0000 48.0000 + *MESH_VERTEX 416 48.0000 64.0000 -0.0000 + *MESH_VERTEX 417 32.0000 64.0000 16.0000 + *MESH_VERTEX 418 48.0000 64.0000 -32.0000 + *MESH_VERTEX 419 32.0000 64.0000 -16.0000 + *MESH_VERTEX 420 32.0000 64.0000 -48.0000 + *MESH_VERTEX 421 16.0000 64.0000 32.0000 + *MESH_VERTEX 422 0.0000 64.0000 48.0000 + *MESH_VERTEX 423 16.0000 64.0000 -0.0000 + *MESH_VERTEX 424 0.0000 64.0000 16.0000 + *MESH_VERTEX 425 16.0000 64.0000 -32.0000 + *MESH_VERTEX 426 0.0000 64.0000 -16.0000 + *MESH_VERTEX 427 0.0000 64.0000 -48.0000 + *MESH_VERTEX 428 -16.0000 64.0000 32.0000 + *MESH_VERTEX 429 -32.0000 64.0000 48.0000 + *MESH_VERTEX 430 -16.0000 64.0000 -0.0000 + *MESH_VERTEX 431 -32.0000 64.0000 16.0000 + *MESH_VERTEX 432 -16.0000 64.0000 -32.0000 + *MESH_VERTEX 433 -32.0000 64.0000 -16.0000 + *MESH_VERTEX 434 -32.0000 64.0000 -48.0000 + *MESH_VERTEX 435 -48.0000 64.0000 32.0000 + *MESH_VERTEX 436 -48.0000 64.0000 -0.0000 + *MESH_VERTEX 437 -48.0000 64.0000 -32.0000 + *MESH_VERTEX 438 64.0000 64.0000 -48.0000 + *MESH_VERTEX 439 64.0000 48.0000 -64.0000 + *MESH_VERTEX 440 64.0000 16.0000 -64.0000 + *MESH_VERTEX 441 64.0000 -16.0000 -64.0000 + *MESH_VERTEX 442 64.0000 -64.0000 -48.0000 + *MESH_VERTEX 443 64.0000 -48.0000 -64.0000 + *MESH_VERTEX 444 64.0000 64.0000 -16.0000 + *MESH_VERTEX 445 64.0000 -64.0000 -16.0000 + *MESH_VERTEX 446 64.0000 64.0000 16.0000 + *MESH_VERTEX 447 64.0000 -64.0000 16.0000 + *MESH_VERTEX 448 64.0000 64.0000 48.0000 + *MESH_VERTEX 449 64.0000 48.0000 64.0000 + *MESH_VERTEX 450 64.0000 16.0000 64.0000 + *MESH_VERTEX 451 64.0000 -16.0000 64.0000 + *MESH_VERTEX 452 64.0000 -48.0000 64.0000 + *MESH_VERTEX 453 64.0000 -64.0000 48.0000 + *MESH_VERTEX 454 -64.0000 48.0000 -64.0000 + *MESH_VERTEX 455 -64.0000 64.0000 -48.0000 + *MESH_VERTEX 456 -64.0000 16.0000 -64.0000 + *MESH_VERTEX 457 -64.0000 -16.0000 -64.0000 + *MESH_VERTEX 458 -64.0000 -48.0000 -64.0000 + *MESH_VERTEX 459 -64.0000 -64.0000 -48.0000 + *MESH_VERTEX 460 -64.0000 64.0000 -16.0000 + *MESH_VERTEX 461 -64.0000 -64.0000 -16.0000 + *MESH_VERTEX 462 -64.0000 64.0000 16.0000 + *MESH_VERTEX 463 -64.0000 -64.0000 16.0000 + *MESH_VERTEX 464 -64.0000 48.0000 64.0000 + *MESH_VERTEX 465 -64.0000 64.0000 48.0000 + *MESH_VERTEX 466 -64.0000 16.0000 64.0000 + *MESH_VERTEX 467 -64.0000 -16.0000 64.0000 + *MESH_VERTEX 468 -64.0000 -64.0000 48.0000 + *MESH_VERTEX 469 -64.0000 -48.0000 64.0000 + *MESH_VERTEX 470 48.0000 64.0000 -64.0000 + *MESH_VERTEX 471 48.0000 -64.0000 -64.0000 + *MESH_VERTEX 472 16.0000 64.0000 -64.0000 + *MESH_VERTEX 473 16.0000 -64.0000 -64.0000 + *MESH_VERTEX 474 -16.0000 64.0000 -64.0000 + *MESH_VERTEX 475 -16.0000 -64.0000 -64.0000 + *MESH_VERTEX 476 -48.0000 64.0000 -64.0000 + *MESH_VERTEX 477 -48.0000 -64.0000 -64.0000 + *MESH_VERTEX 478 48.0000 -64.0000 64.0000 + *MESH_VERTEX 479 16.0000 -64.0000 64.0000 + *MESH_VERTEX 480 -16.0000 -64.0000 64.0000 + *MESH_VERTEX 481 -48.0000 -64.0000 64.0000 + *MESH_VERTEX 482 48.0000 64.0000 64.0000 + *MESH_VERTEX 483 16.0000 64.0000 64.0000 + *MESH_VERTEX 484 -16.0000 64.0000 64.0000 + *MESH_VERTEX 485 -48.0000 64.0000 64.0000 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 98 B: 438 C: 150 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 1: A: 150 B: 439 C: 98 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 2: A: 103 B: 247 C: 150 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 3: A: 150 B: 438 C: 103 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 4: A: 6 B: 248 C: 150 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 5: A: 150 B: 247 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 6: A: 99 B: 439 C: 150 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 7: A: 150 B: 248 C: 99 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 8: A: 99 B: 248 C: 151 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 9: A: 151 B: 440 C: 99 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 10: A: 6 B: 250 C: 151 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 11: A: 151 B: 248 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 12: A: 7 B: 251 C: 151 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 13: A: 151 B: 250 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 14: A: 100 B: 440 C: 151 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 15: A: 151 B: 251 C: 100 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 16: A: 100 B: 251 C: 152 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 17: A: 152 B: 441 C: 100 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 18: A: 7 B: 253 C: 152 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 19: A: 152 B: 251 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 20: A: 8 B: 254 C: 152 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 21: A: 152 B: 253 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 22: A: 101 B: 441 C: 152 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 23: A: 152 B: 254 C: 101 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 24: A: 101 B: 254 C: 153 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 25: A: 153 B: 443 C: 101 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 26: A: 8 B: 256 C: 153 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 27: A: 153 B: 254 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 28: A: 104 B: 442 C: 153 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 29: A: 153 B: 256 C: 104 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 30: A: 102 B: 443 C: 153 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 31: A: 153 B: 442 C: 102 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 32: A: 103 B: 444 C: 154 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 33: A: 154 B: 247 C: 103 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 34: A: 105 B: 260 C: 154 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 35: A: 154 B: 444 C: 105 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 36: A: 11 B: 261 C: 154 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 37: A: 154 B: 260 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 38: A: 6 B: 247 C: 154 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 39: A: 154 B: 261 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 40: A: 6 B: 261 C: 155 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 41: A: 155 B: 250 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 42: A: 11 B: 262 C: 155 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 43: A: 155 B: 261 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 44: A: 12 B: 263 C: 155 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 45: A: 155 B: 262 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 46: A: 7 B: 250 C: 155 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 47: A: 155 B: 263 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 48: A: 7 B: 263 C: 156 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 49: A: 156 B: 253 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 50: A: 12 B: 264 C: 156 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 51: A: 156 B: 263 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 52: A: 13 B: 265 C: 156 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 53: A: 156 B: 264 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 54: A: 8 B: 253 C: 156 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 55: A: 156 B: 265 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 56: A: 8 B: 265 C: 157 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 57: A: 157 B: 256 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 58: A: 13 B: 266 C: 157 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 59: A: 157 B: 265 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 60: A: 106 B: 445 C: 157 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 61: A: 157 B: 266 C: 106 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 62: A: 104 B: 256 C: 157 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 63: A: 157 B: 445 C: 104 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 64: A: 105 B: 446 C: 158 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 65: A: 158 B: 260 C: 105 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 66: A: 107 B: 269 C: 158 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 67: A: 158 B: 446 C: 107 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 68: A: 16 B: 270 C: 158 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 69: A: 158 B: 269 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 70: A: 11 B: 260 C: 158 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 71: A: 158 B: 270 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 72: A: 11 B: 270 C: 159 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 73: A: 159 B: 262 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 74: A: 16 B: 271 C: 159 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 75: A: 159 B: 270 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 76: A: 17 B: 272 C: 159 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 77: A: 159 B: 271 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 78: A: 12 B: 262 C: 159 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 79: A: 159 B: 272 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 80: A: 12 B: 272 C: 160 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 81: A: 160 B: 264 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 82: A: 17 B: 273 C: 160 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 83: A: 160 B: 272 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 84: A: 18 B: 274 C: 160 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 85: A: 160 B: 273 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 86: A: 13 B: 264 C: 160 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 87: A: 160 B: 274 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 88: A: 13 B: 274 C: 161 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 89: A: 161 B: 266 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 90: A: 18 B: 275 C: 161 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 91: A: 161 B: 274 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 92: A: 108 B: 447 C: 161 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 93: A: 161 B: 275 C: 108 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 94: A: 106 B: 266 C: 161 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 95: A: 161 B: 447 C: 106 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 96: A: 107 B: 448 C: 162 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 97: A: 162 B: 269 C: 107 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 98: A: 109 B: 449 C: 162 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 99: A: 162 B: 448 C: 109 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 100: A: 110 B: 279 C: 162 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 101: A: 162 B: 449 C: 110 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 102: A: 16 B: 269 C: 162 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 103: A: 162 B: 279 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 104: A: 16 B: 279 C: 163 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 105: A: 163 B: 271 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 106: A: 110 B: 450 C: 163 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 107: A: 163 B: 279 C: 110 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 108: A: 111 B: 281 C: 163 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 109: A: 163 B: 450 C: 111 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 110: A: 17 B: 271 C: 163 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 111: A: 163 B: 281 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 112: A: 17 B: 281 C: 164 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 113: A: 164 B: 273 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 114: A: 111 B: 451 C: 164 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 115: A: 164 B: 281 C: 111 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 116: A: 112 B: 283 C: 164 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 117: A: 164 B: 451 C: 112 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 118: A: 18 B: 273 C: 164 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 119: A: 164 B: 283 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 120: A: 18 B: 283 C: 165 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 121: A: 165 B: 275 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 1 + *MESH_FACE 122: A: 112 B: 452 C: 165 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 123: A: 165 B: 283 C: 112 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 124: A: 113 B: 453 C: 165 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 125: A: 165 B: 452 C: 113 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 126: A: 108 B: 275 C: 165 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 127: A: 165 B: 453 C: 108 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1 + *MESH_FACE 128: A: 114 B: 454 C: 166 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 129: A: 166 B: 455 C: 114 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 130: A: 115 B: 287 C: 166 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 131: A: 166 B: 454 C: 115 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 132: A: 31 B: 288 C: 166 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 133: A: 166 B: 287 C: 31 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 134: A: 119 B: 455 C: 166 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 135: A: 166 B: 288 C: 119 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 136: A: 115 B: 456 C: 167 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 137: A: 167 B: 287 C: 115 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 138: A: 116 B: 291 C: 167 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 139: A: 167 B: 456 C: 116 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 140: A: 32 B: 292 C: 167 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 141: A: 167 B: 291 C: 32 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 142: A: 31 B: 287 C: 167 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 143: A: 167 B: 292 C: 31 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 144: A: 116 B: 457 C: 168 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 145: A: 168 B: 291 C: 116 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 146: A: 117 B: 294 C: 168 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 147: A: 168 B: 457 C: 117 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 148: A: 33 B: 295 C: 168 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 149: A: 168 B: 294 C: 33 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 150: A: 32 B: 291 C: 168 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 151: A: 168 B: 295 C: 32 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 152: A: 117 B: 458 C: 169 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 153: A: 169 B: 294 C: 117 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 154: A: 118 B: 459 C: 169 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 155: A: 169 B: 458 C: 118 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 156: A: 120 B: 298 C: 169 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 157: A: 169 B: 459 C: 120 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 158: A: 33 B: 294 C: 169 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 159: A: 169 B: 298 C: 33 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 160: A: 119 B: 288 C: 170 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 161: A: 170 B: 460 C: 119 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 162: A: 31 B: 299 C: 170 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 163: A: 170 B: 288 C: 31 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 164: A: 36 B: 300 C: 170 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 165: A: 170 B: 299 C: 36 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 166: A: 121 B: 460 C: 170 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 167: A: 170 B: 300 C: 121 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 168: A: 31 B: 292 C: 171 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 169: A: 171 B: 299 C: 31 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 170: A: 32 B: 302 C: 171 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 171: A: 171 B: 292 C: 32 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 172: A: 37 B: 303 C: 171 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 173: A: 171 B: 302 C: 37 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 174: A: 36 B: 299 C: 171 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 175: A: 171 B: 303 C: 36 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 176: A: 32 B: 295 C: 172 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 177: A: 172 B: 302 C: 32 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 178: A: 33 B: 304 C: 172 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 179: A: 172 B: 295 C: 33 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 180: A: 38 B: 305 C: 172 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 181: A: 172 B: 304 C: 38 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 182: A: 37 B: 302 C: 172 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 183: A: 172 B: 305 C: 37 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 184: A: 33 B: 298 C: 173 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 185: A: 173 B: 304 C: 33 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 186: A: 120 B: 461 C: 173 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 187: A: 173 B: 298 C: 120 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 188: A: 122 B: 307 C: 173 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 189: A: 173 B: 461 C: 122 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 190: A: 38 B: 304 C: 173 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 191: A: 173 B: 307 C: 38 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 192: A: 121 B: 300 C: 174 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 193: A: 174 B: 462 C: 121 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 194: A: 36 B: 308 C: 174 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 195: A: 174 B: 300 C: 36 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 196: A: 41 B: 309 C: 174 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 197: A: 174 B: 308 C: 41 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 198: A: 123 B: 462 C: 174 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 199: A: 174 B: 309 C: 123 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 200: A: 36 B: 303 C: 175 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 201: A: 175 B: 308 C: 36 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 202: A: 37 B: 311 C: 175 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 203: A: 175 B: 303 C: 37 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 204: A: 42 B: 312 C: 175 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 205: A: 175 B: 311 C: 42 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 206: A: 41 B: 308 C: 175 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 207: A: 175 B: 312 C: 41 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 208: A: 37 B: 305 C: 176 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 209: A: 176 B: 311 C: 37 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 210: A: 38 B: 313 C: 176 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 211: A: 176 B: 305 C: 38 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 212: A: 43 B: 314 C: 176 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 213: A: 176 B: 313 C: 43 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 214: A: 42 B: 311 C: 176 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 215: A: 176 B: 314 C: 42 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 216: A: 38 B: 307 C: 177 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 217: A: 177 B: 313 C: 38 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 218: A: 122 B: 463 C: 177 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 219: A: 177 B: 307 C: 122 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 220: A: 124 B: 316 C: 177 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 221: A: 177 B: 463 C: 124 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 222: A: 43 B: 313 C: 177 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 223: A: 177 B: 316 C: 43 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 224: A: 123 B: 309 C: 178 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 225: A: 178 B: 465 C: 123 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 226: A: 41 B: 317 C: 178 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 227: A: 178 B: 309 C: 41 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 228: A: 126 B: 464 C: 178 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 229: A: 178 B: 317 C: 126 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 230: A: 125 B: 465 C: 178 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 231: A: 178 B: 464 C: 125 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 232: A: 41 B: 312 C: 179 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 233: A: 179 B: 317 C: 41 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 234: A: 42 B: 320 C: 179 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 235: A: 179 B: 312 C: 42 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 236: A: 127 B: 466 C: 179 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 237: A: 179 B: 320 C: 127 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 238: A: 126 B: 317 C: 179 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 239: A: 179 B: 466 C: 126 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 240: A: 42 B: 314 C: 180 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 241: A: 180 B: 320 C: 42 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 242: A: 43 B: 322 C: 180 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 243: A: 180 B: 314 C: 43 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 244: A: 128 B: 467 C: 180 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 245: A: 180 B: 322 C: 128 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 246: A: 127 B: 320 C: 180 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 247: A: 180 B: 467 C: 127 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 248: A: 43 B: 316 C: 181 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 249: A: 181 B: 322 C: 43 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 0 + *MESH_FACE 250: A: 124 B: 468 C: 181 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 251: A: 181 B: 316 C: 124 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 252: A: 129 B: 469 C: 181 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 253: A: 181 B: 468 C: 129 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 254: A: 128 B: 322 C: 181 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 255: A: 181 B: 469 C: 128 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 0 + *MESH_FACE 256: A: 0 B: 249 C: 182 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 257: A: 182 B: 328 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 258: A: 1 B: 326 C: 182 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 259: A: 182 B: 249 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 260: A: 51 B: 327 C: 182 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 261: A: 182 B: 326 C: 51 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 262: A: 50 B: 328 C: 182 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 263: A: 182 B: 327 C: 50 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 264: A: 1 B: 252 C: 183 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 265: A: 183 B: 326 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 266: A: 2 B: 329 C: 183 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 267: A: 183 B: 252 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 268: A: 52 B: 330 C: 183 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 269: A: 183 B: 329 C: 52 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 270: A: 51 B: 326 C: 183 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 271: A: 183 B: 330 C: 51 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 272: A: 2 B: 255 C: 184 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 273: A: 184 B: 329 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 274: A: 3 B: 331 C: 184 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 275: A: 184 B: 255 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 276: A: 53 B: 332 C: 184 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 277: A: 184 B: 331 C: 53 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 278: A: 52 B: 329 C: 184 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 279: A: 184 B: 332 C: 52 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 280: A: 3 B: 258 C: 185 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 281: A: 185 B: 331 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 282: A: 4 B: 333 C: 185 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 283: A: 185 B: 258 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 284: A: 54 B: 334 C: 185 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 285: A: 185 B: 333 C: 54 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 286: A: 53 B: 331 C: 185 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 287: A: 185 B: 334 C: 53 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 288: A: 50 B: 327 C: 186 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 289: A: 186 B: 337 C: 50 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 290: A: 51 B: 335 C: 186 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 291: A: 186 B: 327 C: 51 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 292: A: 67 B: 336 C: 186 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 293: A: 186 B: 335 C: 67 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 294: A: 66 B: 337 C: 186 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 295: A: 186 B: 336 C: 66 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 296: A: 51 B: 330 C: 187 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 297: A: 187 B: 335 C: 51 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 298: A: 52 B: 338 C: 187 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 299: A: 187 B: 330 C: 52 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 300: A: 68 B: 339 C: 187 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 301: A: 187 B: 338 C: 68 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 302: A: 67 B: 335 C: 187 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 303: A: 187 B: 339 C: 67 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 304: A: 52 B: 332 C: 188 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 305: A: 188 B: 338 C: 52 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 306: A: 53 B: 340 C: 188 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 307: A: 188 B: 332 C: 53 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 308: A: 69 B: 341 C: 188 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 309: A: 188 B: 340 C: 69 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 310: A: 68 B: 338 C: 188 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 311: A: 188 B: 341 C: 68 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 312: A: 53 B: 334 C: 189 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 313: A: 189 B: 340 C: 53 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 314: A: 54 B: 342 C: 189 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 315: A: 189 B: 334 C: 54 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 316: A: 70 B: 343 C: 189 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 317: A: 189 B: 342 C: 70 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 318: A: 69 B: 340 C: 189 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 319: A: 189 B: 343 C: 69 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 320: A: 66 B: 336 C: 190 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 321: A: 190 B: 346 C: 66 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 322: A: 67 B: 344 C: 190 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 323: A: 190 B: 336 C: 67 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 324: A: 83 B: 345 C: 190 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 325: A: 190 B: 344 C: 83 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 326: A: 82 B: 346 C: 190 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 327: A: 190 B: 345 C: 82 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 328: A: 67 B: 339 C: 191 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 329: A: 191 B: 344 C: 67 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 330: A: 68 B: 347 C: 191 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 331: A: 191 B: 339 C: 68 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 332: A: 84 B: 348 C: 191 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 333: A: 191 B: 347 C: 84 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 334: A: 83 B: 344 C: 191 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 335: A: 191 B: 348 C: 83 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 336: A: 68 B: 341 C: 192 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 337: A: 192 B: 347 C: 68 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 338: A: 69 B: 349 C: 192 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 339: A: 192 B: 341 C: 69 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 340: A: 85 B: 350 C: 192 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 341: A: 192 B: 349 C: 85 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 342: A: 84 B: 347 C: 192 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 343: A: 192 B: 350 C: 84 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 4 + *MESH_FACE 344: A: 69 B: 343 C: 193 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 345: A: 193 B: 349 C: 69 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 346: A: 70 B: 351 C: 193 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 347: A: 193 B: 343 C: 70 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 348: A: 86 B: 352 C: 193 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 349: A: 193 B: 351 C: 86 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 350: A: 85 B: 349 C: 193 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 351: A: 193 B: 352 C: 85 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 352: A: 82 B: 345 C: 194 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 353: A: 194 B: 354 C: 82 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 354: A: 83 B: 353 C: 194 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 355: A: 194 B: 345 C: 83 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 356: A: 26 B: 286 C: 194 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 357: A: 194 B: 353 C: 26 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 358: A: 25 B: 354 C: 194 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 359: A: 194 B: 286 C: 25 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 360: A: 83 B: 348 C: 195 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 361: A: 195 B: 353 C: 83 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 362: A: 84 B: 355 C: 195 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 363: A: 195 B: 348 C: 84 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 364: A: 27 B: 290 C: 195 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 365: A: 195 B: 355 C: 27 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 366: A: 26 B: 353 C: 195 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 367: A: 195 B: 290 C: 26 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 368: A: 84 B: 350 C: 196 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 369: A: 196 B: 355 C: 84 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 370: A: 85 B: 356 C: 196 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 371: A: 196 B: 350 C: 85 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 372: A: 28 B: 293 C: 196 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 373: A: 196 B: 356 C: 28 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 374: A: 27 B: 355 C: 196 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 375: A: 196 B: 293 C: 27 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 376: A: 85 B: 352 C: 197 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 377: A: 197 B: 356 C: 85 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 4 + *MESH_FACE 378: A: 86 B: 357 C: 197 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 379: A: 197 B: 352 C: 86 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 380: A: 29 B: 296 C: 197 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 381: A: 197 B: 357 C: 29 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 382: A: 28 B: 356 C: 197 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 383: A: 197 B: 296 C: 28 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 4 + *MESH_FACE 384: A: 131 B: 257 C: 198 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 385: A: 198 B: 471 C: 131 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 386: A: 9 B: 358 C: 198 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 387: A: 198 B: 257 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 388: A: 55 B: 359 C: 198 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 389: A: 198 B: 358 C: 55 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 390: A: 139 B: 471 C: 198 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 391: A: 198 B: 359 C: 139 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 392: A: 9 B: 267 C: 199 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 393: A: 199 B: 358 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 394: A: 14 B: 360 C: 199 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 395: A: 199 B: 267 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 396: A: 56 B: 361 C: 199 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 397: A: 199 B: 360 C: 56 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 398: A: 55 B: 358 C: 199 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 399: A: 199 B: 361 C: 55 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 400: A: 14 B: 276 C: 200 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 401: A: 200 B: 360 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 402: A: 19 B: 362 C: 200 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 403: A: 200 B: 276 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 404: A: 57 B: 363 C: 200 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 405: A: 200 B: 362 C: 57 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 406: A: 56 B: 360 C: 200 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 407: A: 200 B: 363 C: 56 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 408: A: 19 B: 285 C: 201 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 409: A: 201 B: 362 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 410: A: 133 B: 478 C: 201 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 411: A: 201 B: 285 C: 133 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 412: A: 140 B: 365 C: 201 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 413: A: 201 B: 478 C: 140 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 414: A: 57 B: 362 C: 201 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 415: A: 201 B: 365 C: 57 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 416: A: 139 B: 359 C: 202 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 417: A: 202 B: 473 C: 139 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 418: A: 55 B: 366 C: 202 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 419: A: 202 B: 359 C: 55 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 420: A: 71 B: 367 C: 202 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 421: A: 202 B: 366 C: 71 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 422: A: 143 B: 473 C: 202 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 423: A: 202 B: 367 C: 143 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 424: A: 55 B: 361 C: 203 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 425: A: 203 B: 366 C: 55 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 426: A: 56 B: 368 C: 203 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 427: A: 203 B: 361 C: 56 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 428: A: 72 B: 369 C: 203 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 429: A: 203 B: 368 C: 72 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 430: A: 71 B: 366 C: 203 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 431: A: 203 B: 369 C: 71 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 432: A: 56 B: 363 C: 204 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 433: A: 204 B: 368 C: 56 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 434: A: 57 B: 370 C: 204 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 435: A: 204 B: 363 C: 57 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 436: A: 73 B: 371 C: 204 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 437: A: 204 B: 370 C: 73 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 438: A: 72 B: 368 C: 204 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 439: A: 204 B: 371 C: 72 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 440: A: 57 B: 365 C: 205 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 441: A: 205 B: 370 C: 57 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 442: A: 140 B: 479 C: 205 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 443: A: 205 B: 365 C: 140 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 444: A: 144 B: 373 C: 205 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 445: A: 205 B: 479 C: 144 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 446: A: 73 B: 370 C: 205 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 447: A: 205 B: 373 C: 73 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 448: A: 143 B: 367 C: 206 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 449: A: 206 B: 475 C: 143 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 450: A: 71 B: 374 C: 206 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 451: A: 206 B: 367 C: 71 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 452: A: 87 B: 375 C: 206 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 453: A: 206 B: 374 C: 87 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 454: A: 147 B: 475 C: 206 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 455: A: 206 B: 375 C: 147 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 456: A: 71 B: 369 C: 207 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 457: A: 207 B: 374 C: 71 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 458: A: 72 B: 376 C: 207 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 459: A: 207 B: 369 C: 72 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 460: A: 88 B: 377 C: 207 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 461: A: 207 B: 376 C: 88 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 462: A: 87 B: 374 C: 207 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 463: A: 207 B: 377 C: 87 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 464: A: 72 B: 371 C: 208 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 465: A: 208 B: 376 C: 72 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 466: A: 73 B: 378 C: 208 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 467: A: 208 B: 371 C: 73 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 468: A: 89 B: 379 C: 208 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 469: A: 208 B: 378 C: 89 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 470: A: 88 B: 376 C: 208 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 471: A: 208 B: 379 C: 88 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 3 + *MESH_FACE 472: A: 73 B: 373 C: 209 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 473: A: 209 B: 378 C: 73 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 474: A: 144 B: 480 C: 209 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 475: A: 209 B: 373 C: 144 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 476: A: 148 B: 381 C: 209 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 477: A: 209 B: 480 C: 148 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 478: A: 89 B: 378 C: 209 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 479: A: 209 B: 381 C: 89 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 480: A: 147 B: 375 C: 210 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 481: A: 210 B: 477 C: 147 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 482: A: 87 B: 382 C: 210 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 483: A: 210 B: 375 C: 87 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 484: A: 34 B: 297 C: 210 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 485: A: 210 B: 382 C: 34 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 486: A: 135 B: 477 C: 210 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 487: A: 210 B: 297 C: 135 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 488: A: 87 B: 377 C: 211 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 489: A: 211 B: 382 C: 87 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 490: A: 88 B: 383 C: 211 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 491: A: 211 B: 377 C: 88 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 492: A: 39 B: 306 C: 211 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 493: A: 211 B: 383 C: 39 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 494: A: 34 B: 382 C: 211 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 495: A: 211 B: 306 C: 34 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 496: A: 88 B: 379 C: 212 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 497: A: 212 B: 383 C: 88 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 498: A: 89 B: 384 C: 212 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 499: A: 212 B: 379 C: 89 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 500: A: 44 B: 315 C: 212 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 501: A: 212 B: 384 C: 44 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 502: A: 39 B: 383 C: 212 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 503: A: 212 B: 315 C: 39 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 504: A: 89 B: 381 C: 213 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 505: A: 213 B: 384 C: 89 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 3 + *MESH_FACE 506: A: 148 B: 481 C: 213 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 507: A: 213 B: 381 C: 148 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 508: A: 137 B: 324 C: 213 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 509: A: 213 B: 481 C: 137 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 510: A: 44 B: 384 C: 213 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 511: A: 213 B: 324 C: 44 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 3 + *MESH_FACE 512: A: 24 B: 284 C: 214 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 513: A: 214 B: 364 C: 24 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 514: A: 23 B: 386 C: 214 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 515: A: 214 B: 284 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 516: A: 59 B: 387 C: 214 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 517: A: 214 B: 386 C: 59 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 518: A: 58 B: 364 C: 214 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 519: A: 214 B: 387 C: 58 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 520: A: 23 B: 282 C: 215 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 521: A: 215 B: 386 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 522: A: 22 B: 388 C: 215 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 523: A: 215 B: 282 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 524: A: 60 B: 389 C: 215 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 525: A: 215 B: 388 C: 60 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 526: A: 59 B: 386 C: 215 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 527: A: 215 B: 389 C: 59 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 528: A: 22 B: 280 C: 216 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 529: A: 216 B: 388 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 530: A: 21 B: 390 C: 216 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 531: A: 216 B: 280 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 532: A: 61 B: 391 C: 216 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 533: A: 216 B: 390 C: 61 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 534: A: 60 B: 388 C: 216 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 535: A: 216 B: 391 C: 60 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 536: A: 21 B: 278 C: 217 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 537: A: 217 B: 390 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 538: A: 20 B: 392 C: 217 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 539: A: 217 B: 278 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 540: A: 62 B: 393 C: 217 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 541: A: 217 B: 392 C: 62 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 542: A: 61 B: 390 C: 217 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 543: A: 217 B: 393 C: 61 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 544: A: 58 B: 387 C: 218 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 545: A: 218 B: 372 C: 58 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 546: A: 59 B: 394 C: 218 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 547: A: 218 B: 387 C: 59 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 548: A: 75 B: 395 C: 218 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 549: A: 218 B: 394 C: 75 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 550: A: 74 B: 372 C: 218 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 551: A: 218 B: 395 C: 74 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 552: A: 59 B: 389 C: 219 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 553: A: 219 B: 394 C: 59 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 554: A: 60 B: 396 C: 219 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 555: A: 219 B: 389 C: 60 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 556: A: 76 B: 397 C: 219 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 557: A: 219 B: 396 C: 76 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 558: A: 75 B: 394 C: 219 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 559: A: 219 B: 397 C: 75 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 560: A: 60 B: 391 C: 220 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 561: A: 220 B: 396 C: 60 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 562: A: 61 B: 398 C: 220 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 563: A: 220 B: 391 C: 61 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 564: A: 77 B: 399 C: 220 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 565: A: 220 B: 398 C: 77 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 566: A: 76 B: 396 C: 220 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 567: A: 220 B: 399 C: 76 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 568: A: 61 B: 393 C: 221 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 569: A: 221 B: 398 C: 61 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 570: A: 62 B: 400 C: 221 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 571: A: 221 B: 393 C: 62 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 572: A: 78 B: 401 C: 221 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 573: A: 221 B: 400 C: 78 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 574: A: 77 B: 398 C: 221 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 575: A: 221 B: 401 C: 77 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 576: A: 74 B: 395 C: 222 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 577: A: 222 B: 380 C: 74 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 578: A: 75 B: 402 C: 222 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 579: A: 222 B: 395 C: 75 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 580: A: 91 B: 403 C: 222 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 581: A: 222 B: 402 C: 91 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 582: A: 90 B: 380 C: 222 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 583: A: 222 B: 403 C: 90 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 584: A: 75 B: 397 C: 223 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 585: A: 223 B: 402 C: 75 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 586: A: 76 B: 404 C: 223 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 587: A: 223 B: 397 C: 76 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 588: A: 92 B: 405 C: 223 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 589: A: 223 B: 404 C: 92 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 590: A: 91 B: 402 C: 223 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 591: A: 223 B: 405 C: 91 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 592: A: 76 B: 399 C: 224 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 593: A: 224 B: 404 C: 76 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 594: A: 77 B: 406 C: 224 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 595: A: 224 B: 399 C: 77 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 596: A: 93 B: 407 C: 224 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 597: A: 224 B: 406 C: 93 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 598: A: 92 B: 404 C: 224 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 599: A: 224 B: 407 C: 92 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 5 + *MESH_FACE 600: A: 77 B: 401 C: 225 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 601: A: 225 B: 406 C: 77 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 602: A: 78 B: 408 C: 225 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 603: A: 225 B: 401 C: 78 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 604: A: 94 B: 409 C: 225 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 605: A: 225 B: 408 C: 94 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 606: A: 93 B: 406 C: 225 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 607: A: 225 B: 409 C: 93 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 608: A: 90 B: 403 C: 226 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 609: A: 226 B: 385 C: 90 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 610: A: 91 B: 410 C: 226 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 611: A: 226 B: 403 C: 91 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 612: A: 48 B: 325 C: 226 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 613: A: 226 B: 410 C: 48 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 614: A: 49 B: 385 C: 226 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 615: A: 226 B: 325 C: 49 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 616: A: 91 B: 405 C: 227 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 617: A: 227 B: 410 C: 91 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 618: A: 92 B: 411 C: 227 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 619: A: 227 B: 405 C: 92 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 620: A: 47 B: 323 C: 227 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 621: A: 227 B: 411 C: 47 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 622: A: 48 B: 410 C: 227 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 623: A: 227 B: 323 C: 48 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 624: A: 92 B: 407 C: 228 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 625: A: 228 B: 411 C: 92 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 626: A: 93 B: 412 C: 228 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 627: A: 228 B: 407 C: 93 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 628: A: 46 B: 321 C: 228 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 629: A: 228 B: 412 C: 46 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 630: A: 47 B: 411 C: 228 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 631: A: 228 B: 321 C: 47 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 632: A: 93 B: 409 C: 229 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 633: A: 229 B: 412 C: 93 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 5 + *MESH_FACE 634: A: 94 B: 413 C: 229 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 635: A: 229 B: 409 C: 94 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 636: A: 45 B: 318 C: 229 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 637: A: 229 B: 413 C: 45 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 638: A: 46 B: 412 C: 229 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 639: A: 229 B: 318 C: 46 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 5 + *MESH_FACE 640: A: 132 B: 277 C: 230 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 641: A: 230 B: 482 C: 132 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 642: A: 15 B: 414 C: 230 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 643: A: 230 B: 277 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 644: A: 63 B: 415 C: 230 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 645: A: 230 B: 414 C: 63 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 646: A: 141 B: 482 C: 230 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 647: A: 230 B: 415 C: 141 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 648: A: 15 B: 268 C: 231 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 649: A: 231 B: 414 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 650: A: 10 B: 416 C: 231 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 651: A: 231 B: 268 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 652: A: 64 B: 417 C: 231 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 653: A: 231 B: 416 C: 64 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 654: A: 63 B: 414 C: 231 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 655: A: 231 B: 417 C: 63 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 656: A: 10 B: 259 C: 232 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 657: A: 232 B: 416 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 658: A: 5 B: 418 C: 232 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 659: A: 232 B: 259 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 660: A: 65 B: 419 C: 232 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 661: A: 232 B: 418 C: 65 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 662: A: 64 B: 416 C: 232 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 663: A: 232 B: 419 C: 64 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 664: A: 5 B: 246 C: 233 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 665: A: 233 B: 418 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 666: A: 130 B: 470 C: 233 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 667: A: 233 B: 246 C: 130 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 668: A: 138 B: 420 C: 233 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 669: A: 233 B: 470 C: 138 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 670: A: 65 B: 418 C: 233 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 671: A: 233 B: 420 C: 65 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 672: A: 141 B: 415 C: 234 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 673: A: 234 B: 483 C: 141 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 674: A: 63 B: 421 C: 234 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 675: A: 234 B: 415 C: 63 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 676: A: 79 B: 422 C: 234 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 677: A: 234 B: 421 C: 79 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 678: A: 145 B: 483 C: 234 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 679: A: 234 B: 422 C: 145 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 680: A: 63 B: 417 C: 235 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 681: A: 235 B: 421 C: 63 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 682: A: 64 B: 423 C: 235 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 683: A: 235 B: 417 C: 64 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 684: A: 80 B: 424 C: 235 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 685: A: 235 B: 423 C: 80 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 686: A: 79 B: 421 C: 235 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 687: A: 235 B: 424 C: 79 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 688: A: 64 B: 419 C: 236 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 689: A: 236 B: 423 C: 64 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 690: A: 65 B: 425 C: 236 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 691: A: 236 B: 419 C: 65 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 692: A: 81 B: 426 C: 236 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 693: A: 236 B: 425 C: 81 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 694: A: 80 B: 423 C: 236 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 695: A: 236 B: 426 C: 80 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 696: A: 65 B: 420 C: 237 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 697: A: 237 B: 425 C: 65 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 698: A: 138 B: 472 C: 237 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 699: A: 237 B: 420 C: 138 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 700: A: 142 B: 427 C: 237 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 701: A: 237 B: 472 C: 142 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 702: A: 81 B: 425 C: 237 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 703: A: 237 B: 427 C: 81 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 704: A: 145 B: 422 C: 238 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 705: A: 238 B: 484 C: 145 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 706: A: 79 B: 428 C: 238 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 707: A: 238 B: 422 C: 79 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 708: A: 95 B: 429 C: 238 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 709: A: 238 B: 428 C: 95 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 710: A: 149 B: 484 C: 238 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 711: A: 238 B: 429 C: 149 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 712: A: 79 B: 424 C: 239 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 713: A: 239 B: 428 C: 79 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 714: A: 80 B: 430 C: 239 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 715: A: 239 B: 424 C: 80 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 716: A: 96 B: 431 C: 239 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 717: A: 239 B: 430 C: 96 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 718: A: 95 B: 428 C: 239 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 719: A: 239 B: 431 C: 95 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 720: A: 80 B: 426 C: 240 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 721: A: 240 B: 430 C: 80 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 722: A: 81 B: 432 C: 240 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 723: A: 240 B: 426 C: 81 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 724: A: 97 B: 433 C: 240 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 725: A: 240 B: 432 C: 97 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 726: A: 96 B: 430 C: 240 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 727: A: 240 B: 433 C: 96 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 2 + *MESH_FACE 728: A: 81 B: 427 C: 241 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 729: A: 241 B: 432 C: 81 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 730: A: 142 B: 474 C: 241 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 731: A: 241 B: 427 C: 142 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 732: A: 146 B: 434 C: 241 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 733: A: 241 B: 474 C: 146 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 734: A: 97 B: 432 C: 241 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 735: A: 241 B: 434 C: 97 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 736: A: 149 B: 429 C: 242 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 737: A: 242 B: 485 C: 149 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 738: A: 95 B: 435 C: 242 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 739: A: 242 B: 429 C: 95 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 740: A: 40 B: 319 C: 242 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 741: A: 242 B: 435 C: 40 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 742: A: 136 B: 485 C: 242 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 743: A: 242 B: 319 C: 136 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 744: A: 95 B: 431 C: 243 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 745: A: 243 B: 435 C: 95 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 746: A: 96 B: 436 C: 243 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 747: A: 243 B: 431 C: 96 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 748: A: 35 B: 310 C: 243 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 749: A: 243 B: 436 C: 35 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 750: A: 40 B: 435 C: 243 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 751: A: 243 B: 310 C: 40 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 752: A: 96 B: 433 C: 244 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 753: A: 244 B: 436 C: 96 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 754: A: 97 B: 437 C: 244 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 755: A: 244 B: 433 C: 97 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 756: A: 30 B: 301 C: 244 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 757: A: 244 B: 437 C: 30 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 758: A: 35 B: 436 C: 244 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 759: A: 244 B: 301 C: 35 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 760: A: 97 B: 434 C: 245 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 761: A: 245 B: 437 C: 97 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1,2 *MESH_MTLID 2 + *MESH_FACE 762: A: 146 B: 476 C: 245 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 763: A: 245 B: 434 C: 146 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 764: A: 134 B: 289 C: 245 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 765: A: 245 B: 476 C: 134 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 766: A: 30 B: 437 C: 245 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + *MESH_FACE 767: A: 245 B: 289 C: 30 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 2 + } + *MESH_NUMTVERTEX 558 + *MESH_TVERTLIST { + *MESH_TVERT 0 0.7498 0.2502 0.0005 + *MESH_TVERT 1 0.5000 0.2502 0.0005 + *MESH_TVERT 2 0.2502 0.2502 0.0005 + *MESH_TVERT 3 0.7498 0.5000 0.0005 + *MESH_TVERT 4 0.5000 0.5000 0.0005 + *MESH_TVERT 5 0.2502 0.5000 0.0005 + *MESH_TVERT 6 0.7498 0.7498 0.0005 + *MESH_TVERT 7 0.5000 0.7498 0.0005 + *MESH_TVERT 8 0.2502 0.7498 0.0005 + *MESH_TVERT 9 0.2502 0.2502 0.9995 + *MESH_TVERT 10 0.5000 0.2502 0.9995 + *MESH_TVERT 11 0.7498 0.2502 0.9995 + *MESH_TVERT 12 0.2502 0.5000 0.9995 + *MESH_TVERT 13 0.5000 0.5000 0.9995 + *MESH_TVERT 14 0.7498 0.5000 0.9995 + *MESH_TVERT 15 0.2502 0.7498 0.9995 + *MESH_TVERT 16 0.5000 0.7498 0.9995 + *MESH_TVERT 17 0.7498 0.7498 0.9995 + *MESH_TVERT 18 0.0005 0.2502 0.0005 + *MESH_TVERT 19 0.2502 0.2502 0.0005 + *MESH_TVERT 20 0.5000 0.2502 0.0005 + *MESH_TVERT 21 0.7498 0.2502 0.0005 + *MESH_TVERT 22 0.9995 0.2502 0.0005 + *MESH_TVERT 23 0.7498 0.2502 0.9995 + *MESH_TVERT 24 0.7498 0.5000 0.9995 + *MESH_TVERT 25 0.7498 0.7498 0.9995 + *MESH_TVERT 26 0.7498 0.7498 0.9995 + *MESH_TVERT 27 0.5000 0.7498 0.9995 + *MESH_TVERT 28 0.2502 0.7498 0.9995 + *MESH_TVERT 29 0.0005 0.7498 0.9995 + *MESH_TVERT 30 0.2502 0.7498 0.0005 + *MESH_TVERT 31 0.2502 0.5000 0.0005 + *MESH_TVERT 32 0.2502 0.2502 0.0005 + *MESH_TVERT 33 0.0005 0.5000 0.0005 + *MESH_TVERT 34 0.2502 0.5000 0.0005 + *MESH_TVERT 35 0.5000 0.5000 0.0005 + *MESH_TVERT 36 0.7498 0.5000 0.0005 + *MESH_TVERT 37 0.9995 0.5000 0.0005 + *MESH_TVERT 38 0.5000 0.2502 0.9995 + *MESH_TVERT 39 0.5000 0.5000 0.9995 + *MESH_TVERT 40 0.5000 0.7498 0.9995 + *MESH_TVERT 41 0.7498 0.5000 0.9995 + *MESH_TVERT 42 0.5000 0.5000 0.9995 + *MESH_TVERT 43 0.2502 0.5000 0.9995 + *MESH_TVERT 44 0.0005 0.5000 0.9995 + *MESH_TVERT 45 0.5000 0.7498 0.0005 + *MESH_TVERT 46 0.5000 0.5000 0.0005 + *MESH_TVERT 47 0.5000 0.2502 0.0005 + *MESH_TVERT 48 0.0005 0.7498 0.0005 + *MESH_TVERT 49 0.2502 0.7498 0.0005 + *MESH_TVERT 50 0.5000 0.7498 0.0005 + *MESH_TVERT 51 0.7498 0.7498 0.0005 + *MESH_TVERT 52 0.9995 0.7498 0.0005 + *MESH_TVERT 53 0.2502 0.2502 0.9995 + *MESH_TVERT 54 0.2502 0.5000 0.9995 + *MESH_TVERT 55 0.2502 0.7498 0.9995 + *MESH_TVERT 56 0.7498 0.2502 0.9995 + *MESH_TVERT 57 0.5000 0.2502 0.9995 + *MESH_TVERT 58 0.2502 0.2502 0.9995 + *MESH_TVERT 59 0.0005 0.2502 0.9995 + *MESH_TVERT 60 0.7498 0.7498 0.0005 + *MESH_TVERT 61 0.7498 0.5000 0.0005 + *MESH_TVERT 62 0.7498 0.2502 0.0005 + *MESH_TVERT 63 0.0005 0.0005 0.0005 + *MESH_TVERT 64 0.2502 0.0005 0.0005 + *MESH_TVERT 65 0.2502 0.0005 0.0005 + *MESH_TVERT 66 0.5000 0.0005 0.0005 + *MESH_TVERT 67 0.5000 0.0005 0.0005 + *MESH_TVERT 68 0.7498 0.0005 0.0005 + *MESH_TVERT 69 0.7498 0.0005 0.0005 + *MESH_TVERT 70 0.9995 0.0005 0.0005 + *MESH_TVERT 71 0.2502 0.9995 0.0005 + *MESH_TVERT 72 0.0005 0.9995 0.0005 + *MESH_TVERT 73 0.5000 0.9995 0.0005 + *MESH_TVERT 74 0.2502 0.9995 0.0005 + *MESH_TVERT 75 0.7498 0.9995 0.0005 + *MESH_TVERT 76 0.5000 0.9995 0.0005 + *MESH_TVERT 77 0.9995 0.9995 0.0005 + *MESH_TVERT 78 0.7498 0.9995 0.0005 + *MESH_TVERT 79 0.9995 0.2503 0.9995 + *MESH_TVERT 80 0.9995 0.2503 0.9995 + *MESH_TVERT 81 0.9995 0.5000 0.9995 + *MESH_TVERT 82 0.9995 0.5000 0.9995 + *MESH_TVERT 83 0.9995 0.7498 0.9995 + *MESH_TVERT 84 0.9995 0.7498 0.9995 + *MESH_TVERT 85 0.0005 0.2502 0.9995 + *MESH_TVERT 86 0.0005 0.5000 0.9995 + *MESH_TVERT 87 0.0005 0.2502 0.9995 + *MESH_TVERT 88 0.0005 0.7498 0.9995 + *MESH_TVERT 89 0.0005 0.5000 0.9995 + *MESH_TVERT 90 0.0005 0.7498 0.9995 + *MESH_TVERT 91 0.9995 0.9995 0.9995 + *MESH_TVERT 92 0.7498 0.9995 0.9995 + *MESH_TVERT 93 0.9995 0.7498 0.9995 + *MESH_TVERT 94 0.7498 0.9995 0.9995 + *MESH_TVERT 95 0.5000 0.9995 0.9995 + *MESH_TVERT 96 0.5000 0.9995 0.9995 + *MESH_TVERT 97 0.2502 0.9995 0.9995 + *MESH_TVERT 98 0.2502 0.9995 0.9995 + *MESH_TVERT 99 0.0005 0.9995 0.9995 + *MESH_TVERT 100 0.9995 0.7498 0.9995 + *MESH_TVERT 101 0.9995 0.5000 0.9995 + *MESH_TVERT 102 0.9995 0.5000 0.9995 + *MESH_TVERT 103 0.9995 0.2502 0.9995 + *MESH_TVERT 104 0.9995 0.2502 0.9995 + *MESH_TVERT 105 0.7498 0.0005 0.9995 + *MESH_TVERT 106 0.9995 0.0005 0.9995 + *MESH_TVERT 107 0.5000 0.0005 0.9995 + *MESH_TVERT 108 0.7498 0.0005 0.9995 + *MESH_TVERT 109 0.2502 0.0005 0.9995 + *MESH_TVERT 110 0.5000 0.0005 0.9995 + *MESH_TVERT 111 0.0005 0.0005 0.9995 + *MESH_TVERT 112 0.2502 0.0005 0.9995 + *MESH_TVERT 113 0.0005 0.7498 0.0005 + *MESH_TVERT 114 0.0005 0.7498 0.0005 + *MESH_TVERT 115 0.0005 0.5000 0.0005 + *MESH_TVERT 116 0.0005 0.5000 0.0005 + *MESH_TVERT 117 0.0005 0.2502 0.0005 + *MESH_TVERT 118 0.0005 0.2502 0.0005 + *MESH_TVERT 119 0.9995 0.7498 0.0005 + *MESH_TVERT 120 0.9995 0.5000 0.0005 + *MESH_TVERT 121 0.9995 0.7498 0.0005 + *MESH_TVERT 122 0.9995 0.2502 0.0005 + *MESH_TVERT 123 0.9995 0.5000 0.0005 + *MESH_TVERT 124 0.9995 0.2502 0.0005 + *MESH_TVERT 125 0.9995 0.0005 0.0005 + *MESH_TVERT 126 0.7498 0.0005 0.0005 + *MESH_TVERT 127 0.5000 0.0005 0.0005 + *MESH_TVERT 128 0.2502 0.0005 0.0005 + *MESH_TVERT 129 0.0005 0.0005 0.0005 + *MESH_TVERT 130 0.9995 0.2502 0.0005 + *MESH_TVERT 131 0.0005 0.2502 0.0005 + *MESH_TVERT 132 0.9995 0.5000 0.0005 + *MESH_TVERT 133 0.0005 0.5000 0.0005 + *MESH_TVERT 134 0.9995 0.7498 0.0005 + *MESH_TVERT 135 0.0005 0.7498 0.0005 + *MESH_TVERT 136 0.9995 0.9995 0.0005 + *MESH_TVERT 137 0.7498 0.9995 0.0005 + *MESH_TVERT 138 0.5000 0.9995 0.0005 + *MESH_TVERT 139 0.2502 0.9995 0.0005 + *MESH_TVERT 140 0.0005 0.9995 0.0005 + *MESH_TVERT 141 0.0005 0.0005 0.9995 + *MESH_TVERT 142 0.2502 0.0005 0.9995 + *MESH_TVERT 143 0.5000 0.0005 0.9995 + *MESH_TVERT 144 0.7498 0.0005 0.9995 + *MESH_TVERT 145 0.9995 0.0005 0.9995 + *MESH_TVERT 146 0.0005 0.2502 0.9995 + *MESH_TVERT 147 0.9995 0.2502 0.9995 + *MESH_TVERT 148 0.0005 0.5000 0.9995 + *MESH_TVERT 149 0.9995 0.5000 0.9995 + *MESH_TVERT 150 0.0005 0.7498 0.9995 + *MESH_TVERT 151 0.9995 0.7498 0.9995 + *MESH_TVERT 152 0.0005 0.9995 0.9995 + *MESH_TVERT 153 0.2502 0.9995 0.9995 + *MESH_TVERT 154 0.5000 0.9995 0.9995 + *MESH_TVERT 155 0.7498 0.9995 0.9995 + *MESH_TVERT 156 0.9995 0.9995 0.9995 + *MESH_TVERT 157 0.0005 0.0005 0.0005 + *MESH_TVERT 158 0.9995 0.0005 0.9995 + *MESH_TVERT 159 0.0005 0.9995 0.0005 + *MESH_TVERT 160 0.9995 0.9995 0.9995 + *MESH_TVERT 161 0.9995 0.0005 0.0005 + *MESH_TVERT 162 0.0005 0.0005 0.9995 + *MESH_TVERT 163 0.9995 0.9995 0.0005 + *MESH_TVERT 164 0.0005 0.9995 0.9995 + *MESH_TVERT 165 0.2502 0.0005 0.0005 + *MESH_TVERT 166 0.2502 0.0005 0.0005 + *MESH_TVERT 167 0.7498 0.0005 0.9995 + *MESH_TVERT 168 0.7498 0.0005 0.9995 + *MESH_TVERT 169 0.7498 0.9995 0.9995 + *MESH_TVERT 170 0.2502 0.9995 0.0005 + *MESH_TVERT 171 0.2502 0.9995 0.0005 + *MESH_TVERT 172 0.5000 0.0005 0.0005 + *MESH_TVERT 173 0.5000 0.0005 0.0005 + *MESH_TVERT 174 0.5000 0.0005 0.9995 + *MESH_TVERT 175 0.5000 0.0005 0.9995 + *MESH_TVERT 176 0.5000 0.9995 0.9995 + *MESH_TVERT 177 0.5000 0.9995 0.0005 + *MESH_TVERT 178 0.5000 0.9995 0.0005 + *MESH_TVERT 179 0.7498 0.0005 0.0005 + *MESH_TVERT 180 0.7498 0.0005 0.0005 + *MESH_TVERT 181 0.2502 0.0005 0.9995 + *MESH_TVERT 182 0.2502 0.0005 0.9995 + *MESH_TVERT 183 0.2502 0.9995 0.9995 + *MESH_TVERT 184 0.7498 0.9995 0.0005 + *MESH_TVERT 185 0.7498 0.9995 0.0005 + *MESH_TVERT 186 0.8746 0.1254 0.0005 + *MESH_TVERT 187 0.6249 0.1254 0.0005 + *MESH_TVERT 188 0.3751 0.1254 0.0005 + *MESH_TVERT 189 0.1254 0.1254 0.0005 + *MESH_TVERT 190 0.8746 0.3751 0.0005 + *MESH_TVERT 191 0.6249 0.3751 0.0005 + *MESH_TVERT 192 0.3751 0.3751 0.0005 + *MESH_TVERT 193 0.1254 0.3751 0.0005 + *MESH_TVERT 194 0.8746 0.6249 0.0005 + *MESH_TVERT 195 0.6249 0.6249 0.0005 + *MESH_TVERT 196 0.3751 0.6249 0.0005 + *MESH_TVERT 197 0.1254 0.6249 0.0005 + *MESH_TVERT 198 0.8746 0.8746 0.0005 + *MESH_TVERT 199 0.6249 0.8746 0.0005 + *MESH_TVERT 200 0.3751 0.8746 0.0005 + *MESH_TVERT 201 0.1254 0.8746 0.0005 + *MESH_TVERT 202 0.1254 0.1254 0.9995 + *MESH_TVERT 203 0.3751 0.1254 0.9995 + *MESH_TVERT 204 0.6249 0.1254 0.9995 + *MESH_TVERT 205 0.8746 0.1254 0.9995 + *MESH_TVERT 206 0.1254 0.3751 0.9995 + *MESH_TVERT 207 0.3751 0.3751 0.9995 + *MESH_TVERT 208 0.6249 0.3751 0.9995 + *MESH_TVERT 209 0.8746 0.3751 0.9995 + *MESH_TVERT 210 0.1254 0.6249 0.9995 + *MESH_TVERT 211 0.3751 0.6249 0.9995 + *MESH_TVERT 212 0.6249 0.6249 0.9995 + *MESH_TVERT 213 0.8746 0.6249 0.9995 + *MESH_TVERT 214 0.1254 0.8746 0.9995 + *MESH_TVERT 215 0.3751 0.8746 0.9995 + *MESH_TVERT 216 0.6249 0.8746 0.9995 + *MESH_TVERT 217 0.8746 0.8746 0.9995 + *MESH_TVERT 218 0.1254 0.1254 0.0005 + *MESH_TVERT 219 0.3751 0.1254 0.0005 + *MESH_TVERT 220 0.6249 0.1254 0.0005 + *MESH_TVERT 221 0.8746 0.1254 0.0005 + *MESH_TVERT 222 0.1254 0.3751 0.0005 + *MESH_TVERT 223 0.3751 0.3751 0.0005 + *MESH_TVERT 224 0.6249 0.3751 0.0005 + *MESH_TVERT 225 0.8746 0.3751 0.0005 + *MESH_TVERT 226 0.1254 0.6249 0.0005 + *MESH_TVERT 227 0.3751 0.6249 0.0005 + *MESH_TVERT 228 0.6249 0.6249 0.0005 + *MESH_TVERT 229 0.8746 0.6249 0.0005 + *MESH_TVERT 230 0.1254 0.8746 0.0005 + *MESH_TVERT 231 0.3751 0.8746 0.0005 + *MESH_TVERT 232 0.6249 0.8746 0.0005 + *MESH_TVERT 233 0.8746 0.8746 0.0005 + *MESH_TVERT 234 0.8746 0.1254 0.9995 + *MESH_TVERT 235 0.8746 0.3751 0.9995 + *MESH_TVERT 236 0.8746 0.6249 0.9995 + *MESH_TVERT 237 0.8746 0.8746 0.9995 + *MESH_TVERT 238 0.6249 0.1254 0.9995 + *MESH_TVERT 239 0.6249 0.3751 0.9995 + *MESH_TVERT 240 0.6249 0.6249 0.9995 + *MESH_TVERT 241 0.6249 0.8746 0.9995 + *MESH_TVERT 242 0.3751 0.1254 0.9995 + *MESH_TVERT 243 0.3751 0.3751 0.9995 + *MESH_TVERT 244 0.3751 0.6249 0.9995 + *MESH_TVERT 245 0.3751 0.8746 0.9995 + *MESH_TVERT 246 0.1254 0.1254 0.9995 + *MESH_TVERT 247 0.1254 0.3751 0.9995 + *MESH_TVERT 248 0.1254 0.6249 0.9995 + *MESH_TVERT 249 0.1254 0.8746 0.9995 + *MESH_TVERT 250 0.8746 0.8746 0.9995 + *MESH_TVERT 251 0.6249 0.8746 0.9995 + *MESH_TVERT 252 0.3751 0.8746 0.9995 + *MESH_TVERT 253 0.1254 0.8746 0.9995 + *MESH_TVERT 254 0.8746 0.6249 0.9995 + *MESH_TVERT 255 0.6249 0.6249 0.9995 + *MESH_TVERT 256 0.3751 0.6249 0.9995 + *MESH_TVERT 257 0.1254 0.6249 0.9995 + *MESH_TVERT 258 0.8746 0.3751 0.9995 + *MESH_TVERT 259 0.6249 0.3751 0.9995 + *MESH_TVERT 260 0.3751 0.3751 0.9995 + *MESH_TVERT 261 0.1254 0.3751 0.9995 + *MESH_TVERT 262 0.8746 0.1254 0.9995 + *MESH_TVERT 263 0.6249 0.1254 0.9995 + *MESH_TVERT 264 0.3751 0.1254 0.9995 + *MESH_TVERT 265 0.1254 0.1254 0.9995 + *MESH_TVERT 266 0.1254 0.8746 0.0005 + *MESH_TVERT 267 0.1254 0.6249 0.0005 + *MESH_TVERT 268 0.1254 0.3751 0.0005 + *MESH_TVERT 269 0.1254 0.1254 0.0005 + *MESH_TVERT 270 0.3751 0.8746 0.0005 + *MESH_TVERT 271 0.3751 0.6249 0.0005 + *MESH_TVERT 272 0.3751 0.3751 0.0005 + *MESH_TVERT 273 0.3751 0.1254 0.0005 + *MESH_TVERT 274 0.6249 0.8746 0.0005 + *MESH_TVERT 275 0.6249 0.6249 0.0005 + *MESH_TVERT 276 0.6249 0.3751 0.0005 + *MESH_TVERT 277 0.6249 0.1254 0.0005 + *MESH_TVERT 278 0.8746 0.8746 0.0005 + *MESH_TVERT 279 0.8746 0.6249 0.0005 + *MESH_TVERT 280 0.8746 0.3751 0.0005 + *MESH_TVERT 281 0.8746 0.1254 0.0005 + *MESH_TVERT 282 0.0005 0.1254 0.0005 + *MESH_TVERT 283 0.8746 0.2502 0.0005 + *MESH_TVERT 284 0.7498 0.1254 0.0005 + *MESH_TVERT 285 0.1254 0.0005 0.0005 + *MESH_TVERT 286 0.6249 0.2502 0.0005 + *MESH_TVERT 287 0.5000 0.1254 0.0005 + *MESH_TVERT 288 0.3751 0.0005 0.0005 + *MESH_TVERT 289 0.3751 0.2502 0.0005 + *MESH_TVERT 290 0.2502 0.1254 0.0005 + *MESH_TVERT 291 0.6249 0.0005 0.0005 + *MESH_TVERT 292 0.1254 0.2502 0.0005 + *MESH_TVERT 293 0.9995 0.1254 0.9995 + *MESH_TVERT 294 0.8746 0.0005 0.0005 + *MESH_TVERT 295 0.0005 0.3751 0.0005 + *MESH_TVERT 296 0.8746 0.5000 0.0005 + *MESH_TVERT 297 0.7498 0.3751 0.0005 + *MESH_TVERT 298 0.6249 0.5000 0.0005 + *MESH_TVERT 299 0.5000 0.3751 0.0005 + *MESH_TVERT 300 0.3751 0.5000 0.0005 + *MESH_TVERT 301 0.2502 0.3751 0.0005 + *MESH_TVERT 302 0.1254 0.5000 0.0005 + *MESH_TVERT 303 0.9995 0.3751 0.9995 + *MESH_TVERT 304 0.0005 0.6249 0.0005 + *MESH_TVERT 305 0.8746 0.7498 0.0005 + *MESH_TVERT 306 0.7498 0.6249 0.0005 + *MESH_TVERT 307 0.6249 0.7498 0.0005 + *MESH_TVERT 308 0.5000 0.6249 0.0005 + *MESH_TVERT 309 0.3751 0.7498 0.0005 + *MESH_TVERT 310 0.2502 0.6249 0.0005 + *MESH_TVERT 311 0.1254 0.7498 0.0005 + *MESH_TVERT 312 0.9995 0.6249 0.9995 + *MESH_TVERT 313 0.0005 0.8746 0.0005 + *MESH_TVERT 314 0.1254 0.9995 0.9995 + *MESH_TVERT 315 0.7498 0.8746 0.0005 + *MESH_TVERT 316 0.3751 0.9995 0.9995 + *MESH_TVERT 317 0.5000 0.8746 0.0005 + *MESH_TVERT 318 0.6249 0.9995 0.9995 + *MESH_TVERT 319 0.2502 0.8746 0.0005 + *MESH_TVERT 320 0.8746 0.9995 0.9995 + *MESH_TVERT 321 0.9995 0.8746 0.9995 + *MESH_TVERT 322 0.1254 0.9995 0.0005 + *MESH_TVERT 323 0.2502 0.1254 0.9995 + *MESH_TVERT 324 0.1254 0.2502 0.9995 + *MESH_TVERT 325 0.9995 0.1254 0.0005 + *MESH_TVERT 326 0.3751 0.9995 0.0005 + *MESH_TVERT 327 0.5000 0.1254 0.9995 + *MESH_TVERT 328 0.3751 0.2502 0.9995 + *MESH_TVERT 329 0.6249 0.9995 0.0005 + *MESH_TVERT 330 0.7498 0.1254 0.9995 + *MESH_TVERT 331 0.6249 0.2502 0.9995 + *MESH_TVERT 332 0.8746 0.9995 0.0005 + *MESH_TVERT 333 0.0005 0.1254 0.9995 + *MESH_TVERT 334 0.8746 0.2502 0.9995 + *MESH_TVERT 335 0.2502 0.3751 0.9995 + *MESH_TVERT 336 0.1254 0.5000 0.9995 + *MESH_TVERT 337 0.9995 0.3751 0.0005 + *MESH_TVERT 338 0.5000 0.3751 0.9995 + *MESH_TVERT 339 0.3751 0.5000 0.9995 + *MESH_TVERT 340 0.7498 0.3751 0.9995 + *MESH_TVERT 341 0.6249 0.5000 0.9995 + *MESH_TVERT 342 0.0005 0.3751 0.9995 + *MESH_TVERT 343 0.8746 0.5000 0.9995 + *MESH_TVERT 344 0.2502 0.6249 0.9995 + *MESH_TVERT 345 0.1254 0.7498 0.9995 + *MESH_TVERT 346 0.9995 0.6249 0.0005 + *MESH_TVERT 347 0.5000 0.6249 0.9995 + *MESH_TVERT 348 0.3751 0.7498 0.9995 + *MESH_TVERT 349 0.7498 0.6249 0.9995 + *MESH_TVERT 350 0.6249 0.7498 0.9995 + *MESH_TVERT 351 0.0005 0.6249 0.9995 + *MESH_TVERT 352 0.8746 0.7498 0.9995 + *MESH_TVERT 353 0.2502 0.8746 0.9995 + *MESH_TVERT 354 0.1254 0.0005 0.9995 + *MESH_TVERT 355 0.9995 0.8746 0.0005 + *MESH_TVERT 356 0.5000 0.8746 0.9995 + *MESH_TVERT 357 0.3751 0.0005 0.9995 + *MESH_TVERT 358 0.7498 0.8746 0.9995 + *MESH_TVERT 359 0.6249 0.0005 0.9995 + *MESH_TVERT 360 0.0005 0.8746 0.9995 + *MESH_TVERT 361 0.8746 0.0005 0.9995 + *MESH_TVERT 362 0.2502 0.1254 0.0005 + *MESH_TVERT 363 0.2502 0.1254 0.0005 + *MESH_TVERT 364 0.1254 0.2502 0.0005 + *MESH_TVERT 365 0.0005 0.1254 0.0005 + *MESH_TVERT 366 0.5000 0.1254 0.0005 + *MESH_TVERT 367 0.5000 0.1254 0.0005 + *MESH_TVERT 368 0.3751 0.2502 0.0005 + *MESH_TVERT 369 0.7498 0.1254 0.0005 + *MESH_TVERT 370 0.7498 0.1254 0.0005 + *MESH_TVERT 371 0.6249 0.2502 0.0005 + *MESH_TVERT 372 0.9995 0.1254 0.0005 + *MESH_TVERT 373 0.8746 0.2502 0.0005 + *MESH_TVERT 374 0.2502 0.3751 0.0005 + *MESH_TVERT 375 0.1254 0.5000 0.0005 + *MESH_TVERT 376 0.0005 0.3751 0.0005 + *MESH_TVERT 377 0.5000 0.3751 0.0005 + *MESH_TVERT 378 0.3751 0.5000 0.0005 + *MESH_TVERT 379 0.7498 0.3751 0.0005 + *MESH_TVERT 380 0.6249 0.5000 0.0005 + *MESH_TVERT 381 0.9995 0.3751 0.0005 + *MESH_TVERT 382 0.8746 0.5000 0.0005 + *MESH_TVERT 383 0.2502 0.6249 0.0005 + *MESH_TVERT 384 0.1254 0.7498 0.0005 + *MESH_TVERT 385 0.0005 0.6249 0.0005 + *MESH_TVERT 386 0.5000 0.6249 0.0005 + *MESH_TVERT 387 0.3751 0.7498 0.0005 + *MESH_TVERT 388 0.7498 0.6249 0.0005 + *MESH_TVERT 389 0.6249 0.7498 0.0005 + *MESH_TVERT 390 0.9995 0.6249 0.0005 + *MESH_TVERT 391 0.8746 0.7498 0.0005 + *MESH_TVERT 392 0.2502 0.8746 0.0005 + *MESH_TVERT 393 0.2502 0.8746 0.0005 + *MESH_TVERT 394 0.0005 0.8746 0.0005 + *MESH_TVERT 395 0.5000 0.8746 0.0005 + *MESH_TVERT 396 0.5000 0.8746 0.0005 + *MESH_TVERT 397 0.7498 0.8746 0.0005 + *MESH_TVERT 398 0.7498 0.8746 0.0005 + *MESH_TVERT 399 0.9995 0.8746 0.0005 + *MESH_TVERT 400 0.8746 0.2502 0.9995 + *MESH_TVERT 401 0.8746 0.2502 0.9995 + *MESH_TVERT 402 0.7498 0.1254 0.9995 + *MESH_TVERT 403 0.7498 0.1254 0.9995 + *MESH_TVERT 404 0.8746 0.5000 0.9995 + *MESH_TVERT 405 0.8746 0.5000 0.9995 + *MESH_TVERT 406 0.7498 0.3751 0.9995 + *MESH_TVERT 407 0.8746 0.7498 0.9995 + *MESH_TVERT 408 0.8746 0.7498 0.9995 + *MESH_TVERT 409 0.7498 0.6249 0.9995 + *MESH_TVERT 410 0.9995 0.8746 0.9995 + *MESH_TVERT 411 0.7497 0.8746 0.9995 + *MESH_TVERT 412 0.6249 0.2502 0.9995 + *MESH_TVERT 413 0.5000 0.1254 0.9995 + *MESH_TVERT 414 0.5000 0.1254 0.9995 + *MESH_TVERT 415 0.6249 0.5000 0.9995 + *MESH_TVERT 416 0.5000 0.3751 0.9995 + *MESH_TVERT 417 0.6249 0.7498 0.9995 + *MESH_TVERT 418 0.5000 0.6249 0.9995 + *MESH_TVERT 419 0.9995 0.6249 0.9995 + *MESH_TVERT 420 0.5000 0.8746 0.9995 + *MESH_TVERT 421 0.3751 0.2502 0.9995 + *MESH_TVERT 422 0.2502 0.1254 0.9995 + *MESH_TVERT 423 0.2502 0.1254 0.9995 + *MESH_TVERT 424 0.3751 0.5000 0.9995 + *MESH_TVERT 425 0.2502 0.3751 0.9995 + *MESH_TVERT 426 0.3751 0.7498 0.9995 + *MESH_TVERT 427 0.2502 0.6249 0.9995 + *MESH_TVERT 428 0.9995 0.3751 0.9995 + *MESH_TVERT 429 0.2502 0.8746 0.9995 + *MESH_TVERT 430 0.1254 0.2502 0.9995 + *MESH_TVERT 431 0.1254 0.2502 0.9995 + *MESH_TVERT 432 0.1254 0.5000 0.9995 + *MESH_TVERT 433 0.1254 0.5000 0.9995 + *MESH_TVERT 434 0.1254 0.7498 0.9995 + *MESH_TVERT 435 0.1254 0.7498 0.9995 + *MESH_TVERT 436 0.9995 0.1254 0.9995 + *MESH_TVERT 437 0.7498 0.8746 0.9995 + *MESH_TVERT 438 0.7498 0.8746 0.9995 + *MESH_TVERT 439 0.8746 0.7498 0.9995 + *MESH_TVERT 440 0.8746 0.7498 0.9995 + *MESH_TVERT 441 0.5000 0.8746 0.9995 + *MESH_TVERT 442 0.5000 0.8746 0.9995 + *MESH_TVERT 443 0.6249 0.7498 0.9995 + *MESH_TVERT 444 0.2502 0.8746 0.9995 + *MESH_TVERT 445 0.2502 0.8746 0.9995 + *MESH_TVERT 446 0.3751 0.7498 0.9995 + *MESH_TVERT 447 0.0005 0.8746 0.9995 + *MESH_TVERT 448 0.1254 0.7498 0.9995 + *MESH_TVERT 449 0.7498 0.6249 0.9995 + *MESH_TVERT 450 0.8746 0.5000 0.9995 + *MESH_TVERT 451 0.8746 0.5000 0.9995 + *MESH_TVERT 452 0.5000 0.6249 0.9995 + *MESH_TVERT 453 0.6249 0.5000 0.9995 + *MESH_TVERT 454 0.2502 0.6249 0.9995 + *MESH_TVERT 455 0.3751 0.5000 0.9995 + *MESH_TVERT 456 0.0005 0.6249 0.9995 + *MESH_TVERT 457 0.1254 0.5000 0.9995 + *MESH_TVERT 458 0.7498 0.3751 0.9995 + *MESH_TVERT 459 0.8746 0.2502 0.9995 + *MESH_TVERT 460 0.8746 0.2502 0.9995 + *MESH_TVERT 461 0.5000 0.3751 0.9995 + *MESH_TVERT 462 0.6249 0.2502 0.9995 + *MESH_TVERT 463 0.2502 0.3751 0.9995 + *MESH_TVERT 464 0.3751 0.2502 0.9995 + *MESH_TVERT 465 0.0005 0.3751 0.9995 + *MESH_TVERT 466 0.1254 0.2502 0.9995 + *MESH_TVERT 467 0.7498 0.1254 0.9995 + *MESH_TVERT 468 0.7498 0.1254 0.9995 + *MESH_TVERT 469 0.5000 0.1254 0.9995 + *MESH_TVERT 470 0.5000 0.1254 0.9995 + *MESH_TVERT 471 0.2502 0.1254 0.9995 + *MESH_TVERT 472 0.2502 0.1254 0.9995 + *MESH_TVERT 473 0.0005 0.1254 0.9995 + *MESH_TVERT 474 0.1254 0.7498 0.0005 + *MESH_TVERT 475 0.1254 0.7498 0.0005 + *MESH_TVERT 476 0.2502 0.8746 0.0005 + *MESH_TVERT 477 0.2502 0.8746 0.0005 + *MESH_TVERT 478 0.1254 0.5000 0.0005 + *MESH_TVERT 479 0.1254 0.5000 0.0005 + *MESH_TVERT 480 0.2502 0.6249 0.0005 + *MESH_TVERT 481 0.1254 0.2502 0.0005 + *MESH_TVERT 482 0.1254 0.2502 0.0005 + *MESH_TVERT 483 0.2502 0.3751 0.0005 + *MESH_TVERT 484 0.2502 0.1254 0.0005 + *MESH_TVERT 485 0.2502 0.1254 0.0005 + *MESH_TVERT 486 0.3751 0.7498 0.0005 + *MESH_TVERT 487 0.5000 0.8746 0.0005 + *MESH_TVERT 488 0.5000 0.8746 0.0005 + *MESH_TVERT 489 0.3751 0.5000 0.0005 + *MESH_TVERT 490 0.5000 0.6249 0.0005 + *MESH_TVERT 491 0.3751 0.2502 0.0005 + *MESH_TVERT 492 0.5000 0.3751 0.0005 + *MESH_TVERT 493 0.5000 0.1254 0.0005 + *MESH_TVERT 494 0.5000 0.1254 0.0005 + *MESH_TVERT 495 0.6249 0.7498 0.0005 + *MESH_TVERT 496 0.7498 0.8746 0.0005 + *MESH_TVERT 497 0.7498 0.8746 0.0005 + *MESH_TVERT 498 0.6249 0.5000 0.0005 + *MESH_TVERT 499 0.7498 0.6249 0.0005 + *MESH_TVERT 500 0.6249 0.2502 0.0005 + *MESH_TVERT 501 0.7498 0.3751 0.0005 + *MESH_TVERT 502 0.7498 0.1254 0.0005 + *MESH_TVERT 503 0.7498 0.1254 0.0005 + *MESH_TVERT 504 0.8746 0.7498 0.0005 + *MESH_TVERT 505 0.8746 0.7498 0.0005 + *MESH_TVERT 506 0.8746 0.5000 0.0005 + *MESH_TVERT 507 0.8746 0.5000 0.0005 + *MESH_TVERT 508 0.8746 0.2502 0.0005 + *MESH_TVERT 509 0.8746 0.2502 0.0005 + *MESH_TVERT 510 0.9995 0.1254 0.0005 + *MESH_TVERT 511 0.8746 0.0005 0.0005 + *MESH_TVERT 512 0.6249 0.0005 0.0005 + *MESH_TVERT 513 0.3751 0.0005 0.0005 + *MESH_TVERT 514 0.0005 0.1254 0.0005 + *MESH_TVERT 515 0.1254 0.0005 0.0005 + *MESH_TVERT 516 0.9995 0.3751 0.0005 + *MESH_TVERT 517 0.0005 0.3751 0.0005 + *MESH_TVERT 518 0.9995 0.6249 0.0005 + *MESH_TVERT 519 0.0005 0.6249 0.0005 + *MESH_TVERT 520 0.9995 0.8746 0.0005 + *MESH_TVERT 521 0.8746 0.9995 0.0005 + *MESH_TVERT 522 0.6249 0.9995 0.0005 + *MESH_TVERT 523 0.3751 0.9995 0.0005 + *MESH_TVERT 524 0.1254 0.9995 0.0005 + *MESH_TVERT 525 0.0005 0.8746 0.0005 + *MESH_TVERT 526 0.1254 0.0005 0.9995 + *MESH_TVERT 527 0.0005 0.1254 0.9995 + *MESH_TVERT 528 0.3751 0.0005 0.9995 + *MESH_TVERT 529 0.6249 0.0005 0.9995 + *MESH_TVERT 530 0.8746 0.0005 0.9995 + *MESH_TVERT 531 0.9995 0.1254 0.9995 + *MESH_TVERT 532 0.0005 0.3751 0.9995 + *MESH_TVERT 533 0.9995 0.3751 0.9995 + *MESH_TVERT 534 0.0005 0.6249 0.9995 + *MESH_TVERT 535 0.9995 0.6249 0.9995 + *MESH_TVERT 536 0.1254 0.9995 0.9995 + *MESH_TVERT 537 0.0005 0.8746 0.9995 + *MESH_TVERT 538 0.3751 0.9995 0.9995 + *MESH_TVERT 539 0.6249 0.9995 0.9995 + *MESH_TVERT 540 0.9995 0.8746 0.9995 + *MESH_TVERT 541 0.8746 0.9995 0.9995 + *MESH_TVERT 542 0.1254 0.0005 0.0005 + *MESH_TVERT 543 0.8746 0.0005 0.9995 + *MESH_TVERT 544 0.3751 0.0005 0.0005 + *MESH_TVERT 545 0.6249 0.0005 0.9995 + *MESH_TVERT 546 0.6249 0.0005 0.0005 + *MESH_TVERT 547 0.3751 0.0005 0.9995 + *MESH_TVERT 548 0.8746 0.0005 0.0005 + *MESH_TVERT 549 0.1254 0.0005 0.9995 + *MESH_TVERT 550 0.8746 0.9995 0.9995 + *MESH_TVERT 551 0.6249 0.9995 0.9995 + *MESH_TVERT 552 0.3751 0.9995 0.9995 + *MESH_TVERT 553 0.1254 0.9995 0.9995 + *MESH_TVERT 554 0.1254 0.9995 0.0005 + *MESH_TVERT 555 0.3751 0.9995 0.0005 + *MESH_TVERT 556 0.6249 0.9995 0.0005 + *MESH_TVERT 557 0.8746 0.9995 0.0005 + } + *MESH_NUMTVFACES 768 + *MESH_TFACELIST { + *MESH_TFACE 0 125 510 186 + *MESH_TFACE 1 186 511 125 + *MESH_TFACE 2 130 283 186 + *MESH_TFACE 3 186 510 130 + *MESH_TFACE 4 0 284 186 + *MESH_TFACE 5 186 283 0 + *MESH_TFACE 6 126 511 186 + *MESH_TFACE 7 186 284 126 + *MESH_TFACE 8 126 284 187 + *MESH_TFACE 9 187 512 126 + *MESH_TFACE 10 0 286 187 + *MESH_TFACE 11 187 284 0 + *MESH_TFACE 12 1 287 187 + *MESH_TFACE 13 187 286 1 + *MESH_TFACE 14 127 512 187 + *MESH_TFACE 15 187 287 127 + *MESH_TFACE 16 127 287 188 + *MESH_TFACE 17 188 513 127 + *MESH_TFACE 18 1 289 188 + *MESH_TFACE 19 188 287 1 + *MESH_TFACE 20 2 290 188 + *MESH_TFACE 21 188 289 2 + *MESH_TFACE 22 128 513 188 + *MESH_TFACE 23 188 290 128 + *MESH_TFACE 24 128 290 189 + *MESH_TFACE 25 189 515 128 + *MESH_TFACE 26 2 292 189 + *MESH_TFACE 27 189 290 2 + *MESH_TFACE 28 131 514 189 + *MESH_TFACE 29 189 292 131 + *MESH_TFACE 30 129 515 189 + *MESH_TFACE 31 189 514 129 + *MESH_TFACE 32 130 516 190 + *MESH_TFACE 33 190 283 130 + *MESH_TFACE 34 132 296 190 + *MESH_TFACE 35 190 516 132 + *MESH_TFACE 36 3 297 190 + *MESH_TFACE 37 190 296 3 + *MESH_TFACE 38 0 283 190 + *MESH_TFACE 39 190 297 0 + *MESH_TFACE 40 0 297 191 + *MESH_TFACE 41 191 286 0 + *MESH_TFACE 42 3 298 191 + *MESH_TFACE 43 191 297 3 + *MESH_TFACE 44 4 299 191 + *MESH_TFACE 45 191 298 4 + *MESH_TFACE 46 1 286 191 + *MESH_TFACE 47 191 299 1 + *MESH_TFACE 48 1 299 192 + *MESH_TFACE 49 192 289 1 + *MESH_TFACE 50 4 300 192 + *MESH_TFACE 51 192 299 4 + *MESH_TFACE 52 5 301 192 + *MESH_TFACE 53 192 300 5 + *MESH_TFACE 54 2 289 192 + *MESH_TFACE 55 192 301 2 + *MESH_TFACE 56 2 301 193 + *MESH_TFACE 57 193 292 2 + *MESH_TFACE 58 5 302 193 + *MESH_TFACE 59 193 301 5 + *MESH_TFACE 60 133 517 193 + *MESH_TFACE 61 193 302 133 + *MESH_TFACE 62 131 292 193 + *MESH_TFACE 63 193 517 131 + *MESH_TFACE 64 132 518 194 + *MESH_TFACE 65 194 296 132 + *MESH_TFACE 66 134 305 194 + *MESH_TFACE 67 194 518 134 + *MESH_TFACE 68 6 306 194 + *MESH_TFACE 69 194 305 6 + *MESH_TFACE 70 3 296 194 + *MESH_TFACE 71 194 306 3 + *MESH_TFACE 72 3 306 195 + *MESH_TFACE 73 195 298 3 + *MESH_TFACE 74 6 307 195 + *MESH_TFACE 75 195 306 6 + *MESH_TFACE 76 7 308 195 + *MESH_TFACE 77 195 307 7 + *MESH_TFACE 78 4 298 195 + *MESH_TFACE 79 195 308 4 + *MESH_TFACE 80 4 308 196 + *MESH_TFACE 81 196 300 4 + *MESH_TFACE 82 7 309 196 + *MESH_TFACE 83 196 308 7 + *MESH_TFACE 84 8 310 196 + *MESH_TFACE 85 196 309 8 + *MESH_TFACE 86 5 300 196 + *MESH_TFACE 87 196 310 5 + *MESH_TFACE 88 5 310 197 + *MESH_TFACE 89 197 302 5 + *MESH_TFACE 90 8 311 197 + *MESH_TFACE 91 197 310 8 + *MESH_TFACE 92 135 519 197 + *MESH_TFACE 93 197 311 135 + *MESH_TFACE 94 133 302 197 + *MESH_TFACE 95 197 519 133 + *MESH_TFACE 96 134 520 198 + *MESH_TFACE 97 198 305 134 + *MESH_TFACE 98 136 521 198 + *MESH_TFACE 99 198 520 136 + *MESH_TFACE 100 137 315 198 + *MESH_TFACE 101 198 521 137 + *MESH_TFACE 102 6 305 198 + *MESH_TFACE 103 198 315 6 + *MESH_TFACE 104 6 315 199 + *MESH_TFACE 105 199 307 6 + *MESH_TFACE 106 137 522 199 + *MESH_TFACE 107 199 315 137 + *MESH_TFACE 108 138 317 199 + *MESH_TFACE 109 199 522 138 + *MESH_TFACE 110 7 307 199 + *MESH_TFACE 111 199 317 7 + *MESH_TFACE 112 7 317 200 + *MESH_TFACE 113 200 309 7 + *MESH_TFACE 114 138 523 200 + *MESH_TFACE 115 200 317 138 + *MESH_TFACE 116 139 319 200 + *MESH_TFACE 117 200 523 139 + *MESH_TFACE 118 8 309 200 + *MESH_TFACE 119 200 319 8 + *MESH_TFACE 120 8 319 201 + *MESH_TFACE 121 201 311 8 + *MESH_TFACE 122 139 524 201 + *MESH_TFACE 123 201 319 139 + *MESH_TFACE 124 140 525 201 + *MESH_TFACE 125 201 524 140 + *MESH_TFACE 126 135 311 201 + *MESH_TFACE 127 201 525 135 + *MESH_TFACE 128 141 526 202 + *MESH_TFACE 129 202 527 141 + *MESH_TFACE 130 142 323 202 + *MESH_TFACE 131 202 526 142 + *MESH_TFACE 132 9 324 202 + *MESH_TFACE 133 202 323 9 + *MESH_TFACE 134 146 527 202 + *MESH_TFACE 135 202 324 146 + *MESH_TFACE 136 142 528 203 + *MESH_TFACE 137 203 323 142 + *MESH_TFACE 138 143 327 203 + *MESH_TFACE 139 203 528 143 + *MESH_TFACE 140 10 328 203 + *MESH_TFACE 141 203 327 10 + *MESH_TFACE 142 9 323 203 + *MESH_TFACE 143 203 328 9 + *MESH_TFACE 144 143 529 204 + *MESH_TFACE 145 204 327 143 + *MESH_TFACE 146 144 330 204 + *MESH_TFACE 147 204 529 144 + *MESH_TFACE 148 11 331 204 + *MESH_TFACE 149 204 330 11 + *MESH_TFACE 150 10 327 204 + *MESH_TFACE 151 204 331 10 + *MESH_TFACE 152 144 530 205 + *MESH_TFACE 153 205 330 144 + *MESH_TFACE 154 145 531 205 + *MESH_TFACE 155 205 530 145 + *MESH_TFACE 156 147 334 205 + *MESH_TFACE 157 205 531 147 + *MESH_TFACE 158 11 330 205 + *MESH_TFACE 159 205 334 11 + *MESH_TFACE 160 146 324 206 + *MESH_TFACE 161 206 532 146 + *MESH_TFACE 162 9 335 206 + *MESH_TFACE 163 206 324 9 + *MESH_TFACE 164 12 336 206 + *MESH_TFACE 165 206 335 12 + *MESH_TFACE 166 148 532 206 + *MESH_TFACE 167 206 336 148 + *MESH_TFACE 168 9 328 207 + *MESH_TFACE 169 207 335 9 + *MESH_TFACE 170 10 338 207 + *MESH_TFACE 171 207 328 10 + *MESH_TFACE 172 13 339 207 + *MESH_TFACE 173 207 338 13 + *MESH_TFACE 174 12 335 207 + *MESH_TFACE 175 207 339 12 + *MESH_TFACE 176 10 331 208 + *MESH_TFACE 177 208 338 10 + *MESH_TFACE 178 11 340 208 + *MESH_TFACE 179 208 331 11 + *MESH_TFACE 180 14 341 208 + *MESH_TFACE 181 208 340 14 + *MESH_TFACE 182 13 338 208 + *MESH_TFACE 183 208 341 13 + *MESH_TFACE 184 11 334 209 + *MESH_TFACE 185 209 340 11 + *MESH_TFACE 186 147 533 209 + *MESH_TFACE 187 209 334 147 + *MESH_TFACE 188 149 343 209 + *MESH_TFACE 189 209 533 149 + *MESH_TFACE 190 14 340 209 + *MESH_TFACE 191 209 343 14 + *MESH_TFACE 192 148 336 210 + *MESH_TFACE 193 210 534 148 + *MESH_TFACE 194 12 344 210 + *MESH_TFACE 195 210 336 12 + *MESH_TFACE 196 15 345 210 + *MESH_TFACE 197 210 344 15 + *MESH_TFACE 198 150 534 210 + *MESH_TFACE 199 210 345 150 + *MESH_TFACE 200 12 339 211 + *MESH_TFACE 201 211 344 12 + *MESH_TFACE 202 13 347 211 + *MESH_TFACE 203 211 339 13 + *MESH_TFACE 204 16 348 211 + *MESH_TFACE 205 211 347 16 + *MESH_TFACE 206 15 344 211 + *MESH_TFACE 207 211 348 15 + *MESH_TFACE 208 13 341 212 + *MESH_TFACE 209 212 347 13 + *MESH_TFACE 210 14 349 212 + *MESH_TFACE 211 212 341 14 + *MESH_TFACE 212 17 350 212 + *MESH_TFACE 213 212 349 17 + *MESH_TFACE 214 16 347 212 + *MESH_TFACE 215 212 350 16 + *MESH_TFACE 216 14 343 213 + *MESH_TFACE 217 213 349 14 + *MESH_TFACE 218 149 535 213 + *MESH_TFACE 219 213 343 149 + *MESH_TFACE 220 151 352 213 + *MESH_TFACE 221 213 535 151 + *MESH_TFACE 222 17 349 213 + *MESH_TFACE 223 213 352 17 + *MESH_TFACE 224 150 345 214 + *MESH_TFACE 225 214 537 150 + *MESH_TFACE 226 15 353 214 + *MESH_TFACE 227 214 345 15 + *MESH_TFACE 228 153 536 214 + *MESH_TFACE 229 214 353 153 + *MESH_TFACE 230 152 537 214 + *MESH_TFACE 231 214 536 152 + *MESH_TFACE 232 15 348 215 + *MESH_TFACE 233 215 353 15 + *MESH_TFACE 234 16 356 215 + *MESH_TFACE 235 215 348 16 + *MESH_TFACE 236 154 538 215 + *MESH_TFACE 237 215 356 154 + *MESH_TFACE 238 153 353 215 + *MESH_TFACE 239 215 538 153 + *MESH_TFACE 240 16 350 216 + *MESH_TFACE 241 216 356 16 + *MESH_TFACE 242 17 358 216 + *MESH_TFACE 243 216 350 17 + *MESH_TFACE 244 155 539 216 + *MESH_TFACE 245 216 358 155 + *MESH_TFACE 246 154 356 216 + *MESH_TFACE 247 216 539 154 + *MESH_TFACE 248 17 352 217 + *MESH_TFACE 249 217 358 17 + *MESH_TFACE 250 151 540 217 + *MESH_TFACE 251 217 352 151 + *MESH_TFACE 252 156 541 217 + *MESH_TFACE 253 217 540 156 + *MESH_TFACE 254 155 358 217 + *MESH_TFACE 255 217 541 155 + *MESH_TFACE 256 63 285 218 + *MESH_TFACE 257 218 365 63 + *MESH_TFACE 258 64 362 218 + *MESH_TFACE 259 218 285 64 + *MESH_TFACE 260 19 364 218 + *MESH_TFACE 261 218 362 19 + *MESH_TFACE 262 18 365 218 + *MESH_TFACE 263 218 364 18 + *MESH_TFACE 264 65 288 219 + *MESH_TFACE 265 219 363 65 + *MESH_TFACE 266 66 366 219 + *MESH_TFACE 267 219 288 66 + *MESH_TFACE 268 20 368 219 + *MESH_TFACE 269 219 366 20 + *MESH_TFACE 270 19 363 219 + *MESH_TFACE 271 219 368 19 + *MESH_TFACE 272 67 291 220 + *MESH_TFACE 273 220 367 67 + *MESH_TFACE 274 68 369 220 + *MESH_TFACE 275 220 291 68 + *MESH_TFACE 276 21 371 220 + *MESH_TFACE 277 220 369 21 + *MESH_TFACE 278 20 367 220 + *MESH_TFACE 279 220 371 20 + *MESH_TFACE 280 69 294 221 + *MESH_TFACE 281 221 370 69 + *MESH_TFACE 282 70 372 221 + *MESH_TFACE 283 221 294 70 + *MESH_TFACE 284 22 373 221 + *MESH_TFACE 285 221 372 22 + *MESH_TFACE 286 21 370 221 + *MESH_TFACE 287 221 373 21 + *MESH_TFACE 288 18 364 222 + *MESH_TFACE 289 222 376 18 + *MESH_TFACE 290 19 374 222 + *MESH_TFACE 291 222 364 19 + *MESH_TFACE 292 34 375 222 + *MESH_TFACE 293 222 374 34 + *MESH_TFACE 294 33 376 222 + *MESH_TFACE 295 222 375 33 + *MESH_TFACE 296 19 368 223 + *MESH_TFACE 297 223 374 19 + *MESH_TFACE 298 20 377 223 + *MESH_TFACE 299 223 368 20 + *MESH_TFACE 300 35 378 223 + *MESH_TFACE 301 223 377 35 + *MESH_TFACE 302 34 374 223 + *MESH_TFACE 303 223 378 34 + *MESH_TFACE 304 20 371 224 + *MESH_TFACE 305 224 377 20 + *MESH_TFACE 306 21 379 224 + *MESH_TFACE 307 224 371 21 + *MESH_TFACE 308 36 380 224 + *MESH_TFACE 309 224 379 36 + *MESH_TFACE 310 35 377 224 + *MESH_TFACE 311 224 380 35 + *MESH_TFACE 312 21 373 225 + *MESH_TFACE 313 225 379 21 + *MESH_TFACE 314 22 381 225 + *MESH_TFACE 315 225 373 22 + *MESH_TFACE 316 37 382 225 + *MESH_TFACE 317 225 381 37 + *MESH_TFACE 318 36 379 225 + *MESH_TFACE 319 225 382 36 + *MESH_TFACE 320 33 375 226 + *MESH_TFACE 321 226 385 33 + *MESH_TFACE 322 34 383 226 + *MESH_TFACE 323 226 375 34 + *MESH_TFACE 324 49 384 226 + *MESH_TFACE 325 226 383 49 + *MESH_TFACE 326 48 385 226 + *MESH_TFACE 327 226 384 48 + *MESH_TFACE 328 34 378 227 + *MESH_TFACE 329 227 383 34 + *MESH_TFACE 330 35 386 227 + *MESH_TFACE 331 227 378 35 + *MESH_TFACE 332 50 387 227 + *MESH_TFACE 333 227 386 50 + *MESH_TFACE 334 49 383 227 + *MESH_TFACE 335 227 387 49 + *MESH_TFACE 336 35 380 228 + *MESH_TFACE 337 228 386 35 + *MESH_TFACE 338 36 388 228 + *MESH_TFACE 339 228 380 36 + *MESH_TFACE 340 51 389 228 + *MESH_TFACE 341 228 388 51 + *MESH_TFACE 342 50 386 228 + *MESH_TFACE 343 228 389 50 + *MESH_TFACE 344 36 382 229 + *MESH_TFACE 345 229 388 36 + *MESH_TFACE 346 37 390 229 + *MESH_TFACE 347 229 382 37 + *MESH_TFACE 348 52 391 229 + *MESH_TFACE 349 229 390 52 + *MESH_TFACE 350 51 388 229 + *MESH_TFACE 351 229 391 51 + *MESH_TFACE 352 48 384 230 + *MESH_TFACE 353 230 394 48 + *MESH_TFACE 354 49 392 230 + *MESH_TFACE 355 230 384 49 + *MESH_TFACE 356 71 322 230 + *MESH_TFACE 357 230 392 71 + *MESH_TFACE 358 72 394 230 + *MESH_TFACE 359 230 322 72 + *MESH_TFACE 360 49 387 231 + *MESH_TFACE 361 231 393 49 + *MESH_TFACE 362 50 395 231 + *MESH_TFACE 363 231 387 50 + *MESH_TFACE 364 73 326 231 + *MESH_TFACE 365 231 395 73 + *MESH_TFACE 366 74 393 231 + *MESH_TFACE 367 231 326 74 + *MESH_TFACE 368 50 389 232 + *MESH_TFACE 369 232 396 50 + *MESH_TFACE 370 51 397 232 + *MESH_TFACE 371 232 389 51 + *MESH_TFACE 372 75 329 232 + *MESH_TFACE 373 232 397 75 + *MESH_TFACE 374 76 396 232 + *MESH_TFACE 375 232 329 76 + *MESH_TFACE 376 51 391 233 + *MESH_TFACE 377 233 398 51 + *MESH_TFACE 378 52 399 233 + *MESH_TFACE 379 233 391 52 + *MESH_TFACE 380 77 332 233 + *MESH_TFACE 381 233 399 77 + *MESH_TFACE 382 78 398 233 + *MESH_TFACE 383 233 332 78 + *MESH_TFACE 384 158 293 234 + *MESH_TFACE 385 234 543 158 + *MESH_TFACE 386 79 400 234 + *MESH_TFACE 387 234 293 79 + *MESH_TFACE 388 23 402 234 + *MESH_TFACE 389 234 400 23 + *MESH_TFACE 390 167 543 234 + *MESH_TFACE 391 234 402 167 + *MESH_TFACE 392 80 303 235 + *MESH_TFACE 393 235 401 80 + *MESH_TFACE 394 81 404 235 + *MESH_TFACE 395 235 303 81 + *MESH_TFACE 396 24 406 235 + *MESH_TFACE 397 235 404 24 + *MESH_TFACE 398 23 401 235 + *MESH_TFACE 399 235 406 23 + *MESH_TFACE 400 82 312 236 + *MESH_TFACE 401 236 405 82 + *MESH_TFACE 402 83 407 236 + *MESH_TFACE 403 236 312 83 + *MESH_TFACE 404 25 409 236 + *MESH_TFACE 405 236 407 25 + *MESH_TFACE 406 24 405 236 + *MESH_TFACE 407 236 409 24 + *MESH_TFACE 408 84 321 237 + *MESH_TFACE 409 237 408 84 + *MESH_TFACE 410 160 550 237 + *MESH_TFACE 411 237 321 160 + *MESH_TFACE 412 169 411 237 + *MESH_TFACE 413 237 550 169 + *MESH_TFACE 414 25 408 237 + *MESH_TFACE 415 237 411 25 + *MESH_TFACE 416 168 403 238 + *MESH_TFACE 417 238 545 168 + *MESH_TFACE 418 23 412 238 + *MESH_TFACE 419 238 403 23 + *MESH_TFACE 420 38 413 238 + *MESH_TFACE 421 238 412 38 + *MESH_TFACE 422 174 545 238 + *MESH_TFACE 423 238 413 174 + *MESH_TFACE 424 23 406 239 + *MESH_TFACE 425 239 412 23 + *MESH_TFACE 426 24 415 239 + *MESH_TFACE 427 239 406 24 + *MESH_TFACE 428 39 416 239 + *MESH_TFACE 429 239 415 39 + *MESH_TFACE 430 38 412 239 + *MESH_TFACE 431 239 416 38 + *MESH_TFACE 432 24 409 240 + *MESH_TFACE 433 240 415 24 + *MESH_TFACE 434 25 417 240 + *MESH_TFACE 435 240 409 25 + *MESH_TFACE 436 40 418 240 + *MESH_TFACE 437 240 417 40 + *MESH_TFACE 438 39 415 240 + *MESH_TFACE 439 240 418 39 + *MESH_TFACE 440 25 411 241 + *MESH_TFACE 441 241 417 25 + *MESH_TFACE 442 169 551 241 + *MESH_TFACE 443 241 411 169 + *MESH_TFACE 444 176 420 241 + *MESH_TFACE 445 241 551 176 + *MESH_TFACE 446 40 417 241 + *MESH_TFACE 447 241 420 40 + *MESH_TFACE 448 175 414 242 + *MESH_TFACE 449 242 547 175 + *MESH_TFACE 450 38 421 242 + *MESH_TFACE 451 242 414 38 + *MESH_TFACE 452 53 422 242 + *MESH_TFACE 453 242 421 53 + *MESH_TFACE 454 181 547 242 + *MESH_TFACE 455 242 422 181 + *MESH_TFACE 456 38 416 243 + *MESH_TFACE 457 243 421 38 + *MESH_TFACE 458 39 424 243 + *MESH_TFACE 459 243 416 39 + *MESH_TFACE 460 54 425 243 + *MESH_TFACE 461 243 424 54 + *MESH_TFACE 462 53 421 243 + *MESH_TFACE 463 243 425 53 + *MESH_TFACE 464 39 418 244 + *MESH_TFACE 465 244 424 39 + *MESH_TFACE 466 40 426 244 + *MESH_TFACE 467 244 418 40 + *MESH_TFACE 468 55 427 244 + *MESH_TFACE 469 244 426 55 + *MESH_TFACE 470 54 424 244 + *MESH_TFACE 471 244 427 54 + *MESH_TFACE 472 40 420 245 + *MESH_TFACE 473 245 426 40 + *MESH_TFACE 474 176 552 245 + *MESH_TFACE 475 245 420 176 + *MESH_TFACE 476 183 429 245 + *MESH_TFACE 477 245 552 183 + *MESH_TFACE 478 55 426 245 + *MESH_TFACE 479 245 429 55 + *MESH_TFACE 480 182 423 246 + *MESH_TFACE 481 246 549 182 + *MESH_TFACE 482 53 430 246 + *MESH_TFACE 483 246 423 53 + *MESH_TFACE 484 85 333 246 + *MESH_TFACE 485 246 430 85 + *MESH_TFACE 486 162 549 246 + *MESH_TFACE 487 246 333 162 + *MESH_TFACE 488 53 425 247 + *MESH_TFACE 489 247 431 53 + *MESH_TFACE 490 54 432 247 + *MESH_TFACE 491 247 425 54 + *MESH_TFACE 492 86 342 247 + *MESH_TFACE 493 247 432 86 + *MESH_TFACE 494 87 431 247 + *MESH_TFACE 495 247 342 87 + *MESH_TFACE 496 54 427 248 + *MESH_TFACE 497 248 433 54 + *MESH_TFACE 498 55 434 248 + *MESH_TFACE 499 248 427 55 + *MESH_TFACE 500 88 351 248 + *MESH_TFACE 501 248 434 88 + *MESH_TFACE 502 89 433 248 + *MESH_TFACE 503 248 351 89 + *MESH_TFACE 504 55 429 249 + *MESH_TFACE 505 249 435 55 + *MESH_TFACE 506 183 553 249 + *MESH_TFACE 507 249 429 183 + *MESH_TFACE 508 164 360 249 + *MESH_TFACE 509 249 553 164 + *MESH_TFACE 510 90 435 249 + *MESH_TFACE 511 249 360 90 + *MESH_TFACE 512 91 320 250 + *MESH_TFACE 513 250 410 91 + *MESH_TFACE 514 92 437 250 + *MESH_TFACE 515 250 320 92 + *MESH_TFACE 516 26 439 250 + *MESH_TFACE 517 250 437 26 + *MESH_TFACE 518 93 410 250 + *MESH_TFACE 519 250 439 93 + *MESH_TFACE 520 94 318 251 + *MESH_TFACE 521 251 438 94 + *MESH_TFACE 522 95 441 251 + *MESH_TFACE 523 251 318 95 + *MESH_TFACE 524 27 443 251 + *MESH_TFACE 525 251 441 27 + *MESH_TFACE 526 26 438 251 + *MESH_TFACE 527 251 443 26 + *MESH_TFACE 528 96 316 252 + *MESH_TFACE 529 252 442 96 + *MESH_TFACE 530 97 444 252 + *MESH_TFACE 531 252 316 97 + *MESH_TFACE 532 28 446 252 + *MESH_TFACE 533 252 444 28 + *MESH_TFACE 534 27 442 252 + *MESH_TFACE 535 252 446 27 + *MESH_TFACE 536 98 314 253 + *MESH_TFACE 537 253 445 98 + *MESH_TFACE 538 99 447 253 + *MESH_TFACE 539 253 314 99 + *MESH_TFACE 540 29 448 253 + *MESH_TFACE 541 253 447 29 + *MESH_TFACE 542 28 445 253 + *MESH_TFACE 543 253 448 28 + *MESH_TFACE 544 100 440 254 + *MESH_TFACE 545 254 419 100 + *MESH_TFACE 546 26 449 254 + *MESH_TFACE 547 254 440 26 + *MESH_TFACE 548 41 450 254 + *MESH_TFACE 549 254 449 41 + *MESH_TFACE 550 101 419 254 + *MESH_TFACE 551 254 450 101 + *MESH_TFACE 552 26 443 255 + *MESH_TFACE 553 255 449 26 + *MESH_TFACE 554 27 452 255 + *MESH_TFACE 555 255 443 27 + *MESH_TFACE 556 42 453 255 + *MESH_TFACE 557 255 452 42 + *MESH_TFACE 558 41 449 255 + *MESH_TFACE 559 255 453 41 + *MESH_TFACE 560 27 446 256 + *MESH_TFACE 561 256 452 27 + *MESH_TFACE 562 28 454 256 + *MESH_TFACE 563 256 446 28 + *MESH_TFACE 564 43 455 256 + *MESH_TFACE 565 256 454 43 + *MESH_TFACE 566 42 452 256 + *MESH_TFACE 567 256 455 42 + *MESH_TFACE 568 28 448 257 + *MESH_TFACE 569 257 454 28 + *MESH_TFACE 570 29 456 257 + *MESH_TFACE 571 257 448 29 + *MESH_TFACE 572 44 457 257 + *MESH_TFACE 573 257 456 44 + *MESH_TFACE 574 43 454 257 + *MESH_TFACE 575 257 457 43 + *MESH_TFACE 576 102 451 258 + *MESH_TFACE 577 258 428 102 + *MESH_TFACE 578 41 458 258 + *MESH_TFACE 579 258 451 41 + *MESH_TFACE 580 56 459 258 + *MESH_TFACE 581 258 458 56 + *MESH_TFACE 582 103 428 258 + *MESH_TFACE 583 258 459 103 + *MESH_TFACE 584 41 453 259 + *MESH_TFACE 585 259 458 41 + *MESH_TFACE 586 42 461 259 + *MESH_TFACE 587 259 453 42 + *MESH_TFACE 588 57 462 259 + *MESH_TFACE 589 259 461 57 + *MESH_TFACE 590 56 458 259 + *MESH_TFACE 591 259 462 56 + *MESH_TFACE 592 42 455 260 + *MESH_TFACE 593 260 461 42 + *MESH_TFACE 594 43 463 260 + *MESH_TFACE 595 260 455 43 + *MESH_TFACE 596 58 464 260 + *MESH_TFACE 597 260 463 58 + *MESH_TFACE 598 57 461 260 + *MESH_TFACE 599 260 464 57 + *MESH_TFACE 600 43 457 261 + *MESH_TFACE 601 261 463 43 + *MESH_TFACE 602 44 465 261 + *MESH_TFACE 603 261 457 44 + *MESH_TFACE 604 59 466 261 + *MESH_TFACE 605 261 465 59 + *MESH_TFACE 606 58 463 261 + *MESH_TFACE 607 261 466 58 + *MESH_TFACE 608 104 460 262 + *MESH_TFACE 609 262 436 104 + *MESH_TFACE 610 56 467 262 + *MESH_TFACE 611 262 460 56 + *MESH_TFACE 612 105 361 262 + *MESH_TFACE 613 262 467 105 + *MESH_TFACE 614 106 436 262 + *MESH_TFACE 615 262 361 106 + *MESH_TFACE 616 56 462 263 + *MESH_TFACE 617 263 468 56 + *MESH_TFACE 618 57 469 263 + *MESH_TFACE 619 263 462 57 + *MESH_TFACE 620 107 359 263 + *MESH_TFACE 621 263 469 107 + *MESH_TFACE 622 108 468 263 + *MESH_TFACE 623 263 359 108 + *MESH_TFACE 624 57 464 264 + *MESH_TFACE 625 264 470 57 + *MESH_TFACE 626 58 471 264 + *MESH_TFACE 627 264 464 58 + *MESH_TFACE 628 109 357 264 + *MESH_TFACE 629 264 471 109 + *MESH_TFACE 630 110 470 264 + *MESH_TFACE 631 264 357 110 + *MESH_TFACE 632 58 466 265 + *MESH_TFACE 633 265 472 58 + *MESH_TFACE 634 59 473 265 + *MESH_TFACE 635 265 466 59 + *MESH_TFACE 636 111 354 265 + *MESH_TFACE 637 265 473 111 + *MESH_TFACE 638 112 472 265 + *MESH_TFACE 639 265 354 112 + *MESH_TFACE 640 159 313 266 + *MESH_TFACE 641 266 554 159 + *MESH_TFACE 642 113 474 266 + *MESH_TFACE 643 266 313 113 + *MESH_TFACE 644 30 476 266 + *MESH_TFACE 645 266 474 30 + *MESH_TFACE 646 170 554 266 + *MESH_TFACE 647 266 476 170 + *MESH_TFACE 648 114 304 267 + *MESH_TFACE 649 267 475 114 + *MESH_TFACE 650 115 478 267 + *MESH_TFACE 651 267 304 115 + *MESH_TFACE 652 31 480 267 + *MESH_TFACE 653 267 478 31 + *MESH_TFACE 654 30 475 267 + *MESH_TFACE 655 267 480 30 + *MESH_TFACE 656 116 295 268 + *MESH_TFACE 657 268 479 116 + *MESH_TFACE 658 117 481 268 + *MESH_TFACE 659 268 295 117 + *MESH_TFACE 660 32 483 268 + *MESH_TFACE 661 268 481 32 + *MESH_TFACE 662 31 479 268 + *MESH_TFACE 663 268 483 31 + *MESH_TFACE 664 118 282 269 + *MESH_TFACE 665 269 482 118 + *MESH_TFACE 666 157 542 269 + *MESH_TFACE 667 269 282 157 + *MESH_TFACE 668 165 484 269 + *MESH_TFACE 669 269 542 165 + *MESH_TFACE 670 32 482 269 + *MESH_TFACE 671 269 484 32 + *MESH_TFACE 672 171 477 270 + *MESH_TFACE 673 270 555 171 + *MESH_TFACE 674 30 486 270 + *MESH_TFACE 675 270 477 30 + *MESH_TFACE 676 45 487 270 + *MESH_TFACE 677 270 486 45 + *MESH_TFACE 678 177 555 270 + *MESH_TFACE 679 270 487 177 + *MESH_TFACE 680 30 480 271 + *MESH_TFACE 681 271 486 30 + *MESH_TFACE 682 31 489 271 + *MESH_TFACE 683 271 480 31 + *MESH_TFACE 684 46 490 271 + *MESH_TFACE 685 271 489 46 + *MESH_TFACE 686 45 486 271 + *MESH_TFACE 687 271 490 45 + *MESH_TFACE 688 31 483 272 + *MESH_TFACE 689 272 489 31 + *MESH_TFACE 690 32 491 272 + *MESH_TFACE 691 272 483 32 + *MESH_TFACE 692 47 492 272 + *MESH_TFACE 693 272 491 47 + *MESH_TFACE 694 46 489 272 + *MESH_TFACE 695 272 492 46 + *MESH_TFACE 696 32 485 273 + *MESH_TFACE 697 273 491 32 + *MESH_TFACE 698 166 544 273 + *MESH_TFACE 699 273 485 166 + *MESH_TFACE 700 172 493 273 + *MESH_TFACE 701 273 544 172 + *MESH_TFACE 702 47 491 273 + *MESH_TFACE 703 273 493 47 + *MESH_TFACE 704 178 488 274 + *MESH_TFACE 705 274 556 178 + *MESH_TFACE 706 45 495 274 + *MESH_TFACE 707 274 488 45 + *MESH_TFACE 708 60 496 274 + *MESH_TFACE 709 274 495 60 + *MESH_TFACE 710 184 556 274 + *MESH_TFACE 711 274 496 184 + *MESH_TFACE 712 45 490 275 + *MESH_TFACE 713 275 495 45 + *MESH_TFACE 714 46 498 275 + *MESH_TFACE 715 275 490 46 + *MESH_TFACE 716 61 499 275 + *MESH_TFACE 717 275 498 61 + *MESH_TFACE 718 60 495 275 + *MESH_TFACE 719 275 499 60 + *MESH_TFACE 720 46 492 276 + *MESH_TFACE 721 276 498 46 + *MESH_TFACE 722 47 500 276 + *MESH_TFACE 723 276 492 47 + *MESH_TFACE 724 62 501 276 + *MESH_TFACE 725 276 500 62 + *MESH_TFACE 726 61 498 276 + *MESH_TFACE 727 276 501 61 + *MESH_TFACE 728 47 494 277 + *MESH_TFACE 729 277 500 47 + *MESH_TFACE 730 173 546 277 + *MESH_TFACE 731 277 494 173 + *MESH_TFACE 732 179 502 277 + *MESH_TFACE 733 277 546 179 + *MESH_TFACE 734 62 500 277 + *MESH_TFACE 735 277 502 62 + *MESH_TFACE 736 185 497 278 + *MESH_TFACE 737 278 557 185 + *MESH_TFACE 738 60 504 278 + *MESH_TFACE 739 278 497 60 + *MESH_TFACE 740 119 355 278 + *MESH_TFACE 741 278 504 119 + *MESH_TFACE 742 163 557 278 + *MESH_TFACE 743 278 355 163 + *MESH_TFACE 744 60 499 279 + *MESH_TFACE 745 279 505 60 + *MESH_TFACE 746 61 506 279 + *MESH_TFACE 747 279 499 61 + *MESH_TFACE 748 120 346 279 + *MESH_TFACE 749 279 506 120 + *MESH_TFACE 750 121 505 279 + *MESH_TFACE 751 279 346 121 + *MESH_TFACE 752 61 501 280 + *MESH_TFACE 753 280 507 61 + *MESH_TFACE 754 62 508 280 + *MESH_TFACE 755 280 501 62 + *MESH_TFACE 756 122 337 280 + *MESH_TFACE 757 280 508 122 + *MESH_TFACE 758 123 507 280 + *MESH_TFACE 759 280 337 123 + *MESH_TFACE 760 62 503 281 + *MESH_TFACE 761 281 509 62 + *MESH_TFACE 762 180 548 281 + *MESH_TFACE 763 281 503 180 + *MESH_TFACE 764 161 325 281 + *MESH_TFACE 765 281 548 161 + *MESH_TFACE 766 124 509 281 + *MESH_TFACE 767 281 325 124 + } + *MESH_NORMALS { + *MESH_FACENORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 98 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 438 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 150 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 1 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 150 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 439 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 98 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 2 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 103 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 247 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 150 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 3 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 150 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 438 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 103 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 4 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 248 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 150 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 5 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 150 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 247 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 99 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 439 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 150 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 150 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 248 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 99 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 99 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 248 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 151 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 9 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 151 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 440 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 99 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 10 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 250 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 151 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 151 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 248 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 251 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 151 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 151 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 250 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 14 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 100 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 440 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 151 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 15 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 151 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 251 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 100 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 100 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 251 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 152 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 152 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 441 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 100 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 253 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 152 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 19 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 152 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 251 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 20 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 254 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 152 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 21 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 152 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 253 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 22 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 101 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 441 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 152 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 23 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 152 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 254 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 101 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 24 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 101 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 254 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 153 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 25 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 153 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 443 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 101 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 26 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 256 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 153 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 27 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 153 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 254 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 28 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 104 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 442 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 153 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 29 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 153 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 256 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 104 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 30 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 102 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 443 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 153 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 31 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 153 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 442 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 102 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 32 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 103 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 444 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 154 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 33 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 154 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 247 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 103 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 34 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 105 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 260 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 154 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 35 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 154 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 444 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 105 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 36 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 261 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 154 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 37 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 154 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 260 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 38 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 247 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 154 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 39 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 154 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 261 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 40 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 261 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 155 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 41 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 155 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 250 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 42 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 262 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 155 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 43 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 155 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 261 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 44 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 263 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 155 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 45 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 155 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 262 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 46 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 250 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 155 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 47 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 155 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 263 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 48 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 263 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 156 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 49 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 156 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 253 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 50 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 264 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 156 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 51 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 156 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 263 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 52 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 265 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 156 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 53 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 156 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 264 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 54 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 253 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 156 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 55 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 156 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 265 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 56 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 265 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 157 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 57 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 157 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 256 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 58 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 266 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 157 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 59 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 157 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 265 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 60 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 106 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 445 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 157 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 61 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 157 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 266 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 106 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 62 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 104 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 256 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 157 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 63 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 157 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 445 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 104 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 64 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 105 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 446 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 158 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 65 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 158 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 260 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 105 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 66 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 107 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 269 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 158 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 67 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 158 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 446 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 107 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 68 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 270 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 158 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 69 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 158 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 269 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 70 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 260 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 158 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 71 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 158 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 270 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 72 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 270 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 159 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 73 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 159 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 262 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 74 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 271 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 159 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 75 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 159 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 270 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 76 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 272 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 159 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 77 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 159 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 271 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 78 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 262 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 159 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 79 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 159 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 272 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 80 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 272 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 160 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 81 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 160 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 264 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 82 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 273 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 160 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 83 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 160 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 272 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 84 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 274 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 160 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 85 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 160 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 273 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 86 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 264 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 160 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 87 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 160 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 274 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 88 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 274 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 161 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 89 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 161 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 266 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 90 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 275 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 161 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 91 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 161 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 274 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 92 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 108 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 447 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 161 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 93 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 161 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 275 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 108 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 94 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 106 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 266 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 161 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 95 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 161 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 447 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 106 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 96 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 107 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 448 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 162 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 97 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 162 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 269 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 107 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 98 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 109 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 449 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 162 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 99 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 162 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 448 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 109 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 100 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 110 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 279 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 162 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 101 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 162 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 449 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 110 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 102 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 269 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 162 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 103 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 162 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 279 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 104 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 279 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 163 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 105 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 163 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 271 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 106 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 110 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 450 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 163 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 107 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 163 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 279 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 110 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 108 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 111 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 281 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 163 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 109 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 163 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 450 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 111 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 110 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 271 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 163 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 111 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 163 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 281 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 112 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 281 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 164 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 113 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 164 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 273 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 114 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 111 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 451 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 164 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 115 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 164 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 281 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 111 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 116 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 112 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 283 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 164 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 117 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 164 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 451 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 112 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 118 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 273 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 164 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 119 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 164 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 283 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 120 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 283 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 165 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 121 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 165 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 275 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 122 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 112 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 452 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 165 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 123 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 165 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 283 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 112 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 124 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 113 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 453 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 165 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 125 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 165 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 452 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 113 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 126 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 108 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 275 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 165 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 127 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 165 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 453 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 108 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 128 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 114 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 454 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 166 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 129 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 166 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 455 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 114 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 130 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 115 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 287 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 166 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 131 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 166 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 454 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 115 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 132 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 288 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 166 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 133 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 166 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 287 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 134 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 119 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 455 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 166 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 135 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 166 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 288 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 119 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 136 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 115 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 456 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 167 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 137 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 167 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 287 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 115 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 138 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 116 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 291 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 167 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 139 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 167 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 456 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 116 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 140 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 292 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 167 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 141 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 167 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 291 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 142 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 287 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 167 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 143 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 167 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 292 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 144 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 116 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 457 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 168 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 145 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 168 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 291 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 116 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 146 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 117 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 294 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 168 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 147 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 168 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 457 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 117 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 148 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 295 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 168 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 149 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 168 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 294 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 150 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 291 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 168 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 151 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 168 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 295 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 152 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 117 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 458 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 169 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 153 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 169 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 294 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 117 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 154 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 118 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 459 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 169 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 155 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 169 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 458 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 118 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 156 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 120 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 298 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 169 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 157 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 169 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 459 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 120 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 158 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 294 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 169 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 159 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 169 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 298 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 160 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 119 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 288 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 170 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 161 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 170 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 460 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 119 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 162 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 299 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 170 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 163 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 170 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 288 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 164 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 300 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 170 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 165 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 170 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 299 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 166 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 121 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 460 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 170 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 167 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 170 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 300 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 121 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 168 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 292 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 171 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 169 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 171 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 299 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 31 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 170 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 302 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 171 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 171 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 171 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 292 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 172 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 303 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 171 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 173 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 171 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 302 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 174 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 299 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 171 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 175 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 171 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 303 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 176 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 295 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 172 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 177 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 172 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 302 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 32 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 178 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 304 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 172 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 179 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 172 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 295 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 180 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 305 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 172 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 181 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 172 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 304 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 182 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 302 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 172 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 183 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 172 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 305 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 184 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 298 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 173 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 185 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 173 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 304 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 33 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 186 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 120 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 461 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 173 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 187 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 173 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 298 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 120 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 188 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 122 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 307 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 173 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 189 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 173 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 461 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 122 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 190 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 304 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 173 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 191 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 173 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 307 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 192 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 121 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 300 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 174 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 193 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 174 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 462 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 121 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 194 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 308 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 174 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 195 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 174 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 300 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 196 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 309 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 174 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 197 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 174 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 308 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 198 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 123 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 462 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 174 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 199 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 174 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 309 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 123 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 200 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 303 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 175 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 201 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 175 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 308 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 36 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 202 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 311 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 175 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 203 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 175 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 303 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 204 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 312 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 175 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 205 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 175 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 311 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 206 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 308 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 175 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 207 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 175 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 312 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 208 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 305 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 176 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 209 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 176 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 311 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 37 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 210 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 313 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 176 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 211 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 176 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 305 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 212 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 314 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 176 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 213 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 176 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 313 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 214 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 311 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 176 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 215 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 176 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 314 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 216 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 307 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 177 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 217 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 177 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 313 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 38 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 218 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 122 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 463 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 177 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 219 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 177 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 307 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 122 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 220 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 124 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 316 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 177 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 221 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 177 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 463 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 124 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 222 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 313 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 177 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 223 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 177 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 316 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 224 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 123 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 309 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 178 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 225 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 178 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 465 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 123 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 226 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 317 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 178 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 227 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 178 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 309 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 228 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 126 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 464 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 178 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 229 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 178 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 317 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 126 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 230 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 125 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 465 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 178 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 231 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 178 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 464 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 125 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 232 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 312 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 179 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 233 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 179 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 317 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 41 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 234 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 320 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 179 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 235 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 179 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 312 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 236 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 127 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 466 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 179 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 237 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 179 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 320 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 127 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 238 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 126 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 317 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 179 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 239 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 179 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 466 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 126 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 240 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 314 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 180 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 241 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 180 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 320 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 42 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 242 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 322 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 180 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 243 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 180 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 314 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 244 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 128 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 467 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 180 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 245 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 180 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 322 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 128 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 246 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 127 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 320 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 180 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 247 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 180 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 467 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 127 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 248 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 316 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 181 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 249 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 181 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 322 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 43 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 250 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 124 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 468 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 181 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 251 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 181 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 316 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 124 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 252 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 129 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 469 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 181 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 253 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 181 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 468 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 129 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 254 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 128 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 322 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 181 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 255 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 181 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 469 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 128 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 256 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 0 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 249 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 182 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 257 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 182 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 328 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 0 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 258 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 326 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 182 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 259 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 182 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 249 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 260 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 327 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 182 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 261 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 182 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 326 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 262 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 50 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 328 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 182 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 263 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 182 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 327 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 50 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 264 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 252 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 183 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 265 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 183 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 326 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 1 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 266 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 329 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 183 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 267 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 183 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 252 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 268 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 330 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 183 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 269 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 183 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 329 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 270 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 326 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 183 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 271 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 183 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 330 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 272 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 255 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 184 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 273 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 184 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 329 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 274 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 331 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 184 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 275 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 184 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 255 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 276 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 332 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 184 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 277 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 184 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 331 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 278 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 329 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 184 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 279 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 184 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 332 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 280 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 258 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 185 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 281 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 185 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 331 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 282 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 4 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 333 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 185 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 283 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 185 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 258 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 4 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 284 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 54 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 334 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 185 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 285 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 185 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 333 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 54 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 286 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 331 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 185 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 287 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 185 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 334 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 288 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 50 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 327 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 186 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 289 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 186 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 337 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 50 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 290 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 335 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 186 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 291 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 186 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 327 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 292 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 336 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 186 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 293 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 186 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 335 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 294 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 66 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 337 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 186 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 295 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 186 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 336 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 66 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 296 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 330 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 187 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 297 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 187 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 335 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 51 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 298 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 338 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 187 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 299 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 187 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 330 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 300 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 339 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 187 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 301 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 187 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 338 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 302 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 335 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 187 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 303 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 187 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 339 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 304 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 332 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 188 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 305 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 188 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 338 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 52 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 306 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 340 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 188 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 307 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 188 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 332 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 308 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 341 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 188 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 309 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 188 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 340 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 310 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 338 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 188 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 311 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 188 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 341 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 312 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 334 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 189 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 313 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 189 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 340 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 53 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 314 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 54 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 342 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 189 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 315 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 189 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 334 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 54 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 316 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 70 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 343 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 189 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 317 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 189 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 342 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 70 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 318 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 340 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 189 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 319 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 189 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 343 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 320 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 66 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 336 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 190 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 321 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 190 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 346 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 66 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 322 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 344 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 190 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 323 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 190 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 336 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 324 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 345 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 190 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 325 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 190 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 344 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 326 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 82 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 346 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 190 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 327 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 190 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 345 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 82 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 328 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 339 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 191 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 329 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 191 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 344 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 67 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 330 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 347 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 191 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 331 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 191 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 339 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 332 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 348 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 191 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 333 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 191 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 347 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 334 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 344 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 191 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 335 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 191 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 348 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 336 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 341 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 192 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 337 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 192 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 347 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 68 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 338 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 349 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 192 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 339 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 192 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 341 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 340 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 350 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 192 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 341 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 192 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 349 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 342 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 347 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 192 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 343 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 192 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 350 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 344 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 343 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 193 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 345 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 193 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 349 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 69 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 346 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 70 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 351 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 193 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 347 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 193 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 343 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 70 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 348 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 86 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 352 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 193 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 349 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 193 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 351 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 86 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 350 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 349 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 193 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 351 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 193 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 352 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 352 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 82 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 345 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 194 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 353 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 194 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 354 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 82 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 354 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 353 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 194 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 355 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 194 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 345 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 356 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 26 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 286 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 194 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 357 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 194 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 353 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 26 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 358 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 25 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 354 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 194 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 359 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 194 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 286 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 25 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 360 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 348 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 195 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 361 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 195 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 353 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 83 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 362 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 355 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 195 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 363 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 195 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 348 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 364 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 27 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 290 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 195 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 365 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 195 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 355 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 27 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 366 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 26 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 353 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 195 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 367 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 195 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 290 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 26 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 368 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 350 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 196 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 369 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 196 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 355 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 84 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 370 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 356 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 196 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 371 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 196 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 350 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 372 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 28 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 293 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 196 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 373 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 196 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 356 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 28 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 374 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 27 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 355 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 196 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 375 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 196 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 293 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 27 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 376 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 352 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 197 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 377 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 197 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 356 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 85 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 378 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 86 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 357 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 197 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 379 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 197 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 352 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 86 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 380 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 29 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 296 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 197 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 381 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 197 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 357 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 29 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 382 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 28 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 356 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 197 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 383 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 197 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 296 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 28 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 384 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 131 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 257 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 198 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 385 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 198 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 471 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 131 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 386 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 9 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 358 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 198 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 387 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 198 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 257 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 9 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 388 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 359 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 198 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 389 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 198 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 358 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 390 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 139 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 471 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 198 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 391 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 198 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 359 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 139 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 392 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 9 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 267 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 199 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 393 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 199 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 358 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 9 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 394 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 14 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 360 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 199 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 395 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 199 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 267 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 14 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 396 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 361 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 199 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 397 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 199 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 360 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 398 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 358 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 199 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 399 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 199 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 361 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 400 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 14 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 276 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 200 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 401 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 200 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 360 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 14 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 402 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 19 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 362 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 200 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 403 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 200 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 276 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 19 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 404 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 363 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 200 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 405 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 200 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 362 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 406 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 360 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 200 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 407 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 200 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 363 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 408 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 19 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 285 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 201 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 409 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 201 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 362 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 19 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 410 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 133 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 478 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 201 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 411 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 201 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 285 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 133 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 412 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 140 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 365 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 201 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 413 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 201 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 478 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 140 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 414 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 362 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 201 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 415 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 201 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 365 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 416 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 139 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 359 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 202 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 417 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 202 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 473 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 139 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 418 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 366 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 202 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 419 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 202 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 359 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 420 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 367 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 202 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 421 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 202 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 366 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 422 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 143 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 473 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 202 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 423 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 202 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 367 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 143 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 424 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 361 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 203 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 425 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 203 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 366 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 55 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 426 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 368 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 203 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 427 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 203 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 361 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 428 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 369 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 203 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 429 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 203 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 368 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 430 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 366 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 203 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 431 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 203 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 369 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 432 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 363 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 204 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 433 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 204 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 368 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 56 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 434 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 370 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 204 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 435 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 204 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 363 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 436 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 371 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 204 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 437 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 204 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 370 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 438 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 368 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 204 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 439 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 204 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 371 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 440 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 365 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 205 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 441 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 205 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 370 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 57 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 442 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 140 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 479 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 205 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 443 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 205 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 365 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 140 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 444 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 144 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 373 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 205 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 445 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 205 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 479 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 144 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 446 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 370 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 205 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 447 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 205 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 373 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 448 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 143 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 367 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 206 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 449 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 206 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 475 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 143 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 450 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 374 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 206 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 451 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 206 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 367 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 452 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 375 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 206 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 453 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 206 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 374 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 454 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 147 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 475 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 206 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 455 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 206 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 375 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 147 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 456 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 369 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 207 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 457 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 207 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 374 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 71 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 458 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 376 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 207 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 459 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 207 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 369 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 460 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 377 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 207 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 461 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 207 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 376 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 462 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 374 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 207 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 463 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 207 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 377 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 464 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 371 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 208 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 465 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 208 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 376 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 72 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 466 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 378 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 208 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 467 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 208 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 371 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 468 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 379 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 208 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 469 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 208 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 378 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 470 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 376 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 208 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 471 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 208 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 379 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 472 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 373 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 209 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 473 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 209 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 378 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 73 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 474 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 144 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 480 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 209 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 475 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 209 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 373 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 144 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 476 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 148 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 381 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 209 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 477 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 209 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 480 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 148 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 478 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 378 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 209 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 479 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 209 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 381 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 480 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 147 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 375 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 210 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 481 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 210 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 477 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 147 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 482 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 382 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 210 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 483 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 210 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 375 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 484 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 34 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 297 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 210 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 485 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 210 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 382 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 34 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 486 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 135 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 477 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 210 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 487 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 210 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 297 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 135 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 488 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 377 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 211 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 489 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 211 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 382 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 87 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 490 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 383 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 211 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 491 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 211 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 377 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 492 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 39 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 306 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 211 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 493 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 211 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 383 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 39 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 494 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 34 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 382 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 211 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 495 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 211 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 306 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 34 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 496 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 379 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 212 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 497 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 212 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 383 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 88 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 498 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 384 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 212 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 499 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 212 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 379 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 500 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 44 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 315 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 212 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 501 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 212 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 384 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 44 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 502 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 39 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 383 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 212 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 503 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 212 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 315 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 39 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 504 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 381 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 213 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 505 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 213 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 384 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 89 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 506 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 148 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 481 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 213 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 507 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 213 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 381 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 148 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 508 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 137 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 324 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 213 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 509 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 213 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 481 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 137 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 510 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 44 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 384 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 213 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 511 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 213 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 324 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 44 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 512 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 24 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 284 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 214 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 513 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 214 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 364 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 24 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 514 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 386 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 214 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 515 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 214 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 284 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 516 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 387 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 214 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 517 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 214 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 386 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 518 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 58 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 364 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 214 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 519 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 214 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 387 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 58 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 520 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 282 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 215 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 521 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 215 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 386 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 522 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 388 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 215 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 523 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 215 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 282 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 524 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 389 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 215 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 525 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 215 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 388 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 526 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 386 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 215 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 527 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 215 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 389 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 528 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 280 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 216 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 529 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 216 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 388 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 530 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 21 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 390 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 216 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 531 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 216 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 280 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 21 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 532 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 391 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 216 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 533 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 216 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 390 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 534 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 388 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 216 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 535 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 216 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 391 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 536 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 21 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 278 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 217 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 537 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 217 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 390 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 21 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 538 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 20 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 392 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 217 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 539 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 217 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 278 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 20 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 540 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 62 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 393 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 217 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 541 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 217 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 392 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 62 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 542 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 390 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 217 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 543 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 217 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 393 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 544 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 58 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 387 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 218 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 545 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 218 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 372 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 58 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 546 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 394 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 218 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 547 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 218 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 387 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 548 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 395 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 218 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 549 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 218 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 394 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 550 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 74 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 372 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 218 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 551 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 218 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 395 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 74 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 552 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 389 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 219 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 553 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 219 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 394 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 59 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 554 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 396 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 219 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 555 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 219 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 389 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 556 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 397 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 219 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 557 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 219 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 396 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 558 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 394 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 219 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 559 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 219 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 397 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 560 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 391 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 220 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 561 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 220 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 396 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 60 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 562 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 398 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 220 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 563 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 220 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 391 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 564 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 399 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 220 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 565 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 220 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 398 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 566 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 396 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 220 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 567 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 220 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 399 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 568 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 393 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 221 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 569 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 221 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 398 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 61 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 570 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 62 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 400 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 221 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 571 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 221 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 393 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 62 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 572 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 78 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 401 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 221 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 573 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 221 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 400 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 78 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 574 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 398 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 221 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 575 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 221 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 401 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 576 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 74 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 395 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 222 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 577 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 222 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 380 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 74 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 578 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 402 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 222 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 579 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 222 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 395 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 580 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 403 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 222 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 581 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 222 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 402 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 582 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 90 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 380 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 222 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 583 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 222 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 403 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 90 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 584 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 397 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 223 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 585 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 223 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 402 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 75 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 586 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 404 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 223 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 587 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 223 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 397 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 588 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 405 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 223 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 589 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 223 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 404 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 590 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 402 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 223 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 591 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 223 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 405 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 592 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 399 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 224 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 593 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 224 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 404 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 76 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 594 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 406 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 224 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 595 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 224 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 399 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 596 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 407 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 224 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 597 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 224 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 406 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 598 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 404 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 224 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 599 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 224 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 407 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 600 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 401 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 225 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 601 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 225 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 406 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 77 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 602 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 78 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 408 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 225 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 603 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 225 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 401 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 78 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 604 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 94 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 409 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 225 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 605 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 225 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 408 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 94 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 606 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 406 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 225 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 607 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 225 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 409 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 608 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 90 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 403 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 226 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 609 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 226 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 385 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 90 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 610 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 410 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 226 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 611 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 226 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 403 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 612 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 48 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 325 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 226 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 613 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 226 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 410 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 48 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 614 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 49 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 385 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 226 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 615 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 226 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 325 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 49 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 616 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 405 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 227 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 617 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 227 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 410 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 91 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 618 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 411 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 227 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 619 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 227 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 405 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 620 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 47 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 323 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 227 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 621 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 227 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 411 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 47 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 622 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 48 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 410 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 227 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 623 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 227 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 323 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 48 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 624 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 407 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 228 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 625 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 228 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 411 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 92 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 626 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 412 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 228 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 627 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 228 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 407 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 628 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 46 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 321 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 228 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 629 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 228 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 412 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 46 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 630 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 47 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 411 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 228 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 631 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 228 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 321 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 47 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 632 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 409 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 229 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 633 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 229 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 412 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 93 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 634 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 94 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 413 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 229 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 635 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 229 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 409 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 94 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 636 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 45 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 318 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 229 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 637 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 229 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 413 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 45 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 638 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 46 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 412 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 229 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 639 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 229 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 318 0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 46 0.0000 1.0000 0.0000 + *MESH_FACENORMAL 640 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 132 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 277 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 230 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 641 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 230 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 482 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 132 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 642 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 15 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 414 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 230 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 643 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 230 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 277 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 15 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 644 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 415 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 230 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 645 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 230 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 414 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 646 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 141 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 482 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 230 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 647 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 230 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 415 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 141 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 648 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 15 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 268 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 231 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 649 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 231 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 414 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 15 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 650 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 10 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 416 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 231 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 651 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 231 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 268 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 10 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 652 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 417 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 231 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 653 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 231 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 416 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 654 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 414 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 231 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 655 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 231 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 417 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 656 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 10 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 259 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 232 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 657 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 232 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 416 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 10 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 658 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 5 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 418 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 232 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 659 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 232 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 259 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 5 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 660 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 419 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 232 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 661 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 232 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 418 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 662 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 416 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 232 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 663 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 232 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 419 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 664 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 5 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 246 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 233 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 665 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 233 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 418 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 5 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 666 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 130 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 470 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 233 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 667 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 233 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 246 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 130 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 668 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 138 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 420 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 233 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 669 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 233 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 470 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 138 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 670 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 418 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 233 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 671 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 233 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 420 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 672 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 141 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 415 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 234 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 673 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 234 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 483 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 141 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 674 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 421 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 234 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 675 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 234 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 415 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 676 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 422 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 234 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 677 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 234 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 421 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 678 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 145 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 483 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 234 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 679 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 234 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 422 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 145 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 680 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 417 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 235 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 681 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 235 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 421 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 63 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 682 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 423 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 235 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 683 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 235 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 417 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 684 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 424 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 235 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 685 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 235 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 423 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 686 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 421 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 235 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 687 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 235 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 424 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 688 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 419 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 236 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 689 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 236 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 423 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 64 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 690 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 425 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 236 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 691 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 236 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 419 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 692 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 426 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 236 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 693 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 236 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 425 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 694 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 423 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 236 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 695 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 236 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 426 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 696 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 420 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 237 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 697 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 237 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 425 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 65 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 698 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 138 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 472 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 237 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 699 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 237 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 420 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 138 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 700 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 142 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 427 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 237 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 701 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 237 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 472 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 142 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 702 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 425 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 237 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 703 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 237 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 427 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 704 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 145 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 422 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 238 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 705 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 238 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 484 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 145 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 706 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 428 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 238 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 707 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 238 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 422 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 708 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 429 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 238 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 709 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 238 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 428 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 710 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 149 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 484 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 238 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 711 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 238 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 429 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 149 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 712 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 424 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 239 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 713 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 239 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 428 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 79 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 714 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 430 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 239 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 715 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 239 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 424 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 716 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 431 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 239 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 717 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 239 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 430 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 718 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 428 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 239 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 719 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 239 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 431 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 720 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 426 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 240 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 721 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 240 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 430 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 80 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 722 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 432 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 240 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 723 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 240 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 426 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 724 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 433 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 240 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 725 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 240 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 432 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 726 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 430 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 240 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 727 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 240 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 433 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 728 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 427 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 241 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 729 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 241 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 432 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 81 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 730 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 142 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 474 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 241 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 731 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 241 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 427 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 142 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 732 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 146 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 434 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 241 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 733 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 241 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 474 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 146 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 734 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 432 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 241 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 735 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 241 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 434 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 736 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 149 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 429 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 242 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 737 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 242 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 485 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 149 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 738 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 435 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 242 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 739 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 242 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 429 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 740 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 40 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 319 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 242 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 741 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 242 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 435 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 40 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 742 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 136 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 485 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 242 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 743 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 242 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 319 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 136 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 744 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 431 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 243 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 745 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 243 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 435 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 95 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 746 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 436 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 243 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 747 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 243 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 431 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 748 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 35 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 310 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 243 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 749 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 243 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 436 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 35 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 750 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 40 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 435 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 243 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 751 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 243 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 310 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 40 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 752 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 433 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 244 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 753 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 244 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 436 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 96 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 754 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 437 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 244 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 755 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 244 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 433 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 756 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 30 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 301 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 244 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 757 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 244 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 437 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 30 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 758 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 35 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 436 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 244 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 759 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 244 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 301 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 35 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 760 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 434 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 245 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 761 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 245 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 437 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 97 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 762 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 146 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 476 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 245 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 763 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 245 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 434 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 146 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 764 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 134 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 289 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 245 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 765 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 245 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 476 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 134 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 766 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 30 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 437 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 245 -1.0000 0.0000 0.0000 + *MESH_FACENORMAL 767 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 245 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 289 -1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 30 -1.0000 0.0000 0.0000 + } + } + *PROP_MOTIONBLUR 0 + *PROP_CASTSHADOW 1 + *PROP_RECVSHADOW 1 + *MATERIAL_REF 1 +} diff --git a/base/models/materialeditor/cylinder.ase b/base/models/materialeditor/cylinder.ase new file mode 100644 index 000000000..03ef91a31 --- /dev/null +++ b/base/models/materialeditor/cylinder.ase @@ -0,0 +1,357 @@ +*3DSMAX_ASCIIEXPORT 200 +*COMMENT "AsciiExport Version 2.00 - Fri Dec 05 16:15:10 2003" +*SCENE { + *SCENE_FILENAME "t.max" + *SCENE_FIRSTFRAME 0 + *SCENE_LASTFRAME 300 + *SCENE_FRAMESPEED 30 + *SCENE_TICKSPERFRAME 160 + *SCENE_BACKGROUND_STATIC 0.0000 0.0000 0.0000 + *SCENE_AMBIENT_STATIC 0.0000 0.0000 0.0000 +} +*MATERIAL_LIST { + *MATERIAL_COUNT 4 + *MATERIAL 0 { + *MATERIAL_NAME "1 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4706 0.4706 0.4706 + *MATERIAL_DIFFUSE 0.4706 0.4706 0.4706 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 1 { + *MATERIAL_NAME "9 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5882 0.5882 0.5882 + *MATERIAL_DIFFUSE 0.5882 0.5882 0.5882 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + *MAP_DIFFUSE { + *MAP_NAME "Map #1" + *MAP_CLASS "Bitmap" + *MAP_SUBNO 1 + *MAP_AMOUNT 1.0000 + *BITMAP "C:\Documents and Settings\jshwood\DESKTOP\base\Newguy\t.jpg" + *MAP_TYPE Screen + *UVW_U_OFFSET 0.0000 + *UVW_V_OFFSET 0.0000 + *UVW_U_TILING 1.0000 + *UVW_V_TILING 1.0000 + *UVW_ANGLE 0.0000 + *UVW_BLUR 1.0000 + *UVW_BLUR_OFFSET 0.0000 + *UVW_NOUSE_AMT 1.0000 + *UVW_NOISE_SIZE 1.0000 + *UVW_NOISE_LEVEL 1 + *UVW_NOISE_PHASE 0.0000 + *BITMAP_FILTER Pyramidal + } + } + *MATERIAL 2 { + *MATERIAL_NAME "1 - Defaultas" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 3 { + *MATERIAL_NAME "14 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5882 0.5882 0.5882 + *MATERIAL_DIFFUSE 0.5882 0.5882 0.5882 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } +} +*GEOMOBJECT { + *NODE_NAME "Cylinder03" + *NODE_TM { + *NODE_NAME "Cylinder03" + *INHERIT_POS 0 0 0 + *INHERIT_ROT 0 0 0 + *INHERIT_SCL 0 0 0 + *TM_ROW0 1.0000 0.0000 0.0000 + *TM_ROW1 0.0000 1.0000 0.0000 + *TM_ROW2 0.0000 0.0000 1.0000 + *TM_ROW3 0.0000 0.0000 0.0000 + *TM_POS 0.0000 0.0000 0.0000 + *TM_ROTAXIS 0.0000 0.0000 0.0000 + *TM_ROTANGLE 0.0000 + *TM_SCALE 1.0000 1.0000 1.0000 + *TM_SCALEAXIS 0.0000 0.0000 0.0000 + *TM_SCALEAXISANG 0.0000 + } + *MESH { + *TIMEVALUE 0 + *MESH_NUMVERTEX 26 + *MESH_NUMFACES 48 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 0.0000 0.0000 -64.0000 + *MESH_VERTEX 1 32.0000 0.0000 -64.0000 + *MESH_VERTEX 2 27.7128 16.0000 -64.0000 + *MESH_VERTEX 3 16.0000 27.7128 -64.0000 + *MESH_VERTEX 4 -0.0000 32.0000 -64.0000 + *MESH_VERTEX 5 -16.0000 27.7128 -64.0000 + *MESH_VERTEX 6 -27.7128 16.0000 -64.0000 + *MESH_VERTEX 7 -32.0000 -0.0000 -64.0000 + *MESH_VERTEX 8 -27.7128 -16.0000 -64.0000 + *MESH_VERTEX 9 -16.0000 -27.7128 -64.0000 + *MESH_VERTEX 10 0.0000 -32.0000 -64.0000 + *MESH_VERTEX 11 16.0000 -27.7128 -64.0000 + *MESH_VERTEX 12 27.7128 -16.0000 -64.0000 + *MESH_VERTEX 13 32.0000 0.0000 64.0000 + *MESH_VERTEX 14 27.7128 16.0000 64.0000 + *MESH_VERTEX 15 16.0000 27.7128 64.0000 + *MESH_VERTEX 16 -0.0000 32.0000 64.0000 + *MESH_VERTEX 17 -16.0000 27.7128 64.0000 + *MESH_VERTEX 18 -27.7128 16.0000 64.0000 + *MESH_VERTEX 19 -32.0000 -0.0000 64.0000 + *MESH_VERTEX 20 -27.7128 -16.0000 64.0000 + *MESH_VERTEX 21 -16.0000 -27.7128 64.0000 + *MESH_VERTEX 22 0.0000 -32.0000 64.0000 + *MESH_VERTEX 23 16.0000 -27.7128 64.0000 + *MESH_VERTEX 24 27.7128 -16.0000 64.0000 + *MESH_VERTEX 25 0.0000 0.0000 64.0000 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 1 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 1: A: 0 B: 3 C: 2 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 2: A: 0 B: 4 C: 3 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 3: A: 0 B: 5 C: 4 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 4: A: 0 B: 6 C: 5 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 5: A: 0 B: 7 C: 6 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 6: A: 0 B: 8 C: 7 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 7: A: 0 B: 9 C: 8 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 8: A: 0 B: 10 C: 9 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 9: A: 0 B: 11 C: 10 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 10: A: 0 B: 12 C: 11 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 11: A: 0 B: 1 C: 12 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 12: A: 1 B: 14 C: 13 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 13: A: 1 B: 2 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 14: A: 2 B: 15 C: 14 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 15: A: 2 B: 3 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 16: A: 3 B: 16 C: 15 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 17: A: 3 B: 4 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 18: A: 4 B: 17 C: 16 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 19: A: 4 B: 5 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 20: A: 5 B: 18 C: 17 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 21: A: 5 B: 6 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 22: A: 6 B: 19 C: 18 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 23: A: 6 B: 7 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 24: A: 7 B: 20 C: 19 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 25: A: 7 B: 8 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 26: A: 8 B: 21 C: 20 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 27: A: 8 B: 9 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 28: A: 9 B: 22 C: 21 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 29: A: 9 B: 10 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 30: A: 10 B: 23 C: 22 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 31: A: 10 B: 11 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 32: A: 11 B: 24 C: 23 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 33: A: 11 B: 12 C: 24 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 34: A: 12 B: 13 C: 24 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 35: A: 12 B: 1 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 36: A: 25 B: 13 C: 14 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 37: A: 25 B: 14 C: 15 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 38: A: 25 B: 15 C: 16 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 39: A: 25 B: 16 C: 17 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 40: A: 25 B: 17 C: 18 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 41: A: 25 B: 18 C: 19 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 42: A: 25 B: 19 C: 20 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 43: A: 25 B: 20 C: 21 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 44: A: 25 B: 21 C: 22 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 45: A: 25 B: 22 C: 23 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 46: A: 25 B: 23 C: 24 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 47: A: 25 B: 24 C: 13 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + } + *MESH_NUMTVERTEX 99 + *MESH_TVERTLIST { + *MESH_TVERT 0 0.5000 0.5000 -0.2498 + *MESH_TVERT 1 0.7500 0.0005 0.9990 + *MESH_TVERT 2 0.8333 0.0005 0.9990 + *MESH_TVERT 3 0.9167 0.0005 0.9990 + *MESH_TVERT 4 1.0000 0.0005 0.9990 + *MESH_TVERT 5 0.0833 0.0005 0.9990 + *MESH_TVERT 6 0.1667 0.0005 0.9990 + *MESH_TVERT 7 0.2500 0.0005 0.9990 + *MESH_TVERT 8 0.3333 0.0005 0.9990 + *MESH_TVERT 9 0.4167 0.0005 0.9990 + *MESH_TVERT 10 0.5000 0.0005 0.9990 + *MESH_TVERT 11 0.5833 0.0005 0.9990 + *MESH_TVERT 12 0.6667 0.0005 0.9990 + *MESH_TVERT 13 0.7500 0.9995 0.9990 + *MESH_TVERT 14 0.8333 0.9995 0.9990 + *MESH_TVERT 15 0.9167 0.9995 0.9990 + *MESH_TVERT 16 1.0000 0.9995 0.9990 + *MESH_TVERT 17 0.0833 0.9995 0.9990 + *MESH_TVERT 18 0.1667 0.9995 0.9990 + *MESH_TVERT 19 0.2500 0.9995 0.9990 + *MESH_TVERT 20 0.3333 0.9995 0.9990 + *MESH_TVERT 21 0.4167 0.9995 0.9990 + *MESH_TVERT 22 0.5000 0.9995 0.9990 + *MESH_TVERT 23 0.5833 0.9995 0.9990 + *MESH_TVERT 24 0.6667 0.9995 0.9990 + *MESH_TVERT 25 0.5000 0.5000 0.2498 + *MESH_TVERT 26 0.5000 0.5000 -0.2498 + *MESH_TVERT 27 0.2502 0.9326 -0.2498 + *MESH_TVERT 28 0.5000 0.9995 -0.2498 + *MESH_TVERT 29 0.5000 0.5000 -0.2498 + *MESH_TVERT 30 0.0674 0.7498 -0.2498 + *MESH_TVERT 31 0.2502 0.9326 -0.2498 + *MESH_TVERT 32 0.5000 0.5000 -0.2498 + *MESH_TVERT 33 0.0005 0.5000 -0.2498 + *MESH_TVERT 34 0.0674 0.7498 -0.2498 + *MESH_TVERT 35 0.5000 0.5000 -0.2498 + *MESH_TVERT 36 0.0674 0.2502 -0.2498 + *MESH_TVERT 37 0.0005 0.5000 -0.2498 + *MESH_TVERT 38 0.5000 0.5000 -0.2498 + *MESH_TVERT 39 0.2502 0.0674 -0.2498 + *MESH_TVERT 40 0.0674 0.2502 -0.2498 + *MESH_TVERT 41 0.5000 0.5000 -0.2498 + *MESH_TVERT 42 0.5000 0.0005 -0.2498 + *MESH_TVERT 43 0.2502 0.0674 -0.2498 + *MESH_TVERT 44 0.5000 0.5000 -0.2498 + *MESH_TVERT 45 0.7498 0.0674 -0.2498 + *MESH_TVERT 46 0.5000 0.0005 -0.2498 + *MESH_TVERT 47 0.5000 0.5000 -0.2498 + *MESH_TVERT 48 0.9326 0.2502 -0.2498 + *MESH_TVERT 49 0.7498 0.0674 -0.2498 + *MESH_TVERT 50 0.5000 0.5000 -0.2498 + *MESH_TVERT 51 0.9995 0.5000 -0.2498 + *MESH_TVERT 52 0.9326 0.2502 -0.2498 + *MESH_TVERT 53 0.5000 0.5000 -0.2498 + *MESH_TVERT 54 0.9326 0.7498 -0.2498 + *MESH_TVERT 55 0.9995 0.5000 -0.2498 + *MESH_TVERT 56 0.5000 0.5000 -0.2498 + *MESH_TVERT 57 0.7498 0.9326 -0.2498 + *MESH_TVERT 58 0.9326 0.7498 -0.2498 + *MESH_TVERT 59 0.5000 0.9995 -0.2498 + *MESH_TVERT 60 0.7498 0.9326 -0.2498 + *MESH_TVERT 61 -0.0000 0.0005 0.9990 + *MESH_TVERT 62 -0.0000 0.9995 0.9990 + *MESH_TVERT 63 -0.0000 0.0005 0.9990 + *MESH_TVERT 64 0.5000 0.5000 0.2498 + *MESH_TVERT 65 0.5000 0.9995 0.2498 + *MESH_TVERT 66 0.2502 0.9326 0.2498 + *MESH_TVERT 67 0.5000 0.5000 0.2498 + *MESH_TVERT 68 0.2502 0.9326 0.2498 + *MESH_TVERT 69 0.0674 0.7498 0.2498 + *MESH_TVERT 70 0.5000 0.5000 0.2498 + *MESH_TVERT 71 0.0674 0.7498 0.2498 + *MESH_TVERT 72 0.0005 0.5000 0.2498 + *MESH_TVERT 73 0.5000 0.5000 0.2498 + *MESH_TVERT 74 0.0005 0.5000 0.2498 + *MESH_TVERT 75 0.0674 0.2502 0.2498 + *MESH_TVERT 76 0.5000 0.5000 0.2498 + *MESH_TVERT 77 0.0674 0.2502 0.2498 + *MESH_TVERT 78 0.2502 0.0674 0.2498 + *MESH_TVERT 79 0.5000 0.5000 0.2498 + *MESH_TVERT 80 0.2502 0.0674 0.2498 + *MESH_TVERT 81 0.5000 0.0005 0.2498 + *MESH_TVERT 82 0.5000 0.5000 0.2498 + *MESH_TVERT 83 0.5000 0.0005 0.2498 + *MESH_TVERT 84 0.7498 0.0674 0.2498 + *MESH_TVERT 85 0.5000 0.5000 0.2498 + *MESH_TVERT 86 0.7498 0.0674 0.2498 + *MESH_TVERT 87 0.9326 0.2502 0.2498 + *MESH_TVERT 88 0.5000 0.5000 0.2498 + *MESH_TVERT 89 0.9326 0.2502 0.2498 + *MESH_TVERT 90 0.9995 0.5000 0.2498 + *MESH_TVERT 91 0.5000 0.5000 0.2498 + *MESH_TVERT 92 0.9995 0.5000 0.2498 + *MESH_TVERT 93 0.9326 0.7498 0.2498 + *MESH_TVERT 94 0.5000 0.5000 0.2498 + *MESH_TVERT 95 0.9326 0.7498 0.2498 + *MESH_TVERT 96 0.7498 0.9326 0.2498 + *MESH_TVERT 97 0.7498 0.9326 0.2498 + *MESH_TVERT 98 0.5000 0.9995 0.2498 + } + *MESH_NUMTVFACES 48 + *MESH_TFACELIST { + *MESH_TFACE 0 26 27 28 + *MESH_TFACE 1 29 30 31 + *MESH_TFACE 2 32 33 34 + *MESH_TFACE 3 35 36 37 + *MESH_TFACE 4 38 39 40 + *MESH_TFACE 5 41 42 43 + *MESH_TFACE 6 44 45 46 + *MESH_TFACE 7 47 48 49 + *MESH_TFACE 8 50 51 52 + *MESH_TFACE 9 53 54 55 + *MESH_TFACE 10 56 57 58 + *MESH_TFACE 11 0 59 60 + *MESH_TFACE 12 1 14 13 + *MESH_TFACE 13 1 2 14 + *MESH_TFACE 14 2 15 14 + *MESH_TFACE 15 2 3 15 + *MESH_TFACE 16 3 16 15 + *MESH_TFACE 17 3 4 16 + *MESH_TFACE 18 61 17 62 + *MESH_TFACE 19 63 5 17 + *MESH_TFACE 20 5 18 17 + *MESH_TFACE 21 5 6 18 + *MESH_TFACE 22 6 19 18 + *MESH_TFACE 23 6 7 19 + *MESH_TFACE 24 7 20 19 + *MESH_TFACE 25 7 8 20 + *MESH_TFACE 26 8 21 20 + *MESH_TFACE 27 8 9 21 + *MESH_TFACE 28 9 22 21 + *MESH_TFACE 29 9 10 22 + *MESH_TFACE 30 10 23 22 + *MESH_TFACE 31 10 11 23 + *MESH_TFACE 32 11 24 23 + *MESH_TFACE 33 11 12 24 + *MESH_TFACE 34 12 13 24 + *MESH_TFACE 35 12 1 13 + *MESH_TFACE 36 64 65 66 + *MESH_TFACE 37 67 68 69 + *MESH_TFACE 38 70 71 72 + *MESH_TFACE 39 73 74 75 + *MESH_TFACE 40 76 77 78 + *MESH_TFACE 41 79 80 81 + *MESH_TFACE 42 82 83 84 + *MESH_TFACE 43 85 86 87 + *MESH_TFACE 44 88 89 90 + *MESH_TFACE 45 91 92 93 + *MESH_TFACE 46 94 95 96 + *MESH_TFACE 47 25 97 98 + } + } + *PROP_MOTIONBLUR 0 + *PROP_CASTSHADOW 1 + *PROP_RECVSHADOW 1 + *MATERIAL_REF 3 +} diff --git a/base/models/materialeditor/cylinder_h.ase b/base/models/materialeditor/cylinder_h.ase new file mode 100644 index 000000000..e856f8bb9 --- /dev/null +++ b/base/models/materialeditor/cylinder_h.ase @@ -0,0 +1,611 @@ +*3DSMAX_ASCIIEXPORT 200 +*COMMENT "AsciiExport Version 2.00 - Tue Dec 09 14:22:39 2003" +*SCENE { + *SCENE_FILENAME "t.max" + *SCENE_FIRSTFRAME 0 + *SCENE_LASTFRAME 300 + *SCENE_FRAMESPEED 30 + *SCENE_TICKSPERFRAME 160 + *SCENE_BACKGROUND_STATIC 0.0000 0.0000 0.0000 + *SCENE_AMBIENT_STATIC 0.0000 0.0000 0.0000 +} +*MATERIAL_LIST { + *MATERIAL_COUNT 6 + *MATERIAL 0 { + *MATERIAL_NAME "1 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4706 0.4706 0.4706 + *MATERIAL_DIFFUSE 0.4706 0.4706 0.4706 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 1 { + *MATERIAL_NAME "9 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5882 0.5882 0.5882 + *MATERIAL_DIFFUSE 0.5882 0.5882 0.5882 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + *MAP_DIFFUSE { + *MAP_NAME "Map #1" + *MAP_CLASS "Bitmap" + *MAP_SUBNO 1 + *MAP_AMOUNT 1.0000 + *BITMAP "C:\Documents and Settings\Jay Brushwood\Desktop\base\Newguy\t.jpg" + *MAP_TYPE Screen + *UVW_U_OFFSET 0.0000 + *UVW_V_OFFSET 0.0000 + *UVW_U_TILING 1.0000 + *UVW_V_TILING 1.0000 + *UVW_ANGLE 0.0000 + *UVW_BLUR 1.0000 + *UVW_BLUR_OFFSET 0.0000 + *UVW_NOUSE_AMT 1.0000 + *UVW_NOISE_SIZE 1.0000 + *UVW_NOISE_LEVEL 1 + *UVW_NOISE_PHASE 0.0000 + *BITMAP_FILTER Pyramidal + } + } + *MATERIAL 2 { + *MATERIAL_NAME "Material #12" + *MATERIAL_CLASS "Multi/Sub-Object" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *NUMSUBMTLS 2 + *SUBMATERIAL 0 { + *MATERIAL_NAME "1 - Defaultas" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *SUBMATERIAL 1 { + *MATERIAL_NAME "1 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4706 0.4706 0.4706 + *MATERIAL_DIFFUSE 0.4706 0.4706 0.4706 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + } + *MATERIAL 3 { + *MATERIAL_NAME "1 - Defaultas" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 4 { + *MATERIAL_NAME "14 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5882 0.5882 0.5882 + *MATERIAL_DIFFUSE 0.5882 0.5882 0.5882 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 5 { + *MATERIAL_NAME "7 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4353 0.4471 0.6157 + *MATERIAL_DIFFUSE 0.4353 0.4471 0.6157 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } +} +*GEOMOBJECT { + *NODE_NAME "Cylinder_Horz" + *NODE_TM { + *NODE_NAME "Cylinder_Horz" + *INHERIT_POS 0 0 0 + *INHERIT_ROT 0 0 0 + *INHERIT_SCL 0 0 0 + *TM_ROW0 0.0000 0.0000 -1.0000 + *TM_ROW1 1.0000 -0.0000 0.0000 + *TM_ROW2 0.0000 1.0000 0.0000 + *TM_ROW3 -0.0000 -0.0000 0.0000 + *TM_POS -0.0000 -0.0000 0.0000 + *TM_ROTAXIS 0.5774 -0.5774 0.5774 + *TM_ROTANGLE 4.1888 + *TM_SCALE 1.0000 1.0000 1.0000 + *TM_SCALEAXIS 0.0000 0.0000 0.0000 + *TM_SCALEAXISANG 0.0000 + } + *MESH { + *TIMEVALUE 0 + *MESH_NUMVERTEX 26 + *MESH_NUMFACES 48 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 -0.0000 -64.0000 -0.0000 + *MESH_VERTEX 1 -0.0000 -64.0000 -32.0000 + *MESH_VERTEX 2 16.0000 -64.0000 -27.7128 + *MESH_VERTEX 3 27.7128 -64.0000 -16.0000 + *MESH_VERTEX 4 32.0000 -64.0000 -0.0000 + *MESH_VERTEX 5 27.7128 -64.0000 16.0000 + *MESH_VERTEX 6 16.0000 -64.0000 27.7128 + *MESH_VERTEX 7 -0.0000 -64.0000 32.0000 + *MESH_VERTEX 8 -16.0000 -64.0000 27.7128 + *MESH_VERTEX 9 -27.7128 -64.0000 16.0000 + *MESH_VERTEX 10 -32.0000 -64.0000 -0.0000 + *MESH_VERTEX 11 -27.7128 -64.0000 -16.0000 + *MESH_VERTEX 12 -16.0000 -64.0000 -27.7128 + *MESH_VERTEX 13 0.0000 64.0000 -32.0000 + *MESH_VERTEX 14 16.0000 64.0000 -27.7128 + *MESH_VERTEX 15 27.7128 64.0000 -16.0000 + *MESH_VERTEX 16 32.0000 64.0000 0.0000 + *MESH_VERTEX 17 27.7128 64.0000 16.0000 + *MESH_VERTEX 18 16.0000 64.0000 27.7128 + *MESH_VERTEX 19 -0.0000 64.0000 32.0000 + *MESH_VERTEX 20 -16.0000 64.0000 27.7128 + *MESH_VERTEX 21 -27.7128 64.0000 16.0000 + *MESH_VERTEX 22 -32.0000 64.0000 0.0000 + *MESH_VERTEX 23 -27.7128 64.0000 -16.0000 + *MESH_VERTEX 24 -16.0000 64.0000 -27.7128 + *MESH_VERTEX 25 0.0000 64.0000 0.0000 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 1 B: 2 C: 0 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 1: A: 2 B: 3 C: 0 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 2: A: 3 B: 4 C: 0 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 3: A: 4 B: 5 C: 0 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 4: A: 5 B: 6 C: 0 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 5: A: 6 B: 7 C: 0 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 6: A: 7 B: 8 C: 0 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 7: A: 8 B: 9 C: 0 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 8: A: 9 B: 10 C: 0 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 9: A: 10 B: 11 C: 0 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 10: A: 11 B: 12 C: 0 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 11: A: 12 B: 1 C: 0 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 12: A: 13 B: 14 C: 1 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 13: A: 14 B: 2 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 14: A: 14 B: 15 C: 2 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 15: A: 15 B: 3 C: 2 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 16: A: 15 B: 16 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 17: A: 16 B: 4 C: 3 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 18: A: 16 B: 17 C: 4 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 19: A: 17 B: 5 C: 4 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 20: A: 17 B: 18 C: 5 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 21: A: 18 B: 6 C: 5 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 22: A: 18 B: 19 C: 6 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 23: A: 19 B: 7 C: 6 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 24: A: 19 B: 20 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 25: A: 20 B: 8 C: 7 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 26: A: 20 B: 21 C: 8 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 27: A: 21 B: 9 C: 8 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 28: A: 21 B: 22 C: 9 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 29: A: 22 B: 10 C: 9 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 30: A: 22 B: 23 C: 10 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 31: A: 23 B: 11 C: 10 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 32: A: 23 B: 24 C: 11 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 33: A: 24 B: 12 C: 11 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 34: A: 24 B: 13 C: 12 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 35: A: 13 B: 1 C: 12 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 36: A: 14 B: 13 C: 25 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 37: A: 15 B: 14 C: 25 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 38: A: 16 B: 15 C: 25 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 39: A: 17 B: 16 C: 25 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 40: A: 18 B: 17 C: 25 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 41: A: 19 B: 18 C: 25 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 42: A: 20 B: 19 C: 25 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 43: A: 21 B: 20 C: 25 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 44: A: 22 B: 21 C: 25 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 45: A: 23 B: 22 C: 25 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 46: A: 24 B: 23 C: 25 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 47: A: 13 B: 24 C: 25 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + } + *MESH_NUMTVERTEX 99 + *MESH_TVERTLIST { + *MESH_TVERT 0 0.5000 0.5000 -0.2498 + *MESH_TVERT 1 0.0005 0.2500 0.9990 + *MESH_TVERT 2 0.0005 0.1667 0.9990 + *MESH_TVERT 3 0.0005 0.0833 0.9990 + *MESH_TVERT 4 0.0005 0.0000 0.9990 + *MESH_TVERT 5 0.0005 0.9167 0.9990 + *MESH_TVERT 6 0.0005 0.8333 0.9990 + *MESH_TVERT 7 0.0005 0.7500 0.9990 + *MESH_TVERT 8 0.0005 0.6667 0.9990 + *MESH_TVERT 9 0.0005 0.5833 0.9990 + *MESH_TVERT 10 0.0005 0.5000 0.9990 + *MESH_TVERT 11 0.0005 0.4167 0.9990 + *MESH_TVERT 12 0.0005 0.3333 0.9990 + *MESH_TVERT 13 0.9995 0.2500 0.9990 + *MESH_TVERT 14 0.9995 0.1667 0.9990 + *MESH_TVERT 15 0.9995 0.0833 0.9990 + *MESH_TVERT 16 0.9995 -0.0000 0.9990 + *MESH_TVERT 17 0.9995 0.9167 0.9990 + *MESH_TVERT 18 0.9995 0.8333 0.9990 + *MESH_TVERT 19 0.9995 0.7500 0.9990 + *MESH_TVERT 20 0.9995 0.6667 0.9990 + *MESH_TVERT 21 0.9995 0.5833 0.9990 + *MESH_TVERT 22 0.9995 0.5000 0.9990 + *MESH_TVERT 23 0.9995 0.4167 0.9990 + *MESH_TVERT 24 0.9995 0.3333 0.9990 + *MESH_TVERT 25 0.5000 0.5000 0.2498 + *MESH_TVERT 26 0.5000 0.5000 -0.2498 + *MESH_TVERT 27 0.2502 0.9326 -0.2498 + *MESH_TVERT 28 0.5000 0.9995 -0.2498 + *MESH_TVERT 29 0.5000 0.5000 -0.2498 + *MESH_TVERT 30 0.0674 0.7498 -0.2498 + *MESH_TVERT 31 0.2502 0.9326 -0.2498 + *MESH_TVERT 32 0.5000 0.5000 -0.2498 + *MESH_TVERT 33 0.0005 0.5000 -0.2498 + *MESH_TVERT 34 0.0674 0.7498 -0.2498 + *MESH_TVERT 35 0.5000 0.5000 -0.2498 + *MESH_TVERT 36 0.0674 0.2502 -0.2498 + *MESH_TVERT 37 0.0005 0.5000 -0.2498 + *MESH_TVERT 38 0.5000 0.5000 -0.2498 + *MESH_TVERT 39 0.2502 0.0674 -0.2498 + *MESH_TVERT 40 0.0674 0.2502 -0.2498 + *MESH_TVERT 41 0.5000 0.5000 -0.2498 + *MESH_TVERT 42 0.5000 0.0005 -0.2498 + *MESH_TVERT 43 0.2502 0.0674 -0.2498 + *MESH_TVERT 44 0.5000 0.5000 -0.2498 + *MESH_TVERT 45 0.7498 0.0674 -0.2498 + *MESH_TVERT 46 0.5000 0.0005 -0.2498 + *MESH_TVERT 47 0.5000 0.5000 -0.2498 + *MESH_TVERT 48 0.9326 0.2502 -0.2498 + *MESH_TVERT 49 0.7498 0.0674 -0.2498 + *MESH_TVERT 50 0.5000 0.5000 -0.2498 + *MESH_TVERT 51 0.9995 0.5000 -0.2498 + *MESH_TVERT 52 0.9326 0.2502 -0.2498 + *MESH_TVERT 53 0.5000 0.5000 -0.2498 + *MESH_TVERT 54 0.9326 0.7498 -0.2498 + *MESH_TVERT 55 0.9995 0.5000 -0.2498 + *MESH_TVERT 56 0.5000 0.5000 -0.2498 + *MESH_TVERT 57 0.7498 0.9326 -0.2498 + *MESH_TVERT 58 0.9326 0.7498 -0.2498 + *MESH_TVERT 59 0.5000 0.9995 -0.2498 + *MESH_TVERT 60 0.7498 0.9326 -0.2498 + *MESH_TVERT 61 0.0005 1.0000 0.9990 + *MESH_TVERT 62 0.9995 1.0000 0.9990 + *MESH_TVERT 63 0.0005 1.0000 0.9990 + *MESH_TVERT 64 0.5000 0.5000 0.2498 + *MESH_TVERT 65 0.5000 0.9995 0.2498 + *MESH_TVERT 66 0.2502 0.9326 0.2498 + *MESH_TVERT 67 0.5000 0.5000 0.2498 + *MESH_TVERT 68 0.2502 0.9326 0.2498 + *MESH_TVERT 69 0.0674 0.7498 0.2498 + *MESH_TVERT 70 0.5000 0.5000 0.2498 + *MESH_TVERT 71 0.0674 0.7498 0.2498 + *MESH_TVERT 72 0.0005 0.5000 0.2498 + *MESH_TVERT 73 0.5000 0.5000 0.2498 + *MESH_TVERT 74 0.0005 0.5000 0.2498 + *MESH_TVERT 75 0.0674 0.2502 0.2498 + *MESH_TVERT 76 0.5000 0.5000 0.2498 + *MESH_TVERT 77 0.0674 0.2502 0.2498 + *MESH_TVERT 78 0.2502 0.0674 0.2498 + *MESH_TVERT 79 0.5000 0.5000 0.2498 + *MESH_TVERT 80 0.2502 0.0674 0.2498 + *MESH_TVERT 81 0.5000 0.0005 0.2498 + *MESH_TVERT 82 0.5000 0.5000 0.2498 + *MESH_TVERT 83 0.5000 0.0005 0.2498 + *MESH_TVERT 84 0.7498 0.0674 0.2498 + *MESH_TVERT 85 0.5000 0.5000 0.2498 + *MESH_TVERT 86 0.7498 0.0674 0.2498 + *MESH_TVERT 87 0.9326 0.2502 0.2498 + *MESH_TVERT 88 0.5000 0.5000 0.2498 + *MESH_TVERT 89 0.9326 0.2502 0.2498 + *MESH_TVERT 90 0.9995 0.5000 0.2498 + *MESH_TVERT 91 0.5000 0.5000 0.2498 + *MESH_TVERT 92 0.9995 0.5000 0.2498 + *MESH_TVERT 93 0.9326 0.7498 0.2498 + *MESH_TVERT 94 0.5000 0.5000 0.2498 + *MESH_TVERT 95 0.9326 0.7498 0.2498 + *MESH_TVERT 96 0.7498 0.9326 0.2498 + *MESH_TVERT 97 0.7498 0.9326 0.2498 + *MESH_TVERT 98 0.5000 0.9995 0.2498 + } + *MESH_NUMTVFACES 48 + *MESH_TFACELIST { + *MESH_TFACE 0 28 27 26 + *MESH_TFACE 1 31 30 29 + *MESH_TFACE 2 34 33 32 + *MESH_TFACE 3 37 36 35 + *MESH_TFACE 4 40 39 38 + *MESH_TFACE 5 43 42 41 + *MESH_TFACE 6 46 45 44 + *MESH_TFACE 7 49 48 47 + *MESH_TFACE 8 52 51 50 + *MESH_TFACE 9 55 54 53 + *MESH_TFACE 10 58 57 56 + *MESH_TFACE 11 60 59 0 + *MESH_TFACE 12 13 14 1 + *MESH_TFACE 13 14 2 1 + *MESH_TFACE 14 14 15 2 + *MESH_TFACE 15 15 3 2 + *MESH_TFACE 16 15 16 3 + *MESH_TFACE 17 16 4 3 + *MESH_TFACE 18 62 17 61 + *MESH_TFACE 19 17 5 63 + *MESH_TFACE 20 17 18 5 + *MESH_TFACE 21 18 6 5 + *MESH_TFACE 22 18 19 6 + *MESH_TFACE 23 19 7 6 + *MESH_TFACE 24 19 20 7 + *MESH_TFACE 25 20 8 7 + *MESH_TFACE 26 20 21 8 + *MESH_TFACE 27 21 9 8 + *MESH_TFACE 28 21 22 9 + *MESH_TFACE 29 22 10 9 + *MESH_TFACE 30 22 23 10 + *MESH_TFACE 31 23 11 10 + *MESH_TFACE 32 23 24 11 + *MESH_TFACE 33 24 12 11 + *MESH_TFACE 34 24 13 12 + *MESH_TFACE 35 13 1 12 + *MESH_TFACE 36 66 65 64 + *MESH_TFACE 37 69 68 67 + *MESH_TFACE 38 72 71 70 + *MESH_TFACE 39 75 74 73 + *MESH_TFACE 40 78 77 76 + *MESH_TFACE 41 81 80 79 + *MESH_TFACE 42 84 83 82 + *MESH_TFACE 43 87 86 85 + *MESH_TFACE 44 90 89 88 + *MESH_TFACE 45 93 92 91 + *MESH_TFACE 46 96 95 94 + *MESH_TFACE 47 98 97 25 + } + *MESH_NORMALS { + *MESH_FACENORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 1 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 2 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 1 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 2 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 3 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 2 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 3 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 4 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 3 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 4 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 5 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 4 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 5 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 5 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 9 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 9 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 10 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 9 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 10 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 10 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 1 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 12 0.9659 0.2588 0.0000 + *MESH_VERTEXNORMAL 13 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 14 0.8660 0.5000 0.0000 + *MESH_VERTEXNORMAL 1 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 13 0.9659 0.2588 0.0000 + *MESH_VERTEXNORMAL 14 0.8660 0.5000 0.0000 + *MESH_VERTEXNORMAL 2 0.8660 0.5000 0.0000 + *MESH_VERTEXNORMAL 1 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 14 0.7071 0.7071 0.0000 + *MESH_VERTEXNORMAL 14 0.8660 0.5000 0.0000 + *MESH_VERTEXNORMAL 15 0.5000 0.8660 0.0000 + *MESH_VERTEXNORMAL 2 0.8660 0.5000 0.0000 + *MESH_FACENORMAL 15 0.7071 0.7071 0.0000 + *MESH_VERTEXNORMAL 15 0.5000 0.8660 0.0000 + *MESH_VERTEXNORMAL 3 0.5000 0.8660 0.0000 + *MESH_VERTEXNORMAL 2 0.8660 0.5000 0.0000 + *MESH_FACENORMAL 16 0.2588 0.9659 0.0000 + *MESH_VERTEXNORMAL 15 0.5000 0.8660 0.0000 + *MESH_VERTEXNORMAL 16 -0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.5000 0.8660 0.0000 + *MESH_FACENORMAL 17 0.2588 0.9659 0.0000 + *MESH_VERTEXNORMAL 16 -0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 4 -0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 3 0.5000 0.8660 0.0000 + *MESH_FACENORMAL 18 -0.2588 0.9659 0.0000 + *MESH_VERTEXNORMAL 16 -0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 17 -0.5000 0.8660 0.0000 + *MESH_VERTEXNORMAL 4 -0.0000 1.0000 0.0000 + *MESH_FACENORMAL 19 -0.2588 0.9659 0.0000 + *MESH_VERTEXNORMAL 17 -0.5000 0.8660 0.0000 + *MESH_VERTEXNORMAL 5 -0.5000 0.8660 0.0000 + *MESH_VERTEXNORMAL 4 -0.0000 1.0000 0.0000 + *MESH_FACENORMAL 20 -0.7071 0.7071 0.0000 + *MESH_VERTEXNORMAL 17 -0.5000 0.8660 0.0000 + *MESH_VERTEXNORMAL 18 -0.8660 0.5000 0.0000 + *MESH_VERTEXNORMAL 5 -0.5000 0.8660 0.0000 + *MESH_FACENORMAL 21 -0.7071 0.7071 0.0000 + *MESH_VERTEXNORMAL 18 -0.8660 0.5000 0.0000 + *MESH_VERTEXNORMAL 6 -0.8660 0.5000 0.0000 + *MESH_VERTEXNORMAL 5 -0.5000 0.8660 0.0000 + *MESH_FACENORMAL 22 -0.9659 0.2588 0.0000 + *MESH_VERTEXNORMAL 18 -0.8660 0.5000 0.0000 + *MESH_VERTEXNORMAL 19 -1.0000 -0.0000 0.0000 + *MESH_VERTEXNORMAL 6 -0.8660 0.5000 0.0000 + *MESH_FACENORMAL 23 -0.9659 0.2588 0.0000 + *MESH_VERTEXNORMAL 19 -1.0000 -0.0000 0.0000 + *MESH_VERTEXNORMAL 7 -1.0000 -0.0000 0.0000 + *MESH_VERTEXNORMAL 6 -0.8660 0.5000 0.0000 + *MESH_FACENORMAL 24 -0.9659 -0.2588 0.0000 + *MESH_VERTEXNORMAL 19 -1.0000 -0.0000 0.0000 + *MESH_VERTEXNORMAL 20 -0.8660 -0.5000 0.0000 + *MESH_VERTEXNORMAL 7 -1.0000 -0.0000 0.0000 + *MESH_FACENORMAL 25 -0.9659 -0.2588 0.0000 + *MESH_VERTEXNORMAL 20 -0.8660 -0.5000 0.0000 + *MESH_VERTEXNORMAL 8 -0.8660 -0.5000 0.0000 + *MESH_VERTEXNORMAL 7 -1.0000 -0.0000 0.0000 + *MESH_FACENORMAL 26 -0.7071 -0.7071 0.0000 + *MESH_VERTEXNORMAL 20 -0.8660 -0.5000 0.0000 + *MESH_VERTEXNORMAL 21 -0.5000 -0.8660 0.0000 + *MESH_VERTEXNORMAL 8 -0.8660 -0.5000 0.0000 + *MESH_FACENORMAL 27 -0.7071 -0.7071 0.0000 + *MESH_VERTEXNORMAL 21 -0.5000 -0.8660 0.0000 + *MESH_VERTEXNORMAL 9 -0.5000 -0.8660 0.0000 + *MESH_VERTEXNORMAL 8 -0.8660 -0.5000 0.0000 + *MESH_FACENORMAL 28 -0.2588 -0.9659 0.0000 + *MESH_VERTEXNORMAL 21 -0.5000 -0.8660 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 9 -0.5000 -0.8660 0.0000 + *MESH_FACENORMAL 29 -0.2588 -0.9659 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 10 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 9 -0.5000 -0.8660 0.0000 + *MESH_FACENORMAL 30 0.2588 -0.9659 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.5000 -0.8660 0.0000 + *MESH_VERTEXNORMAL 10 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 31 0.2588 -0.9659 0.0000 + *MESH_VERTEXNORMAL 23 0.5000 -0.8660 0.0000 + *MESH_VERTEXNORMAL 11 0.5000 -0.8660 0.0000 + *MESH_VERTEXNORMAL 10 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 32 0.7071 -0.7071 0.0000 + *MESH_VERTEXNORMAL 23 0.5000 -0.8660 0.0000 + *MESH_VERTEXNORMAL 24 0.8660 -0.5000 0.0000 + *MESH_VERTEXNORMAL 11 0.5000 -0.8660 0.0000 + *MESH_FACENORMAL 33 0.7071 -0.7071 0.0000 + *MESH_VERTEXNORMAL 24 0.8660 -0.5000 0.0000 + *MESH_VERTEXNORMAL 12 0.8660 -0.5000 0.0000 + *MESH_VERTEXNORMAL 11 0.5000 -0.8660 0.0000 + *MESH_FACENORMAL 34 0.9659 -0.2588 0.0000 + *MESH_VERTEXNORMAL 24 0.8660 -0.5000 0.0000 + *MESH_VERTEXNORMAL 13 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 12 0.8660 -0.5000 0.0000 + *MESH_FACENORMAL 35 0.9659 -0.2588 0.0000 + *MESH_VERTEXNORMAL 13 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 1 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 12 0.8660 -0.5000 0.0000 + *MESH_FACENORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 14 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 15 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 14 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 15 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 39 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 40 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 19 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 20 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 19 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 21 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 20 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 44 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 22 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 21 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 45 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 23 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 22 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 46 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 24 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 23 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 47 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 24 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + } + } + *PROP_MOTIONBLUR 0 + *PROP_CASTSHADOW 1 + *PROP_RECVSHADOW 1 + *MATERIAL_REF 1 +} diff --git a/base/models/materialeditor/cylinder_v.ase b/base/models/materialeditor/cylinder_v.ase new file mode 100644 index 000000000..c28ea7db3 --- /dev/null +++ b/base/models/materialeditor/cylinder_v.ase @@ -0,0 +1,611 @@ +*3DSMAX_ASCIIEXPORT 200 +*COMMENT "AsciiExport Version 2.00 - Tue Dec 09 14:15:33 2003" +*SCENE { + *SCENE_FILENAME "t.max" + *SCENE_FIRSTFRAME 0 + *SCENE_LASTFRAME 300 + *SCENE_FRAMESPEED 30 + *SCENE_TICKSPERFRAME 160 + *SCENE_BACKGROUND_STATIC 0.0000 0.0000 0.0000 + *SCENE_AMBIENT_STATIC 0.0000 0.0000 0.0000 +} +*MATERIAL_LIST { + *MATERIAL_COUNT 6 + *MATERIAL 0 { + *MATERIAL_NAME "1 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4706 0.4706 0.4706 + *MATERIAL_DIFFUSE 0.4706 0.4706 0.4706 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 1 { + *MATERIAL_NAME "9 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5882 0.5882 0.5882 + *MATERIAL_DIFFUSE 0.5882 0.5882 0.5882 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + *MAP_DIFFUSE { + *MAP_NAME "Map #1" + *MAP_CLASS "Bitmap" + *MAP_SUBNO 1 + *MAP_AMOUNT 1.0000 + *BITMAP "C:\Documents and Settings\Jay Brushwood\Desktop\base\Newguy\t.jpg" + *MAP_TYPE Screen + *UVW_U_OFFSET 0.0000 + *UVW_V_OFFSET 0.0000 + *UVW_U_TILING 1.0000 + *UVW_V_TILING 1.0000 + *UVW_ANGLE 0.0000 + *UVW_BLUR 1.0000 + *UVW_BLUR_OFFSET 0.0000 + *UVW_NOUSE_AMT 1.0000 + *UVW_NOISE_SIZE 1.0000 + *UVW_NOISE_LEVEL 1 + *UVW_NOISE_PHASE 0.0000 + *BITMAP_FILTER Pyramidal + } + } + *MATERIAL 2 { + *MATERIAL_NAME "Material #12" + *MATERIAL_CLASS "Multi/Sub-Object" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *NUMSUBMTLS 2 + *SUBMATERIAL 0 { + *MATERIAL_NAME "1 - Defaultas" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *SUBMATERIAL 1 { + *MATERIAL_NAME "1 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4706 0.4706 0.4706 + *MATERIAL_DIFFUSE 0.4706 0.4706 0.4706 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + } + *MATERIAL 3 { + *MATERIAL_NAME "1 - Defaultas" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 4 { + *MATERIAL_NAME "14 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5882 0.5882 0.5882 + *MATERIAL_DIFFUSE 0.5882 0.5882 0.5882 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 5 { + *MATERIAL_NAME "7 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4353 0.4471 0.6157 + *MATERIAL_DIFFUSE 0.4353 0.4471 0.6157 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } +} +*GEOMOBJECT { + *NODE_NAME "Cylinder_Vert" + *NODE_TM { + *NODE_NAME "Cylinder_Vert" + *INHERIT_POS 0 0 0 + *INHERIT_ROT 0 0 0 + *INHERIT_SCL 0 0 0 + *TM_ROW0 -0.0000 -1.0000 0.0000 + *TM_ROW1 1.0000 -0.0000 0.0000 + *TM_ROW2 0.0000 0.0000 1.0000 + *TM_ROW3 -0.0000 -0.0000 0.0000 + *TM_POS -0.0000 -0.0000 0.0000 + *TM_ROTAXIS 0.0000 0.0000 1.0000 + *TM_ROTANGLE 1.5708 + *TM_SCALE 1.0000 1.0000 1.0000 + *TM_SCALEAXIS 0.0000 0.0000 0.0000 + *TM_SCALEAXISANG 0.0000 + } + *MESH { + *TIMEVALUE 0 + *MESH_NUMVERTEX 26 + *MESH_NUMFACES 48 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 -0.0000 -0.0000 -64.0000 + *MESH_VERTEX 1 -0.0000 -32.0000 -64.0000 + *MESH_VERTEX 2 16.0000 -27.7128 -64.0000 + *MESH_VERTEX 3 27.7128 -16.0000 -64.0000 + *MESH_VERTEX 4 32.0000 -0.0000 -64.0000 + *MESH_VERTEX 5 27.7128 16.0000 -64.0000 + *MESH_VERTEX 6 16.0000 27.7128 -64.0000 + *MESH_VERTEX 7 -0.0000 32.0000 -64.0000 + *MESH_VERTEX 8 -16.0000 27.7128 -64.0000 + *MESH_VERTEX 9 -27.7128 16.0000 -64.0000 + *MESH_VERTEX 10 -32.0000 -0.0000 -64.0000 + *MESH_VERTEX 11 -27.7128 -16.0000 -64.0000 + *MESH_VERTEX 12 -16.0000 -27.7128 -64.0000 + *MESH_VERTEX 13 -0.0000 -32.0000 64.0000 + *MESH_VERTEX 14 16.0000 -27.7128 64.0000 + *MESH_VERTEX 15 27.7128 -16.0000 64.0000 + *MESH_VERTEX 16 32.0000 -0.0000 64.0000 + *MESH_VERTEX 17 27.7128 16.0000 64.0000 + *MESH_VERTEX 18 16.0000 27.7128 64.0000 + *MESH_VERTEX 19 -0.0000 32.0000 64.0000 + *MESH_VERTEX 20 -16.0000 27.7128 64.0000 + *MESH_VERTEX 21 -27.7128 16.0000 64.0000 + *MESH_VERTEX 22 -32.0000 -0.0000 64.0000 + *MESH_VERTEX 23 -27.7128 -16.0000 64.0000 + *MESH_VERTEX 24 -16.0000 -27.7128 64.0000 + *MESH_VERTEX 25 -0.0000 -0.0000 64.0000 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 2 C: 1 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 1: A: 0 B: 3 C: 2 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 2: A: 0 B: 4 C: 3 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 3: A: 0 B: 5 C: 4 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 4: A: 0 B: 6 C: 5 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 5: A: 0 B: 7 C: 6 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 6: A: 0 B: 8 C: 7 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 7: A: 0 B: 9 C: 8 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 8: A: 0 B: 10 C: 9 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 9: A: 0 B: 11 C: 10 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 10: A: 0 B: 12 C: 11 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 11: A: 0 B: 1 C: 12 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 12: A: 1 B: 14 C: 13 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 13: A: 1 B: 2 C: 14 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 14: A: 2 B: 15 C: 14 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 15: A: 2 B: 3 C: 15 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 16: A: 3 B: 16 C: 15 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 17: A: 3 B: 4 C: 16 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 18: A: 4 B: 17 C: 16 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 19: A: 4 B: 5 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 20: A: 5 B: 18 C: 17 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 21: A: 5 B: 6 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 22: A: 6 B: 19 C: 18 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 23: A: 6 B: 7 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 24: A: 7 B: 20 C: 19 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 25: A: 7 B: 8 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 26: A: 8 B: 21 C: 20 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 27: A: 8 B: 9 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 28: A: 9 B: 22 C: 21 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 29: A: 9 B: 10 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 30: A: 10 B: 23 C: 22 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 31: A: 10 B: 11 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 32: A: 11 B: 24 C: 23 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 33: A: 11 B: 12 C: 24 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 34: A: 12 B: 13 C: 24 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 35: A: 12 B: 1 C: 13 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 4 *MESH_MTLID 2 + *MESH_FACE 36: A: 25 B: 13 C: 14 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 37: A: 25 B: 14 C: 15 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 38: A: 25 B: 15 C: 16 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 39: A: 25 B: 16 C: 17 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 40: A: 25 B: 17 C: 18 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 41: A: 25 B: 18 C: 19 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 42: A: 25 B: 19 C: 20 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 43: A: 25 B: 20 C: 21 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 44: A: 25 B: 21 C: 22 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 45: A: 25 B: 22 C: 23 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 46: A: 25 B: 23 C: 24 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + *MESH_FACE 47: A: 25 B: 24 C: 13 AB: 0 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 0 + } + *MESH_NUMTVERTEX 99 + *MESH_TVERTLIST { + *MESH_TVERT 0 0.5000 0.5000 -0.2498 + *MESH_TVERT 1 0.7500 0.0005 0.9990 + *MESH_TVERT 2 0.8333 0.0005 0.9990 + *MESH_TVERT 3 0.9167 0.0005 0.9990 + *MESH_TVERT 4 1.0000 0.0005 0.9990 + *MESH_TVERT 5 0.0833 0.0005 0.9990 + *MESH_TVERT 6 0.1667 0.0005 0.9990 + *MESH_TVERT 7 0.2500 0.0005 0.9990 + *MESH_TVERT 8 0.3333 0.0005 0.9990 + *MESH_TVERT 9 0.4167 0.0005 0.9990 + *MESH_TVERT 10 0.5000 0.0005 0.9990 + *MESH_TVERT 11 0.5833 0.0005 0.9990 + *MESH_TVERT 12 0.6667 0.0005 0.9990 + *MESH_TVERT 13 0.7500 0.9995 0.9990 + *MESH_TVERT 14 0.8333 0.9995 0.9990 + *MESH_TVERT 15 0.9167 0.9995 0.9990 + *MESH_TVERT 16 1.0000 0.9995 0.9990 + *MESH_TVERT 17 0.0833 0.9995 0.9990 + *MESH_TVERT 18 0.1667 0.9995 0.9990 + *MESH_TVERT 19 0.2500 0.9995 0.9990 + *MESH_TVERT 20 0.3333 0.9995 0.9990 + *MESH_TVERT 21 0.4167 0.9995 0.9990 + *MESH_TVERT 22 0.5000 0.9995 0.9990 + *MESH_TVERT 23 0.5833 0.9995 0.9990 + *MESH_TVERT 24 0.6667 0.9995 0.9990 + *MESH_TVERT 25 0.5000 0.5000 0.2498 + *MESH_TVERT 26 0.5000 0.5000 -0.2498 + *MESH_TVERT 27 0.2502 0.9326 -0.2498 + *MESH_TVERT 28 0.5000 0.9995 -0.2498 + *MESH_TVERT 29 0.5000 0.5000 -0.2498 + *MESH_TVERT 30 0.0674 0.7498 -0.2498 + *MESH_TVERT 31 0.2502 0.9326 -0.2498 + *MESH_TVERT 32 0.5000 0.5000 -0.2498 + *MESH_TVERT 33 0.0005 0.5000 -0.2498 + *MESH_TVERT 34 0.0674 0.7498 -0.2498 + *MESH_TVERT 35 0.5000 0.5000 -0.2498 + *MESH_TVERT 36 0.0674 0.2502 -0.2498 + *MESH_TVERT 37 0.0005 0.5000 -0.2498 + *MESH_TVERT 38 0.5000 0.5000 -0.2498 + *MESH_TVERT 39 0.2502 0.0674 -0.2498 + *MESH_TVERT 40 0.0674 0.2502 -0.2498 + *MESH_TVERT 41 0.5000 0.5000 -0.2498 + *MESH_TVERT 42 0.5000 0.0005 -0.2498 + *MESH_TVERT 43 0.2502 0.0674 -0.2498 + *MESH_TVERT 44 0.5000 0.5000 -0.2498 + *MESH_TVERT 45 0.7498 0.0674 -0.2498 + *MESH_TVERT 46 0.5000 0.0005 -0.2498 + *MESH_TVERT 47 0.5000 0.5000 -0.2498 + *MESH_TVERT 48 0.9326 0.2502 -0.2498 + *MESH_TVERT 49 0.7498 0.0674 -0.2498 + *MESH_TVERT 50 0.5000 0.5000 -0.2498 + *MESH_TVERT 51 0.9995 0.5000 -0.2498 + *MESH_TVERT 52 0.9326 0.2502 -0.2498 + *MESH_TVERT 53 0.5000 0.5000 -0.2498 + *MESH_TVERT 54 0.9326 0.7498 -0.2498 + *MESH_TVERT 55 0.9995 0.5000 -0.2498 + *MESH_TVERT 56 0.5000 0.5000 -0.2498 + *MESH_TVERT 57 0.7498 0.9326 -0.2498 + *MESH_TVERT 58 0.9326 0.7498 -0.2498 + *MESH_TVERT 59 0.5000 0.9995 -0.2498 + *MESH_TVERT 60 0.7498 0.9326 -0.2498 + *MESH_TVERT 61 -0.0000 0.0005 0.9990 + *MESH_TVERT 62 -0.0000 0.9995 0.9990 + *MESH_TVERT 63 -0.0000 0.0005 0.9990 + *MESH_TVERT 64 0.5000 0.5000 0.2498 + *MESH_TVERT 65 0.5000 0.9995 0.2498 + *MESH_TVERT 66 0.2502 0.9326 0.2498 + *MESH_TVERT 67 0.5000 0.5000 0.2498 + *MESH_TVERT 68 0.2502 0.9326 0.2498 + *MESH_TVERT 69 0.0674 0.7498 0.2498 + *MESH_TVERT 70 0.5000 0.5000 0.2498 + *MESH_TVERT 71 0.0674 0.7498 0.2498 + *MESH_TVERT 72 0.0005 0.5000 0.2498 + *MESH_TVERT 73 0.5000 0.5000 0.2498 + *MESH_TVERT 74 0.0005 0.5000 0.2498 + *MESH_TVERT 75 0.0674 0.2502 0.2498 + *MESH_TVERT 76 0.5000 0.5000 0.2498 + *MESH_TVERT 77 0.0674 0.2502 0.2498 + *MESH_TVERT 78 0.2502 0.0674 0.2498 + *MESH_TVERT 79 0.5000 0.5000 0.2498 + *MESH_TVERT 80 0.2502 0.0674 0.2498 + *MESH_TVERT 81 0.5000 0.0005 0.2498 + *MESH_TVERT 82 0.5000 0.5000 0.2498 + *MESH_TVERT 83 0.5000 0.0005 0.2498 + *MESH_TVERT 84 0.7498 0.0674 0.2498 + *MESH_TVERT 85 0.5000 0.5000 0.2498 + *MESH_TVERT 86 0.7498 0.0674 0.2498 + *MESH_TVERT 87 0.9326 0.2502 0.2498 + *MESH_TVERT 88 0.5000 0.5000 0.2498 + *MESH_TVERT 89 0.9326 0.2502 0.2498 + *MESH_TVERT 90 0.9995 0.5000 0.2498 + *MESH_TVERT 91 0.5000 0.5000 0.2498 + *MESH_TVERT 92 0.9995 0.5000 0.2498 + *MESH_TVERT 93 0.9326 0.7498 0.2498 + *MESH_TVERT 94 0.5000 0.5000 0.2498 + *MESH_TVERT 95 0.9326 0.7498 0.2498 + *MESH_TVERT 96 0.7498 0.9326 0.2498 + *MESH_TVERT 97 0.7498 0.9326 0.2498 + *MESH_TVERT 98 0.5000 0.9995 0.2498 + } + *MESH_NUMTVFACES 48 + *MESH_TFACELIST { + *MESH_TFACE 0 26 27 28 + *MESH_TFACE 1 29 30 31 + *MESH_TFACE 2 32 33 34 + *MESH_TFACE 3 35 36 37 + *MESH_TFACE 4 38 39 40 + *MESH_TFACE 5 41 42 43 + *MESH_TFACE 6 44 45 46 + *MESH_TFACE 7 47 48 49 + *MESH_TFACE 8 50 51 52 + *MESH_TFACE 9 53 54 55 + *MESH_TFACE 10 56 57 58 + *MESH_TFACE 11 0 59 60 + *MESH_TFACE 12 1 14 13 + *MESH_TFACE 13 1 2 14 + *MESH_TFACE 14 2 15 14 + *MESH_TFACE 15 2 3 15 + *MESH_TFACE 16 3 16 15 + *MESH_TFACE 17 3 4 16 + *MESH_TFACE 18 61 17 62 + *MESH_TFACE 19 63 5 17 + *MESH_TFACE 20 5 18 17 + *MESH_TFACE 21 5 6 18 + *MESH_TFACE 22 6 19 18 + *MESH_TFACE 23 6 7 19 + *MESH_TFACE 24 7 20 19 + *MESH_TFACE 25 7 8 20 + *MESH_TFACE 26 8 21 20 + *MESH_TFACE 27 8 9 21 + *MESH_TFACE 28 9 22 21 + *MESH_TFACE 29 9 10 22 + *MESH_TFACE 30 10 23 22 + *MESH_TFACE 31 10 11 23 + *MESH_TFACE 32 11 24 23 + *MESH_TFACE 33 11 12 24 + *MESH_TFACE 34 12 13 24 + *MESH_TFACE 35 12 1 13 + *MESH_TFACE 36 64 65 66 + *MESH_TFACE 37 67 68 69 + *MESH_TFACE 38 70 71 72 + *MESH_TFACE 39 73 74 75 + *MESH_TFACE 40 76 77 78 + *MESH_TFACE 41 79 80 81 + *MESH_TFACE 42 82 83 84 + *MESH_TFACE 43 85 86 87 + *MESH_TFACE 44 88 89 90 + *MESH_TFACE 45 91 92 93 + *MESH_TFACE 46 94 95 96 + *MESH_TFACE 47 25 97 98 + } + *MESH_NORMALS { + *MESH_FACENORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 2 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 1 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 1 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 3 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 2 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 2 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 4 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 3 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 3 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 5 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 4 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 4 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 5 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 5 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 6 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 6 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 7 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 7 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 9 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 8 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 8 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 10 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 9 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 9 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 10 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 10 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 11 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 11 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 1 0.0000 0.0000 -1.0000 + *MESH_VERTEXNORMAL 12 0.0000 0.0000 -1.0000 + *MESH_FACENORMAL 12 0.9659 0.2588 0.0000 + *MESH_VERTEXNORMAL 1 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 14 0.8660 0.5000 0.0000 + *MESH_VERTEXNORMAL 13 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 13 0.9659 0.2588 0.0000 + *MESH_VERTEXNORMAL 1 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 2 0.8660 0.5000 0.0000 + *MESH_VERTEXNORMAL 14 0.8660 0.5000 0.0000 + *MESH_FACENORMAL 14 0.7071 0.7071 0.0000 + *MESH_VERTEXNORMAL 2 0.8660 0.5000 0.0000 + *MESH_VERTEXNORMAL 15 0.5000 0.8660 0.0000 + *MESH_VERTEXNORMAL 14 0.8660 0.5000 0.0000 + *MESH_FACENORMAL 15 0.7071 0.7071 0.0000 + *MESH_VERTEXNORMAL 2 0.8660 0.5000 0.0000 + *MESH_VERTEXNORMAL 3 0.5000 0.8660 0.0000 + *MESH_VERTEXNORMAL 15 0.5000 0.8660 0.0000 + *MESH_FACENORMAL 16 0.2588 0.9659 0.0000 + *MESH_VERTEXNORMAL 3 0.5000 0.8660 0.0000 + *MESH_VERTEXNORMAL 16 -0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 15 0.5000 0.8660 0.0000 + *MESH_FACENORMAL 17 0.2588 0.9659 0.0000 + *MESH_VERTEXNORMAL 3 0.5000 0.8660 0.0000 + *MESH_VERTEXNORMAL 4 -0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 16 -0.0000 1.0000 0.0000 + *MESH_FACENORMAL 18 -0.2588 0.9659 0.0000 + *MESH_VERTEXNORMAL 4 -0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 17 -0.5000 0.8660 0.0000 + *MESH_VERTEXNORMAL 16 -0.0000 1.0000 0.0000 + *MESH_FACENORMAL 19 -0.2588 0.9659 0.0000 + *MESH_VERTEXNORMAL 4 -0.0000 1.0000 0.0000 + *MESH_VERTEXNORMAL 5 -0.5000 0.8660 0.0000 + *MESH_VERTEXNORMAL 17 -0.5000 0.8660 0.0000 + *MESH_FACENORMAL 20 -0.7071 0.7071 0.0000 + *MESH_VERTEXNORMAL 5 -0.5000 0.8660 0.0000 + *MESH_VERTEXNORMAL 18 -0.8660 0.5000 0.0000 + *MESH_VERTEXNORMAL 17 -0.5000 0.8660 0.0000 + *MESH_FACENORMAL 21 -0.7071 0.7071 0.0000 + *MESH_VERTEXNORMAL 5 -0.5000 0.8660 0.0000 + *MESH_VERTEXNORMAL 6 -0.8660 0.5000 0.0000 + *MESH_VERTEXNORMAL 18 -0.8660 0.5000 0.0000 + *MESH_FACENORMAL 22 -0.9659 0.2588 0.0000 + *MESH_VERTEXNORMAL 6 -0.8660 0.5000 0.0000 + *MESH_VERTEXNORMAL 19 -1.0000 -0.0000 0.0000 + *MESH_VERTEXNORMAL 18 -0.8660 0.5000 0.0000 + *MESH_FACENORMAL 23 -0.9659 0.2588 0.0000 + *MESH_VERTEXNORMAL 6 -0.8660 0.5000 0.0000 + *MESH_VERTEXNORMAL 7 -1.0000 -0.0000 0.0000 + *MESH_VERTEXNORMAL 19 -1.0000 -0.0000 0.0000 + *MESH_FACENORMAL 24 -0.9659 -0.2588 0.0000 + *MESH_VERTEXNORMAL 7 -1.0000 -0.0000 0.0000 + *MESH_VERTEXNORMAL 20 -0.8660 -0.5000 0.0000 + *MESH_VERTEXNORMAL 19 -1.0000 -0.0000 0.0000 + *MESH_FACENORMAL 25 -0.9659 -0.2588 0.0000 + *MESH_VERTEXNORMAL 7 -1.0000 -0.0000 0.0000 + *MESH_VERTEXNORMAL 8 -0.8660 -0.5000 0.0000 + *MESH_VERTEXNORMAL 20 -0.8660 -0.5000 0.0000 + *MESH_FACENORMAL 26 -0.7071 -0.7071 0.0000 + *MESH_VERTEXNORMAL 8 -0.8660 -0.5000 0.0000 + *MESH_VERTEXNORMAL 21 -0.5000 -0.8660 0.0000 + *MESH_VERTEXNORMAL 20 -0.8660 -0.5000 0.0000 + *MESH_FACENORMAL 27 -0.7071 -0.7071 0.0000 + *MESH_VERTEXNORMAL 8 -0.8660 -0.5000 0.0000 + *MESH_VERTEXNORMAL 9 -0.5000 -0.8660 0.0000 + *MESH_VERTEXNORMAL 21 -0.5000 -0.8660 0.0000 + *MESH_FACENORMAL 28 -0.2588 -0.9659 0.0000 + *MESH_VERTEXNORMAL 9 -0.5000 -0.8660 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 21 -0.5000 -0.8660 0.0000 + *MESH_FACENORMAL 29 -0.2588 -0.9659 0.0000 + *MESH_VERTEXNORMAL 9 -0.5000 -0.8660 0.0000 + *MESH_VERTEXNORMAL 10 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 30 0.2588 -0.9659 0.0000 + *MESH_VERTEXNORMAL 10 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 23 0.5000 -0.8660 0.0000 + *MESH_VERTEXNORMAL 22 0.0000 -1.0000 0.0000 + *MESH_FACENORMAL 31 0.2588 -0.9659 0.0000 + *MESH_VERTEXNORMAL 10 0.0000 -1.0000 0.0000 + *MESH_VERTEXNORMAL 11 0.5000 -0.8660 0.0000 + *MESH_VERTEXNORMAL 23 0.5000 -0.8660 0.0000 + *MESH_FACENORMAL 32 0.7071 -0.7071 0.0000 + *MESH_VERTEXNORMAL 11 0.5000 -0.8660 0.0000 + *MESH_VERTEXNORMAL 24 0.8660 -0.5000 0.0000 + *MESH_VERTEXNORMAL 23 0.5000 -0.8660 0.0000 + *MESH_FACENORMAL 33 0.7071 -0.7071 0.0000 + *MESH_VERTEXNORMAL 11 0.5000 -0.8660 0.0000 + *MESH_VERTEXNORMAL 12 0.8660 -0.5000 0.0000 + *MESH_VERTEXNORMAL 24 0.8660 -0.5000 0.0000 + *MESH_FACENORMAL 34 0.9659 -0.2588 0.0000 + *MESH_VERTEXNORMAL 12 0.8660 -0.5000 0.0000 + *MESH_VERTEXNORMAL 13 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 24 0.8660 -0.5000 0.0000 + *MESH_FACENORMAL 35 0.9659 -0.2588 0.0000 + *MESH_VERTEXNORMAL 12 0.8660 -0.5000 0.0000 + *MESH_VERTEXNORMAL 1 1.0000 0.0000 0.0000 + *MESH_VERTEXNORMAL 13 1.0000 0.0000 0.0000 + *MESH_FACENORMAL 36 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 14 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 37 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 14 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 15 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 38 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 15 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 39 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 16 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 40 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 17 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 41 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 18 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 19 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 42 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 19 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 20 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 43 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 20 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 21 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 44 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 21 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 22 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 45 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 22 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 23 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 46 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 23 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 24 0.0000 0.0000 1.0000 + *MESH_FACENORMAL 47 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 25 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 24 0.0000 0.0000 1.0000 + *MESH_VERTEXNORMAL 13 0.0000 0.0000 1.0000 + } + } + *PROP_MOTIONBLUR 0 + *PROP_CASTSHADOW 1 + *PROP_RECVSHADOW 1 + *MATERIAL_REF 1 +} diff --git a/base/models/materialeditor/sphere64.ase b/base/models/materialeditor/sphere64.ase new file mode 100644 index 000000000..c93b6fbb5 --- /dev/null +++ b/base/models/materialeditor/sphere64.ase @@ -0,0 +1,862 @@ +*3DSMAX_ASCIIEXPORT 200 +*COMMENT "AsciiExport Version 2.00 - Fri Dec 05 16:15:01 2003" +*SCENE { + *SCENE_FILENAME "test.max" + *SCENE_FIRSTFRAME 0 + *SCENE_LASTFRAME 300 + *SCENE_FRAMESPEED 30 + *SCENE_TICKSPERFRAME 160 + *SCENE_BACKGROUND_STATIC 0.0000 0.0000 0.0000 + *SCENE_AMBIENT_STATIC 0.0000 0.0000 0.0000 +} +*MATERIAL_LIST { + *MATERIAL_COUNT 4 + *MATERIAL 0 { + *MATERIAL_NAME "1 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.4706 0.4706 0.4706 + *MATERIAL_DIFFUSE 0.4706 0.4706 0.4706 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 1 { + *MATERIAL_NAME "9 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5882 0.5882 0.5882 + *MATERIAL_DIFFUSE 0.5882 0.5882 0.5882 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + *MAP_DIFFUSE { + *MAP_NAME "Map #1" + *MAP_CLASS "Bitmap" + *MAP_SUBNO 1 + *MAP_AMOUNT 1.0000 + *BITMAP "C:\Documents and Settings\jshwood\DESKTOP\base\Newguy\test.jpg" + *MAP_TYPE Screen + *UVW_U_OFFSET 0.0000 + *UVW_V_OFFSET 0.0000 + *UVW_U_TILING 1.0000 + *UVW_V_TILING 1.0000 + *UVW_ANGLE 0.0000 + *UVW_BLUR 1.0000 + *UVW_BLUR_OFFSET 0.0000 + *UVW_NOUSE_AMT 1.0000 + *UVW_NOISE_SIZE 1.0000 + *UVW_NOISE_LEVEL 1 + *UVW_NOISE_PHASE 0.0000 + *BITMAP_FILTER Pyramidal + } + } + *MATERIAL 2 { + *MATERIAL_NAME "1 - Defaultas" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.3137 0.3137 0.3137 + *MATERIAL_DIFFUSE 0.3137 0.3137 0.3137 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.3000 + *MATERIAL_SHINESTRENGTH 0.3000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } + *MATERIAL 3 { + *MATERIAL_NAME "14 - Default" + *MATERIAL_CLASS "Standard" + *MATERIAL_AMBIENT 0.5882 0.5882 0.5882 + *MATERIAL_DIFFUSE 0.5882 0.5882 0.5882 + *MATERIAL_SPECULAR 0.9000 0.9000 0.9000 + *MATERIAL_SHINE 0.1000 + *MATERIAL_SHINESTRENGTH 0.0000 + *MATERIAL_TRANSPARENCY 0.0000 + *MATERIAL_WIRESIZE 1.0000 + *MATERIAL_SHADING Blinn + *MATERIAL_XP_FALLOFF 0.0000 + *MATERIAL_SELFILLUM 0.0000 + *MATERIAL_FALLOFF In + *MATERIAL_XP_TYPE Filter + } +} +*GEOMOBJECT { + *NODE_NAME "Sphere02" + *NODE_TM { + *NODE_NAME "Sphere02" + *INHERIT_POS 0 0 0 + *INHERIT_ROT 0 0 0 + *INHERIT_SCL 0 0 0 + *TM_ROW0 1.0000 0.0000 0.0000 + *TM_ROW1 0.0000 1.0000 0.0000 + *TM_ROW2 0.0000 0.0000 1.0000 + *TM_ROW3 0.0000 0.0000 0.0000 + *TM_POS 0.0000 0.0000 0.0000 + *TM_ROTAXIS 0.0000 0.0000 0.0000 + *TM_ROTANGLE 0.0000 + *TM_SCALE 1.0000 1.0000 1.0000 + *TM_SCALEAXIS 0.0000 0.0000 0.0000 + *TM_SCALEAXISANG 0.0000 + } + *MESH { + *TIMEVALUE 0 + *MESH_NUMVERTEX 114 + *MESH_NUMFACES 224 + *MESH_VERTEX_LIST { + *MESH_VERTEX 0 0.0000 0.0000 64.0000 + *MESH_VERTEX 1 -0.0000 24.4917 59.1283 + *MESH_VERTEX 2 -9.3726 22.6274 59.1283 + *MESH_VERTEX 3 -17.3183 17.3183 59.1283 + *MESH_VERTEX 4 -22.6274 9.3726 59.1283 + *MESH_VERTEX 5 -24.4917 -0.0000 59.1283 + *MESH_VERTEX 6 -22.6274 -9.3726 59.1283 + *MESH_VERTEX 7 -17.3183 -17.3183 59.1283 + *MESH_VERTEX 8 -9.3726 -22.6274 59.1283 + *MESH_VERTEX 9 0.0000 -24.4917 59.1283 + *MESH_VERTEX 10 9.3726 -22.6274 59.1283 + *MESH_VERTEX 11 17.3183 -17.3183 59.1283 + *MESH_VERTEX 12 22.6274 -9.3726 59.1283 + *MESH_VERTEX 13 24.4917 0.0000 59.1283 + *MESH_VERTEX 14 22.6274 9.3726 59.1283 + *MESH_VERTEX 15 17.3183 17.3183 59.1283 + *MESH_VERTEX 16 9.3726 22.6274 59.1283 + *MESH_VERTEX 17 -0.0000 45.2548 45.2548 + *MESH_VERTEX 18 -17.3183 41.8100 45.2548 + *MESH_VERTEX 19 -32.0000 32.0000 45.2548 + *MESH_VERTEX 20 -41.8100 17.3183 45.2548 + *MESH_VERTEX 21 -45.2548 -0.0000 45.2548 + *MESH_VERTEX 22 -41.8100 -17.3183 45.2548 + *MESH_VERTEX 23 -32.0000 -32.0000 45.2548 + *MESH_VERTEX 24 -17.3183 -41.8100 45.2548 + *MESH_VERTEX 25 0.0000 -45.2548 45.2548 + *MESH_VERTEX 26 17.3183 -41.8100 45.2548 + *MESH_VERTEX 27 32.0000 -32.0000 45.2548 + *MESH_VERTEX 28 41.8100 -17.3183 45.2548 + *MESH_VERTEX 29 45.2548 0.0000 45.2548 + *MESH_VERTEX 30 41.8100 17.3183 45.2548 + *MESH_VERTEX 31 32.0000 32.0000 45.2548 + *MESH_VERTEX 32 17.3183 41.8100 45.2548 + *MESH_VERTEX 33 -0.0000 59.1283 24.4917 + *MESH_VERTEX 34 -22.6274 54.6274 24.4917 + *MESH_VERTEX 35 -41.8100 41.8100 24.4917 + *MESH_VERTEX 36 -54.6274 22.6274 24.4917 + *MESH_VERTEX 37 -59.1283 -0.0000 24.4917 + *MESH_VERTEX 38 -54.6274 -22.6274 24.4917 + *MESH_VERTEX 39 -41.8100 -41.8100 24.4917 + *MESH_VERTEX 40 -22.6274 -54.6274 24.4917 + *MESH_VERTEX 41 0.0000 -59.1283 24.4917 + *MESH_VERTEX 42 22.6274 -54.6274 24.4917 + *MESH_VERTEX 43 41.8100 -41.8100 24.4917 + *MESH_VERTEX 44 54.6274 -22.6274 24.4917 + *MESH_VERTEX 45 59.1283 0.0000 24.4917 + *MESH_VERTEX 46 54.6274 22.6274 24.4917 + *MESH_VERTEX 47 41.8100 41.8100 24.4917 + *MESH_VERTEX 48 22.6274 54.6274 24.4917 + *MESH_VERTEX 49 -0.0000 64.0000 -0.0000 + *MESH_VERTEX 50 -24.4917 59.1283 -0.0000 + *MESH_VERTEX 51 -45.2548 45.2548 -0.0000 + *MESH_VERTEX 52 -59.1283 24.4917 -0.0000 + *MESH_VERTEX 53 -64.0000 -0.0000 -0.0000 + *MESH_VERTEX 54 -59.1283 -24.4917 -0.0000 + *MESH_VERTEX 55 -45.2548 -45.2548 -0.0000 + *MESH_VERTEX 56 -24.4917 -59.1283 -0.0000 + *MESH_VERTEX 57 0.0000 -64.0000 -0.0000 + *MESH_VERTEX 58 24.4917 -59.1283 -0.0000 + *MESH_VERTEX 59 45.2548 -45.2548 -0.0000 + *MESH_VERTEX 60 59.1283 -24.4917 -0.0000 + *MESH_VERTEX 61 64.0000 0.0000 -0.0000 + *MESH_VERTEX 62 59.1283 24.4918 -0.0000 + *MESH_VERTEX 63 45.2548 45.2548 -0.0000 + *MESH_VERTEX 64 24.4917 59.1283 -0.0000 + *MESH_VERTEX 65 -0.0000 59.1283 -24.4917 + *MESH_VERTEX 66 -22.6274 54.6274 -24.4917 + *MESH_VERTEX 67 -41.8100 41.8100 -24.4917 + *MESH_VERTEX 68 -54.6274 22.6274 -24.4917 + *MESH_VERTEX 69 -59.1283 -0.0000 -24.4917 + *MESH_VERTEX 70 -54.6274 -22.6274 -24.4917 + *MESH_VERTEX 71 -41.8100 -41.8100 -24.4917 + *MESH_VERTEX 72 -22.6274 -54.6274 -24.4917 + *MESH_VERTEX 73 0.0000 -59.1283 -24.4917 + *MESH_VERTEX 74 22.6274 -54.6274 -24.4917 + *MESH_VERTEX 75 41.8100 -41.8100 -24.4917 + *MESH_VERTEX 76 54.6274 -22.6274 -24.4917 + *MESH_VERTEX 77 59.1283 0.0000 -24.4917 + *MESH_VERTEX 78 54.6274 22.6274 -24.4917 + *MESH_VERTEX 79 41.8100 41.8100 -24.4917 + *MESH_VERTEX 80 22.6274 54.6274 -24.4917 + *MESH_VERTEX 81 -0.0000 45.2548 -45.2548 + *MESH_VERTEX 82 -17.3183 41.8100 -45.2548 + *MESH_VERTEX 83 -32.0000 32.0000 -45.2548 + *MESH_VERTEX 84 -41.8100 17.3183 -45.2548 + *MESH_VERTEX 85 -45.2548 -0.0000 -45.2548 + *MESH_VERTEX 86 -41.8100 -17.3183 -45.2548 + *MESH_VERTEX 87 -32.0000 -32.0000 -45.2548 + *MESH_VERTEX 88 -17.3183 -41.8100 -45.2548 + *MESH_VERTEX 89 0.0000 -45.2548 -45.2548 + *MESH_VERTEX 90 17.3183 -41.8100 -45.2548 + *MESH_VERTEX 91 32.0000 -32.0000 -45.2548 + *MESH_VERTEX 92 41.8100 -17.3183 -45.2548 + *MESH_VERTEX 93 45.2548 0.0000 -45.2548 + *MESH_VERTEX 94 41.8100 17.3183 -45.2548 + *MESH_VERTEX 95 32.0000 32.0000 -45.2548 + *MESH_VERTEX 96 17.3183 41.8100 -45.2548 + *MESH_VERTEX 97 -0.0000 24.4917 -59.1283 + *MESH_VERTEX 98 -9.3726 22.6274 -59.1283 + *MESH_VERTEX 99 -17.3183 17.3183 -59.1283 + *MESH_VERTEX 100 -22.6274 9.3726 -59.1283 + *MESH_VERTEX 101 -24.4917 -0.0000 -59.1283 + *MESH_VERTEX 102 -22.6274 -9.3726 -59.1283 + *MESH_VERTEX 103 -17.3183 -17.3183 -59.1283 + *MESH_VERTEX 104 -9.3726 -22.6274 -59.1283 + *MESH_VERTEX 105 0.0000 -24.4917 -59.1283 + *MESH_VERTEX 106 9.3726 -22.6274 -59.1283 + *MESH_VERTEX 107 17.3183 -17.3183 -59.1283 + *MESH_VERTEX 108 22.6274 -9.3726 -59.1283 + *MESH_VERTEX 109 24.4917 0.0000 -59.1283 + *MESH_VERTEX 110 22.6274 9.3726 -59.1283 + *MESH_VERTEX 111 17.3183 17.3183 -59.1283 + *MESH_VERTEX 112 9.3726 22.6274 -59.1283 + *MESH_VERTEX 113 0.0000 0.0000 -64.0000 + } + *MESH_FACE_LIST { + *MESH_FACE 0: A: 0 B: 1 C: 2 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 1: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 2: A: 0 B: 3 C: 4 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 3: A: 0 B: 4 C: 5 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 4: A: 0 B: 5 C: 6 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 5: A: 0 B: 6 C: 7 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 6: A: 0 B: 7 C: 8 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 7: A: 0 B: 8 C: 9 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 8: A: 0 B: 9 C: 10 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 9: A: 0 B: 10 C: 11 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 10: A: 0 B: 11 C: 12 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 11: A: 0 B: 12 C: 13 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 12: A: 0 B: 13 C: 14 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 13: A: 0 B: 14 C: 15 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 14: A: 0 B: 15 C: 16 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 15: A: 0 B: 16 C: 1 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 16: A: 1 B: 17 C: 18 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 17: A: 1 B: 18 C: 2 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 18: A: 2 B: 18 C: 19 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 19: A: 2 B: 19 C: 3 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 20: A: 3 B: 19 C: 20 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 21: A: 3 B: 20 C: 4 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 22: A: 4 B: 20 C: 21 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 23: A: 4 B: 21 C: 5 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 24: A: 5 B: 21 C: 22 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 25: A: 5 B: 22 C: 6 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 26: A: 6 B: 22 C: 23 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 27: A: 6 B: 23 C: 7 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 28: A: 7 B: 23 C: 24 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 29: A: 7 B: 24 C: 8 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 30: A: 8 B: 24 C: 25 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 31: A: 8 B: 25 C: 9 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 32: A: 9 B: 25 C: 26 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 33: A: 9 B: 26 C: 10 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 34: A: 10 B: 26 C: 27 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 35: A: 10 B: 27 C: 11 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 36: A: 11 B: 27 C: 28 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 37: A: 11 B: 28 C: 12 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 38: A: 12 B: 28 C: 29 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 39: A: 12 B: 29 C: 13 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 40: A: 13 B: 29 C: 30 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 41: A: 13 B: 30 C: 14 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 42: A: 14 B: 30 C: 31 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 43: A: 14 B: 31 C: 15 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 44: A: 15 B: 31 C: 32 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 45: A: 15 B: 32 C: 16 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 46: A: 16 B: 32 C: 17 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 47: A: 16 B: 17 C: 1 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 48: A: 17 B: 33 C: 34 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 49: A: 17 B: 34 C: 18 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 50: A: 18 B: 34 C: 35 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 51: A: 18 B: 35 C: 19 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 52: A: 19 B: 35 C: 36 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 53: A: 19 B: 36 C: 20 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 54: A: 20 B: 36 C: 37 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 55: A: 20 B: 37 C: 21 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 56: A: 21 B: 37 C: 38 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 57: A: 21 B: 38 C: 22 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 58: A: 22 B: 38 C: 39 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 59: A: 22 B: 39 C: 23 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 60: A: 23 B: 39 C: 40 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 61: A: 23 B: 40 C: 24 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 62: A: 24 B: 40 C: 41 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 63: A: 24 B: 41 C: 25 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 64: A: 25 B: 41 C: 42 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 65: A: 25 B: 42 C: 26 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 66: A: 26 B: 42 C: 43 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 67: A: 26 B: 43 C: 27 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 68: A: 27 B: 43 C: 44 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 69: A: 27 B: 44 C: 28 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 70: A: 28 B: 44 C: 45 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 71: A: 28 B: 45 C: 29 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 72: A: 29 B: 45 C: 46 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 73: A: 29 B: 46 C: 30 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 74: A: 30 B: 46 C: 47 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 75: A: 30 B: 47 C: 31 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 76: A: 31 B: 47 C: 48 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 77: A: 31 B: 48 C: 32 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 78: A: 32 B: 48 C: 33 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 79: A: 32 B: 33 C: 17 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 80: A: 33 B: 49 C: 50 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 81: A: 33 B: 50 C: 34 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 82: A: 34 B: 50 C: 51 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 83: A: 34 B: 51 C: 35 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 84: A: 35 B: 51 C: 52 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 85: A: 35 B: 52 C: 36 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 86: A: 36 B: 52 C: 53 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 87: A: 36 B: 53 C: 37 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 88: A: 37 B: 53 C: 54 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 89: A: 37 B: 54 C: 38 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 90: A: 38 B: 54 C: 55 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 91: A: 38 B: 55 C: 39 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 92: A: 39 B: 55 C: 56 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 93: A: 39 B: 56 C: 40 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 94: A: 40 B: 56 C: 57 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 95: A: 40 B: 57 C: 41 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 96: A: 41 B: 57 C: 58 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 97: A: 41 B: 58 C: 42 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 98: A: 42 B: 58 C: 59 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 99: A: 42 B: 59 C: 43 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 100: A: 43 B: 59 C: 60 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 101: A: 43 B: 60 C: 44 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 102: A: 44 B: 60 C: 61 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 103: A: 44 B: 61 C: 45 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 104: A: 45 B: 61 C: 62 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 105: A: 45 B: 62 C: 46 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 106: A: 46 B: 62 C: 63 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 107: A: 46 B: 63 C: 47 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 108: A: 47 B: 63 C: 64 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 109: A: 47 B: 64 C: 48 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 110: A: 48 B: 64 C: 49 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 111: A: 48 B: 49 C: 33 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 112: A: 49 B: 65 C: 66 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 113: A: 49 B: 66 C: 50 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 114: A: 50 B: 66 C: 67 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 115: A: 50 B: 67 C: 51 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 116: A: 51 B: 67 C: 68 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 117: A: 51 B: 68 C: 52 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 118: A: 52 B: 68 C: 69 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 119: A: 52 B: 69 C: 53 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 120: A: 53 B: 69 C: 70 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 121: A: 53 B: 70 C: 54 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 122: A: 54 B: 70 C: 71 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 123: A: 54 B: 71 C: 55 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 124: A: 55 B: 71 C: 72 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 125: A: 55 B: 72 C: 56 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 126: A: 56 B: 72 C: 73 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 127: A: 56 B: 73 C: 57 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 128: A: 57 B: 73 C: 74 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 129: A: 57 B: 74 C: 58 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 130: A: 58 B: 74 C: 75 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 131: A: 58 B: 75 C: 59 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 132: A: 59 B: 75 C: 76 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 133: A: 59 B: 76 C: 60 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 134: A: 60 B: 76 C: 77 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 135: A: 60 B: 77 C: 61 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 136: A: 61 B: 77 C: 78 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 137: A: 61 B: 78 C: 62 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 138: A: 62 B: 78 C: 79 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 139: A: 62 B: 79 C: 63 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 140: A: 63 B: 79 C: 80 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 141: A: 63 B: 80 C: 64 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 142: A: 64 B: 80 C: 65 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 143: A: 64 B: 65 C: 49 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 144: A: 65 B: 81 C: 82 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 145: A: 65 B: 82 C: 66 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 146: A: 66 B: 82 C: 83 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 147: A: 66 B: 83 C: 67 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 148: A: 67 B: 83 C: 84 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 149: A: 67 B: 84 C: 68 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 150: A: 68 B: 84 C: 85 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 151: A: 68 B: 85 C: 69 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 152: A: 69 B: 85 C: 86 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 153: A: 69 B: 86 C: 70 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 154: A: 70 B: 86 C: 87 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 155: A: 70 B: 87 C: 71 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 156: A: 71 B: 87 C: 88 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 157: A: 71 B: 88 C: 72 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 158: A: 72 B: 88 C: 89 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 159: A: 72 B: 89 C: 73 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 160: A: 73 B: 89 C: 90 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 161: A: 73 B: 90 C: 74 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 162: A: 74 B: 90 C: 91 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 163: A: 74 B: 91 C: 75 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 164: A: 75 B: 91 C: 92 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 165: A: 75 B: 92 C: 76 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 166: A: 76 B: 92 C: 93 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 167: A: 76 B: 93 C: 77 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 168: A: 77 B: 93 C: 94 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 169: A: 77 B: 94 C: 78 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 170: A: 78 B: 94 C: 95 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 171: A: 78 B: 95 C: 79 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 172: A: 79 B: 95 C: 96 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 173: A: 79 B: 96 C: 80 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 174: A: 80 B: 96 C: 81 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 175: A: 80 B: 81 C: 65 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 176: A: 81 B: 97 C: 98 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 177: A: 81 B: 98 C: 82 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 178: A: 82 B: 98 C: 99 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 179: A: 82 B: 99 C: 83 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 180: A: 83 B: 99 C: 100 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 181: A: 83 B: 100 C: 84 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 182: A: 84 B: 100 C: 101 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 183: A: 84 B: 101 C: 85 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 184: A: 85 B: 101 C: 102 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 185: A: 85 B: 102 C: 86 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 186: A: 86 B: 102 C: 103 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 187: A: 86 B: 103 C: 87 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 188: A: 87 B: 103 C: 104 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 189: A: 87 B: 104 C: 88 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 190: A: 88 B: 104 C: 105 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 191: A: 88 B: 105 C: 89 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 192: A: 89 B: 105 C: 106 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 193: A: 89 B: 106 C: 90 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 194: A: 90 B: 106 C: 107 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 195: A: 90 B: 107 C: 91 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 196: A: 91 B: 107 C: 108 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 197: A: 91 B: 108 C: 92 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 198: A: 92 B: 108 C: 109 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 199: A: 92 B: 109 C: 93 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 200: A: 93 B: 109 C: 110 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 201: A: 93 B: 110 C: 94 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 202: A: 94 B: 110 C: 111 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 203: A: 94 B: 111 C: 95 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 204: A: 95 B: 111 C: 112 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 205: A: 95 B: 112 C: 96 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 206: A: 96 B: 112 C: 97 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 207: A: 96 B: 97 C: 81 AB: 0 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 208: A: 113 B: 98 C: 97 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 209: A: 113 B: 99 C: 98 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 210: A: 113 B: 100 C: 99 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 211: A: 113 B: 101 C: 100 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 212: A: 113 B: 102 C: 101 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 213: A: 113 B: 103 C: 102 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 214: A: 113 B: 104 C: 103 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 215: A: 113 B: 105 C: 104 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 216: A: 113 B: 106 C: 105 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 217: A: 113 B: 107 C: 106 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 218: A: 113 B: 108 C: 107 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 219: A: 113 B: 109 C: 108 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 220: A: 113 B: 110 C: 109 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 221: A: 113 B: 111 C: 110 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 222: A: 113 B: 112 C: 111 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + *MESH_FACE 223: A: 113 B: 97 C: 112 AB: 1 BC: 1 CA: 1 *MESH_SMOOTHING 1 *MESH_MTLID 1 + } + *MESH_NUMTVERTEX 164 + *MESH_TVERTLIST { + *MESH_TVERT 0 0.9688 1.0000 0.9990 + *MESH_TVERT 1 1.0000 0.8750 0.9990 + *MESH_TVERT 2 0.0625 0.8750 0.9990 + *MESH_TVERT 3 0.1250 0.8750 0.9990 + *MESH_TVERT 4 0.1875 0.8750 0.9990 + *MESH_TVERT 5 0.2500 0.8750 0.9990 + *MESH_TVERT 6 0.3125 0.8750 0.9990 + *MESH_TVERT 7 0.3750 0.8750 0.9990 + *MESH_TVERT 8 0.4375 0.8750 0.9990 + *MESH_TVERT 9 0.5000 0.8750 0.9990 + *MESH_TVERT 10 0.5625 0.8750 0.9990 + *MESH_TVERT 11 0.6250 0.8750 0.9990 + *MESH_TVERT 12 0.6875 0.8750 0.9990 + *MESH_TVERT 13 0.7500 0.8750 0.9990 + *MESH_TVERT 14 0.8125 0.8750 0.9990 + *MESH_TVERT 15 0.8750 0.8750 0.9990 + *MESH_TVERT 16 0.9375 0.8750 0.9990 + *MESH_TVERT 17 1.0000 0.7500 0.9990 + *MESH_TVERT 18 0.0625 0.7500 0.9990 + *MESH_TVERT 19 0.1250 0.7500 0.9990 + *MESH_TVERT 20 0.1875 0.7500 0.9990 + *MESH_TVERT 21 0.2500 0.7500 0.9990 + *MESH_TVERT 22 0.3125 0.7500 0.9990 + *MESH_TVERT 23 0.3750 0.7500 0.9990 + *MESH_TVERT 24 0.4375 0.7500 0.9990 + *MESH_TVERT 25 0.5000 0.7500 0.9990 + *MESH_TVERT 26 0.5625 0.7500 0.9990 + *MESH_TVERT 27 0.6250 0.7500 0.9990 + *MESH_TVERT 28 0.6875 0.7500 0.9990 + *MESH_TVERT 29 0.7500 0.7500 0.9990 + *MESH_TVERT 30 0.8125 0.7500 0.9990 + *MESH_TVERT 31 0.8750 0.7500 0.9990 + *MESH_TVERT 32 0.9375 0.7500 0.9990 + *MESH_TVERT 33 1.0000 0.6250 0.9990 + *MESH_TVERT 34 0.0625 0.6250 0.9990 + *MESH_TVERT 35 0.1250 0.6250 0.9990 + *MESH_TVERT 36 0.1875 0.6250 0.9990 + *MESH_TVERT 37 0.2500 0.6250 0.9990 + *MESH_TVERT 38 0.3125 0.6250 0.9990 + *MESH_TVERT 39 0.3750 0.6250 0.9990 + *MESH_TVERT 40 0.4375 0.6250 0.9990 + *MESH_TVERT 41 0.5000 0.6250 0.9990 + *MESH_TVERT 42 0.5625 0.6250 0.9990 + *MESH_TVERT 43 0.6250 0.6250 0.9990 + *MESH_TVERT 44 0.6875 0.6250 0.9990 + *MESH_TVERT 45 0.7500 0.6250 0.9990 + *MESH_TVERT 46 0.8125 0.6250 0.9990 + *MESH_TVERT 47 0.8750 0.6250 0.9990 + *MESH_TVERT 48 0.9375 0.6250 0.9990 + *MESH_TVERT 49 1.0000 0.5000 0.9990 + *MESH_TVERT 50 0.0625 0.5000 0.9990 + *MESH_TVERT 51 0.1250 0.5000 0.9990 + *MESH_TVERT 52 0.1875 0.5000 0.9990 + *MESH_TVERT 53 0.2500 0.5000 0.9990 + *MESH_TVERT 54 0.3125 0.5000 0.9990 + *MESH_TVERT 55 0.3750 0.5000 0.9990 + *MESH_TVERT 56 0.4375 0.5000 0.9990 + *MESH_TVERT 57 0.5000 0.5000 0.9990 + *MESH_TVERT 58 0.5625 0.5000 0.9990 + *MESH_TVERT 59 0.6250 0.5000 0.9990 + *MESH_TVERT 60 0.6875 0.5000 0.9990 + *MESH_TVERT 61 0.7500 0.5000 0.9990 + *MESH_TVERT 62 0.8125 0.5000 0.9990 + *MESH_TVERT 63 0.8750 0.5000 0.9990 + *MESH_TVERT 64 0.9375 0.5000 0.9990 + *MESH_TVERT 65 1.0000 0.3750 0.9990 + *MESH_TVERT 66 0.0625 0.3750 0.9990 + *MESH_TVERT 67 0.1250 0.3750 0.9990 + *MESH_TVERT 68 0.1875 0.3750 0.9990 + *MESH_TVERT 69 0.2500 0.3750 0.9990 + *MESH_TVERT 70 0.3125 0.3750 0.9990 + *MESH_TVERT 71 0.3750 0.3750 0.9990 + *MESH_TVERT 72 0.4375 0.3750 0.9990 + *MESH_TVERT 73 0.5000 0.3750 0.9990 + *MESH_TVERT 74 0.5625 0.3750 0.9990 + *MESH_TVERT 75 0.6250 0.3750 0.9990 + *MESH_TVERT 76 0.6875 0.3750 0.9990 + *MESH_TVERT 77 0.7500 0.3750 0.9990 + *MESH_TVERT 78 0.8125 0.3750 0.9990 + *MESH_TVERT 79 0.8750 0.3750 0.9990 + *MESH_TVERT 80 0.9375 0.3750 0.9990 + *MESH_TVERT 81 1.0000 0.2500 0.9990 + *MESH_TVERT 82 0.0625 0.2500 0.9990 + *MESH_TVERT 83 0.1250 0.2500 0.9990 + *MESH_TVERT 84 0.1875 0.2500 0.9990 + *MESH_TVERT 85 0.2500 0.2500 0.9990 + *MESH_TVERT 86 0.3125 0.2500 0.9990 + *MESH_TVERT 87 0.3750 0.2500 0.9990 + *MESH_TVERT 88 0.4375 0.2500 0.9990 + *MESH_TVERT 89 0.5000 0.2500 0.9990 + *MESH_TVERT 90 0.5625 0.2500 0.9990 + *MESH_TVERT 91 0.6250 0.2500 0.9990 + *MESH_TVERT 92 0.6875 0.2500 0.9990 + *MESH_TVERT 93 0.7500 0.2500 0.9990 + *MESH_TVERT 94 0.8125 0.2500 0.9990 + *MESH_TVERT 95 0.8750 0.2500 0.9990 + *MESH_TVERT 96 0.9375 0.2500 0.9990 + *MESH_TVERT 97 1.0000 0.1250 0.9990 + *MESH_TVERT 98 0.0625 0.1250 0.9990 + *MESH_TVERT 99 0.1250 0.1250 0.9990 + *MESH_TVERT 100 0.1875 0.1250 0.9990 + *MESH_TVERT 101 0.2500 0.1250 0.9990 + *MESH_TVERT 102 0.3125 0.1250 0.9990 + *MESH_TVERT 103 0.3750 0.1250 0.9990 + *MESH_TVERT 104 0.4375 0.1250 0.9990 + *MESH_TVERT 105 0.5000 0.1250 0.9990 + *MESH_TVERT 106 0.5625 0.1250 0.9990 + *MESH_TVERT 107 0.6250 0.1250 0.9990 + *MESH_TVERT 108 0.6875 0.1250 0.9990 + *MESH_TVERT 109 0.7500 0.1250 0.9990 + *MESH_TVERT 110 0.8125 0.1250 0.9990 + *MESH_TVERT 111 0.8750 0.1250 0.9990 + *MESH_TVERT 112 0.9375 0.1250 0.9990 + *MESH_TVERT 113 0.9688 0.0000 0.9990 + *MESH_TVERT 114 0.0312 1.0000 0.9990 + *MESH_TVERT 115 -0.0000 0.8750 0.9990 + *MESH_TVERT 116 0.0937 1.0000 0.9990 + *MESH_TVERT 117 0.1562 1.0000 0.9990 + *MESH_TVERT 118 0.2187 1.0000 0.9990 + *MESH_TVERT 119 0.2813 1.0000 0.9990 + *MESH_TVERT 120 0.3438 1.0000 0.9990 + *MESH_TVERT 121 0.4063 1.0000 0.9990 + *MESH_TVERT 122 0.4688 1.0000 0.9990 + *MESH_TVERT 123 0.5313 1.0000 0.9990 + *MESH_TVERT 124 0.5938 1.0000 0.9990 + *MESH_TVERT 125 0.6563 1.0000 0.9990 + *MESH_TVERT 126 0.7188 1.0000 0.9990 + *MESH_TVERT 127 0.7813 1.0000 0.9990 + *MESH_TVERT 128 0.8438 1.0000 0.9990 + *MESH_TVERT 129 0.9063 1.0000 0.9990 + *MESH_TVERT 130 -0.0000 0.8750 0.9990 + *MESH_TVERT 131 -0.0000 0.7500 0.9990 + *MESH_TVERT 132 -0.0000 0.8750 0.9990 + *MESH_TVERT 133 -0.0000 0.7500 0.9990 + *MESH_TVERT 134 -0.0000 0.6250 0.9990 + *MESH_TVERT 135 -0.0000 0.7500 0.9990 + *MESH_TVERT 136 -0.0000 0.6250 0.9990 + *MESH_TVERT 137 -0.0000 0.5000 0.9990 + *MESH_TVERT 138 -0.0000 0.6250 0.9990 + *MESH_TVERT 139 -0.0000 0.5000 0.9990 + *MESH_TVERT 140 -0.0000 0.3750 0.9990 + *MESH_TVERT 141 -0.0000 0.5000 0.9990 + *MESH_TVERT 142 -0.0000 0.3750 0.9990 + *MESH_TVERT 143 -0.0000 0.2500 0.9990 + *MESH_TVERT 144 -0.0000 0.3750 0.9990 + *MESH_TVERT 145 -0.0000 0.2500 0.9990 + *MESH_TVERT 146 -0.0000 0.1250 0.9990 + *MESH_TVERT 147 -0.0000 0.2500 0.9990 + *MESH_TVERT 148 0.0312 0.0000 0.9990 + *MESH_TVERT 149 -0.0000 0.1250 0.9990 + *MESH_TVERT 150 0.0937 0.0000 0.9990 + *MESH_TVERT 151 0.1562 0.0000 0.9990 + *MESH_TVERT 152 0.2187 0.0000 0.9990 + *MESH_TVERT 153 0.2812 0.0000 0.9990 + *MESH_TVERT 154 0.3438 0.0000 0.9990 + *MESH_TVERT 155 0.4063 0.0000 0.9990 + *MESH_TVERT 156 0.4688 0.0000 0.9990 + *MESH_TVERT 157 0.5313 0.0000 0.9990 + *MESH_TVERT 158 0.5938 0.0000 0.9990 + *MESH_TVERT 159 0.6563 0.0000 0.9990 + *MESH_TVERT 160 0.7188 0.0000 0.9990 + *MESH_TVERT 161 0.7813 0.0000 0.9990 + *MESH_TVERT 162 0.8438 0.0000 0.9990 + *MESH_TVERT 163 0.9063 0.0000 0.9990 + } + *MESH_NUMTVFACES 224 + *MESH_TFACELIST { + *MESH_TFACE 0 114 115 2 + *MESH_TFACE 1 116 2 3 + *MESH_TFACE 2 117 3 4 + *MESH_TFACE 3 118 4 5 + *MESH_TFACE 4 119 5 6 + *MESH_TFACE 5 120 6 7 + *MESH_TFACE 6 121 7 8 + *MESH_TFACE 7 122 8 9 + *MESH_TFACE 8 123 9 10 + *MESH_TFACE 9 124 10 11 + *MESH_TFACE 10 125 11 12 + *MESH_TFACE 11 126 12 13 + *MESH_TFACE 12 127 13 14 + *MESH_TFACE 13 128 14 15 + *MESH_TFACE 14 129 15 16 + *MESH_TFACE 15 0 16 1 + *MESH_TFACE 16 130 131 18 + *MESH_TFACE 17 132 18 2 + *MESH_TFACE 18 2 18 19 + *MESH_TFACE 19 2 19 3 + *MESH_TFACE 20 3 19 20 + *MESH_TFACE 21 3 20 4 + *MESH_TFACE 22 4 20 21 + *MESH_TFACE 23 4 21 5 + *MESH_TFACE 24 5 21 22 + *MESH_TFACE 25 5 22 6 + *MESH_TFACE 26 6 22 23 + *MESH_TFACE 27 6 23 7 + *MESH_TFACE 28 7 23 24 + *MESH_TFACE 29 7 24 8 + *MESH_TFACE 30 8 24 25 + *MESH_TFACE 31 8 25 9 + *MESH_TFACE 32 9 25 26 + *MESH_TFACE 33 9 26 10 + *MESH_TFACE 34 10 26 27 + *MESH_TFACE 35 10 27 11 + *MESH_TFACE 36 11 27 28 + *MESH_TFACE 37 11 28 12 + *MESH_TFACE 38 12 28 29 + *MESH_TFACE 39 12 29 13 + *MESH_TFACE 40 13 29 30 + *MESH_TFACE 41 13 30 14 + *MESH_TFACE 42 14 30 31 + *MESH_TFACE 43 14 31 15 + *MESH_TFACE 44 15 31 32 + *MESH_TFACE 45 15 32 16 + *MESH_TFACE 46 16 32 17 + *MESH_TFACE 47 16 17 1 + *MESH_TFACE 48 133 134 34 + *MESH_TFACE 49 135 34 18 + *MESH_TFACE 50 18 34 35 + *MESH_TFACE 51 18 35 19 + *MESH_TFACE 52 19 35 36 + *MESH_TFACE 53 19 36 20 + *MESH_TFACE 54 20 36 37 + *MESH_TFACE 55 20 37 21 + *MESH_TFACE 56 21 37 38 + *MESH_TFACE 57 21 38 22 + *MESH_TFACE 58 22 38 39 + *MESH_TFACE 59 22 39 23 + *MESH_TFACE 60 23 39 40 + *MESH_TFACE 61 23 40 24 + *MESH_TFACE 62 24 40 41 + *MESH_TFACE 63 24 41 25 + *MESH_TFACE 64 25 41 42 + *MESH_TFACE 65 25 42 26 + *MESH_TFACE 66 26 42 43 + *MESH_TFACE 67 26 43 27 + *MESH_TFACE 68 27 43 44 + *MESH_TFACE 69 27 44 28 + *MESH_TFACE 70 28 44 45 + *MESH_TFACE 71 28 45 29 + *MESH_TFACE 72 29 45 46 + *MESH_TFACE 73 29 46 30 + *MESH_TFACE 74 30 46 47 + *MESH_TFACE 75 30 47 31 + *MESH_TFACE 76 31 47 48 + *MESH_TFACE 77 31 48 32 + *MESH_TFACE 78 32 48 33 + *MESH_TFACE 79 32 33 17 + *MESH_TFACE 80 136 137 50 + *MESH_TFACE 81 138 50 34 + *MESH_TFACE 82 34 50 51 + *MESH_TFACE 83 34 51 35 + *MESH_TFACE 84 35 51 52 + *MESH_TFACE 85 35 52 36 + *MESH_TFACE 86 36 52 53 + *MESH_TFACE 87 36 53 37 + *MESH_TFACE 88 37 53 54 + *MESH_TFACE 89 37 54 38 + *MESH_TFACE 90 38 54 55 + *MESH_TFACE 91 38 55 39 + *MESH_TFACE 92 39 55 56 + *MESH_TFACE 93 39 56 40 + *MESH_TFACE 94 40 56 57 + *MESH_TFACE 95 40 57 41 + *MESH_TFACE 96 41 57 58 + *MESH_TFACE 97 41 58 42 + *MESH_TFACE 98 42 58 59 + *MESH_TFACE 99 42 59 43 + *MESH_TFACE 100 43 59 60 + *MESH_TFACE 101 43 60 44 + *MESH_TFACE 102 44 60 61 + *MESH_TFACE 103 44 61 45 + *MESH_TFACE 104 45 61 62 + *MESH_TFACE 105 45 62 46 + *MESH_TFACE 106 46 62 63 + *MESH_TFACE 107 46 63 47 + *MESH_TFACE 108 47 63 64 + *MESH_TFACE 109 47 64 48 + *MESH_TFACE 110 48 64 49 + *MESH_TFACE 111 48 49 33 + *MESH_TFACE 112 139 140 66 + *MESH_TFACE 113 141 66 50 + *MESH_TFACE 114 50 66 67 + *MESH_TFACE 115 50 67 51 + *MESH_TFACE 116 51 67 68 + *MESH_TFACE 117 51 68 52 + *MESH_TFACE 118 52 68 69 + *MESH_TFACE 119 52 69 53 + *MESH_TFACE 120 53 69 70 + *MESH_TFACE 121 53 70 54 + *MESH_TFACE 122 54 70 71 + *MESH_TFACE 123 54 71 55 + *MESH_TFACE 124 55 71 72 + *MESH_TFACE 125 55 72 56 + *MESH_TFACE 126 56 72 73 + *MESH_TFACE 127 56 73 57 + *MESH_TFACE 128 57 73 74 + *MESH_TFACE 129 57 74 58 + *MESH_TFACE 130 58 74 75 + *MESH_TFACE 131 58 75 59 + *MESH_TFACE 132 59 75 76 + *MESH_TFACE 133 59 76 60 + *MESH_TFACE 134 60 76 77 + *MESH_TFACE 135 60 77 61 + *MESH_TFACE 136 61 77 78 + *MESH_TFACE 137 61 78 62 + *MESH_TFACE 138 62 78 79 + *MESH_TFACE 139 62 79 63 + *MESH_TFACE 140 63 79 80 + *MESH_TFACE 141 63 80 64 + *MESH_TFACE 142 64 80 65 + *MESH_TFACE 143 64 65 49 + *MESH_TFACE 144 142 143 82 + *MESH_TFACE 145 144 82 66 + *MESH_TFACE 146 66 82 83 + *MESH_TFACE 147 66 83 67 + *MESH_TFACE 148 67 83 84 + *MESH_TFACE 149 67 84 68 + *MESH_TFACE 150 68 84 85 + *MESH_TFACE 151 68 85 69 + *MESH_TFACE 152 69 85 86 + *MESH_TFACE 153 69 86 70 + *MESH_TFACE 154 70 86 87 + *MESH_TFACE 155 70 87 71 + *MESH_TFACE 156 71 87 88 + *MESH_TFACE 157 71 88 72 + *MESH_TFACE 158 72 88 89 + *MESH_TFACE 159 72 89 73 + *MESH_TFACE 160 73 89 90 + *MESH_TFACE 161 73 90 74 + *MESH_TFACE 162 74 90 91 + *MESH_TFACE 163 74 91 75 + *MESH_TFACE 164 75 91 92 + *MESH_TFACE 165 75 92 76 + *MESH_TFACE 166 76 92 93 + *MESH_TFACE 167 76 93 77 + *MESH_TFACE 168 77 93 94 + *MESH_TFACE 169 77 94 78 + *MESH_TFACE 170 78 94 95 + *MESH_TFACE 171 78 95 79 + *MESH_TFACE 172 79 95 96 + *MESH_TFACE 173 79 96 80 + *MESH_TFACE 174 80 96 81 + *MESH_TFACE 175 80 81 65 + *MESH_TFACE 176 145 146 98 + *MESH_TFACE 177 147 98 82 + *MESH_TFACE 178 82 98 99 + *MESH_TFACE 179 82 99 83 + *MESH_TFACE 180 83 99 100 + *MESH_TFACE 181 83 100 84 + *MESH_TFACE 182 84 100 101 + *MESH_TFACE 183 84 101 85 + *MESH_TFACE 184 85 101 102 + *MESH_TFACE 185 85 102 86 + *MESH_TFACE 186 86 102 103 + *MESH_TFACE 187 86 103 87 + *MESH_TFACE 188 87 103 104 + *MESH_TFACE 189 87 104 88 + *MESH_TFACE 190 88 104 105 + *MESH_TFACE 191 88 105 89 + *MESH_TFACE 192 89 105 106 + *MESH_TFACE 193 89 106 90 + *MESH_TFACE 194 90 106 107 + *MESH_TFACE 195 90 107 91 + *MESH_TFACE 196 91 107 108 + *MESH_TFACE 197 91 108 92 + *MESH_TFACE 198 92 108 109 + *MESH_TFACE 199 92 109 93 + *MESH_TFACE 200 93 109 110 + *MESH_TFACE 201 93 110 94 + *MESH_TFACE 202 94 110 111 + *MESH_TFACE 203 94 111 95 + *MESH_TFACE 204 95 111 112 + *MESH_TFACE 205 95 112 96 + *MESH_TFACE 206 96 112 97 + *MESH_TFACE 207 96 97 81 + *MESH_TFACE 208 148 98 149 + *MESH_TFACE 209 150 99 98 + *MESH_TFACE 210 151 100 99 + *MESH_TFACE 211 152 101 100 + *MESH_TFACE 212 153 102 101 + *MESH_TFACE 213 154 103 102 + *MESH_TFACE 214 155 104 103 + *MESH_TFACE 215 156 105 104 + *MESH_TFACE 216 157 106 105 + *MESH_TFACE 217 158 107 106 + *MESH_TFACE 218 159 108 107 + *MESH_TFACE 219 160 109 108 + *MESH_TFACE 220 161 110 109 + *MESH_TFACE 221 162 111 110 + *MESH_TFACE 222 163 112 111 + *MESH_TFACE 223 113 97 112 + } + } + *PROP_MOTIONBLUR 0 + *PROP_CASTSHADOW 1 + *PROP_RECVSHADOW 1 + *MATERIAL_REF 3 +} diff --git a/neo/CMakeLists.txt b/neo/CMakeLists.txt index afcdda0e1..a2bb329b3 100644 --- a/neo/CMakeLists.txt +++ b/neo/CMakeLists.txt @@ -826,10 +826,83 @@ set(src_imgui ${src_imgui} libs/imgui/imgui_widgets.cpp libs/imgui/imgui_demo.cpp - + + libs/ImGuiColorTextEdit/TextEditor.h + libs/ImGuiColorTextEdit/TextEditor.cpp + sys/sys_imgui.h sys/sys_imgui.cpp sys/imgui_savestyle.cpp + + tools/imgui/ImGuiTools.cpp + tools/imgui/util/ImGui_IdWidgets.h + tools/imgui/util/ImGui_IdWidgets.cpp + tools/imgui/util/SyntaxRichEditCtrl.h + tools/imgui/util/SyntaxRichEditCtrl.cpp + tools/imgui/util/TreeCtrl.h + tools/imgui/util/TreeCtrl.cpp + tools/imgui/util/RegistryOptions.h + tools/imgui/util/RegistryOptions.cpp + + tools/imgui/afeditor/AfEditor.h + tools/imgui/afeditor/AfEditor.cpp + tools/imgui/afeditor/AfBodyEditor.h + tools/imgui/afeditor/AfBodyEditor.cpp + tools/imgui/afeditor/AfConstraintEditor.h + tools/imgui/afeditor/AfConstraintEditor.cpp + tools/imgui/afeditor/AfPropertyEditor.h + tools/imgui/afeditor/AfPropertyEditor.cpp + + tools/imgui/lighteditor/LightEditor.h + tools/imgui/lighteditor/LightEditor.cpp + tools/imgui/pdaeditor/PDAEditor.h + tools/imgui/pdaeditor/PDAEditor.cpp + tools/imgui/particleeditor/ParticleEditor.h + tools/imgui/particleeditor/ParticleEditor.cpp + tools/imgui/scripteditor/ScriptEditor.h + tools/imgui/scripteditor/ScriptEditor.cpp + + tools/imgui/decleditor/PathTreeCtrl.h + tools/imgui/decleditor/PathTreeCtrl.cpp + tools/imgui/decleditor/DeclBrowser.h + tools/imgui/decleditor/DeclBrowser.cpp + tools/imgui/decleditor/DeclEditor.h + tools/imgui/decleditor/DeclEditor.cpp + tools/imgui/decleditor/DeclNew.h + tools/imgui/decleditor/DeclNew.cpp + + tools/imgui/materialeditor/MaterialEditor.h + tools/imgui/materialeditor/MaterialEditor.cpp + tools/imgui/materialeditor/MaterialDef.h + tools/imgui/materialeditor/MaterialDef.cpp + tools/imgui/materialeditor/ConsoleView.h + tools/imgui/materialeditor/ConsoleView.cpp + tools/imgui/materialeditor/MaterialModifier.h + tools/imgui/materialeditor/MaterialModifier.cpp + tools/imgui/materialeditor/MaterialDoc.h + tools/imgui/materialeditor/MaterialDoc.cpp + tools/imgui/materialeditor/MaterialDocManager.h + tools/imgui/materialeditor/MaterialDocManager.cpp + tools/imgui/materialeditor/MaterialView.h + tools/imgui/materialeditor/MaterialView.cpp + tools/imgui/materialeditor/MaterialEditView.h + tools/imgui/materialeditor/MaterialEditView.cpp + tools/imgui/materialeditor/MaterialTreeView.h + tools/imgui/materialeditor/MaterialTreeView.cpp + tools/imgui/materialeditor/MaterialPropTreeView.h + tools/imgui/materialeditor/MaterialPropTreeView.cpp + tools/imgui/materialeditor/MaterialPreviewPropView.h + tools/imgui/materialeditor/MaterialPreviewPropView.cpp + tools/imgui/materialeditor/MaterialPreviewView.h + tools/imgui/materialeditor/MaterialPreviewView.cpp + tools/imgui/materialeditor/StageView.h + tools/imgui/materialeditor/StageView.cpp + tools/imgui/materialeditor/MEOptions.h + tools/imgui/materialeditor/MEOptions.cpp + tools/imgui/materialeditor/MEMainFrame.h + tools/imgui/materialeditor/MEMainFrame.cpp + tools/imgui/materialeditor/FindDialog.h + tools/imgui/materialeditor/FindDialog.cpp ) else() set(src_imgui sys/sys_imgui.h) diff --git a/neo/framework/Common.cpp b/neo/framework/Common.cpp index 668b25abd..89b80686d 100644 --- a/neo/framework/Common.cpp +++ b/neo/framework/Common.cpp @@ -984,6 +984,24 @@ idCommonLocal::InitTool ================= */ void idCommonLocal::InitTool( const toolFlag_t tool, const idDict *dict ) { +#ifndef IMGUI_DISABLE + if ( tool & EDITOR_SOUND ) { + //SoundEditorInit( dict ); + } else if ( tool & EDITOR_LIGHT ) { + ImGuiTools::LightEditorInit( dict ); + } else if ( tool & EDITOR_PARTICLE ) { + ImGuiTools::ParticleEditorInit( dict ); + } else if ( tool & EDITOR_AF ) { + ImGuiTools::AfEditorInit(); // TODO: dict ? + } else if ( tool & EDITOR_PDA ) { + ImGuiTools::PDAEditorInit( dict ); + } else if ( tool & EDITOR_SCRIPT ) { + ImGuiTools::ScriptEditorInit( dict ); + } else if ( tool & EDITOR_DECL ) { + ImGuiTools::DeclBrowserInit( dict ); + } +#endif + #ifdef ID_ALLOW_TOOLS if ( tool & EDITOR_SOUND ) { SoundEditorInit( dict ); @@ -1174,17 +1192,6 @@ Com_EditGUIs_f static void Com_EditGUIs_f( const idCmdArgs &args ) { GUIEditorInit(); } - -/* -============= -Com_MaterialEditor_f -============= -*/ -static void Com_MaterialEditor_f( const idCmdArgs &args ) { - // Turn off sounds - soundSystem->SetMute( true ); - MaterialEditorInit(); -} #endif // ID_ALLOW_TOOLS /* @@ -1228,52 +1235,83 @@ static void PrintMemInfo_f( const idCmdArgs &args ) { fileSystem->CloseFile( f ); } -#ifdef ID_ALLOW_TOOLS + /* ================== Com_EditLights_f ================== */ static void Com_EditLights_f( const idCmdArgs &args ) { +#ifndef IMGUI_DISABLE + D3::ImGuiHooks::ShowInfoOverlay( "Shoot a light to open it in the Light Editor" ); + cvarSystem->SetCVarInteger( "g_editEntityMode", 1 ); +#elif defined(ID_ALLOW_TOOLS) LightEditorInit( NULL ); cvarSystem->SetCVarInteger( "g_editEntityMode", 1 ); +#else + common->Warning( "Editors not available because dhewm3 was built without ImGui or MFC Tools" ); +#endif } /* ================== -Com_EditSounds_f +Com_EditPDAs_f ================== */ -static void Com_EditSounds_f( const idCmdArgs &args ) { - SoundEditorInit( NULL ); - cvarSystem->SetCVarInteger( "g_editEntityMode", 2 ); +static void Com_EditPDAs_f( const idCmdArgs &args ) { +#ifndef IMGUI_DISABLE + ImGuiTools::PDAEditorInit( NULL ); +#elif defined(ID_ALLOW_TOOLS) + PDAEditorInit( NULL ); +#else + common->Warning( "Editors not available because dhewm3 was built without ImGui or MFC Tools" ); +#endif } /* ================== -Com_EditDecls_f +Com_EditAFs_f ================== */ -static void Com_EditDecls_f( const idCmdArgs &args ) { - DeclBrowserInit( NULL ); +static void Com_EditAFs_f( const idCmdArgs &args ) { + // TODO: cvarSystem->SetCVarInteger( "g_editEntityMode", 3 ); ? +#ifndef IMGUI_DISABLE + ImGuiTools::AfEditorInit(); +#elif defined(ID_ALLOW_TOOLS) + AFEditorInit( NULL ); +#else + common->Warning( "Editors not available because dhewm3 was built without ImGui or MFC Tools" ); +#endif } /* ================== -Com_EditAFs_f +Com_EditParticles_f ================== */ -static void Com_EditAFs_f( const idCmdArgs &args ) { - AFEditorInit( NULL ); +static void Com_EditParticles_f(const idCmdArgs& args) { +#ifndef IMGUI_DISABLE + ImGuiTools::ParticleEditorInit( NULL ); +#elif defined(ID_ALLOW_TOOLS) + ParticleEditorInit(NULL); +#else + common->Warning( "Editors not available because dhewm3 was built without ImGui or MFC Tools" ); +#endif } /* ================== -Com_EditParticles_f +Com_EditDecls_f ================== */ -static void Com_EditParticles_f( const idCmdArgs &args ) { - ParticleEditorInit( NULL ); +static void Com_EditDecls_f( const idCmdArgs &args ) { +#ifndef IMGUI_DISABLE + ImGuiTools::DeclBrowserInit( NULL ); +#elif defined(ID_ALLOW_TOOLS) + DeclBrowserInit( NULL ); +#else + common->Warning("Editors not available because dhewm3 was built without ImGui or MFC Tools"); +#endif } /* @@ -1282,17 +1320,44 @@ Com_EditScripts_f ================== */ static void Com_EditScripts_f( const idCmdArgs &args ) { +#ifndef IMGUI_DISABLE + ImGuiTools::ScriptEditorInit( NULL ); +#elif defined(ID_ALLOW_TOOLS) ScriptEditorInit( NULL ); +#else + common->Warning("Editors not available because dhewm3 was built without ImGui or MFC Tools"); +#endif } +/* +============= +Com_MaterialEditor_f +============= +*/ +static void Com_MaterialEditor_f( const idCmdArgs &args ) { + // Turn off sounds + soundSystem->SetMute( true ); +#ifndef IMGUI_DISABLE + ImGuiTools::MaterialEditorInit(); +#elif defined(ID_ALLOW_TOOLS) + MaterialEditorInit(); +#else + common->Warning("Editors not available because dhewm3 was built without ImGui or MFC Tools"); +#endif +} + + +#ifdef ID_ALLOW_TOOLS /* ================== -Com_EditPDAs_f +Com_EditSounds_f ================== */ -static void Com_EditPDAs_f( const idCmdArgs &args ) { - PDAEditorInit( NULL ); +static void Com_EditSounds_f( const idCmdArgs &args ) { + SoundEditorInit( NULL ); + cvarSystem->SetCVarInteger( "g_editEntityMode", 2 ); } + #endif // ID_ALLOW_TOOLS /* @@ -2331,21 +2396,20 @@ void idCommonLocal::InitCommands( void ) { cmdSystem->AddCommand( "roq", RoQFileEncode_f, CMD_FL_TOOL, "encodes a roq file" ); #endif -#ifdef ID_ALLOW_TOOLS // editors - cmdSystem->AddCommand( "editor", Com_Editor_f, CMD_FL_TOOL, "launches the level editor Radiant" ); cmdSystem->AddCommand( "editLights", Com_EditLights_f, CMD_FL_TOOL, "launches the in-game Light Editor" ); - cmdSystem->AddCommand( "editSounds", Com_EditSounds_f, CMD_FL_TOOL, "launches the in-game Sound Editor" ); - cmdSystem->AddCommand( "editDecls", Com_EditDecls_f, CMD_FL_TOOL, "launches the in-game Declaration Editor" ); + cmdSystem->AddCommand( "editPDAs", Com_EditPDAs_f, CMD_FL_TOOL, "launches the in-game PDA Editor" ); cmdSystem->AddCommand( "editAFs", Com_EditAFs_f, CMD_FL_TOOL, "launches the in-game Articulated Figure Editor" ); + cmdSystem->AddCommand( "editDecls", Com_EditDecls_f, CMD_FL_TOOL, "launches the in-game Declaration Editor" ); cmdSystem->AddCommand( "editParticles", Com_EditParticles_f, CMD_FL_TOOL, "launches the in-game Particle Editor" ); cmdSystem->AddCommand( "editScripts", Com_EditScripts_f, CMD_FL_TOOL, "launches the in-game Script Editor" ); - cmdSystem->AddCommand( "editGUIs", Com_EditGUIs_f, CMD_FL_TOOL, "launches the GUI Editor" ); - cmdSystem->AddCommand( "editPDAs", Com_EditPDAs_f, CMD_FL_TOOL, "launches the in-game PDA Editor" ); - cmdSystem->AddCommand( "debugger", Com_ScriptDebugger_f, CMD_FL_TOOL, "launches the Script Debugger" ); - //BSM Nerve: Add support for the material editor cmdSystem->AddCommand( "materialEditor", Com_MaterialEditor_f, CMD_FL_TOOL, "launches the Material Editor" ); +#ifdef ID_ALLOW_TOOLS + cmdSystem->AddCommand( "editor", Com_Editor_f, CMD_FL_TOOL, "launches the level editor Radiant" ); + cmdSystem->AddCommand( "editSounds", Com_EditSounds_f, CMD_FL_TOOL, "launches the in-game Sound Editor" ); + cmdSystem->AddCommand( "editGUIs", Com_EditGUIs_f, CMD_FL_TOOL, "launches the GUI Editor" ); + cmdSystem->AddCommand( "debugger", Com_ScriptDebugger_f, CMD_FL_TOOL, "launches the Script Debugger" ); #endif cmdSystem->AddCommand( "printMemInfo", PrintMemInfo_f, CMD_FL_SYSTEM, "prints memory debugging data" ); diff --git a/neo/framework/Console.cpp b/neo/framework/Console.cpp index 96357022b..409608b5c 100644 --- a/neo/framework/Console.cpp +++ b/neo/framework/Console.cpp @@ -916,8 +916,14 @@ void idConsoleLocal::Print( const char *txt ) { #ifdef ID_ALLOW_TOOLS RadiantPrint( txt ); +#endif - if( com_editors & EDITOR_MATERIAL ) { +#ifndef IMGUI_DISABLE + if (com_editors & EDITOR_MATERIAL) { + ImGuiTools::MaterialEditorPrintConsole(txt); + } +#elif defined(ID_ALLOW_TOOLS) + if (com_editors & EDITOR_MATERIAL) { MaterialEditorPrintConsole(txt); } #endif diff --git a/neo/framework/Dhewm3SettingsMenu.cpp b/neo/framework/Dhewm3SettingsMenu.cpp index 85d04d88d..e72472b34 100644 --- a/neo/framework/Dhewm3SettingsMenu.cpp +++ b/neo/framework/Dhewm3SettingsMenu.cpp @@ -2337,6 +2337,12 @@ static void DrawOtherOptionsMenu() if ( ImGui::Button("Reset") ) { D3::ImGuiHooks::SetScale( -1.0f ); } + ImGuiIO& io = ImGui::GetIO(); + ImGui::CheckboxFlags( "Enable multiple ImGui Viewports", &io.ConfigFlags, ImGuiConfigFlags_ViewportsEnable ); + AddDescrTooltip( "Allows dragging ImGui windows out of the main dhewm3 window. Might not work (well) on all platforms or window managers" ); + + ImGui::CheckboxFlags( "Enable ImGui Docking support", &io.ConfigFlags, ImGuiConfigFlags_DockingEnable ); + AddDescrTooltip( "Allows docking ImGui windows to each other (or the edge of real windows, if dhewm3 enables it, which it currently does not, but might for tools..)" ); int style_idx = imgui_style.GetInteger(); if ( ImGui::Combo( "ImGui Style", &style_idx, "dhewm3\0ImGui Default\0Userstyle\0") ) @@ -2407,11 +2413,12 @@ static void InitDhewm3SettingsMenu() const ImGuiStyle& style = ImGui::GetStyle(); float defaultWidth = ImGui::CalcTextSize( "Control BindingsControl OptionsVideo OptionsAudio OptionsGame OptionsOther Options" ).x; defaultWidth += 2.0f * style.WindowPadding.x + 12.0f * style.FramePadding.x + 5.0f * style.ItemInnerSpacing.x; - ImVec2 displaySize = ImGui::GetIO().DisplaySize; - settingsMenuDefaultSize.x = fminf( defaultWidth, displaySize.x * 0.8f ); - settingsMenuDefaultSize.y = displaySize.y * 0.95f; - - settingsMenuDefaultPos = displaySize * 0.025f; + ImGuiViewport* mainViewPort = ImGui::GetMainViewport(); + ImVec2 viewPortSize = mainViewPort->Size; + settingsMenuDefaultSize.x = fminf( defaultWidth, viewPortSize.x * 0.8f ); + settingsMenuDefaultSize.y = viewPortSize.y * 0.95f; + // NOTE: in ImGui's docking branch, positions are in global (screen/OS) coordinates + settingsMenuDefaultPos = mainViewPort->Pos + viewPortSize * 0.025f; d3settingsWinInitialized = true; } diff --git a/neo/idlib/Str.h b/neo/idlib/Str.h index 79b0aa835..9d5bf7a45 100644 --- a/neo/idlib/Str.h +++ b/neo/idlib/Str.h @@ -29,6 +29,7 @@ If you have questions concerning this license or the applicable additional terms #ifndef __STR_H__ #define __STR_H__ +#include "sys/platform.h" // ID_INLINE #include "idlib/CmdArgs.h" /* diff --git a/neo/libs/ImGuiColorTextEdit/TextEditor.cpp b/neo/libs/ImGuiColorTextEdit/TextEditor.cpp new file mode 100644 index 000000000..cedac5f78 --- /dev/null +++ b/neo/libs/ImGuiColorTextEdit/TextEditor.cpp @@ -0,0 +1,3572 @@ +#include +#include +#include +#include +#include + +#include "TextEditor.h" + +#define IMGUI_DEFINE_MATH_OPERATORS +#include "imgui.h" // for imGui::GetCurrentWindow() + +// TODO +// - multiline comments vs single-line: latter is blocking start of a ML + +template +bool equals(InputIt1 first1, InputIt1 last1, + InputIt2 first2, InputIt2 last2, BinaryPredicate p) +{ + for (; first1 != last1 && first2 != last2; ++first1, ++first2) + { + if (!p(*first1, *first2)) + return false; + } + return first1 == last1 && first2 == last2; +} + +TextEditor::TextEditor() + : mLineSpacing(1.0f) + , mUndoIndex(0) + , mTabSize(4) + , mOverwrite(false) + , mReadOnly(false) + , mWithinRender(false) + , mScrollToCursor(false) + , mScrollToTop(false) + , mTextChanged(false) + , mColorizerEnabled(true) + , mTextStart(20.0f) + , mLeftMargin(10) + , mCursorPositionChanged(false) + , mColorRangeMin(0) + , mColorRangeMax(0) + , mSelectionMode(SelectionMode::Normal) + , mCheckComments(true) + , mHandleKeyboardInputs(true) + , mHandleMouseInputs(true) + , mIgnoreImGuiChild(false) + , mShowWhitespaces(true) + , mStartTime(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()) + , mLastClick(-1.0f) + , mKeyPressData(NULL) + , mKeyPressHandler(NULL) + , mKeyDownHandler(NULL) + , mMouseButtonDownHandler(NULL) + , mGetToolTipHandler(NULL) + , mSetFocus(false) + , mCursorScreenPos(0.0f, 0.0f) + , mShowLineNumber(true) + , mShowCurrentLine(true) +{ + SetPalette(GetDarkPalette()); + SetLanguageDefinition(LanguageDefinition::HLSL()); + mLines.push_back(Line()); + + MarkSaved(); +} + +TextEditor::~TextEditor() +{ +} + +void TextEditor::Focus() { + mSetFocus = true; +} + +void TextEditor::SetHandlers(void* data, textEditKeyPress_t keyPress, textEditKeyDown_t keyDown, textEditMouseButtonDown_t mouseButtonDown, textEditGetToolTip_t getToolTip) { + mKeyPressData = data; + mKeyPressHandler = keyPress; + mKeyDownHandler = keyDown; + mMouseButtonDownHandler = mouseButtonDown; + mGetToolTipHandler = getToolTip; +} + +void TextEditor::SetLanguageDefinition(const LanguageDefinition & aLanguageDef) +{ + mLanguageDefinition = aLanguageDef; + mRegexList.clear(); + + for (auto& r : mLanguageDefinition.mTokenRegexStrings) + mRegexList.push_back(std::make_pair(std::regex(r.first, std::regex_constants::optimize), r.second)); + + Colorize(); +} + +void TextEditor::SetPalette(const Palette & aValue) +{ + mPaletteBase = aValue; +} + +std::string TextEditor::GetText(const Coordinates & aStart, const Coordinates & aEnd) const +{ + std::string result; + + auto lstart = aStart.mLine; + auto lend = aEnd.mLine; + auto istart = GetCharacterIndex(aStart); + auto iend = GetCharacterIndex(aEnd); + size_t s = 0; + + for (size_t i = lstart; i < lend; i++) + s += mLines[i].size(); + + result.reserve(s + s / 8); + + while (istart < iend || lstart < lend) + { + if (lstart >= (int)mLines.size()) + break; + + auto& line = mLines[lstart]; + if (istart < (int)line.size()) + { + result += line[istart].mChar; + istart++; + } + else + { + istart = 0; + ++lstart; + result += '\n'; + } + } + + return result; +} + +TextEditor::Coordinates TextEditor::GetActualCursorCoordinates() const +{ + return SanitizeCoordinates(mState.mCursorPosition); +} + +TextEditor::Coordinates TextEditor::SanitizeCoordinates(const Coordinates & aValue) const +{ + auto line = aValue.mLine; + auto column = aValue.mColumn; + if (line >= (int)mLines.size()) + { + if (mLines.empty()) + { + line = 0; + column = 0; + } + else + { + line = (int)mLines.size() - 1; + column = GetLineMaxColumn(line); + } + return Coordinates(line, column); + } + else + { + column = mLines.empty() ? 0 : std::min(column, GetLineMaxColumn(line)); + return Coordinates(line, column); + } +} + +// https://en.wikipedia.org/wiki/UTF-8 +// We assume that the char is a standalone character (<128) or a leading byte of an UTF-8 code sequence (non-10xxxxxx code) +static int UTF8CharLength(TextEditor::Char c) +{ + if ((c & 0xFE) == 0xFC) + return 6; + if ((c & 0xFC) == 0xF8) + return 5; + if ((c & 0xF8) == 0xF0) + return 4; + else if ((c & 0xF0) == 0xE0) + return 3; + else if ((c & 0xE0) == 0xC0) + return 2; + return 1; +} + +// "Borrowed" from ImGui source +static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) +{ + if (c < 0x80) + { + buf[0] = (char)c; + return 1; + } + if (c < 0x800) + { + if (buf_size < 2) return 0; + buf[0] = (char)(0xc0 + (c >> 6)); + buf[1] = (char)(0x80 + (c & 0x3f)); + return 2; + } + if (c >= 0xdc00 && c < 0xe000) + { + return 0; + } + if (c >= 0xd800 && c < 0xdc00) + { + if (buf_size < 4) return 0; + buf[0] = (char)(0xf0 + (c >> 18)); + buf[1] = (char)(0x80 + ((c >> 12) & 0x3f)); + buf[2] = (char)(0x80 + ((c >> 6) & 0x3f)); + buf[3] = (char)(0x80 + ((c) & 0x3f)); + return 4; + } + //else if (c < 0x10000) + { + if (buf_size < 3) return 0; + buf[0] = (char)(0xe0 + (c >> 12)); + buf[1] = (char)(0x80 + ((c >> 6) & 0x3f)); + buf[2] = (char)(0x80 + ((c) & 0x3f)); + return 3; + } +} + +void TextEditor::Advance(Coordinates & aCoordinates) const +{ + if (aCoordinates.mLine < (int)mLines.size()) + { + auto& line = mLines[aCoordinates.mLine]; + auto cindex = GetCharacterIndex(aCoordinates); + + if (cindex + 1 < (int)line.size()) + { + auto delta = UTF8CharLength(line[cindex].mChar); + cindex = std::min(cindex + delta, (int)line.size() - 1); + } + else + { + ++aCoordinates.mLine; + cindex = 0; + } + aCoordinates.mColumn = GetCharacterColumn(aCoordinates.mLine, cindex); + } +} + +void TextEditor::DeleteRange(const Coordinates & aStart, const Coordinates & aEnd) +{ + assert(aEnd >= aStart); + assert(!mReadOnly); + + //printf("D(%d.%d)-(%d.%d)\n", aStart.mLine, aStart.mColumn, aEnd.mLine, aEnd.mColumn); + + if (aEnd == aStart) + return; + + auto start = GetCharacterIndex(aStart); + auto end = GetCharacterIndex(aEnd); + + if (aStart.mLine == aEnd.mLine) + { + auto& line = mLines[aStart.mLine]; + auto n = GetLineMaxColumn(aStart.mLine); + if (aEnd.mColumn >= n) + line.erase(line.begin() + start, line.end()); + else + line.erase(line.begin() + start, line.begin() + end); + } + else + { + auto& firstLine = mLines[aStart.mLine]; + auto& lastLine = mLines[aEnd.mLine]; + + firstLine.erase(firstLine.begin() + start, firstLine.end()); + lastLine.erase(lastLine.begin(), lastLine.begin() + end); + + if (aStart.mLine < aEnd.mLine) + firstLine.insert(firstLine.end(), lastLine.begin(), lastLine.end()); + + if (aStart.mLine < aEnd.mLine) + RemoveLine(aStart.mLine + 1, aEnd.mLine + 1); + } + + mTextChanged = true; +} + +int TextEditor::InsertTextAt(Coordinates& /* inout */ aWhere, const char * aValue) +{ + assert(!mReadOnly); + + int cindex = GetCharacterIndex(aWhere); + int totalLines = 0; + while (*aValue != '\0') + { + assert(!mLines.empty()); + + if (*aValue == '\r') + { + // skip + ++aValue; + } + else if (*aValue == '\n') + { + if (cindex < (int)mLines[aWhere.mLine].size()) + { + auto& newLine = InsertLine(aWhere.mLine + 1); + auto& line = mLines[aWhere.mLine]; + newLine.insert(newLine.begin(), line.begin() + cindex, line.end()); + line.erase(line.begin() + cindex, line.end()); + } + else + { + InsertLine(aWhere.mLine + 1); + } + ++aWhere.mLine; + aWhere.mColumn = 0; + cindex = 0; + ++totalLines; + ++aValue; + } + else + { + auto& line = mLines[aWhere.mLine]; + auto d = UTF8CharLength(*aValue); + while (d-- > 0 && *aValue != '\0') + line.insert(line.begin() + cindex++, Glyph(*aValue++, PaletteIndex::Default)); + ++aWhere.mColumn; + } + + mTextChanged = true; + } + + return totalLines; +} + +void TextEditor::AddUndo(UndoRecord& aValue) +{ + assert(!mReadOnly); + //printf("AddUndo: (@%d.%d) +\'%s' [%d.%d .. %d.%d], -\'%s', [%d.%d .. %d.%d] (@%d.%d)\n", + // aValue.mBefore.mCursorPosition.mLine, aValue.mBefore.mCursorPosition.mColumn, + // aValue.mAdded.c_str(), aValue.mAddedStart.mLine, aValue.mAddedStart.mColumn, aValue.mAddedEnd.mLine, aValue.mAddedEnd.mColumn, + // aValue.mRemoved.c_str(), aValue.mRemovedStart.mLine, aValue.mRemovedStart.mColumn, aValue.mRemovedEnd.mLine, aValue.mRemovedEnd.mColumn, + // aValue.mAfter.mCursorPosition.mLine, aValue.mAfter.mCursorPosition.mColumn + // ); + + mUndoBuffer.resize((size_t)(mUndoIndex + 1)); + mUndoBuffer.back() = aValue; + + if (mUndoIndex < mUndoIndexFirstEdit) + mUndoIndexFirstEdit = mUndoIndex; + + ++mUndoIndex; + + mSaveState = false; +} + +TextEditor::Coordinates TextEditor::ScreenPosToCoordinates(const ImVec2& aPosition) const +{ + ImVec2 origin = ImGui::GetCursorScreenPos(); + ImVec2 local(aPosition.x - origin.x, aPosition.y - origin.y); + + int lineNo = std::max(0, (int)floor(local.y / mCharAdvance.y)); + + int columnCoord = 0; + + if (lineNo >= 0 && lineNo < (int)mLines.size()) + { + auto& line = mLines.at(lineNo); + + int columnIndex = 0; + float columnX = 0.0f; + + while ((size_t)columnIndex < line.size()) + { + float columnWidth = 0.0f; + + if (line[columnIndex].mChar == '\t') + { + float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ").x; + float oldX = columnX; + float newColumnX = (1.0f + std::floor((1.0f + columnX) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); + columnWidth = newColumnX - oldX; + if (mTextStart + columnX + columnWidth * 0.5f > local.x) + break; + columnX = newColumnX; + columnCoord = (columnCoord / mTabSize) * mTabSize + mTabSize; + columnIndex++; + } + else + { + char buf[7]; + auto d = UTF8CharLength(line[columnIndex].mChar); + int i = 0; + while (i < 6 && d-- > 0) + buf[i++] = line[columnIndex++].mChar; + buf[i] = '\0'; + columnWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf).x; + if (mTextStart + columnX + columnWidth * 0.5f > local.x) + break; + columnX += columnWidth; + columnCoord++; + } + } + } + + return SanitizeCoordinates(Coordinates(lineNo, columnCoord)); +} + +TextEditor::Coordinates TextEditor::FindWordStart(const Coordinates & aFrom) const +{ + Coordinates at = aFrom; + if (at.mLine >= (int)mLines.size()) + return at; + + auto& line = mLines[at.mLine]; + auto cindex = GetCharacterIndex(at); + + if (cindex >= (int)line.size()) + return at; + + while (cindex > 0 && isspace(line[cindex].mChar)) + --cindex; + + auto cstart = (PaletteIndex)line[cindex].mColorIndex; + while (cindex > 0) + { + auto c = line[cindex].mChar; + if ((c & 0xC0) != 0x80) // not UTF code sequence 10xxxxxx + { + if (c <= 32 && isspace(c)) + { + cindex++; + break; + } + if (cstart != (PaletteIndex)line[size_t(cindex - 1)].mColorIndex) + break; + } + --cindex; + } + return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex)); +} + +TextEditor::Coordinates TextEditor::FindWordEnd(const Coordinates & aFrom) const +{ + Coordinates at = aFrom; + if (at.mLine >= (int)mLines.size()) + return at; + + auto& line = mLines[at.mLine]; + auto cindex = GetCharacterIndex(at); + + if (cindex >= (int)line.size()) + return at; + + bool prevspace = (bool)isspace(line[cindex].mChar); + auto cstart = (PaletteIndex)line[cindex].mColorIndex; + while (cindex < (int)line.size()) + { + auto c = line[cindex].mChar; + auto d = UTF8CharLength(c); + if (cstart != (PaletteIndex)line[cindex].mColorIndex) + break; + + if (prevspace != !!isspace(c)) + { + if (isspace(c)) + while (cindex < (int)line.size() && isspace(line[cindex].mChar)) + ++cindex; + break; + } + cindex += d; + } + return Coordinates(aFrom.mLine, GetCharacterColumn(aFrom.mLine, cindex)); +} + +TextEditor::Coordinates TextEditor::FindNextWord(const Coordinates & aFrom) const +{ + Coordinates at = aFrom; + if (at.mLine >= (int)mLines.size()) + return at; + + // skip to the next non-word character + auto cindex = GetCharacterIndex(aFrom); + bool isword = false; + bool skip = false; + if (cindex < (int)mLines[at.mLine].size()) + { + auto& line = mLines[at.mLine]; + isword = isalnum(line[cindex].mChar); + skip = isword; + } + + while (!isword || skip) + { + if (at.mLine >= mLines.size()) + { + auto l = std::max(0, (int) mLines.size() - 1); + return Coordinates(l, GetLineMaxColumn(l)); + } + + auto& line = mLines[at.mLine]; + if (cindex < (int)line.size()) + { + isword = isalnum(line[cindex].mChar); + + if (isword && !skip) + return Coordinates(at.mLine, GetCharacterColumn(at.mLine, cindex)); + + if (!isword) + skip = false; + + cindex++; + } + else + { + cindex = 0; + ++at.mLine; + skip = false; + isword = false; + } + } + + return at; +} + +int TextEditor::GetCharacterIndex(const Coordinates& aCoordinates) const +{ + if (aCoordinates.mLine >= mLines.size()) + return -1; + auto& line = mLines[aCoordinates.mLine]; + int c = 0; + int i = 0; + for (; i < line.size() && c < aCoordinates.mColumn;) + { + if (line[i].mChar == '\t') + c = (c / mTabSize) * mTabSize + mTabSize; + else + ++c; + i += UTF8CharLength(line[i].mChar); + } + return i; +} + +int TextEditor::GetCharacterColumn(int aLine, int aIndex) const +{ + if (aLine >= mLines.size()) + return 0; + auto& line = mLines[aLine]; + int col = 0; + int i = 0; + while (i < aIndex && i < (int)line.size()) + { + auto c = line[i].mChar; + i += UTF8CharLength(c); + if (c == '\t') + col = (col / mTabSize) * mTabSize + mTabSize; + else + col++; + } + return col; +} + +int TextEditor::GetLineCharacterCount(int aLine) const +{ + if (aLine >= mLines.size()) + return 0; + auto& line = mLines[aLine]; + int c = 0; + for (unsigned i = 0; i < line.size(); c++) + i += UTF8CharLength(line[i].mChar); + return c; +} + +int TextEditor::GetLineMaxColumn(int aLine) const +{ + if (aLine >= mLines.size()) + return 0; + auto& line = mLines[aLine]; + int col = 0; + for (unsigned i = 0; i < line.size(); ) + { + auto c = line[i].mChar; + if (c == '\t') + col = (col / mTabSize) * mTabSize + mTabSize; + else + col++; + i += UTF8CharLength(c); + } + return col; +} + +bool TextEditor::IsOnWordBoundary(const Coordinates & aAt) const +{ + if (aAt.mLine >= (int)mLines.size() || aAt.mColumn == 0) + return true; + + auto& line = mLines[aAt.mLine]; + auto cindex = GetCharacterIndex(aAt); + if (cindex >= (int)line.size()) + return true; + + if (mColorizerEnabled) + return line[cindex].mColorIndex != line[size_t(cindex - 1)].mColorIndex; + + return isspace(line[cindex].mChar) != isspace(line[cindex - 1].mChar); +} + +void TextEditor::RemoveLine(int aStart, int aEnd) +{ + assert(!mReadOnly); + assert(aEnd >= aStart); + assert(mLines.size() > (size_t)(aEnd - aStart)); + + ErrorMarkers etmp; + for (auto& i : mErrorMarkers) + { + ErrorMarkers::value_type e(i.first >= aStart ? i.first - 1 : i.first, i.second); + if (e.first >= aStart && e.first <= aEnd) + continue; + etmp.insert(e); + } + mErrorMarkers = std::move(etmp); + + Breakpoints btmp; + for (auto i : mBreakpoints) + { + if (i >= aStart && i <= aEnd) + continue; + btmp.insert(i >= aStart ? i - 1 : i); + } + mBreakpoints = std::move(btmp); + + mLines.erase(mLines.begin() + aStart, mLines.begin() + aEnd); + assert(!mLines.empty()); + + mTextChanged = true; +} + +void TextEditor::RemoveLine(int aIndex) +{ + assert(!mReadOnly); + assert(mLines.size() > 1); + + ErrorMarkers etmp; + for (auto& i : mErrorMarkers) + { + ErrorMarkers::value_type e(i.first > aIndex ? i.first - 1 : i.first, i.second); + if (e.first - 1 == aIndex) + continue; + etmp.insert(e); + } + mErrorMarkers = std::move(etmp); + + Breakpoints btmp; + for (auto i : mBreakpoints) + { + if (i == aIndex) + continue; + btmp.insert(i >= aIndex ? i - 1 : i); + } + mBreakpoints = std::move(btmp); + + mLines.erase(mLines.begin() + aIndex); + assert(!mLines.empty()); + + mTextChanged = true; +} + +TextEditor::Line& TextEditor::InsertLine(int aIndex) +{ + assert(!mReadOnly); + + auto& result = *mLines.insert(mLines.begin() + aIndex, Line()); + + ErrorMarkers etmp; + for (auto& i : mErrorMarkers) + etmp.insert(ErrorMarkers::value_type(i.first >= aIndex ? i.first + 1 : i.first, i.second)); + mErrorMarkers = std::move(etmp); + + Breakpoints btmp; + for (auto i : mBreakpoints) + btmp.insert(i >= aIndex ? i + 1 : i); + mBreakpoints = std::move(btmp); + + return result; +} + +std::string TextEditor::GetWordBeforeCursor() const +{ + auto c = GetCursorPosition(); + if (c.mColumn > 2) + { + c.mColumn -= 2; // remove . + } + return GetWordAt(c); +} + +std::string TextEditor::GetWordUnderCursor() const +{ + auto c = GetCursorPosition(); + return GetWordAt(c); +} + +std::string TextEditor::GetWordAt(const Coordinates & aCoords) const +{ + auto start = FindWordStart(aCoords); + auto end = FindWordEnd(aCoords); + + std::string r; + + auto istart = GetCharacterIndex(start); + auto iend = GetCharacterIndex(end); + + for (auto it = istart; it < iend; ++it) + r.push_back(mLines[aCoords.mLine][it].mChar); + + return r; +} + +ImU32 TextEditor::GetGlyphColor(const Glyph & aGlyph) const +{ + if (!mColorizerEnabled) + return mPalette[(int)PaletteIndex::Default]; + if (aGlyph.mComment) + return mPalette[(int)PaletteIndex::Comment]; + if (aGlyph.mMultiLineComment) + return mPalette[(int)PaletteIndex::MultiLineComment]; + auto const color = mPalette[(int)aGlyph.mColorIndex]; + if (aGlyph.mPreprocessor) + { + const auto ppcolor = mPalette[(int)PaletteIndex::Preprocessor]; + const int c0 = ((ppcolor & 0xff) + (color & 0xff)) / 2; + const int c1 = (((ppcolor >> 8) & 0xff) + ((color >> 8) & 0xff)) / 2; + const int c2 = (((ppcolor >> 16) & 0xff) + ((color >> 16) & 0xff)) / 2; + const int c3 = (((ppcolor >> 24) & 0xff) + ((color >> 24) & 0xff)) / 2; + return ImU32(c0 | (c1 << 8) | (c2 << 16) | (c3 << 24)); + } + return color; +} + +void TextEditor::HandleKeyboardInputs() +{ + ImGuiIO& io = ImGui::GetIO(); + auto shift = io.KeyShift; + auto ctrl = io.KeyCtrl; + auto alt = io.KeyAlt; + + if (ImGui::IsWindowFocused()) + { + if (ImGui::IsWindowHovered()) + ImGui::SetMouseCursor(ImGuiMouseCursor_TextInput); + //ImGui::CaptureKeyboardFromApp(true); + + io.WantCaptureKeyboard = true; + io.WantTextInput = true; + + if (mKeyDownHandler && mKeyDownHandler(mKeyPressData)) + { + // already handled + } + else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Z)) + Undo(); + else if (!IsReadOnly() && !ctrl && !shift && alt && ImGui::IsKeyPressed(ImGuiKey_Backspace)) + Undo(); + else if (!IsReadOnly() && (io.ConfigMacOSXBehaviors ? (ctrl && shift && ImGui::IsKeyPressed( ImGuiKey_Z )) + : (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Y)))) + Redo(); + else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_UpArrow)) + MoveUp(1, shift); + else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_DownArrow)) + MoveDown(1, shift); + else if (!alt && ImGui::IsKeyPressed(ImGuiKey_LeftArrow)) + MoveLeft(1, shift, ctrl); + else if (!alt && ImGui::IsKeyPressed(ImGuiKey_RightArrow)) + MoveRight(1, shift, ctrl); + else if (!alt && ImGui::IsKeyPressed(ImGuiKey_PageUp)) + MoveUp(GetPageSize() - 4, shift); + else if (!alt && ImGui::IsKeyPressed(ImGuiKey_PageDown)) + MoveDown(GetPageSize() - 4, shift); + else if (!alt && ctrl && ImGui::IsKeyPressed(ImGuiKey_Home)) + MoveTop(shift); + else if (ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_End)) + MoveBottom(shift); + else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_Home)) + MoveHome(shift); + else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_End)) + MoveEnd(shift); + else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Delete)) + Delete(); + else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Backspace)) + { + Backspace(); + if (mKeyPressHandler) + { + // allow the autosuggest to react to this event + mKeyPressHandler(mKeyPressData, ctrl, shift, alt, 0x08); + } + } + else if (!ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Insert)) + mOverwrite ^= true; + else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Insert)) + Copy(); + else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_C)) + Copy(); + else if (!IsReadOnly() && !ctrl && shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Insert)) + Paste(); + else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_V)) + Paste(); + else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_X)) + Cut(); + else if (!ctrl && shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Delete)) + Cut(); + else if (ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_A)) + SelectAll(); + else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGuiKey_Enter)) + EnterCharacter('\n', false); + else if (!IsReadOnly() && !ctrl && !alt && ImGui::IsKeyPressed(ImGuiKey_Tab)) + EnterCharacter('\t', shift); + + if (!IsReadOnly() && !io.InputQueueCharacters.empty()) + { + for (int i = 0; i < io.InputQueueCharacters.Size; i++) + { + auto c = io.InputQueueCharacters[i]; + if (c != 0 && (c == '\n' || c >= 32)) + EnterCharacter(c, shift); + } + io.InputQueueCharacters.resize(0); + } + } +} + +void TextEditor::HandleMouseInputs() +{ + ImGuiIO& io = ImGui::GetIO(); + auto shift = io.KeyShift; + auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl; + auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt; + + if (ImGui::IsWindowHovered()) + { + if (mMouseButtonDownHandler && mMouseButtonDownHandler(mKeyPressData)) + { + return; + } + + if (!shift && !alt) + { + auto click = ImGui::IsMouseClicked(0); + auto doubleClick = ImGui::IsMouseDoubleClicked(0); + auto t = ImGui::GetTime(); + auto tripleClick = click && !doubleClick && (mLastClick != -1.0f && (t - mLastClick) < io.MouseDoubleClickTime); + + /* + Left mouse button triple click + */ + + if (tripleClick) + { + if (!ctrl) + { + mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); + mSelectionMode = SelectionMode::Line; + SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + } + + mLastClick = -1.0f; + } + + /* + Left mouse button double click + */ + + else if (doubleClick) + { + if (!ctrl) + { + mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); + if (mSelectionMode == SelectionMode::Line) + mSelectionMode = SelectionMode::Normal; + else + mSelectionMode = SelectionMode::Word; + SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + } + + mLastClick = (float)ImGui::GetTime(); + } + + /* + Left mouse button click + */ + else if (click) + { + mState.mCursorPosition = mInteractiveStart = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); + if (ctrl) + mSelectionMode = SelectionMode::Word; + else + mSelectionMode = SelectionMode::Normal; + SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + + mLastClick = (float)ImGui::GetTime(); + } + // Mouse left button dragging (=> update selection) + else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0)) + { + io.WantCaptureMouse = true; + mState.mCursorPosition = mInteractiveEnd = ScreenPosToCoordinates(ImGui::GetMousePos()); + SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); + } + } + } +} + +void TextEditor::Render() +{ + /* Compute mCharAdvance regarding to scaled font size (Ctrl + mouse wheel)*/ + const float fontSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, "#", nullptr, nullptr).x; + mCharAdvance = ImVec2(fontSize, ImGui::GetTextLineHeightWithSpacing() * mLineSpacing); + + /* Update palette with the current alpha from style */ + for (int i = 0; i < (int)PaletteIndex::Max; ++i) + { + auto color = ImGui::ColorConvertU32ToFloat4(mPaletteBase[i]); + color.w *= ImGui::GetStyle().Alpha; + mPalette[i] = ImGui::ColorConvertFloat4ToU32(color); + } + + assert(mLineBuffer.empty()); + + auto contentSize = ImGui::GetWindowContentRegionMax(); + auto drawList = ImGui::GetWindowDrawList(); + float longest(mTextStart); + + if (mScrollToTop) + { + mScrollToTop = false; + ImGui::SetScrollY(0.f); + } + + ImVec2 cursorScreenPos = ImGui::GetCursorScreenPos(); + auto scrollX = ImGui::GetScrollX(); + auto scrollY = ImGui::GetScrollY(); + + auto lineNo = (int)floor(scrollY / mCharAdvance.y); + auto globalLineMax = (int)mLines.size(); + auto lineMax = std::max(0, std::min((int)mLines.size() - 1, lineNo + (int)floor((scrollY + contentSize.y) / mCharAdvance.y))); + + // Deduce mTextStart by evaluating mLines size (global lineMax) plus two spaces as text width + char buf[16]; + snprintf(buf, 16, " %d ", globalLineMax); + mTextStart = (mShowLineNumber ? ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x : 0.0f) + mLeftMargin; + + if (!mLines.empty()) + { + float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x; + + while (lineNo <= lineMax) + { + ImVec2 lineStartScreenPos = ImVec2(cursorScreenPos.x, cursorScreenPos.y + lineNo * mCharAdvance.y); + ImVec2 textScreenPos = ImVec2(lineStartScreenPos.x + mTextStart, lineStartScreenPos.y); + + auto& line = mLines[lineNo]; + longest = std::max(mTextStart + TextDistanceToLineStart(Coordinates(lineNo, GetLineMaxColumn(lineNo))), longest); + auto columnNo = 0; + Coordinates lineStartCoord(lineNo, 0); + Coordinates lineEndCoord(lineNo, GetLineMaxColumn(lineNo)); + + // Draw selection for the current line + float sstart = -1.0f; + float ssend = -1.0f; + + assert(mState.mSelectionStart <= mState.mSelectionEnd); + if (mState.mSelectionStart <= lineEndCoord) + sstart = mState.mSelectionStart > lineStartCoord ? TextDistanceToLineStart(mState.mSelectionStart) : 0.0f; + if (mState.mSelectionEnd > lineStartCoord) + ssend = TextDistanceToLineStart(mState.mSelectionEnd < lineEndCoord ? mState.mSelectionEnd : lineEndCoord); + + if (mState.mSelectionEnd.mLine > lineNo) + ssend += mCharAdvance.x; + + if (sstart != -1 && ssend != -1 && sstart < ssend) + { + ImVec2 vstart(lineStartScreenPos.x + mTextStart + sstart, lineStartScreenPos.y); + ImVec2 vend(lineStartScreenPos.x + mTextStart + ssend, lineStartScreenPos.y + mCharAdvance.y); + drawList->AddRectFilled(vstart, vend, mPalette[(int)PaletteIndex::Selection]); + } + + // Draw breakpoints + auto start = ImVec2(lineStartScreenPos.x + scrollX, lineStartScreenPos.y); + + if (mBreakpoints.count(lineNo + 1) != 0) + { + auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, lineStartScreenPos.y + mCharAdvance.y); + drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::Breakpoint]); + } + + // Draw error markers + auto errorIt = mErrorMarkers.find(lineNo + 1); + if (errorIt != mErrorMarkers.end()) + { + auto end = ImVec2(lineStartScreenPos.x + contentSize.x + 2.0f * scrollX, lineStartScreenPos.y + mCharAdvance.y); + drawList->AddRectFilled(start, end, mPalette[(int)PaletteIndex::ErrorMarker]); + + if (ImGui::IsMouseHoveringRect(lineStartScreenPos, end)) + { + ImGui::BeginTooltip(); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.2f, 0.2f, 1.0f)); + ImGui::Text("Error at line %d:", errorIt->first); + ImGui::PopStyleColor(); + ImGui::Separator(); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 0.2f, 1.0f)); + ImGui::Text("%s", errorIt->second.c_str()); + ImGui::PopStyleColor(); + ImGui::EndTooltip(); + } + } + + // Draw line number (right aligned) + snprintf(buf, 16, "%d ", lineNo + 1); + + auto lineNoWidth = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf, nullptr, nullptr).x; + if (mShowLineNumber) + { + drawList->AddText(ImVec2(lineStartScreenPos.x + mTextStart - lineNoWidth, lineStartScreenPos.y), mPalette[(int)PaletteIndex::LineNumber], buf); + } + + if (mState.mCursorPosition.mLine == lineNo) + { + auto focused = ImGui::IsWindowFocused(); + + // Highlight the current line (where the cursor is) + if (mShowCurrentLine && !HasSelection()) + { + auto end = ImVec2(start.x + contentSize.x + scrollX, start.y + mCharAdvance.y); + drawList->AddRectFilled(start, end, mPalette[(int)(focused ? PaletteIndex::CurrentLineFill : PaletteIndex::CurrentLineFillInactive)]); + drawList->AddRect(start, end, mPalette[(int)PaletteIndex::CurrentLineEdge], 1.0f); + } + + // Render the cursor + float cx = TextDistanceToLineStart(mState.mCursorPosition); + mCursorScreenPos = ImVec2(textScreenPos.x + cx - cursorScreenPos.x + scrollX, lineStartScreenPos.y - cursorScreenPos.y - scrollY); + if (focused) + { + auto timeEnd = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + auto elapsed = timeEnd - mStartTime; + if (elapsed > 400) + { + float width = 1.0f; + auto cindex = GetCharacterIndex(mState.mCursorPosition); + + if (mOverwrite && cindex < (int)line.size()) + { + auto c = line[cindex].mChar; + if (c == '\t') + { + auto x = (1.0f + std::floor((1.0f + cx) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); + width = x - cx; + } + else + { + char buf2[2]; + buf2[0] = line[cindex].mChar; + buf2[1] = '\0'; + width = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, buf2).x; + } + } + ImVec2 cstart(textScreenPos.x + cx, lineStartScreenPos.y); + ImVec2 cend(textScreenPos.x + cx + width, lineStartScreenPos.y + mCharAdvance.y); + drawList->AddRectFilled(cstart, cend, mPalette[(int)PaletteIndex::Cursor]); + if (elapsed > 800) + mStartTime = timeEnd; + } + } + } + + // Render colorized text + auto prevColor = line.empty() ? mPalette[(int)PaletteIndex::Default] : GetGlyphColor(line[0]); + ImVec2 bufferOffset; + + for (int i = 0; i < line.size();) + { + auto& glyph = line[i]; + auto color = GetGlyphColor(glyph); + + if ((color != prevColor || glyph.mChar == '\t' || glyph.mChar == ' ') && !mLineBuffer.empty()) + { + const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y); + drawList->AddText(newOffset, prevColor, mLineBuffer.c_str()); + auto textSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, mLineBuffer.c_str(), nullptr, nullptr); + bufferOffset.x += textSize.x; + mLineBuffer.clear(); + } + prevColor = color; + + if (glyph.mChar == '\t') + { + auto oldX = bufferOffset.x; + bufferOffset.x = (1.0f + std::floor((1.0f + bufferOffset.x) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); + ++i; + + if (mShowWhitespaces) + { + const auto s = ImGui::GetFontSize(); + const auto x1 = textScreenPos.x + oldX + 1.0f; + const auto x2 = textScreenPos.x + bufferOffset.x - 1.0f; + const auto y = textScreenPos.y + bufferOffset.y + s * 0.5f; + const ImVec2 p1(x1, y); + const ImVec2 p2(x2, y); + const ImVec2 p3(x2 - s * 0.2f, y - s * 0.2f); + const ImVec2 p4(x2 - s * 0.2f, y + s * 0.2f); + drawList->AddLine(p1, p2, 0x90909090); + drawList->AddLine(p2, p3, 0x90909090); + drawList->AddLine(p2, p4, 0x90909090); + } + } + else if (glyph.mChar == ' ') + { + if (mShowWhitespaces) + { + const auto s = ImGui::GetFontSize(); + const auto x = textScreenPos.x + bufferOffset.x + spaceSize * 0.5f; + const auto y = textScreenPos.y + bufferOffset.y + s * 0.5f; + drawList->AddCircleFilled(ImVec2(x, y), 1.5f, 0x80808080, 4); + } + bufferOffset.x += spaceSize; + i++; + } + else + { + auto l = UTF8CharLength(glyph.mChar); + while (l-- > 0) + mLineBuffer.push_back(line[i++].mChar); + } + ++columnNo; + } + + if (!mLineBuffer.empty()) + { + const ImVec2 newOffset(textScreenPos.x + bufferOffset.x, textScreenPos.y + bufferOffset.y); + drawList->AddText(newOffset, prevColor, mLineBuffer.c_str()); + mLineBuffer.clear(); + } + + ++lineNo; + } + + // Draw a tooltip on known identifiers/preprocessor symbols + if (ImGui::IsMousePosValid() && ImGui::IsWindowHovered()) + { + auto id = GetWordAt(ScreenPosToCoordinates(ImGui::GetMousePos())); + if (!id.empty()) + { + char tip[512]; + if (mGetToolTipHandler && mGetToolTipHandler(mKeyPressData, id.c_str(), tip, sizeof(tip))) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(tip); + ImGui::EndTooltip(); + } + else + { + auto it = mLanguageDefinition.mIdentifiers.find(id); + if (it != mLanguageDefinition.mIdentifiers.end()) + { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(it->second.mDeclaration.c_str()); + ImGui::EndTooltip(); + } + else + { + auto pi = mLanguageDefinition.mPreprocIdentifiers.find(id); + if (pi != mLanguageDefinition.mPreprocIdentifiers.end()) + { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(pi->second.mDeclaration.c_str()); + ImGui::EndTooltip(); + } + } + } + } + } + } + + + ImGui::Dummy(ImVec2((longest + 2), mLines.size() * mCharAdvance.y)); + + if (mScrollToCursor) + { + EnsureCursorVisible(); + ImGui::SetWindowFocus(); + mScrollToCursor = false; + } + if (mSetFocus) { + ImGui::SetWindowFocus(); + mSetFocus = false; + } +} + +void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) +{ + mWithinRender = true; + mTextChanged = false; + mCursorPositionChanged = false; + + ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::ColorConvertU32ToFloat4(mPalette[(int)PaletteIndex::Background])); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f)); + if (!mIgnoreImGuiChild) + ImGui::BeginChild(aTitle, aSize, aBorder, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNavInputs); + + if (mHandleKeyboardInputs) + { + HandleKeyboardInputs(); + ImGui::PushItemFlag(ImGuiItemFlags_NoTabStop, false); + } + + if (mHandleMouseInputs) + HandleMouseInputs(); + + ColorizeInternal(); + Render(); + + if (mHandleKeyboardInputs) + ImGui::PopItemFlag(); + + if (!mIgnoreImGuiChild) + ImGui::EndChild(); + + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + + mWithinRender = false; +} + +void TextEditor::SetText(const std::string & aText) +{ + mLines.clear(); + mLines.emplace_back(Line()); + for (auto chr : aText) + { + if (chr == '\r') + { + // ignore the carriage return character + } + else if (chr == '\n') + mLines.emplace_back(Line()); + else + { + mLines.back().emplace_back(Glyph(chr, PaletteIndex::Default)); + } + } + + mTextChanged = true; + MarkSaved(); + mScrollToTop = true; + + mUndoBuffer.clear(); + mUndoIndex = 0; + + Colorize(); +} + +void TextEditor::SetTextLines(const std::vector & aLines) +{ + mLines.clear(); + + if (aLines.empty()) + { + mLines.emplace_back(Line()); + } + else + { + mLines.resize(aLines.size()); + + for (size_t i = 0; i < aLines.size(); ++i) + { + const std::string & aLine = aLines[i]; + + mLines[i].reserve(aLine.size()); + for (size_t j = 0; j < aLine.size(); ++j) + mLines[i].emplace_back(Glyph(aLine[j], PaletteIndex::Default)); + } + } + + mTextChanged = true; + MarkSaved(); + mScrollToTop = true; + + mUndoBuffer.clear(); + mUndoIndex = 0; + + Colorize(); +} + +void TextEditor::EnterCharacter(ImWchar aChar, bool aShift) +{ + assert(!mReadOnly); + + UndoRecord u; + + u.mBefore = mState; + + if (HasSelection()) + { + if (aChar == '\t' && mState.mSelectionStart.mLine != mState.mSelectionEnd.mLine) + { + + auto start = mState.mSelectionStart; + auto end = mState.mSelectionEnd; + auto originalEnd = end; + + if (start > end) + std::swap(start, end); + start.mColumn = 0; + // end.mColumn = end.mLine < mLines.size() ? mLines[end.mLine].size() : 0; + if (end.mColumn == 0 && end.mLine > 0) + --end.mLine; + if (end.mLine >= (int)mLines.size()) + end.mLine = mLines.empty() ? 0 : (int)mLines.size() - 1; + end.mColumn = GetLineMaxColumn(end.mLine); + + //if (end.mColumn >= GetLineMaxColumn(end.mLine)) + // end.mColumn = GetLineMaxColumn(end.mLine) - 1; + + u.mRemovedStart = start; + u.mRemovedEnd = end; + u.mRemoved = GetText(start, end); + + bool modified = false; + + for (int i = start.mLine; i <= end.mLine; i++) + { + auto& line = mLines[i]; + if (aShift) + { + if (!line.empty()) + { + if (line.front().mChar == '\t') + { + line.erase(line.begin()); + modified = true; + } + else + { + for (int j = 0; j < mTabSize && !line.empty() && line.front().mChar == ' '; j++) + { + line.erase(line.begin()); + modified = true; + } + } + } + } + else + { + line.insert(line.begin(), Glyph('\t', TextEditor::PaletteIndex::Background)); + modified = true; + } + } + + if (modified) + { + start = Coordinates(start.mLine, GetCharacterColumn(start.mLine, 0)); + Coordinates rangeEnd; + if (originalEnd.mColumn != 0) + { + end = Coordinates(end.mLine, GetLineMaxColumn(end.mLine)); + rangeEnd = end; + u.mAdded = GetText(start, end); + } + else + { + end = Coordinates(originalEnd.mLine, 0); + rangeEnd = Coordinates(end.mLine - 1, GetLineMaxColumn(end.mLine - 1)); + u.mAdded = GetText(start, rangeEnd); + } + + u.mAddedStart = start; + u.mAddedEnd = rangeEnd; + u.mAfter = mState; + + mState.mSelectionStart = start; + mState.mSelectionEnd = end; + AddUndo(u); + + mTextChanged = true; + + EnsureCursorVisible(); + } + + return; + } // c == '\t' + else + { + u.mRemoved = GetSelectedText(); + u.mRemovedStart = mState.mSelectionStart; + u.mRemovedEnd = mState.mSelectionEnd; + DeleteSelection(); + } + } // HasSelection + + auto coord = GetActualCursorCoordinates(); + u.mAddedStart = coord; + + assert(!mLines.empty()); + + if (aChar == '\n') + { + InsertLine(coord.mLine + 1); + auto& line = mLines[coord.mLine]; + auto& newLine = mLines[coord.mLine + 1]; + + if (mLanguageDefinition.mAutoIndentation) + for (size_t it = 0; it < line.size() && isascii(line[it].mChar) && isblank(line[it].mChar); ++it) + newLine.push_back(line[it]); + + const size_t whitespaceSize = newLine.size(); + auto cindex = GetCharacterIndex(coord); + newLine.insert(newLine.end(), line.begin() + cindex, line.end()); + line.erase(line.begin() + cindex, line.begin() + line.size()); + SetCursorPosition(Coordinates(coord.mLine + 1, GetCharacterColumn(coord.mLine + 1, (int)whitespaceSize))); + u.mAdded = (char)aChar; + } + else + { + char buf[7]; + int e = ImTextCharToUtf8(buf, 7, aChar); + if (e > 0) + { + buf[e] = '\0'; + auto& line = mLines[coord.mLine]; + auto cindex = GetCharacterIndex(coord); + + if (mOverwrite && cindex < (int)line.size()) + { + auto d = UTF8CharLength(line[cindex].mChar); + + u.mRemovedStart = mState.mCursorPosition; + u.mRemovedEnd = Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex + d)); + + while (d-- > 0 && cindex < (int)line.size()) + { + u.mRemoved += line[cindex].mChar; + line.erase(line.begin() + cindex); + } + } + + for (auto p = buf; *p != '\0'; p++, ++cindex) + line.insert(line.begin() + cindex, Glyph(*p, PaletteIndex::Default)); + u.mAdded = buf; + + SetCursorPosition(Coordinates(coord.mLine, GetCharacterColumn(coord.mLine, cindex))); + } + else + return; + } + + mTextChanged = true; + + u.mAddedEnd = GetActualCursorCoordinates(); + u.mAfter = mState; + + AddUndo(u); + + Colorize(coord.mLine - 1, 3); + EnsureCursorVisible(); + + if (mKeyPressHandler) + { + ImGuiIO& io = ImGui::GetIO(); + auto shift = io.KeyShift; + auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl; + auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt; + + mKeyPressHandler(mKeyPressData, ctrl, shift, alt, aChar); + } +} + +void TextEditor::SetReadOnly(bool aValue) +{ + mReadOnly = aValue; +} + +void TextEditor::SetShowLineNumber(bool aValue) +{ + mShowLineNumber = aValue; +} + +void TextEditor::SetShowCurrentLine(bool aValue) +{ + mShowCurrentLine = aValue; +} + +void TextEditor::SetColorizerEnable(bool aValue) +{ + mColorizerEnabled = aValue; +} + +void TextEditor::SetCursorPosition(const Coordinates & aPosition) +{ + if (mState.mCursorPosition != aPosition) + { + mState.mCursorPosition = aPosition; + mCursorPositionChanged = true; + EnsureCursorVisible(); + } +} + +void TextEditor::SetSelectionStart(const Coordinates & aPosition) +{ + mState.mSelectionStart = SanitizeCoordinates(aPosition); + if (mState.mSelectionStart > mState.mSelectionEnd) + std::swap(mState.mSelectionStart, mState.mSelectionEnd); +} + +void TextEditor::SetSelectionEnd(const Coordinates & aPosition) +{ + mState.mSelectionEnd = SanitizeCoordinates(aPosition); + if (mState.mSelectionStart > mState.mSelectionEnd) + std::swap(mState.mSelectionStart, mState.mSelectionEnd); +} + +void TextEditor::SetSelection(const Coordinates & aStart, const Coordinates & aEnd, SelectionMode aMode) +{ + auto oldSelStart = mState.mSelectionStart; + auto oldSelEnd = mState.mSelectionEnd; + + mState.mSelectionStart = SanitizeCoordinates(aStart); + mState.mSelectionEnd = SanitizeCoordinates(aEnd); + if (mState.mSelectionStart > mState.mSelectionEnd) + std::swap(mState.mSelectionStart, mState.mSelectionEnd); + + switch (aMode) + { + case TextEditor::SelectionMode::Normal: + break; + case TextEditor::SelectionMode::Word: + { + mState.mSelectionStart = FindWordStart(mState.mSelectionStart); + if (!IsOnWordBoundary(mState.mSelectionEnd)) + mState.mSelectionEnd = FindWordEnd(FindWordStart(mState.mSelectionEnd)); + break; + } + case TextEditor::SelectionMode::Line: + { + const auto lineNo = mState.mSelectionEnd.mLine; + const auto lineSize = (size_t)lineNo < mLines.size() ? mLines[lineNo].size() : 0; + mState.mSelectionStart = Coordinates(mState.mSelectionStart.mLine, 0); + mState.mSelectionEnd = Coordinates(lineNo, GetLineMaxColumn(lineNo)); + break; + } + default: + break; + } + + if (mState.mSelectionStart != oldSelStart || + mState.mSelectionEnd != oldSelEnd) + mCursorPositionChanged = true; +} + +void TextEditor::SetTabSize(int aValue) +{ + mTabSize = std::max(0, std::min(32, aValue)); +} + +void TextEditor::InsertText(const std::string & aValue) +{ + InsertText(aValue.c_str()); +} + +void TextEditor::InsertText(const char * aValue) +{ + if (aValue == nullptr) + return; + + auto pos = GetActualCursorCoordinates(); + auto start = std::min(pos, mState.mSelectionStart); + int totalLines = pos.mLine - start.mLine; + + totalLines += InsertTextAt(pos, aValue); + + SetSelection(pos, pos); + SetCursorPosition(pos); + Colorize(start.mLine - 1, totalLines + 2); +} + +void TextEditor::DeleteSelection() +{ + assert(mState.mSelectionEnd >= mState.mSelectionStart); + + if (mState.mSelectionEnd == mState.mSelectionStart) + return; + + DeleteRange(mState.mSelectionStart, mState.mSelectionEnd); + + SetSelection(mState.mSelectionStart, mState.mSelectionStart); + SetCursorPosition(mState.mSelectionStart); + Colorize(mState.mSelectionStart.mLine, 1); +} + +void TextEditor::ReplaceSelection(const char *text) +{ + if (IsReadOnly()) + return; + + if (text != nullptr && strlen(text) > 0) + { + UndoRecord u; + u.mBefore = mState; + + if (HasSelection()) + { + u.mRemoved = GetSelectedText(); + u.mRemovedStart = mState.mSelectionStart; + u.mRemovedEnd = mState.mSelectionEnd; + DeleteSelection(); + } + + u.mAdded = text; + u.mAddedStart = GetActualCursorCoordinates(); + + InsertText(text); + + u.mAddedEnd = GetActualCursorCoordinates(); + u.mAfter = mState; + AddUndo(u); + } +} + +void TextEditor::MoveUp(int aAmount, bool aSelect) +{ + auto oldPos = mState.mCursorPosition; + mState.mCursorPosition.mLine = std::max(0, mState.mCursorPosition.mLine - aAmount); + if (oldPos != mState.mCursorPosition) + { + if (aSelect) + { + if (oldPos == mInteractiveStart) + mInteractiveStart = mState.mCursorPosition; + else if (oldPos == mInteractiveEnd) + mInteractiveEnd = mState.mCursorPosition; + else + { + mInteractiveStart = mState.mCursorPosition; + mInteractiveEnd = oldPos; + } + } + else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd); + + EnsureCursorVisible(); + } +} + +void TextEditor::MoveDown(int aAmount, bool aSelect) +{ + assert(mState.mCursorPosition.mColumn >= 0); + auto oldPos = mState.mCursorPosition; + mState.mCursorPosition.mLine = std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + aAmount)); + + if (mState.mCursorPosition != oldPos) + { + if (aSelect) + { + if (oldPos == mInteractiveEnd) + mInteractiveEnd = mState.mCursorPosition; + else if (oldPos == mInteractiveStart) + mInteractiveStart = mState.mCursorPosition; + else + { + mInteractiveStart = oldPos; + mInteractiveEnd = mState.mCursorPosition; + } + } + else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd); + + EnsureCursorVisible(); + } +} + +static bool IsUTFSequence(char c) +{ + return (c & 0xC0) == 0x80; +} + +void TextEditor::MoveLeft(int aAmount, bool aSelect, bool aWordMode) +{ + if (mLines.empty()) + return; + + auto oldPos = mState.mCursorPosition; + mState.mCursorPosition = GetActualCursorCoordinates(); + auto line = mState.mCursorPosition.mLine; + auto cindex = GetCharacterIndex(mState.mCursorPosition); + + while (aAmount-- > 0) + { + if (cindex == 0) + { + if (line > 0) + { + --line; + if ((int)mLines.size() > line) + cindex = (int)mLines[line].size(); + else + cindex = 0; + } + } + else + { + --cindex; + if (cindex > 0) + { + if ((int)mLines.size() > line) + { + while (cindex > 0 && IsUTFSequence(mLines[line][cindex].mChar)) + --cindex; + } + } + } + + mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex)); + if (aWordMode) + { + mState.mCursorPosition = FindWordStart(mState.mCursorPosition); + cindex = GetCharacterIndex(mState.mCursorPosition); + } + } + + mState.mCursorPosition = Coordinates(line, GetCharacterColumn(line, cindex)); + + assert(mState.mCursorPosition.mColumn >= 0); + if (aSelect) + { + if (oldPos == mInteractiveStart) + mInteractiveStart = mState.mCursorPosition; + else if (oldPos == mInteractiveEnd) + mInteractiveEnd = mState.mCursorPosition; + else + { + mInteractiveStart = mState.mCursorPosition; + mInteractiveEnd = oldPos; + } + } + else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal); + + EnsureCursorVisible(); +} + +bool TextEditor::PeekLeftIsWhiteSpace(int aAmount) { + if (mLines.empty()) + return true; + + auto oldPos = mState.mCursorPosition; + auto line = mState.mCursorPosition.mLine; + auto cindex = GetCharacterIndex(mState.mCursorPosition); + + while (aAmount-- > 0) + { + if (cindex == 0) + { + return true; + } + else + { + --cindex; + if (cindex > 0) + { + if ((int)mLines.size() > line) + { + while (cindex > 0 && IsUTFSequence(mLines[line][cindex].mChar)) + --cindex; + } + } + } + } + + return isspace(mLines[line][cindex].mChar); +} + +void TextEditor::MoveRight(int aAmount, bool aSelect, bool aWordMode) +{ + auto oldPos = mState.mCursorPosition; + + if (mLines.empty() || oldPos.mLine >= mLines.size()) + return; + + auto cindex = GetCharacterIndex(mState.mCursorPosition); + while (aAmount-- > 0) + { + auto lindex = mState.mCursorPosition.mLine; + auto& line = mLines[lindex]; + + if (cindex >= line.size()) + { + if (mState.mCursorPosition.mLine < mLines.size() - 1) + { + mState.mCursorPosition.mLine = std::max(0, std::min((int)mLines.size() - 1, mState.mCursorPosition.mLine + 1)); + mState.mCursorPosition.mColumn = 0; + } + else + return; + } + else + { + cindex += UTF8CharLength(line[cindex].mChar); + mState.mCursorPosition = Coordinates(lindex, GetCharacterColumn(lindex, cindex)); + if (aWordMode) + mState.mCursorPosition = FindNextWord(mState.mCursorPosition); + } + } + + if (aSelect) + { + if (oldPos == mInteractiveEnd) + mInteractiveEnd = SanitizeCoordinates(mState.mCursorPosition); + else if (oldPos == mInteractiveStart) + mInteractiveStart = mState.mCursorPosition; + else + { + mInteractiveStart = oldPos; + mInteractiveEnd = mState.mCursorPosition; + } + } + else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd, aSelect && aWordMode ? SelectionMode::Word : SelectionMode::Normal); + + EnsureCursorVisible(); +} + +void TextEditor::MoveTop(bool aSelect) +{ + auto oldPos = mState.mCursorPosition; + SetCursorPosition(Coordinates(0, 0)); + + if (mState.mCursorPosition != oldPos) + { + if (aSelect) + { + mInteractiveEnd = oldPos; + mInteractiveStart = mState.mCursorPosition; + } + else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd); + } +} + +void TextEditor::TextEditor::MoveBottom(bool aSelect) +{ + auto oldPos = GetCursorPosition(); + auto newPos = Coordinates((int)mLines.size() - 1, 0); + SetCursorPosition(newPos); + if (aSelect) + { + mInteractiveStart = oldPos; + mInteractiveEnd = newPos; + } + else + mInteractiveStart = mInteractiveEnd = newPos; + SetSelection(mInteractiveStart, mInteractiveEnd); +} + +void TextEditor::MoveHome(bool aSelect) +{ + auto oldPos = mState.mCursorPosition; + SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, 0)); + + if (mState.mCursorPosition != oldPos) + { + if (aSelect) + { + if (oldPos == mInteractiveStart) + mInteractiveStart = mState.mCursorPosition; + else if (oldPos == mInteractiveEnd) + mInteractiveEnd = mState.mCursorPosition; + else + { + mInteractiveStart = mState.mCursorPosition; + mInteractiveEnd = oldPos; + } + } + else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd); + } +} + +void TextEditor::MoveEnd(bool aSelect) +{ + auto oldPos = mState.mCursorPosition; + SetCursorPosition(Coordinates(mState.mCursorPosition.mLine, GetLineMaxColumn(oldPos.mLine))); + + if (mState.mCursorPosition != oldPos) + { + if (aSelect) + { + if (oldPos == mInteractiveEnd) + mInteractiveEnd = mState.mCursorPosition; + else if (oldPos == mInteractiveStart) + mInteractiveStart = mState.mCursorPosition; + else + { + mInteractiveStart = oldPos; + mInteractiveEnd = mState.mCursorPosition; + } + } + else + mInteractiveStart = mInteractiveEnd = mState.mCursorPosition; + SetSelection(mInteractiveStart, mInteractiveEnd); + } +} + +bool TextEditor::SubstringMatch(const char *find, size_t findLen, bool matchCase, bool matchWholeWords, const TextEditor::Line line, int lineNum, size_t i) +{ + bool found = true; + + if (i + findLen >= line.size()) + { + return false; + } + + if (matchCase) + { + for (std::size_t j = 0; j < findLen; j++) + { + if (line[i + j].mChar != find[j]) + { + found = false; + break; + } + } + } + else + { + for (std::size_t j = 0; j < findLen; j++) + { + if (std::tolower(line[i + j].mChar) != std::tolower(find[j])) { + found = false; + break; + } + } + } + + if (found && matchWholeWords) + { + if (i > 0) + { + int preIndex = GetCharacterIndex(Coordinates(lineNum, i - 1)); + Char c = line[preIndex].mChar; + + if (isalnum(c) || c == '_') + { + found = false; + } + } + if (i + findLen < line.size()) + { + int postIndex = i + findLen; + Char c = line[postIndex].mChar; + + if (isalnum(c) || c == '_') + { + found = false; + } + } + } + + return found; +} + +bool TextEditor::FindNext(const char* find, bool matchCase, bool matchWholeWords, bool searchForward) +{ + Coordinates rangeStart, rangeEnd; + Coordinates pos; + + if (!find || find[0] == '\0') + { + return false; + } + + if (searchForward) + { + rangeStart = mState.mSelectionEnd; + rangeEnd = Coordinates((int)mLines.size() - 1, 0); + } + else + { + rangeStart = Coordinates(0, 0); + rangeEnd = mState.mSelectionStart; + } + + bool found = false; + + size_t findLen = strlen(find); + + if (searchForward) + { + pos = rangeStart; + while (!found && pos < rangeEnd) + { + auto& line = mLines[pos.mLine]; + int lineCol = GetLineMaxColumn(pos.mLine); + + if (lineCol >= findLen) + { + for (int i = pos.mColumn; i <= lineCol - findLen; i++) + { + int index = GetCharacterIndex(Coordinates(pos.mLine, i)); + + found = SubstringMatch(find, findLen, matchCase, matchWholeWords, line, pos.mLine, index); + + if (found) + { + // found it + int column = GetCharacterColumn(pos.mLine, index); + pos = Coordinates(pos.mLine, column); + break; + } + } + } + if (found) + { + break; + } + pos = Coordinates(pos.mLine + 1, 0); + } + } + else + { + pos = rangeEnd; + while (!found && pos > rangeStart) + { + auto& line = mLines[pos.mLine]; + int lineCol = GetLineMaxColumn(pos.mLine); + + if (lineCol >= findLen) + { + for (int i = pos.mColumn - findLen; i >= 0; i--) + { + int index = GetCharacterIndex(Coordinates(pos.mLine, i)); + + found = SubstringMatch(find, findLen, matchCase, matchWholeWords, line, pos.mLine, index); + + if (found) + { + int column = GetCharacterColumn(pos.mLine, index); + pos = Coordinates(pos.mLine, column); + break; + } + } + } + + if (found) + { + break; + } + if (pos.mLine == 0) + { + break; + } + pos = Coordinates(pos.mLine - 1, GetLineMaxColumn(pos.mLine - 1)); + } + } + + if (found) + { + SetCursorPosition(pos); + SetSelection(pos, Coordinates(pos.mLine, pos.mColumn + findLen)); + EnsureCursorVisible(); + } + + return found; +} + +int TextEditor::ReplaceAll(const char* find, const char* replace, bool matchCase, bool matchWholeWords) { + int replacements; + + replacements = 0; + SetCursorPosition(Coordinates(0, 0)); + while ( FindNext( find, matchCase, matchWholeWords, true ) ) { + DeleteSelection(); + InsertText(replace); + replacements++; + } + + return replacements; +} + +static int GetGlyphWidth(const TextEditor::Line& line, int cindex, int maxColumn, int tabSize) +{ + if (line[cindex].mChar == '\t') + { + int c = 0; + int i = 0; + int charWidth = 0; + for (; i < line.size() && i < cindex + 1 && c < maxColumn;) + { + if (line[i].mChar == '\t') + charWidth = ((c / tabSize) * tabSize + tabSize) - c; + else + charWidth = 1; + c += charWidth; + i += UTF8CharLength(line[i].mChar); + } + return charWidth; + } + else + { + return 1; + } +} + +void TextEditor::Delete() +{ + assert(!mReadOnly); + + if (mLines.empty()) + return; + + UndoRecord u; + u.mBefore = mState; + + if (HasSelection()) + { + u.mRemoved = GetSelectedText(); + u.mRemovedStart = mState.mSelectionStart; + u.mRemovedEnd = mState.mSelectionEnd; + + DeleteSelection(); + } + else + { + auto pos = GetActualCursorCoordinates(); + SetCursorPosition(pos); + auto& line = mLines[pos.mLine]; + + if (pos.mColumn == GetLineMaxColumn(pos.mLine)) + { + if (pos.mLine == (int)mLines.size() - 1) + return; + + u.mRemoved = '\n'; + u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); + Advance(u.mRemovedEnd); + + auto& nextLine = mLines[pos.mLine + 1]; + line.insert(line.end(), nextLine.begin(), nextLine.end()); + RemoveLine(pos.mLine + 1); + } + else + { + auto cindex = GetCharacterIndex(pos); + u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); + u.mRemovedEnd.mColumn++; + u.mRemoved = GetText(u.mRemovedStart, u.mRemovedEnd); + + auto d = UTF8CharLength(line[cindex].mChar); + while (d-- > 0 && cindex < (int)line.size()) + line.erase(line.begin() + cindex); + } + + mTextChanged = true; + + Colorize(pos.mLine, 1); + } + + u.mAfter = mState; + AddUndo(u); +} + +void TextEditor::Backspace() +{ + assert(!mReadOnly); + + if (mLines.empty()) + return; + + UndoRecord u; + u.mBefore = mState; + + if (HasSelection()) + { + u.mRemoved = GetSelectedText(); + u.mRemovedStart = mState.mSelectionStart; + u.mRemovedEnd = mState.mSelectionEnd; + + DeleteSelection(); + } + else + { + auto pos = GetActualCursorCoordinates(); + SetCursorPosition(pos); + + if (mState.mCursorPosition.mColumn == 0) + { + if (mState.mCursorPosition.mLine == 0) + return; + + u.mRemoved = '\n'; + u.mRemovedStart = u.mRemovedEnd = Coordinates(pos.mLine - 1, GetLineMaxColumn(pos.mLine - 1)); + Advance(u.mRemovedEnd); + + auto& line = mLines[mState.mCursorPosition.mLine]; + auto& prevLine = mLines[mState.mCursorPosition.mLine - 1]; + auto prevSize = GetLineMaxColumn(mState.mCursorPosition.mLine - 1); + prevLine.insert(prevLine.end(), line.begin(), line.end()); + + ErrorMarkers etmp; + for (auto& i : mErrorMarkers) + etmp.insert(ErrorMarkers::value_type(i.first - 1 == mState.mCursorPosition.mLine ? i.first - 1 : i.first, i.second)); + mErrorMarkers = std::move(etmp); + + RemoveLine(mState.mCursorPosition.mLine); + --mState.mCursorPosition.mLine; + mState.mCursorPosition.mColumn = prevSize; + } + else + { + auto& line = mLines[mState.mCursorPosition.mLine]; + auto cindex = GetCharacterIndex(pos) - 1; + auto cend = cindex + 1; + while (cindex > 0 && IsUTFSequence(line[cindex].mChar)) + --cindex; + + //if (cindex > 0 && UTF8CharLength(line[cindex].mChar) > 1) + // --cindex; + + u.mRemovedStart = u.mRemovedEnd = GetActualCursorCoordinates(); + int width = GetGlyphWidth(line, cindex, mState.mCursorPosition.mColumn, mTabSize); + u.mRemovedStart.mColumn -= width; + mState.mCursorPosition.mColumn -= width; + + while (cindex < line.size() && cend-- > cindex) + { + u.mRemoved += line[cindex].mChar; + line.erase(line.begin() + cindex); + } + } + + mTextChanged = true; + + EnsureCursorVisible(); + Colorize(mState.mCursorPosition.mLine, 1); + } + + u.mAfter = mState; + AddUndo(u); +} + +void TextEditor::SelectWordUnderCursor() +{ + auto c = GetCursorPosition(); + SetSelection(FindWordStart(c), FindWordEnd(c)); +} + +void TextEditor::SelectAll() +{ + SetSelection(Coordinates(0, 0), Coordinates((int)mLines.size(), 0)); +} + +bool TextEditor::HasSelection() const +{ + return mState.mSelectionEnd > mState.mSelectionStart; +} + +void TextEditor::Copy() +{ + if (HasSelection()) + { + ImGui::SetClipboardText(GetSelectedText().c_str()); + } + else + { + if (!mLines.empty()) + { + std::string str; + auto& line = mLines[GetActualCursorCoordinates().mLine]; + for (auto& g : line) + str.push_back(g.mChar); + ImGui::SetClipboardText(str.c_str()); + } + } +} + +void TextEditor::Cut() +{ + if (IsReadOnly()) + { + Copy(); + } + else + { + if (HasSelection()) + { + UndoRecord u; + u.mBefore = mState; + u.mRemoved = GetSelectedText(); + u.mRemovedStart = mState.mSelectionStart; + u.mRemovedEnd = mState.mSelectionEnd; + + Copy(); + DeleteSelection(); + + u.mAfter = mState; + AddUndo(u); + } + } +} + +void TextEditor::Paste() +{ + if (IsReadOnly()) + return; + + auto clipText = ImGui::GetClipboardText(); + if (clipText != nullptr && strlen(clipText) > 0) + { + UndoRecord u; + u.mBefore = mState; + + if (HasSelection()) + { + u.mRemoved = GetSelectedText(); + u.mRemovedStart = mState.mSelectionStart; + u.mRemovedEnd = mState.mSelectionEnd; + DeleteSelection(); + } + + u.mAdded = clipText; + u.mAddedStart = GetActualCursorCoordinates(); + + InsertText(clipText); + + u.mAddedEnd = GetActualCursorCoordinates(); + u.mAfter = mState; + AddUndo(u); + } +} + +bool TextEditor::CanUndo() const +{ + return !mReadOnly && mUndoIndex > 0; +} + +int TextEditor::GetUndoIndex() const +{ + return mUndoIndex; +} +size_t TextEditor::GetUndoBufferSize() const +{ + return mUndoBuffer.size(); +} + +bool TextEditor::CanRedo() const +{ + return !mReadOnly && mUndoIndex < (int)mUndoBuffer.size(); +} + +void TextEditor::Undo(int aSteps) +{ + while (CanUndo() && aSteps-- > 0) + mUndoBuffer[--mUndoIndex].Undo(this); + + if (mUndoIndex == mUndoIndexLastSave) { + if (mUndoIndexFirstEdit >= mUndoIndexLastSave && !mMarkedUnsaved) + mSaveState = true; + } + else + mSaveState = false; +} + +void TextEditor::Redo(int aSteps) +{ + while (CanRedo() && aSteps-- > 0) + mUndoBuffer[mUndoIndex++].Redo(this); + + if (mUndoIndex == mUndoIndexLastSave) { + if (mUndoIndexFirstEdit >= mUndoIndexLastSave && !mMarkedUnsaved) + mSaveState = true; + } + else + mSaveState = false; +} + +void TextEditor::MarkSaved() +{ + mSaveState = true; + mMarkedUnsaved = false; + mUndoIndexLastSave = mUndoIndex; + mUndoIndexFirstEdit = mUndoIndex; +} + +// Marks the file changed in a way that can't be reverted with undo/redo +void TextEditor::MarkUnsaved() +{ + mSaveState = false; + mMarkedUnsaved = true; +} +void TextEditor::MarkSaved(bool saveState) +{ + if (saveState) { + mUndoIndexLastSave = mUndoIndex; + mUndoIndexFirstEdit = mUndoIndex; + } + else + mMarkedUnsaved = true; + mSaveState = saveState; + mMarkedUnsaved = !saveState; +} +bool TextEditor::IsSaved() const { + return mSaveState; +} + +const TextEditor::Palette & TextEditor::GetDarkPalette() +{ + const static Palette p = { { + 0xff7f7f7f, // Default + 0xffd69c56, // Keyword + 0xff00ff00, // Number + 0xff7070e0, // String + 0xff70a0e0, // Char literal + 0xffffffff, // Punctuation + 0xff408080, // Preprocessor + 0xffaaaaaa, // Identifier + 0xff9bc64d, // Known identifier + 0xffc040a0, // Preproc identifier + 0xff206020, // Comment (single line) + 0xff406020, // Comment (multi line) + 0xff101010, // Background + 0xffe0e0e0, // Cursor + 0x80a06020, // Selection + 0x800020ff, // ErrorMarker + 0x40f08000, // Breakpoint + 0xff707000, // Line number + 0x40000000, // Current line fill + 0x40808080, // Current line fill (inactive) + 0x40a0a0a0, // Current line edge + } }; + return p; +} + +const TextEditor::Palette & TextEditor::GetLightPalette() +{ + const static Palette p = { { + 0xff7f7f7f, // None + 0xffff0c06, // Keyword + 0xff008000, // Number + 0xff2020a0, // String + 0xff304070, // Char literal + 0xff000000, // Punctuation + 0xff406060, // Preprocessor + 0xff404040, // Identifier + 0xff606010, // Known identifier + 0xffc040a0, // Preproc identifier + 0xff205020, // Comment (single line) + 0xff405020, // Comment (multi line) + 0xffffffff, // Background + 0xff000000, // Cursor + 0x80600000, // Selection + 0xa00010ff, // ErrorMarker + 0x80f08000, // Breakpoint + 0xff505000, // Line number + 0x40000000, // Current line fill + 0x40808080, // Current line fill (inactive) + 0x40000000, // Current line edge + } }; + return p; +} + +const TextEditor::Palette & TextEditor::GetRetroBluePalette() +{ + const static Palette p = { { + 0xff00ffff, // None + 0xffffff00, // Keyword + 0xff00ff00, // Number + 0xff808000, // String + 0xff808000, // Char literal + 0xffffffff, // Punctuation + 0xff008000, // Preprocessor + 0xff00ffff, // Identifier + 0xffffffff, // Known identifier + 0xffff00ff, // Preproc identifier + 0xff808080, // Comment (single line) + 0xff404040, // Comment (multi line) + 0xff800000, // Background + 0xff0080ff, // Cursor + 0x80ffff00, // Selection + 0xa00000ff, // ErrorMarker + 0x80ff8000, // Breakpoint + 0xff808000, // Line number + 0x40000000, // Current line fill + 0x40808080, // Current line fill (inactive) + 0x40000000, // Current line edge + } }; + return p; +} + + +std::string TextEditor::GetText() const +{ + return GetText(Coordinates(), Coordinates((int)mLines.size(), 0)); +} + +std::vector TextEditor::GetTextLines() const +{ + std::vector result; + + result.reserve(mLines.size()); + + for (auto & line : mLines) + { + std::string text; + + text.resize(line.size()); + + for (size_t i = 0; i < line.size(); ++i) + text[i] = line[i].mChar; + + result.emplace_back(std::move(text)); + } + + return result; +} + +std::string TextEditor::GetSelectedText() const +{ + return GetText(mState.mSelectionStart, mState.mSelectionEnd); +} + +std::string TextEditor::GetCurrentLineText() const +{ + auto lineLength = GetLineMaxColumn(mState.mCursorPosition.mLine); + return GetText( + Coordinates(mState.mCursorPosition.mLine, 0), + Coordinates(mState.mCursorPosition.mLine, lineLength)); +} + +ImVec2 TextEditor::GetCursorScreenCoordinates() const { + return mCursorScreenPos; +} + +void TextEditor::ProcessInputs() +{ +} + +void TextEditor::Colorize(int aFromLine, int aLines) +{ + int toLine = aLines == -1 ? (int)mLines.size() : std::min((int)mLines.size(), aFromLine + aLines); + mColorRangeMin = std::min(mColorRangeMin, aFromLine); + mColorRangeMax = std::max(mColorRangeMax, toLine); + mColorRangeMin = std::max(0, mColorRangeMin); + mColorRangeMax = std::max(mColorRangeMin, mColorRangeMax); + mCheckComments = true; +} + +void TextEditor::ColorizeRange(int aFromLine, int aToLine) +{ + if (mLines.empty() || aFromLine >= aToLine) + return; + + std::string buffer; + std::cmatch results; + std::string id; + + int endLine = std::max(0, std::min((int)mLines.size(), aToLine)); + for (int i = aFromLine; i < endLine; ++i) + { + auto& line = mLines[i]; + + if (line.empty()) + continue; + + buffer.resize(line.size()); + for (size_t j = 0; j < line.size(); ++j) + { + auto& col = line[j]; + buffer[j] = col.mChar; + col.mColorIndex = PaletteIndex::Default; + } + + const char * bufferBegin = &buffer.front(); + const char * bufferEnd = bufferBegin + buffer.size(); + + auto last = bufferEnd; + + for (auto first = bufferBegin; first != last; ) + { + const char * token_begin = nullptr; + const char * token_end = nullptr; + PaletteIndex token_color = PaletteIndex::Default; + + bool hasTokenizeResult = false; + + if (mLanguageDefinition.mTokenize != nullptr) + { + if (mLanguageDefinition.mTokenize(first, last, token_begin, token_end, token_color)) + hasTokenizeResult = true; + } + + if (hasTokenizeResult == false) + { + // todo : remove + //printf("using regex for %.*s\n", first + 10 < last ? 10 : int(last - first), first); + + for (auto& p : mRegexList) + { + if (std::regex_search(first, last, results, p.first, std::regex_constants::match_continuous)) + { + hasTokenizeResult = true; + + auto& v = *results.begin(); + token_begin = v.first; + token_end = v.second; + token_color = p.second; + break; + } + } + } + + if (hasTokenizeResult == false) + { + first++; + } + else + { + const size_t token_length = token_end - token_begin; + + if (token_color == PaletteIndex::Identifier) + { + id.assign(token_begin, token_end); + + // todo : allmost all language definitions use lower case to specify keywords, so shouldn't this use ::tolower ? + if (!mLanguageDefinition.mCaseSensitive) + std::transform(id.begin(), id.end(), id.begin(), ::toupper); + + if (!line[first - bufferBegin].mPreprocessor) + { + if (mLanguageDefinition.mKeywords.count(id) != 0) + token_color = PaletteIndex::Keyword; + else if (mLanguageDefinition.mIdentifiers.count(id) != 0) + token_color = PaletteIndex::KnownIdentifier; + else if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0) + token_color = PaletteIndex::PreprocIdentifier; + } + else + { + if (mLanguageDefinition.mPreprocIdentifiers.count(id) != 0) + token_color = PaletteIndex::PreprocIdentifier; + } + } + + for (size_t j = 0; j < token_length; ++j) + line[(token_begin - bufferBegin) + j].mColorIndex = token_color; + + first = token_end; + } + } + } +} + +void TextEditor::ColorizeInternal() +{ + if (mLines.empty() || !mColorizerEnabled) + return; + + if (mCheckComments) + { + auto endLine = mLines.size(); + auto endIndex = 0; + auto commentStartLine = endLine; + auto commentStartIndex = endIndex; + auto withinString = false; + auto withinSingleLineComment = false; + auto withinPreproc = false; + auto firstChar = true; // there is no other non-whitespace characters in the line before + auto concatenate = false; // '\' on the very end of the line + auto currentLine = 0; + auto currentIndex = 0; + while (currentLine < endLine || currentIndex < endIndex) + { + auto& line = mLines[currentLine]; + + if (currentIndex == 0 && !concatenate) + { + withinSingleLineComment = false; + withinPreproc = false; + firstChar = true; + } + + concatenate = false; + + if (!line.empty()) + { + auto& g = line[currentIndex]; + auto c = g.mChar; + + if (c != mLanguageDefinition.mPreprocChar && !isspace(c)) + firstChar = false; + + if (currentIndex == (int)line.size() - 1 && line[line.size() - 1].mChar == '\\') + concatenate = true; + + bool inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); + + if (withinString) + { + line[currentIndex].mMultiLineComment = inComment; + + if (c == '\"') + { + if (currentIndex + 1 < (int)line.size() && line[currentIndex + 1].mChar == '\"') + { + currentIndex += 1; + if (currentIndex < (int)line.size()) + line[currentIndex].mMultiLineComment = inComment; + } + else + withinString = false; + } + else if (c == '\\') + { + currentIndex += 1; + if (currentIndex < (int)line.size()) + line[currentIndex].mMultiLineComment = inComment; + } + } + else + { + if (firstChar && c == mLanguageDefinition.mPreprocChar) + withinPreproc = true; + + if (c == '\"') + { + withinString = true; + line[currentIndex].mMultiLineComment = inComment; + } + else + { + auto pred = [](const char& a, const Glyph& b) { return a == b.mChar; }; + auto from = line.begin() + currentIndex; + auto& startStr = mLanguageDefinition.mCommentStart; + auto& singleStartStr = mLanguageDefinition.mSingleLineComment; + + if (singleStartStr.size() > 0 && + currentIndex + singleStartStr.size() <= line.size() && + equals(singleStartStr.begin(), singleStartStr.end(), from, from + singleStartStr.size(), pred)) + { + withinSingleLineComment = true; + } + else if (startStr.size() != 0 && !withinSingleLineComment && currentIndex + startStr.size() <= line.size() && + equals(startStr.begin(), startStr.end(), from, from + startStr.size(), pred)) + { + commentStartLine = currentLine; + commentStartIndex = currentIndex; + } + + inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); + + line[currentIndex].mMultiLineComment = inComment; + line[currentIndex].mComment = withinSingleLineComment; + + auto& endStr = mLanguageDefinition.mCommentEnd; + if (currentIndex + 1 >= (int)endStr.size() && + equals(endStr.begin(), endStr.end(), from + 1 - endStr.size(), from + 1, pred)) + { + commentStartIndex = endIndex; + commentStartLine = endLine; + } + } + } + line[currentIndex].mPreprocessor = withinPreproc; + currentIndex += UTF8CharLength(c); + if (currentIndex >= (int)line.size()) + { + currentIndex = 0; + ++currentLine; + } + } + else + { + currentIndex = 0; + ++currentLine; + } + } + mCheckComments = false; + } + + if (mColorRangeMin < mColorRangeMax) + { + const int increment = (mLanguageDefinition.mTokenize == nullptr) ? 10 : 10000; + const int to = std::min(mColorRangeMin + increment, mColorRangeMax); + ColorizeRange(mColorRangeMin, to); + mColorRangeMin = to; + + if (mColorRangeMax == mColorRangeMin) + { + mColorRangeMin = std::numeric_limits::max(); + mColorRangeMax = 0; + } + return; + } +} + +float TextEditor::TextDistanceToLineStart(const Coordinates& aFrom) const +{ + auto& line = mLines[aFrom.mLine]; + float distance = 0.0f; + float spaceSize = ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).x; + int colIndex = GetCharacterIndex(aFrom); + for (size_t it = 0u; it < line.size() && it < colIndex; ) + { + if (line[it].mChar == '\t') + { + distance = (1.0f + std::floor((1.0f + distance) / (float(mTabSize) * spaceSize))) * (float(mTabSize) * spaceSize); + ++it; + } + else + { + auto d = UTF8CharLength(line[it].mChar); + char tempCString[7]; + int i = 0; + for (; i < 6 && d-- > 0 && it < (int)line.size(); i++, it++) + tempCString[i] = line[it].mChar; + + tempCString[i] = '\0'; + distance += ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, tempCString, nullptr, nullptr).x; + } + } + + return distance; +} + +void TextEditor::EnsureCursorVisible() +{ + if (!mWithinRender) + { + mScrollToCursor = true; + return; + } + + float scrollX = ImGui::GetScrollX(); + float scrollY = ImGui::GetScrollY(); + + auto height = ImGui::GetWindowHeight(); + auto width = ImGui::GetWindowWidth(); + + auto top = 1 + (int)ceil(scrollY / mCharAdvance.y); + auto bottom = (int)ceil((scrollY + height) / mCharAdvance.y); + + auto left = (int)ceil(scrollX / mCharAdvance.x); + auto right = (int)ceil((scrollX + width) / mCharAdvance.x); + + auto pos = GetActualCursorCoordinates(); + auto len = TextDistanceToLineStart(pos); + + if (pos.mLine < top) + ImGui::SetScrollY(std::max(0.0f, (pos.mLine - 1) * mCharAdvance.y)); + if (pos.mLine > bottom - 4) + ImGui::SetScrollY(std::max(0.0f, (pos.mLine + 4) * mCharAdvance.y - height)); + if (len + mTextStart < left + 4) + ImGui::SetScrollX(std::max(0.0f, len + mTextStart - 4)); + if (len + mTextStart > right - 4) + ImGui::SetScrollX(std::max(0.0f, len + mTextStart + 4 - width)); +} + +int TextEditor::GetPageSize() const +{ + auto height = ImGui::GetWindowHeight() - 20.0f; + return (int)floor(height / mCharAdvance.y); +} + +TextEditor::UndoRecord::UndoRecord( + const std::string& aAdded, + const TextEditor::Coordinates aAddedStart, + const TextEditor::Coordinates aAddedEnd, + const std::string& aRemoved, + const TextEditor::Coordinates aRemovedStart, + const TextEditor::Coordinates aRemovedEnd, + TextEditor::EditorState& aBefore, + TextEditor::EditorState& aAfter) + : mAdded(aAdded) + , mAddedStart(aAddedStart) + , mAddedEnd(aAddedEnd) + , mRemoved(aRemoved) + , mRemovedStart(aRemovedStart) + , mRemovedEnd(aRemovedEnd) + , mBefore(aBefore) + , mAfter(aAfter) +{ + assert(mAddedStart <= mAddedEnd); + assert(mRemovedStart <= mRemovedEnd); +} + +void TextEditor::UndoRecord::Undo(TextEditor * aEditor) +{ + if (!mAdded.empty()) + { + aEditor->DeleteRange(mAddedStart, mAddedEnd); + aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 2); + } + + if (!mRemoved.empty()) + { + auto start = mRemovedStart; + aEditor->InsertTextAt(start, mRemoved.c_str()); + aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 2); + } + + aEditor->mState = mBefore; + aEditor->EnsureCursorVisible(); + +} + +void TextEditor::UndoRecord::Redo(TextEditor * aEditor) +{ + if (!mRemoved.empty()) + { + aEditor->DeleteRange(mRemovedStart, mRemovedEnd); + aEditor->Colorize(mRemovedStart.mLine - 1, mRemovedEnd.mLine - mRemovedStart.mLine + 1); + } + + if (!mAdded.empty()) + { + auto start = mAddedStart; + aEditor->InsertTextAt(start, mAdded.c_str()); + aEditor->Colorize(mAddedStart.mLine - 1, mAddedEnd.mLine - mAddedStart.mLine + 1); + } + + aEditor->mState = mAfter; + aEditor->EnsureCursorVisible(); +} + +static bool TokenizeCStyleString(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end) +{ + const char * p = in_begin; + + if (*p == '"') + { + p++; + + while (p < in_end) + { + // handle end of string + if (*p == '"') + { + out_begin = in_begin; + out_end = p + 1; + return true; + } + + // handle escape character for " + if (*p == '\\' && p + 1 < in_end && p[1] == '"') + p++; + + p++; + } + } + + return false; +} + +static bool TokenizeCStyleCharacterLiteral(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end) +{ + const char * p = in_begin; + + if (*p == '\'') + { + p++; + + // handle escape characters + if (p < in_end && *p == '\\') + p++; + + if (p < in_end) + p++; + + // handle end of character literal + if (p < in_end && *p == '\'') + { + out_begin = in_begin; + out_end = p + 1; + return true; + } + } + + return false; +} + +static bool TokenizeCStyleIdentifier(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end) +{ + const char * p = in_begin; + + if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || *p == '_') + { + p++; + + while ((p < in_end) && ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == '_')) + p++; + + out_begin = in_begin; + out_end = p; + return true; + } + + return false; +} + +static bool TokenizeCStyleNumber(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end) +{ + const char * p = in_begin; + + const bool startsWithNumber = *p >= '0' && *p <= '9'; + + if (*p != '+' && *p != '-' && !startsWithNumber) + return false; + + p++; + + bool hasNumber = startsWithNumber; + + while (p < in_end && (*p >= '0' && *p <= '9')) + { + hasNumber = true; + + p++; + } + + if (hasNumber == false) + return false; + + bool isFloat = false; + bool isHex = false; + bool isBinary = false; + + if (p < in_end) + { + if (*p == '.') + { + isFloat = true; + + p++; + + while (p < in_end && (*p >= '0' && *p <= '9')) + p++; + } + else if (*p == 'x' || *p == 'X') + { + // hex formatted integer of the type 0xef80 + + isHex = true; + + p++; + + while (p < in_end && ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F'))) + p++; + } + else if (*p == 'b' || *p == 'B') + { + // binary formatted integer of the type 0b01011101 + + isBinary = true; + + p++; + + while (p < in_end && (*p >= '0' && *p <= '1')) + p++; + } + } + + if (isHex == false && isBinary == false) + { + // floating point exponent + if (p < in_end && (*p == 'e' || *p == 'E')) + { + isFloat = true; + + p++; + + if (p < in_end && (*p == '+' || *p == '-')) + p++; + + bool hasDigits = false; + + while (p < in_end && (*p >= '0' && *p <= '9')) + { + hasDigits = true; + + p++; + } + + if (hasDigits == false) + return false; + } + + // single precision floating point type + if (p < in_end && *p == 'f') + p++; + } + + if (isFloat == false) + { + // integer size type + while (p < in_end && (*p == 'u' || *p == 'U' || *p == 'l' || *p == 'L')) + p++; + } + + out_begin = in_begin; + out_end = p; + return true; +} + +static bool TokenizeCStylePunctuation(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end) +{ + (void)in_end; + + switch (*in_begin) + { + case '[': + case ']': + case '{': + case '}': + case '!': + case '%': + case '^': + case '&': + case '*': + case '(': + case ')': + case '-': + case '+': + case '=': + case '~': + case '|': + case '<': + case '>': + case '?': + case ':': + case '/': + case ';': + case ',': + case '.': + out_begin = in_begin; + out_end = in_begin + 1; + return true; + } + + return false; +} + +const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::CPlusPlus() +{ + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) + { + static const char* const cppKeywords[] = { + "alignas", "alignof", "and", "and_eq", "asm", "atomic_cancel", "atomic_commit", "atomic_noexcept", "auto", "bitand", "bitor", "bool", "break", "case", "catch", "char", "char16_t", "char32_t", "class", + "compl", "concept", "const", "constexpr", "const_cast", "continue", "decltype", "default", "delete", "do", "double", "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", "float", + "for", "friend", "goto", "if", "import", "inline", "int", "long", "module", "mutable", "namespace", "new", "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", "private", "protected", "public", + "register", "reinterpret_cast", "requires", "return", "short", "signed", "sizeof", "static", "static_assert", "static_cast", "struct", "switch", "synchronized", "template", "this", "thread_local", + "throw", "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq" + }; + for (auto& k : cppKeywords) + langDef.mKeywords.insert(k); + + static const char* const identifiers[] = { + "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph", + "ispunct", "isspace", "isupper", "kbhit", "log10", "log2", "log", "memcmp", "modf", "pow", "printf", "sprintf", "snprintf", "putchar", "putenv", "puts", "rand", "remove", "rename", "sinh", "sqrt", "srand", "strcat", "strcmp", "strerror", "time", "tolower", "toupper", + "std", "string", "vector", "map", "unordered_map", "set", "unordered_set", "min", "max" + }; + for (auto& k : identifiers) + { + Identifier id; + id.mDeclaration = "Built-in function"; + langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); + } + + langDef.mTokenize = [](const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end, PaletteIndex & paletteIndex) -> bool + { + paletteIndex = PaletteIndex::Max; + + while (in_begin < in_end && isascii(*in_begin) && isblank(*in_begin)) + in_begin++; + + if (in_begin == in_end) + { + out_begin = in_end; + out_end = in_end; + paletteIndex = PaletteIndex::Default; + } + else if (TokenizeCStyleString(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::String; + else if (TokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::CharLiteral; + else if (TokenizeCStyleIdentifier(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::Identifier; + else if (TokenizeCStyleNumber(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::Number; + else if (TokenizeCStylePunctuation(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::Punctuation; + + return paletteIndex != PaletteIndex::Max; + }; + + langDef.mCommentStart = "/*"; + langDef.mCommentEnd = "*/"; + langDef.mSingleLineComment = "//"; + + langDef.mCaseSensitive = true; + langDef.mAutoIndentation = true; + + langDef.mName = "C++"; + + inited = true; + } + return langDef; +} + +const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::HLSL() +{ + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) + { + static const char* const keywords[] = { + "AppendStructuredBuffer", "asm", "asm_fragment", "BlendState", "bool", "break", "Buffer", "ByteAddressBuffer", "case", "cbuffer", "centroid", "class", "column_major", "compile", "compile_fragment", + "CompileShader", "const", "continue", "ComputeShader", "ConsumeStructuredBuffer", "default", "DepthStencilState", "DepthStencilView", "discard", "do", "double", "DomainShader", "dword", "else", + "export", "extern", "false", "float", "for", "fxgroup", "GeometryShader", "groupshared", "half", "Hullshader", "if", "in", "inline", "inout", "InputPatch", "int", "interface", "line", "lineadj", + "linear", "LineStream", "matrix", "min16float", "min10float", "min16int", "min12int", "min16uint", "namespace", "nointerpolation", "noperspective", "NULL", "out", "OutputPatch", "packoffset", + "pass", "pixelfragment", "PixelShader", "point", "PointStream", "precise", "RasterizerState", "RenderTargetView", "return", "register", "row_major", "RWBuffer", "RWByteAddressBuffer", "RWStructuredBuffer", + "RWTexture1D", "RWTexture1DArray", "RWTexture2D", "RWTexture2DArray", "RWTexture3D", "sample", "sampler", "SamplerState", "SamplerComparisonState", "shared", "snorm", "stateblock", "stateblock_state", + "static", "string", "struct", "switch", "StructuredBuffer", "tbuffer", "technique", "technique10", "technique11", "texture", "Texture1D", "Texture1DArray", "Texture2D", "Texture2DArray", "Texture2DMS", + "Texture2DMSArray", "Texture3D", "TextureCube", "TextureCubeArray", "true", "typedef", "triangle", "triangleadj", "TriangleStream", "uint", "uniform", "unorm", "unsigned", "vector", "vertexfragment", + "VertexShader", "void", "volatile", "while", + "bool1","bool2","bool3","bool4","double1","double2","double3","double4", "float1", "float2", "float3", "float4", "int1", "int2", "int3", "int4", "in", "out", "inout", + "uint1", "uint2", "uint3", "uint4", "dword1", "dword2", "dword3", "dword4", "half1", "half2", "half3", "half4", + "float1x1","float2x1","float3x1","float4x1","float1x2","float2x2","float3x2","float4x2", + "float1x3","float2x3","float3x3","float4x3","float1x4","float2x4","float3x4","float4x4", + "half1x1","half2x1","half3x1","half4x1","half1x2","half2x2","half3x2","half4x2", + "half1x3","half2x3","half3x3","half4x3","half1x4","half2x4","half3x4","half4x4", + }; + for (auto& k : keywords) + langDef.mKeywords.insert(k); + + static const char* const identifiers[] = { + "abort", "abs", "acos", "all", "AllMemoryBarrier", "AllMemoryBarrierWithGroupSync", "any", "asdouble", "asfloat", "asin", "asint", "asint", "asuint", + "asuint", "atan", "atan2", "ceil", "CheckAccessFullyMapped", "clamp", "clip", "cos", "cosh", "countbits", "cross", "D3DCOLORtoUBYTE4", "ddx", + "ddx_coarse", "ddx_fine", "ddy", "ddy_coarse", "ddy_fine", "degrees", "determinant", "DeviceMemoryBarrier", "DeviceMemoryBarrierWithGroupSync", + "distance", "dot", "dst", "errorf", "EvaluateAttributeAtCentroid", "EvaluateAttributeAtSample", "EvaluateAttributeSnapped", "exp", "exp2", + "f16tof32", "f32tof16", "faceforward", "firstbithigh", "firstbitlow", "floor", "fma", "fmod", "frac", "frexp", "fwidth", "GetRenderTargetSampleCount", + "GetRenderTargetSamplePosition", "GroupMemoryBarrier", "GroupMemoryBarrierWithGroupSync", "InterlockedAdd", "InterlockedAnd", "InterlockedCompareExchange", + "InterlockedCompareStore", "InterlockedExchange", "InterlockedMax", "InterlockedMin", "InterlockedOr", "InterlockedXor", "isfinite", "isinf", "isnan", + "ldexp", "length", "lerp", "lit", "log", "log10", "log2", "mad", "max", "min", "modf", "msad4", "mul", "noise", "normalize", "pow", "printf", + "Process2DQuadTessFactorsAvg", "Process2DQuadTessFactorsMax", "Process2DQuadTessFactorsMin", "ProcessIsolineTessFactors", "ProcessQuadTessFactorsAvg", + "ProcessQuadTessFactorsMax", "ProcessQuadTessFactorsMin", "ProcessTriTessFactorsAvg", "ProcessTriTessFactorsMax", "ProcessTriTessFactorsMin", + "radians", "rcp", "reflect", "refract", "reversebits", "round", "rsqrt", "saturate", "sign", "sin", "sincos", "sinh", "smoothstep", "sqrt", "step", + "tan", "tanh", "tex1D", "tex1D", "tex1Dbias", "tex1Dgrad", "tex1Dlod", "tex1Dproj", "tex2D", "tex2D", "tex2Dbias", "tex2Dgrad", "tex2Dlod", "tex2Dproj", + "tex3D", "tex3D", "tex3Dbias", "tex3Dgrad", "tex3Dlod", "tex3Dproj", "texCUBE", "texCUBE", "texCUBEbias", "texCUBEgrad", "texCUBElod", "texCUBEproj", "transpose", "trunc" + }; + for (auto& k : identifiers) + { + Identifier id; + id.mDeclaration = "Built-in function"; + langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); + } + + langDef.mTokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); + langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); + langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); + + langDef.mCommentStart = "/*"; + langDef.mCommentEnd = "*/"; + langDef.mSingleLineComment = "//"; + + langDef.mCaseSensitive = true; + langDef.mAutoIndentation = true; + + langDef.mName = "HLSL"; + + inited = true; + } + return langDef; +} + +const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::GLSL() +{ + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) + { + static const char* const keywords[] = { + "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long", "register", "restrict", "return", "short", + "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while", "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary", + "_Noreturn", "_Static_assert", "_Thread_local" + }; + for (auto& k : keywords) + langDef.mKeywords.insert(k); + + static const char* const identifiers[] = { + "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph", + "ispunct", "isspace", "isupper", "kbhit", "log10", "log2", "log", "memcmp", "modf", "pow", "putchar", "putenv", "puts", "rand", "remove", "rename", "sinh", "sqrt", "srand", "strcat", "strcmp", "strerror", "time", "tolower", "toupper" + }; + for (auto& k : identifiers) + { + Identifier id; + id.mDeclaration = "Built-in function"; + langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); + } + + langDef.mTokenRegexStrings.push_back(std::make_pair("[ \\t]*#[ \\t]*[a-zA-Z_]+", PaletteIndex::Preprocessor)); + langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); + langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::CharLiteral)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); + + langDef.mCommentStart = "/*"; + langDef.mCommentEnd = "*/"; + langDef.mSingleLineComment = "//"; + + langDef.mCaseSensitive = true; + langDef.mAutoIndentation = true; + + langDef.mName = "GLSL"; + + inited = true; + } + return langDef; +} + +const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::C() +{ + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) + { + static const char* const keywords[] = { + "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long", "register", "restrict", "return", "short", + "signed", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while", "_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary", + "_Noreturn", "_Static_assert", "_Thread_local" + }; + for (auto& k : keywords) + langDef.mKeywords.insert(k); + + static const char* const identifiers[] = { + "abort", "abs", "acos", "asin", "atan", "atexit", "atof", "atoi", "atol", "ceil", "clock", "cosh", "ctime", "div", "exit", "fabs", "floor", "fmod", "getchar", "getenv", "isalnum", "isalpha", "isdigit", "isgraph", + "ispunct", "isspace", "isupper", "kbhit", "log10", "log2", "log", "memcmp", "modf", "pow", "putchar", "putenv", "puts", "rand", "remove", "rename", "sinh", "sqrt", "srand", "strcat", "strcmp", "strerror", "time", "tolower", "toupper" + }; + for (auto& k : identifiers) + { + Identifier id; + id.mDeclaration = "Built-in function"; + langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); + } + + langDef.mTokenize = [](const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end, PaletteIndex & paletteIndex) -> bool + { + paletteIndex = PaletteIndex::Max; + + while (in_begin < in_end && isascii(*in_begin) && isblank(*in_begin)) + in_begin++; + + if (in_begin == in_end) + { + out_begin = in_end; + out_end = in_end; + paletteIndex = PaletteIndex::Default; + } + else if (TokenizeCStyleString(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::String; + else if (TokenizeCStyleCharacterLiteral(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::CharLiteral; + else if (TokenizeCStyleIdentifier(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::Identifier; + else if (TokenizeCStyleNumber(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::Number; + else if (TokenizeCStylePunctuation(in_begin, in_end, out_begin, out_end)) + paletteIndex = PaletteIndex::Punctuation; + + return paletteIndex != PaletteIndex::Max; + }; + + langDef.mCommentStart = "/*"; + langDef.mCommentEnd = "*/"; + langDef.mSingleLineComment = "//"; + + langDef.mCaseSensitive = true; + langDef.mAutoIndentation = true; + + langDef.mName = "C"; + + inited = true; + } + return langDef; +} + +const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::SQL() +{ + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) + { + static const char* const keywords[] = { + "ADD", "EXCEPT", "PERCENT", "ALL", "EXEC", "PLAN", "ALTER", "EXECUTE", "PRECISION", "AND", "EXISTS", "PRIMARY", "ANY", "EXIT", "PRINT", "AS", "FETCH", "PROC", "ASC", "FILE", "PROCEDURE", + "AUTHORIZATION", "FILLFACTOR", "PUBLIC", "BACKUP", "FOR", "RAISERROR", "BEGIN", "FOREIGN", "READ", "BETWEEN", "FREETEXT", "READTEXT", "BREAK", "FREETEXTTABLE", "RECONFIGURE", + "BROWSE", "FROM", "REFERENCES", "BULK", "FULL", "REPLICATION", "BY", "FUNCTION", "RESTORE", "CASCADE", "GOTO", "RESTRICT", "CASE", "GRANT", "RETURN", "CHECK", "GROUP", "REVOKE", + "CHECKPOINT", "HAVING", "RIGHT", "CLOSE", "HOLDLOCK", "ROLLBACK", "CLUSTERED", "IDENTITY", "ROWCOUNT", "COALESCE", "IDENTITY_INSERT", "ROWGUIDCOL", "COLLATE", "IDENTITYCOL", "RULE", + "COLUMN", "IF", "SAVE", "COMMIT", "IN", "SCHEMA", "COMPUTE", "INDEX", "SELECT", "CONSTRAINT", "INNER", "SESSION_USER", "CONTAINS", "INSERT", "SET", "CONTAINSTABLE", "INTERSECT", "SETUSER", + "CONTINUE", "INTO", "SHUTDOWN", "CONVERT", "IS", "SOME", "CREATE", "JOIN", "STATISTICS", "CROSS", "KEY", "SYSTEM_USER", "CURRENT", "KILL", "TABLE", "CURRENT_DATE", "LEFT", "TEXTSIZE", + "CURRENT_TIME", "LIKE", "THEN", "CURRENT_TIMESTAMP", "LINENO", "TO", "CURRENT_USER", "LOAD", "TOP", "CURSOR", "NATIONAL", "TRAN", "DATABASE", "NOCHECK", "TRANSACTION", + "DBCC", "NONCLUSTERED", "TRIGGER", "DEALLOCATE", "NOT", "TRUNCATE", "DECLARE", "NULL", "TSEQUAL", "DEFAULT", "NULLIF", "UNION", "DELETE", "OF", "UNIQUE", "DENY", "OFF", "UPDATE", + "DESC", "OFFSETS", "UPDATETEXT", "DISK", "ON", "USE", "DISTINCT", "OPEN", "USER", "DISTRIBUTED", "OPENDATASOURCE", "VALUES", "DOUBLE", "OPENQUERY", "VARYING","DROP", "OPENROWSET", "VIEW", + "DUMMY", "OPENXML", "WAITFOR", "DUMP", "OPTION", "WHEN", "ELSE", "OR", "WHERE", "END", "ORDER", "WHILE", "ERRLVL", "OUTER", "WITH", "ESCAPE", "OVER", "WRITETEXT" + }; + + for (auto& k : keywords) + langDef.mKeywords.insert(k); + + static const char* const identifiers[] = { + "ABS", "ACOS", "ADD_MONTHS", "ASCII", "ASCIISTR", "ASIN", "ATAN", "ATAN2", "AVG", "BFILENAME", "BIN_TO_NUM", "BITAND", "CARDINALITY", "CASE", "CAST", "CEIL", + "CHARTOROWID", "CHR", "COALESCE", "COMPOSE", "CONCAT", "CONVERT", "CORR", "COS", "COSH", "COUNT", "COVAR_POP", "COVAR_SAMP", "CUME_DIST", "CURRENT_DATE", + "CURRENT_TIMESTAMP", "DBTIMEZONE", "DECODE", "DECOMPOSE", "DENSE_RANK", "DUMP", "EMPTY_BLOB", "EMPTY_CLOB", "EXP", "EXTRACT", "FIRST_VALUE", "FLOOR", "FROM_TZ", "GREATEST", + "GROUP_ID", "HEXTORAW", "INITCAP", "INSTR", "INSTR2", "INSTR4", "INSTRB", "INSTRC", "LAG", "LAST_DAY", "LAST_VALUE", "LEAD", "LEAST", "LENGTH", "LENGTH2", "LENGTH4", + "LENGTHB", "LENGTHC", "LISTAGG", "LN", "LNNVL", "LOCALTIMESTAMP", "LOG", "LOWER", "LPAD", "LTRIM", "MAX", "MEDIAN", "MIN", "MOD", "MONTHS_BETWEEN", "NANVL", "NCHR", + "NEW_TIME", "NEXT_DAY", "NTH_VALUE", "NULLIF", "NUMTODSINTERVAL", "NUMTOYMINTERVAL", "NVL", "NVL2", "POWER", "RANK", "RAWTOHEX", "REGEXP_COUNT", "REGEXP_INSTR", + "REGEXP_REPLACE", "REGEXP_SUBSTR", "REMAINDER", "REPLACE", "ROUND", "ROWNUM", "RPAD", "RTRIM", "SESSIONTIMEZONE", "SIGN", "SIN", "SINH", + "SOUNDEX", "SQRT", "STDDEV", "SUBSTR", "SUM", "SYS_CONTEXT", "SYSDATE", "SYSTIMESTAMP", "TAN", "TANH", "TO_CHAR", "TO_CLOB", "TO_DATE", "TO_DSINTERVAL", "TO_LOB", + "TO_MULTI_BYTE", "TO_NCLOB", "TO_NUMBER", "TO_SINGLE_BYTE", "TO_TIMESTAMP", "TO_TIMESTAMP_TZ", "TO_YMINTERVAL", "TRANSLATE", "TRIM", "TRUNC", "TZ_OFFSET", "UID", "UPPER", + "USER", "USERENV", "VAR_POP", "VAR_SAMP", "VARIANCE", "VSIZE " + }; + for (auto& k : identifiers) + { + Identifier id; + id.mDeclaration = "Built-in function"; + langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); + } + + langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); + langDef.mTokenRegexStrings.push_back(std::make_pair("\\\'[^\\\']*\\\'", PaletteIndex::String)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); + + langDef.mCommentStart = "/*"; + langDef.mCommentEnd = "*/"; + langDef.mSingleLineComment = "//"; + + langDef.mCaseSensitive = false; + langDef.mAutoIndentation = false; + + langDef.mName = "SQL"; + + inited = true; + } + return langDef; +} + +const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::AngelScript() +{ + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) + { + static const char* const keywords[] = { + "and", "abstract", "auto", "bool", "break", "case", "cast", "class", "const", "continue", "default", "do", "double", "else", "enum", "false", "final", "float", "for", + "from", "funcdef", "function", "get", "if", "import", "in", "inout", "int", "interface", "int8", "int16", "int32", "int64", "is", "mixin", "namespace", "not", + "null", "or", "out", "override", "private", "protected", "return", "set", "shared", "super", "switch", "this ", "true", "typedef", "uint", "uint8", "uint16", "uint32", + "uint64", "void", "while", "xor" + }; + + for (auto& k : keywords) + langDef.mKeywords.insert(k); + + static const char* const identifiers[] = { + "cos", "sin", "tab", "acos", "asin", "atan", "atan2", "cosh", "sinh", "tanh", "log", "log10", "pow", "sqrt", "abs", "ceil", "floor", "fraction", "closeTo", "fpFromIEEE", "fpToIEEE", + "complex", "opEquals", "opAddAssign", "opSubAssign", "opMulAssign", "opDivAssign", "opAdd", "opSub", "opMul", "opDiv" + }; + for (auto& k : identifiers) + { + Identifier id; + id.mDeclaration = "Built-in function"; + langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); + } + + langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); + langDef.mTokenRegexStrings.push_back(std::make_pair("\\'\\\\?[^\\']\\'", PaletteIndex::String)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("0[0-7]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); + + langDef.mCommentStart = "/*"; + langDef.mCommentEnd = "*/"; + langDef.mSingleLineComment = "//"; + + langDef.mCaseSensitive = true; + langDef.mAutoIndentation = true; + + langDef.mName = "AngelScript"; + + inited = true; + } + return langDef; +} + +const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::Lua() +{ + static bool inited = false; + static LanguageDefinition langDef; + if (!inited) + { + static const char* const keywords[] = { + "and", "break", "do", "", "else", "elseif", "end", "false", "for", "function", "if", "in", "", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while" + }; + + for (auto& k : keywords) + langDef.mKeywords.insert(k); + + static const char* const identifiers[] = { + "assert", "collectgarbage", "dofile", "error", "getmetatable", "ipairs", "loadfile", "load", "loadstring", "next", "pairs", "pcall", "print", "rawequal", "rawlen", "rawget", "rawset", + "select", "setmetatable", "tonumber", "tostring", "type", "xpcall", "_G", "_VERSION","arshift", "band", "bnot", "bor", "bxor", "btest", "extract", "lrotate", "lshift", "replace", + "rrotate", "rshift", "create", "resume", "running", "status", "wrap", "yield", "isyieldable", "debug","getuservalue", "gethook", "getinfo", "getlocal", "getregistry", "getmetatable", + "getupvalue", "upvaluejoin", "upvalueid", "setuservalue", "sethook", "setlocal", "setmetatable", "setupvalue", "traceback", "close", "flush", "input", "lines", "open", "output", "popen", + "read", "tmpfile", "type", "write", "close", "flush", "lines", "read", "seek", "setvbuf", "write", "__gc", "__tostring", "abs", "acos", "asin", "atan", "ceil", "cos", "deg", "exp", "tointeger", + "floor", "fmod", "ult", "log", "max", "min", "modf", "rad", "random", "randomseed", "sin", "sqrt", "string", "tan", "type", "atan2", "cosh", "sinh", "tanh", + "pow", "frexp", "ldexp", "log10", "pi", "huge", "maxinteger", "mininteger", "loadlib", "searchpath", "seeall", "preload", "cpath", "path", "searchers", "loaded", "module", "require", "clock", + "date", "difftime", "execute", "exit", "getenv", "remove", "rename", "setlocale", "time", "tmpname", "byte", "char", "dump", "find", "format", "gmatch", "gsub", "len", "lower", "match", "rep", + "reverse", "sub", "upper", "pack", "packsize", "unpack", "concat", "maxn", "insert", "pack", "unpack", "remove", "move", "sort", "offset", "codepoint", "char", "len", "codes", "charpattern", + "coroutine", "table", "io", "os", "string", "utf8", "bit32", "math", "debug", "package" + }; + for (auto& k : identifiers) + { + Identifier id; + id.mDeclaration = "Built-in function"; + langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); + } + + langDef.mTokenRegexStrings.push_back(std::make_pair("L?\\\"(\\\\.|[^\\\"])*\\\"", PaletteIndex::String)); + langDef.mTokenRegexStrings.push_back(std::make_pair("\\\'[^\\\']*\\\'", PaletteIndex::String)); + langDef.mTokenRegexStrings.push_back(std::make_pair("0[xX][0-9a-fA-F]+[uU]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)([eE][+-]?[0-9]+)?[fF]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[+-]?[0-9]+[Uu]?[lL]?[lL]?", PaletteIndex::Number)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[a-zA-Z_][a-zA-Z0-9_]*", PaletteIndex::Identifier)); + langDef.mTokenRegexStrings.push_back(std::make_pair("[\\[\\]\\{\\}\\!\\%\\^\\&\\*\\(\\)\\-\\+\\=\\~\\|\\<\\>\\?\\/\\;\\,\\.]", PaletteIndex::Punctuation)); + + langDef.mCommentStart = "--[["; + langDef.mCommentEnd = "]]"; + langDef.mSingleLineComment = "--"; + + langDef.mCaseSensitive = true; + langDef.mAutoIndentation = false; + + langDef.mName = "Lua"; + + inited = true; + } + return langDef; +} diff --git a/neo/libs/ImGuiColorTextEdit/TextEditor.h b/neo/libs/ImGuiColorTextEdit/TextEditor.h new file mode 100644 index 000000000..e26a32259 --- /dev/null +++ b/neo/libs/ImGuiColorTextEdit/TextEditor.h @@ -0,0 +1,437 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include "imgui.h" + +class TextEditor +{ +public: + typedef void (*textEditKeyPress_t)(void* data, bool ctrl, bool shift, bool alt, int chr); + typedef bool (*textEditKeyDown_t)(void* data); + typedef bool (*textEditMouseButtonDown_t)(void* data); + typedef bool (*textEditGetToolTip_t)(void* data, const char *identifier, char *tip, size_t tipLength); + + enum class PaletteIndex + { + Default, + Keyword, + Number, + String, + CharLiteral, + Punctuation, + Preprocessor, + Identifier, + KnownIdentifier, + PreprocIdentifier, + Comment, + MultiLineComment, + Background, + Cursor, + Selection, + ErrorMarker, + Breakpoint, + LineNumber, + CurrentLineFill, + CurrentLineFillInactive, + CurrentLineEdge, + Max + }; + + enum class SelectionMode + { + Normal, + Word, + Line + }; + + struct Breakpoint + { + int mLine; + bool mEnabled; + std::string mCondition; + + Breakpoint() + : mLine(-1) + , mEnabled(false) + {} + }; + + // Represents a character coordinate from the user's point of view, + // i. e. consider an uniform grid (assuming fixed-width font) on the + // screen as it is rendered, and each cell has its own coordinate, starting from 0. + // Tabs are counted as [1..mTabSize] count empty spaces, depending on + // how many space is necessary to reach the next tab stop. + // For example, coordinate (1, 5) represents the character 'B' in a line "\tABC", when mTabSize = 4, + // because it is rendered as " ABC" on the screen. + struct Coordinates + { + int mLine, mColumn; + Coordinates() : mLine(0), mColumn(0) {} + Coordinates(int aLine, int aColumn) : mLine(aLine), mColumn(aColumn) + { + assert(aLine >= 0); + assert(aColumn >= 0); + } + static Coordinates Invalid() { static Coordinates invalid(-1, -1); return invalid; } + + bool operator ==(const Coordinates& o) const + { + return + mLine == o.mLine && + mColumn == o.mColumn; + } + + bool operator !=(const Coordinates& o) const + { + return + mLine != o.mLine || + mColumn != o.mColumn; + } + + bool operator <(const Coordinates& o) const + { + if (mLine != o.mLine) + return mLine < o.mLine; + return mColumn < o.mColumn; + } + + bool operator >(const Coordinates& o) const + { + if (mLine != o.mLine) + return mLine > o.mLine; + return mColumn > o.mColumn; + } + + bool operator <=(const Coordinates& o) const + { + if (mLine != o.mLine) + return mLine < o.mLine; + return mColumn <= o.mColumn; + } + + bool operator >=(const Coordinates& o) const + { + if (mLine != o.mLine) + return mLine > o.mLine; + return mColumn >= o.mColumn; + } + }; + + struct Identifier + { + Coordinates mLocation; + std::string mDeclaration; + }; + + typedef std::string String; + typedef std::unordered_map Identifiers; + typedef std::unordered_set Keywords; + typedef std::map ErrorMarkers; + typedef std::unordered_set Breakpoints; + typedef std::array Palette; + typedef uint8_t Char; + + struct Glyph + { + Char mChar; + PaletteIndex mColorIndex = PaletteIndex::Default; + bool mComment : 1; + bool mMultiLineComment : 1; + bool mPreprocessor : 1; + + Glyph(Char aChar, PaletteIndex aColorIndex) : mChar(aChar), mColorIndex(aColorIndex), + mComment(false), mMultiLineComment(false), mPreprocessor(false) {} + }; + + typedef std::vector Line; + typedef std::vector Lines; + + struct LanguageDefinition + { + typedef std::pair TokenRegexString; + typedef std::vector TokenRegexStrings; + typedef bool(*TokenizeCallback)(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end, PaletteIndex & paletteIndex); + + std::string mName; + Keywords mKeywords; + Identifiers mIdentifiers; + Identifiers mPreprocIdentifiers; + std::string mCommentStart, mCommentEnd, mSingleLineComment; + char mPreprocChar; + bool mAutoIndentation; + + TokenizeCallback mTokenize; + + TokenRegexStrings mTokenRegexStrings; + + bool mCaseSensitive; + + LanguageDefinition() + : mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true) + { + } + + static const LanguageDefinition& CPlusPlus(); + static const LanguageDefinition& HLSL(); + static const LanguageDefinition& GLSL(); + static const LanguageDefinition& C(); + static const LanguageDefinition& SQL(); + static const LanguageDefinition& AngelScript(); + static const LanguageDefinition& Lua(); + }; + + TextEditor(); + ~TextEditor(); + + void Focus(); + + void SetHandlers(void *data, textEditKeyPress_t keyPress, textEditKeyDown_t keyDown, textEditMouseButtonDown_t mouseButtonDown, textEditGetToolTip_t getTooltip); + + void SetLanguageDefinition(const LanguageDefinition& aLanguageDef); + const LanguageDefinition& GetLanguageDefinition() const { return mLanguageDefinition; } + + const Palette& GetPalette() const { return mPaletteBase; } + void SetPalette(const Palette& aValue); + + void SetErrorMarkers(const ErrorMarkers& aMarkers) { mErrorMarkers = aMarkers; } + void SetBreakpoints(const Breakpoints& aMarkers) { mBreakpoints = aMarkers; } + + void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false); + void SetText(const std::string& aText); + std::string GetText() const; + + void SetTextLines(const std::vector& aLines); + std::vector GetTextLines() const; + + std::string GetSelectedText() const; + std::string GetCurrentLineText()const; + + ImVec2 GetCursorScreenCoordinates() const; + + int GetTotalLines() const { return (int)mLines.size(); } + bool IsOverwrite() const { return mOverwrite; } + + void SetReadOnly(bool aValue); + bool IsReadOnly() const { return mReadOnly; } + void SetShowLineNumber(bool aValue); + bool IsShowLineNumber() const { return mShowLineNumber; } + void SetShowCurrentLine(bool aValue); + bool IsShowCurrentLine() const { return mShowCurrentLine; } + bool IsTextChanged() const { return mTextChanged; } + bool IsCursorPositionChanged() const { return mCursorPositionChanged; } + + bool IsColorizerEnabled() const { return mColorizerEnabled; } + void SetColorizerEnable(bool aValue); + + Coordinates GetCursorPosition() const { return GetActualCursorCoordinates(); } + void SetCursorPosition(const Coordinates& aPosition); + + inline void SetHandleMouseInputs (bool aValue){ mHandleMouseInputs = aValue;} + inline bool IsHandleMouseInputsEnabled() const { return mHandleMouseInputs; } + + inline void SetHandleKeyboardInputs (bool aValue){ mHandleKeyboardInputs = aValue;} + inline bool IsHandleKeyboardInputsEnabled() const { return mHandleKeyboardInputs; } + + inline void SetImGuiChildIgnored (bool aValue){ mIgnoreImGuiChild = aValue;} + inline bool IsImGuiChildIgnored() const { return mIgnoreImGuiChild; } + + inline void SetShowWhitespaces(bool aValue) { mShowWhitespaces = aValue; } + inline bool IsShowingWhitespaces() const { return mShowWhitespaces; } + + void SetTabSize(int aValue); + inline int GetTabSize() const { return mTabSize; } + + void InsertText(const std::string& aValue); + void InsertText(const char* aValue); + + std::string GetWordBeforeCursor() const; + + void MoveUp(int aAmount = 1, bool aSelect = false); + void MoveDown(int aAmount = 1, bool aSelect = false); + void MoveLeft(int aAmount = 1, bool aSelect = false, bool aWordMode = false); + bool PeekLeftIsWhiteSpace(int aAmount = 1); + void MoveRight(int aAmount = 1, bool aSelect = false, bool aWordMode = false); + void MoveTop(bool aSelect = false); + void MoveBottom(bool aSelect = false); + void MoveHome(bool aSelect = false); + void MoveEnd(bool aSelect = false); + + bool SubstringMatch(const char* find, size_t findLen, bool matchCase, bool matchWholeWords, const TextEditor::Line line, int lineNum, size_t index); + bool FindNext(const char *str, bool matchCase, bool matchWholeWords, bool searchForward); + int ReplaceAll(const char *find, const char *replace, bool matchCase, bool matchWholeWords); + + void SetSelectionStart(const Coordinates& aPosition); + void SetSelectionEnd(const Coordinates& aPosition); + void SetSelection(const Coordinates& aStart, const Coordinates& aEnd, SelectionMode aMode = SelectionMode::Normal); + void SelectWordUnderCursor(); + void SelectAll(); + bool HasSelection() const; + void DeleteSelection(); + void ReplaceSelection(const char* text); + + void Copy(); + void Cut(); + void Paste(); + void Delete(); + + bool CanUndo() const; + bool CanRedo() const; + void Undo(int aSteps = 1); + void Redo(int aSteps = 1); + + int GetUndoIndex() const; + size_t GetUndoBufferSize() const; + + void MarkSaved(); + void MarkUnsaved(); + void MarkSaved(bool saveState); + bool IsSaved() const; + + static const Palette& GetDarkPalette(); + static const Palette& GetLightPalette(); + static const Palette& GetRetroBluePalette(); + + std::string GetText(const Coordinates& aStart, const Coordinates& aEnd) const; + +private: + typedef std::vector> RegexList; + + struct EditorState + { + Coordinates mSelectionStart; + Coordinates mSelectionEnd; + Coordinates mCursorPosition; + }; + + class UndoRecord + { + public: + UndoRecord() {} + ~UndoRecord() {} + + UndoRecord( + const std::string& aAdded, + const TextEditor::Coordinates aAddedStart, + const TextEditor::Coordinates aAddedEnd, + + const std::string& aRemoved, + const TextEditor::Coordinates aRemovedStart, + const TextEditor::Coordinates aRemovedEnd, + + TextEditor::EditorState& aBefore, + TextEditor::EditorState& aAfter); + + void Undo(TextEditor* aEditor); + void Redo(TextEditor* aEditor); + + std::string mAdded; + Coordinates mAddedStart; + Coordinates mAddedEnd; + + std::string mRemoved; + Coordinates mRemovedStart; + Coordinates mRemovedEnd; + + EditorState mBefore; + EditorState mAfter; + }; + + typedef std::vector UndoBuffer; + + void ProcessInputs(); + void Colorize(int aFromLine = 0, int aCount = -1); + void ColorizeRange(int aFromLine = 0, int aToLine = 0); + void ColorizeInternal(); + float TextDistanceToLineStart(const Coordinates& aFrom) const; + void EnsureCursorVisible(); + int GetPageSize() const; + Coordinates GetActualCursorCoordinates() const; + Coordinates SanitizeCoordinates(const Coordinates& aValue) const; + void Advance(Coordinates& aCoordinates) const; + void DeleteRange(const Coordinates& aStart, const Coordinates& aEnd); + int InsertTextAt(Coordinates& aWhere, const char* aValue); + void AddUndo(UndoRecord& aValue); + Coordinates ScreenPosToCoordinates(const ImVec2& aPosition) const; + Coordinates FindWordStart(const Coordinates& aFrom) const; + Coordinates FindWordEnd(const Coordinates& aFrom) const; + Coordinates FindNextWord(const Coordinates& aFrom) const; + int GetCharacterIndex(const Coordinates& aCoordinates) const; + int GetCharacterColumn(int aLine, int aIndex) const; + int GetLineCharacterCount(int aLine) const; + int GetLineMaxColumn(int aLine) const; + bool IsOnWordBoundary(const Coordinates& aAt) const; + void RemoveLine(int aStart, int aEnd); + void RemoveLine(int aIndex); + Line& InsertLine(int aIndex); + void EnterCharacter(ImWchar aChar, bool aShift); + void Backspace(); + std::string GetWordUnderCursor() const; + std::string GetWordAt(const Coordinates& aCoords) const; + ImU32 GetGlyphColor(const Glyph& aGlyph) const; + + void HandleKeyboardInputs(); + void HandleMouseInputs(); + void Render(); + + float mLineSpacing; + Lines mLines; + EditorState mState; + UndoBuffer mUndoBuffer; + int mUndoIndex; + + int mTabSize; + bool mOverwrite; + bool mReadOnly; + bool mWithinRender; + bool mScrollToCursor; + bool mScrollToTop; + bool mTextChanged; + bool mColorizerEnabled; + float mTextStart; // position (in pixels) where a code line starts relative to the left of the TextEditor. + int mLeftMargin; + bool mCursorPositionChanged; + int mColorRangeMin, mColorRangeMax; + SelectionMode mSelectionMode; + bool mHandleKeyboardInputs; + bool mHandleMouseInputs; + bool mIgnoreImGuiChild; + bool mShowWhitespaces; + + Palette mPaletteBase; + Palette mPalette; + LanguageDefinition mLanguageDefinition; + RegexList mRegexList; + + bool mCheckComments; + Breakpoints mBreakpoints; + ErrorMarkers mErrorMarkers; + ImVec2 mCharAdvance; + Coordinates mInteractiveStart, mInteractiveEnd; + std::string mLineBuffer; + uint64_t mStartTime; + + float mLastClick; + + bool mSaveState; + bool mMarkedUnsaved; + int mUndoIndexFirstEdit; + int mUndoIndexLastSave; + + void* mKeyPressData; + textEditKeyPress_t mKeyPressHandler; + textEditKeyDown_t mKeyDownHandler; + textEditMouseButtonDown_t mMouseButtonDownHandler; + textEditGetToolTip_t mGetToolTipHandler; + + bool mSetFocus; + ImVec2 mCursorScreenPos; + bool mShowLineNumber; + bool mShowCurrentLine; +}; diff --git a/neo/libs/imgui/.gitignore b/neo/libs/imgui/.gitignore index f632636e0..15a908273 100644 --- a/neo/libs/imgui/.gitignore +++ b/neo/libs/imgui/.gitignore @@ -49,6 +49,9 @@ examples/example_sdl2_opengl3/web/* .idea cmake-build-* +## VS code artifacts +.vscode + ## Unix executables from our example Makefiles examples/example_glfw_metal/example_glfw_metal examples/example_glfw_opengl2/example_glfw_opengl2 diff --git a/neo/libs/imgui/backends/imgui_impl_allegro5.cpp b/neo/libs/imgui/backends/imgui_impl_allegro5.cpp index 3718b64ea..d0265b0a4 100644 --- a/neo/libs/imgui/backends/imgui_impl_allegro5.cpp +++ b/neo/libs/imgui/backends/imgui_impl_allegro5.cpp @@ -7,8 +7,9 @@ // [X] Platform: Clipboard support (from Allegro 5.1.12). // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // Missing features or Issues: -// [ ] Renderer: The renderer is suboptimal as we need to convert vertices manually. +// [ ] Renderer: The renderer is suboptimal as we need to unindex our buffers and convert vertices manually. // [ ] Platform: Missing gamepad support. +// [ ] Renderer: Multi-viewport support (multiple windows). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/neo/libs/imgui/backends/imgui_impl_allegro5.h b/neo/libs/imgui/backends/imgui_impl_allegro5.h index 356ec7d0a..e67ffe509 100644 --- a/neo/libs/imgui/backends/imgui_impl_allegro5.h +++ b/neo/libs/imgui/backends/imgui_impl_allegro5.h @@ -9,6 +9,7 @@ // Missing features or Issues: // [ ] Renderer: The renderer is suboptimal as we need to unindex our buffers and convert vertices manually. // [ ] Platform: Missing gamepad support. +// [ ] Renderer: Multi-viewport support (multiple windows). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/neo/libs/imgui/backends/imgui_impl_android.cpp b/neo/libs/imgui/backends/imgui_impl_android.cpp index 1c2d62476..66eb88d74 100644 --- a/neo/libs/imgui/backends/imgui_impl_android.cpp +++ b/neo/libs/imgui/backends/imgui_impl_android.cpp @@ -8,6 +8,7 @@ // [ ] Platform: Clipboard support. // [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [ ] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android. +// [ ] Platform: Multi-viewport support (multiple windows). Not meaningful on Android. // Important: // - Consider using SDL or GLFW backend on Android, which will be more full-featured than this. // - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446) diff --git a/neo/libs/imgui/backends/imgui_impl_android.h b/neo/libs/imgui/backends/imgui_impl_android.h index 3eaeca725..da3e11a5c 100644 --- a/neo/libs/imgui/backends/imgui_impl_android.h +++ b/neo/libs/imgui/backends/imgui_impl_android.h @@ -8,6 +8,7 @@ // [ ] Platform: Clipboard support. // [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [ ] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android. +// [ ] Platform: Multi-viewport support (multiple windows). Not meaningful on Android. // Important: // - Consider using SDL or GLFW backend on Android, which will be more full-featured than this. // - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446) diff --git a/neo/libs/imgui/backends/imgui_impl_dx10.cpp b/neo/libs/imgui/backends/imgui_impl_dx10.cpp index 89499af0c..5ac6514d0 100644 --- a/neo/libs/imgui/backends/imgui_impl_dx10.cpp +++ b/neo/libs/imgui/backends/imgui_impl_dx10.cpp @@ -4,6 +4,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -15,6 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2025-01-06: DirectX10: Expose selected render state in ImGui_ImplDX10_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks. // 2024-10-07: DirectX10: Changed default texture sampler to Clamp instead of Repeat/Wrap. // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. @@ -47,7 +49,7 @@ #pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. #endif -// DirectX data +// DirectX10 data struct ImGui_ImplDX10_Data { ID3D10Device* pd3dDevice; @@ -81,6 +83,10 @@ static ImGui_ImplDX10_Data* ImGui_ImplDX10_GetBackendData() return ImGui::GetCurrentContext() ? (ImGui_ImplDX10_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; } +// Forward Declarations +static void ImGui_ImplDX10_InitMultiViewportSupport(); +static void ImGui_ImplDX10_ShutdownMultiViewportSupport(); + // Functions static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* device) { @@ -563,6 +569,7 @@ bool ImGui_ImplDX10_Init(ID3D10Device* device) io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx10"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) // Get factory from device IDXGIDevice* pDXGIDevice = nullptr; @@ -579,6 +586,8 @@ bool ImGui_ImplDX10_Init(ID3D10Device* device) if (pDXGIAdapter) pDXGIAdapter->Release(); bd->pd3dDevice->AddRef(); + ImGui_ImplDX10_InitMultiViewportSupport(); + return true; } @@ -588,12 +597,13 @@ void ImGui_ImplDX10_Shutdown() IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplDX10_ShutdownMultiViewportSupport(); ImGui_ImplDX10_InvalidateDeviceObjects(); if (bd->pFactory) { bd->pFactory->Release(); } if (bd->pd3dDevice) { bd->pd3dDevice->Release(); } io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasViewports); IM_DELETE(bd); } @@ -606,6 +616,129 @@ void ImGui_ImplDX10_NewFrame() ImGui_ImplDX10_CreateDeviceObjects(); } +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data. +struct ImGui_ImplDX10_ViewportData +{ + IDXGISwapChain* SwapChain; + ID3D10RenderTargetView* RTView; + + ImGui_ImplDX10_ViewportData() { SwapChain = nullptr; RTView = nullptr; } + ~ImGui_ImplDX10_ViewportData() { IM_ASSERT(SwapChain == nullptr && RTView == nullptr); } +}; + +static void ImGui_ImplDX10_CreateWindow(ImGuiViewport* viewport) +{ + ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); + ImGui_ImplDX10_ViewportData* vd = IM_NEW(ImGui_ImplDX10_ViewportData)(); + viewport->RendererUserData = vd; + + // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). + // Some backends will leave PlatformHandleRaw == 0, in which case we assume PlatformHandle will contain the HWND. + HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle; + IM_ASSERT(hwnd != 0); + + // Create swap chain + DXGI_SWAP_CHAIN_DESC sd; + ZeroMemory(&sd, sizeof(sd)); + sd.BufferDesc.Width = (UINT)viewport->Size.x; + sd.BufferDesc.Height = (UINT)viewport->Size.y; + sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + sd.SampleDesc.Count = 1; + sd.SampleDesc.Quality = 0; + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.BufferCount = 1; + sd.OutputWindow = hwnd; + sd.Windowed = TRUE; + sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + sd.Flags = 0; + + IM_ASSERT(vd->SwapChain == nullptr && vd->RTView == nullptr); + bd->pFactory->CreateSwapChain(bd->pd3dDevice, &sd, &vd->SwapChain); + + // Create the render target + if (vd->SwapChain) + { + ID3D10Texture2D* pBackBuffer; + vd->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); + bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &vd->RTView); + pBackBuffer->Release(); + } +} + +static void ImGui_ImplDX10_DestroyWindow(ImGuiViewport* viewport) +{ + // The main viewport (owned by the application) will always have RendererUserData == 0 here since we didn't create the data for it. + if (ImGui_ImplDX10_ViewportData* vd = (ImGui_ImplDX10_ViewportData*)viewport->RendererUserData) + { + if (vd->SwapChain) + vd->SwapChain->Release(); + vd->SwapChain = nullptr; + if (vd->RTView) + vd->RTView->Release(); + vd->RTView = nullptr; + IM_DELETE(vd); + } + viewport->RendererUserData = nullptr; +} + +static void ImGui_ImplDX10_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); + ImGui_ImplDX10_ViewportData* vd = (ImGui_ImplDX10_ViewportData*)viewport->RendererUserData; + if (vd->RTView) + { + vd->RTView->Release(); + vd->RTView = nullptr; + } + if (vd->SwapChain) + { + ID3D10Texture2D* pBackBuffer = nullptr; + vd->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0); + vd->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); + if (pBackBuffer == nullptr) { fprintf(stderr, "ImGui_ImplDX10_SetWindowSize() failed creating buffers.\n"); return; } + bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &vd->RTView); + pBackBuffer->Release(); + } +} + +static void ImGui_ImplDX10_RenderViewport(ImGuiViewport* viewport, void*) +{ + ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData(); + ImGui_ImplDX10_ViewportData* vd = (ImGui_ImplDX10_ViewportData*)viewport->RendererUserData; + ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); + bd->pd3dDevice->OMSetRenderTargets(1, &vd->RTView, nullptr); + if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) + bd->pd3dDevice->ClearRenderTargetView(vd->RTView, (float*)&clear_color); + ImGui_ImplDX10_RenderDrawData(viewport->DrawData); +} + +static void ImGui_ImplDX10_SwapBuffers(ImGuiViewport* viewport, void*) +{ + ImGui_ImplDX10_ViewportData* vd = (ImGui_ImplDX10_ViewportData*)viewport->RendererUserData; + vd->SwapChain->Present(0, 0); // Present without vsync +} + +void ImGui_ImplDX10_InitMultiViewportSupport() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_CreateWindow = ImGui_ImplDX10_CreateWindow; + platform_io.Renderer_DestroyWindow = ImGui_ImplDX10_DestroyWindow; + platform_io.Renderer_SetWindowSize = ImGui_ImplDX10_SetWindowSize; + platform_io.Renderer_RenderWindow = ImGui_ImplDX10_RenderViewport; + platform_io.Renderer_SwapBuffers = ImGui_ImplDX10_SwapBuffers; +} + +void ImGui_ImplDX10_ShutdownMultiViewportSupport() +{ + ImGui::DestroyPlatformWindows(); +} + //----------------------------------------------------------------------------- #endif // #ifndef IMGUI_DISABLE diff --git a/neo/libs/imgui/backends/imgui_impl_dx10.h b/neo/libs/imgui/backends/imgui_impl_dx10.h index d9f29987d..07bbd9ffd 100644 --- a/neo/libs/imgui/backends/imgui_impl_dx10.h +++ b/neo/libs/imgui/backends/imgui_impl_dx10.h @@ -4,6 +4,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/neo/libs/imgui/backends/imgui_impl_dx11.cpp b/neo/libs/imgui/backends/imgui_impl_dx11.cpp index b38fece26..9c784b694 100644 --- a/neo/libs/imgui/backends/imgui_impl_dx11.cpp +++ b/neo/libs/imgui/backends/imgui_impl_dx11.cpp @@ -5,6 +5,7 @@ // [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -16,6 +17,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2025-01-06: DirectX11: Expose VertexConstantBuffer in ImGui_ImplDX11_RenderState. Reset projection matrix in ImDrawCallback_ResetRenderState handler. // 2024-10-07: DirectX11: Changed default texture sampler to Clamp instead of Repeat/Wrap. // 2024-10-07: DirectX11: Expose selected render state in ImGui_ImplDX11_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks. @@ -84,6 +86,10 @@ static ImGui_ImplDX11_Data* ImGui_ImplDX11_GetBackendData() return ImGui::GetCurrentContext() ? (ImGui_ImplDX11_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; } +// Forward Declarations +static void ImGui_ImplDX11_InitMultiViewportSupport(); +static void ImGui_ImplDX11_ShutdownMultiViewportSupport(); + // Functions static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* device_ctx) { @@ -579,6 +585,7 @@ bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx11"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) // Get factory from device IDXGIDevice* pDXGIDevice = nullptr; @@ -598,6 +605,8 @@ bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co bd->pd3dDevice->AddRef(); bd->pd3dDeviceContext->AddRef(); + ImGui_ImplDX11_InitMultiViewportSupport(); + return true; } @@ -607,13 +616,14 @@ void ImGui_ImplDX11_Shutdown() IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplDX11_ShutdownMultiViewportSupport(); ImGui_ImplDX11_InvalidateDeviceObjects(); if (bd->pFactory) { bd->pFactory->Release(); } if (bd->pd3dDevice) { bd->pd3dDevice->Release(); } if (bd->pd3dDeviceContext) { bd->pd3dDeviceContext->Release(); } io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasViewports); IM_DELETE(bd); } @@ -626,6 +636,129 @@ void ImGui_ImplDX11_NewFrame() ImGui_ImplDX11_CreateDeviceObjects(); } +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data. +struct ImGui_ImplDX11_ViewportData +{ + IDXGISwapChain* SwapChain; + ID3D11RenderTargetView* RTView; + + ImGui_ImplDX11_ViewportData() { SwapChain = nullptr; RTView = nullptr; } + ~ImGui_ImplDX11_ViewportData() { IM_ASSERT(SwapChain == nullptr && RTView == nullptr); } +}; + +static void ImGui_ImplDX11_CreateWindow(ImGuiViewport* viewport) +{ + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + ImGui_ImplDX11_ViewportData* vd = IM_NEW(ImGui_ImplDX11_ViewportData)(); + viewport->RendererUserData = vd; + + // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). + // Some backends will leave PlatformHandleRaw == 0, in which case we assume PlatformHandle will contain the HWND. + HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle; + IM_ASSERT(hwnd != 0); + + // Create swap chain + DXGI_SWAP_CHAIN_DESC sd; + ZeroMemory(&sd, sizeof(sd)); + sd.BufferDesc.Width = (UINT)viewport->Size.x; + sd.BufferDesc.Height = (UINT)viewport->Size.y; + sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + sd.SampleDesc.Count = 1; + sd.SampleDesc.Quality = 0; + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.BufferCount = 1; + sd.OutputWindow = hwnd; + sd.Windowed = TRUE; + sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + sd.Flags = 0; + + IM_ASSERT(vd->SwapChain == nullptr && vd->RTView == nullptr); + bd->pFactory->CreateSwapChain(bd->pd3dDevice, &sd, &vd->SwapChain); + + // Create the render target + if (vd->SwapChain) + { + ID3D11Texture2D* pBackBuffer; + vd->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); + bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &vd->RTView); + pBackBuffer->Release(); + } +} + +static void ImGui_ImplDX11_DestroyWindow(ImGuiViewport* viewport) +{ + // The main viewport (owned by the application) will always have RendererUserData == nullptr since we didn't create the data for it. + if (ImGui_ImplDX11_ViewportData* vd = (ImGui_ImplDX11_ViewportData*)viewport->RendererUserData) + { + if (vd->SwapChain) + vd->SwapChain->Release(); + vd->SwapChain = nullptr; + if (vd->RTView) + vd->RTView->Release(); + vd->RTView = nullptr; + IM_DELETE(vd); + } + viewport->RendererUserData = nullptr; +} + +static void ImGui_ImplDX11_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + ImGui_ImplDX11_ViewportData* vd = (ImGui_ImplDX11_ViewportData*)viewport->RendererUserData; + if (vd->RTView) + { + vd->RTView->Release(); + vd->RTView = nullptr; + } + if (vd->SwapChain) + { + ID3D11Texture2D* pBackBuffer = nullptr; + vd->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0); + vd->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer)); + if (pBackBuffer == nullptr) { fprintf(stderr, "ImGui_ImplDX11_SetWindowSize() failed creating buffers.\n"); return; } + bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &vd->RTView); + pBackBuffer->Release(); + } +} + +static void ImGui_ImplDX11_RenderWindow(ImGuiViewport* viewport, void*) +{ + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + ImGui_ImplDX11_ViewportData* vd = (ImGui_ImplDX11_ViewportData*)viewport->RendererUserData; + ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); + bd->pd3dDeviceContext->OMSetRenderTargets(1, &vd->RTView, nullptr); + if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) + bd->pd3dDeviceContext->ClearRenderTargetView(vd->RTView, (float*)&clear_color); + ImGui_ImplDX11_RenderDrawData(viewport->DrawData); +} + +static void ImGui_ImplDX11_SwapBuffers(ImGuiViewport* viewport, void*) +{ + ImGui_ImplDX11_ViewportData* vd = (ImGui_ImplDX11_ViewportData*)viewport->RendererUserData; + vd->SwapChain->Present(0, 0); // Present without vsync +} + +static void ImGui_ImplDX11_InitMultiViewportSupport() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_CreateWindow = ImGui_ImplDX11_CreateWindow; + platform_io.Renderer_DestroyWindow = ImGui_ImplDX11_DestroyWindow; + platform_io.Renderer_SetWindowSize = ImGui_ImplDX11_SetWindowSize; + platform_io.Renderer_RenderWindow = ImGui_ImplDX11_RenderWindow; + platform_io.Renderer_SwapBuffers = ImGui_ImplDX11_SwapBuffers; +} + +static void ImGui_ImplDX11_ShutdownMultiViewportSupport() +{ + ImGui::DestroyPlatformWindows(); +} + //----------------------------------------------------------------------------- #endif // #ifndef IMGUI_DISABLE diff --git a/neo/libs/imgui/backends/imgui_impl_dx11.h b/neo/libs/imgui/backends/imgui_impl_dx11.h index 772c1b920..acdd62cc5 100644 --- a/neo/libs/imgui/backends/imgui_impl_dx11.h +++ b/neo/libs/imgui/backends/imgui_impl_dx11.h @@ -5,6 +5,7 @@ // [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/neo/libs/imgui/backends/imgui_impl_dx12.cpp b/neo/libs/imgui/backends/imgui_impl_dx12.cpp index 24fdb7aa0..e53a0e7b8 100644 --- a/neo/libs/imgui/backends/imgui_impl_dx12.cpp +++ b/neo/libs/imgui/backends/imgui_impl_dx12.cpp @@ -5,6 +5,8 @@ // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// FIXME: The transition from removing a viewport and moving the window in an existing hosted viewport tends to flicker. // The aim of imgui_impl_dx12.h/.cpp is to be usable in your engine without any modification. // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ @@ -19,6 +21,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2025-01-15: DirectX12: Texture upload use the command queue provided in ImGui_ImplDX12_InitInfo instead of creating its own. // 2024-12-09: DirectX12: Let user specifies the DepthStencilView format by setting ImGui_ImplDX12_InitInfo::DSVFormat. // 2024-11-15: DirectX12: *BREAKING CHANGE* Changed ImGui_ImplDX12_Init() signature to take a ImGui_ImplDX12_InitInfo struct. Legacy ImGui_ImplDX12_Init() signature is still supported (will obsolete). // 2024-11-15: DirectX12: *BREAKING CHANGE* User is now required to pass function pointers to allocate/free SRV Descriptors. We provide convenience legacy fields to pass a single descriptor, matching the old API, but upcoming features will want multiple. @@ -64,6 +68,8 @@ struct ImGui_ImplDX12_Texture ID3D12Resource* pTextureResource; D3D12_CPU_DESCRIPTOR_HANDLE hFontSrvCpuDescHandle; D3D12_GPU_DESCRIPTOR_HANDLE hFontSrvGpuDescHandle; + + ImGui_ImplDX12_Texture() { memset((void*)this, 0, sizeof(*this)); } }; struct ImGui_ImplDX12_Data @@ -76,14 +82,10 @@ struct ImGui_ImplDX12_Data DXGI_FORMAT DSVFormat; ID3D12DescriptorHeap* pd3dSrvDescHeap; UINT numFramesInFlight; - - ImGui_ImplDX12_RenderBuffers* pFrameResources; - UINT frameIndex; - ImGui_ImplDX12_Texture FontTexture; bool LegacySingleDescriptorUsed; - ImGui_ImplDX12_Data() { memset((void*)this, 0, sizeof(*this)); frameIndex = UINT_MAX; } + ImGui_ImplDX12_Data() { memset((void*)this, 0, sizeof(*this)); } }; // Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts @@ -102,11 +104,88 @@ struct ImGui_ImplDX12_RenderBuffers int VertexBufferSize; }; +// Buffers used for secondary viewports created by the multi-viewports systems +struct ImGui_ImplDX12_FrameContext +{ + ID3D12CommandAllocator* CommandAllocator; + ID3D12Resource* RenderTarget; + D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetCpuDescriptors; +}; + +// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data. +// Main viewport created by application will only use the Resources field. +// Secondary viewports created by this backend will use all the fields (including Window fields), +struct ImGui_ImplDX12_ViewportData +{ + // Window + ID3D12CommandQueue* CommandQueue; + ID3D12GraphicsCommandList* CommandList; + ID3D12DescriptorHeap* RtvDescHeap; + IDXGISwapChain3* SwapChain; + ID3D12Fence* Fence; + UINT64 FenceSignaledValue; + HANDLE FenceEvent; + UINT NumFramesInFlight; + ImGui_ImplDX12_FrameContext* FrameCtx; + + // Render buffers + UINT FrameIndex; + ImGui_ImplDX12_RenderBuffers* FrameRenderBuffers; + + ImGui_ImplDX12_ViewportData(UINT num_frames_in_flight) + { + CommandQueue = nullptr; + CommandList = nullptr; + RtvDescHeap = nullptr; + SwapChain = nullptr; + Fence = nullptr; + FenceSignaledValue = 0; + FenceEvent = nullptr; + NumFramesInFlight = num_frames_in_flight; + FrameCtx = new ImGui_ImplDX12_FrameContext[NumFramesInFlight]; + FrameIndex = UINT_MAX; + FrameRenderBuffers = new ImGui_ImplDX12_RenderBuffers[NumFramesInFlight]; + + for (UINT i = 0; i < NumFramesInFlight; ++i) + { + FrameCtx[i].CommandAllocator = nullptr; + FrameCtx[i].RenderTarget = nullptr; + + // Create buffers with a default size (they will later be grown as needed) + FrameRenderBuffers[i].IndexBuffer = nullptr; + FrameRenderBuffers[i].VertexBuffer = nullptr; + FrameRenderBuffers[i].VertexBufferSize = 5000; + FrameRenderBuffers[i].IndexBufferSize = 10000; + } + } + ~ImGui_ImplDX12_ViewportData() + { + IM_ASSERT(CommandQueue == nullptr && CommandList == nullptr); + IM_ASSERT(RtvDescHeap == nullptr); + IM_ASSERT(SwapChain == nullptr); + IM_ASSERT(Fence == nullptr); + IM_ASSERT(FenceEvent == nullptr); + + for (UINT i = 0; i < NumFramesInFlight; ++i) + { + IM_ASSERT(FrameCtx[i].CommandAllocator == nullptr && FrameCtx[i].RenderTarget == nullptr); + IM_ASSERT(FrameRenderBuffers[i].IndexBuffer == nullptr && FrameRenderBuffers[i].VertexBuffer == nullptr); + } + + delete[] FrameCtx; FrameCtx = nullptr; + delete[] FrameRenderBuffers; FrameRenderBuffers = nullptr; + } +}; + struct VERTEX_CONSTANT_BUFFER_DX12 { float mvp[4][4]; }; +// Forward Declarations +static void ImGui_ImplDX12_InitPlatformInterface(); +static void ImGui_ImplDX12_ShutdownPlatformInterface(); + // Functions static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* command_list, ImGui_ImplDX12_RenderBuffers* fr) { @@ -180,11 +259,11 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) return; - // FIXME: I'm assuming that this only gets called once per frame! - // If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator. + // FIXME: We are assuming that this only gets called once per frame! ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - bd->frameIndex = bd->frameIndex + 1; - ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[bd->frameIndex % bd->numFramesInFlight]; + ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)draw_data->OwnerViewport->RendererUserData; + vd->FrameIndex++; + ImGui_ImplDX12_RenderBuffers* fr = &vd->FrameRenderBuffers[vd->FrameIndex % bd->numFramesInFlight]; // Create and grow vertex/index buffers if needed if (fr->VertexBuffer == nullptr || fr->VertexBufferSize < draw_data->TotalVtxCount) @@ -352,11 +431,11 @@ static void ImGui_ImplDX12_CreateFontsTexture() bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&pTexture)); - UINT uploadPitch = (width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u); - UINT uploadSize = height * uploadPitch; + UINT upload_pitch = (width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u); + UINT upload_size = height * upload_pitch; desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; desc.Alignment = 0; - desc.Width = uploadSize; + desc.Width = upload_size; desc.Height = 1; desc.DepthOrArraySize = 1; desc.MipLevels = 1; @@ -376,26 +455,28 @@ static void ImGui_ImplDX12_CreateFontsTexture() IM_ASSERT(SUCCEEDED(hr)); void* mapped = nullptr; - D3D12_RANGE range = { 0, uploadSize }; + D3D12_RANGE range = { 0, upload_size }; hr = uploadBuffer->Map(0, &range, &mapped); IM_ASSERT(SUCCEEDED(hr)); for (int y = 0; y < height; y++) - memcpy((void*) ((uintptr_t) mapped + y * uploadPitch), pixels + y * width * 4, width * 4); + memcpy((void*) ((uintptr_t) mapped + y * upload_pitch), pixels + y * width * 4, width * 4); uploadBuffer->Unmap(0, &range); D3D12_TEXTURE_COPY_LOCATION srcLocation = {}; - srcLocation.pResource = uploadBuffer; - srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; - srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - srcLocation.PlacedFootprint.Footprint.Width = width; - srcLocation.PlacedFootprint.Footprint.Height = height; - srcLocation.PlacedFootprint.Footprint.Depth = 1; - srcLocation.PlacedFootprint.Footprint.RowPitch = uploadPitch; - D3D12_TEXTURE_COPY_LOCATION dstLocation = {}; - dstLocation.pResource = pTexture; - dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - dstLocation.SubresourceIndex = 0; + { + srcLocation.pResource = uploadBuffer; + srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + srcLocation.PlacedFootprint.Footprint.Width = width; + srcLocation.PlacedFootprint.Footprint.Height = height; + srcLocation.PlacedFootprint.Footprint.Depth = 1; + srcLocation.PlacedFootprint.Footprint.RowPitch = upload_pitch; + + dstLocation.pResource = pTexture; + dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + dstLocation.SubresourceIndex = 0; + } D3D12_RESOURCE_BARRIER barrier = {}; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; @@ -412,15 +493,6 @@ static void ImGui_ImplDX12_CreateFontsTexture() HANDLE event = ::CreateEvent(0, 0, 0, 0); IM_ASSERT(event != nullptr); - D3D12_COMMAND_QUEUE_DESC queueDesc = {}; - queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; - queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; - queueDesc.NodeMask = 1; - - ID3D12CommandQueue* cmdQueue = nullptr; - hr = bd->pd3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&cmdQueue)); - IM_ASSERT(SUCCEEDED(hr)); - ID3D12CommandAllocator* cmdAlloc = nullptr; hr = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc)); IM_ASSERT(SUCCEEDED(hr)); @@ -435,6 +507,7 @@ static void ImGui_ImplDX12_CreateFontsTexture() hr = cmdList->Close(); IM_ASSERT(SUCCEEDED(hr)); + ID3D12CommandQueue* cmdQueue = bd->InitInfo.CommandQueue; cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmdList); hr = cmdQueue->Signal(fence, 1); IM_ASSERT(SUCCEEDED(hr)); @@ -444,7 +517,6 @@ static void ImGui_ImplDX12_CreateFontsTexture() cmdList->Release(); cmdAlloc->Release(); - cmdQueue->Release(); ::CloseHandle(event); fence->Release(); uploadBuffer->Release(); @@ -701,28 +773,28 @@ bool ImGui_ImplDX12_CreateDeviceObjects() return true; } +static void ImGui_ImplDX12_DestroyRenderBuffers(ImGui_ImplDX12_RenderBuffers* render_buffers) +{ + SafeRelease(render_buffers->IndexBuffer); + SafeRelease(render_buffers->VertexBuffer); + render_buffers->IndexBufferSize = render_buffers->VertexBufferSize = 0; +} + void ImGui_ImplDX12_InvalidateDeviceObjects() { ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); if (!bd || !bd->pd3dDevice) return; - ImGuiIO& io = ImGui::GetIO(); SafeRelease(bd->pRootSignature); SafeRelease(bd->pPipelineState); // Free SRV descriptor used by texture + ImGuiIO& io = ImGui::GetIO(); ImGui_ImplDX12_Texture* font_tex = &bd->FontTexture; bd->InitInfo.SrvDescriptorFreeFn(&bd->InitInfo, font_tex->hFontSrvCpuDescHandle, font_tex->hFontSrvGpuDescHandle); SafeRelease(font_tex->pTextureResource); io.Fonts->SetTexID(0); // We copied bd->hFontSrvGpuDescHandle to io.Fonts->TexID so let's clear that as well. - - for (UINT i = 0; i < bd->numFramesInFlight; i++) - { - ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[i]; - SafeRelease(fr->IndexBuffer); - SafeRelease(fr->VertexBuffer); - } } bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info) @@ -745,6 +817,14 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info) io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx12"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + ImGui_ImplDX12_InitPlatformInterface(); + + // Create a dummy ImGui_ImplDX12_ViewportData holder for the main viewport, + // Since this is created and managed by the application, we will only use the ->Resources[] fields. + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + main_viewport->RendererUserData = IM_NEW(ImGui_ImplDX12_ViewportData)(bd->numFramesInFlight); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS if (init_info->SrvDescriptorAllocFn == nullptr) @@ -772,18 +852,6 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info) IM_ASSERT(init_info->SrvDescriptorAllocFn != nullptr && init_info->SrvDescriptorFreeFn != nullptr); init_info->SrvDescriptorAllocFn(&bd->InitInfo, &bd->FontTexture.hFontSrvCpuDescHandle, &bd->FontTexture.hFontSrvGpuDescHandle); - // Create buffers with a default size (they will later be grown as needed) - bd->frameIndex = UINT_MAX; - bd->pFrameResources = new ImGui_ImplDX12_RenderBuffers[bd->numFramesInFlight]; - for (int i = 0; i < (int)bd->numFramesInFlight; i++) - { - ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[i]; - fr->IndexBuffer = nullptr; - fr->VertexBuffer = nullptr; - fr->IndexBufferSize = 10000; - fr->VertexBufferSize = 5000; - } - return true; } @@ -809,13 +877,24 @@ void ImGui_ImplDX12_Shutdown() IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + // Manually delete main viewport render resources in-case we haven't initialized for viewports + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + if (ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)main_viewport->RendererUserData) + { + // We could just call ImGui_ImplDX12_DestroyWindow(main_viewport) as a convenience but that would be misleading since we only use data->Resources[] + for (UINT i = 0; i < bd->numFramesInFlight; i++) + ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]); + IM_DELETE(vd); + main_viewport->RendererUserData = nullptr; + } + // Clean up windows and device objects + ImGui_ImplDX12_ShutdownPlatformInterface(); ImGui_ImplDX12_InvalidateDeviceObjects(); - delete[] bd->pFrameResources; io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasViewports); IM_DELETE(bd); } @@ -828,6 +907,246 @@ void ImGui_ImplDX12_NewFrame() ImGui_ImplDX12_CreateDeviceObjects(); } +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) +{ + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + ImGui_ImplDX12_ViewportData* vd = IM_NEW(ImGui_ImplDX12_ViewportData)(bd->numFramesInFlight); + viewport->RendererUserData = vd; + + // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). + // Some backends will leave PlatformHandleRaw == 0, in which case we assume PlatformHandle will contain the HWND. + HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle; + IM_ASSERT(hwnd != 0); + + vd->FrameIndex = UINT_MAX; + + // Create command queue. + D3D12_COMMAND_QUEUE_DESC queue_desc = {}; + queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + + HRESULT res = S_OK; + res = bd->pd3dDevice->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&vd->CommandQueue)); + IM_ASSERT(res == S_OK); + + // Create command allocator. + for (UINT i = 0; i < bd->numFramesInFlight; ++i) + { + res = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&vd->FrameCtx[i].CommandAllocator)); + IM_ASSERT(res == S_OK); + } + + // Create command list. + res = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, vd->FrameCtx[0].CommandAllocator, nullptr, IID_PPV_ARGS(&vd->CommandList)); + IM_ASSERT(res == S_OK); + vd->CommandList->Close(); + + // Create fence. + res = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&vd->Fence)); + IM_ASSERT(res == S_OK); + + vd->FenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); + IM_ASSERT(vd->FenceEvent != nullptr); + + // Create swap chain + // FIXME-VIEWPORT: May want to copy/inherit swap chain settings from the user/application. + DXGI_SWAP_CHAIN_DESC1 sd1; + ZeroMemory(&sd1, sizeof(sd1)); + sd1.BufferCount = bd->numFramesInFlight; + sd1.Width = (UINT)viewport->Size.x; + sd1.Height = (UINT)viewport->Size.y; + sd1.Format = bd->RTVFormat; + sd1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd1.SampleDesc.Count = 1; + sd1.SampleDesc.Quality = 0; + sd1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + sd1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; + sd1.Scaling = DXGI_SCALING_NONE; + sd1.Stereo = FALSE; + + IDXGIFactory4* dxgi_factory = nullptr; + res = ::CreateDXGIFactory1(IID_PPV_ARGS(&dxgi_factory)); + IM_ASSERT(res == S_OK); + + IDXGISwapChain1* swap_chain = nullptr; + res = dxgi_factory->CreateSwapChainForHwnd(vd->CommandQueue, hwnd, &sd1, nullptr, nullptr, &swap_chain); + IM_ASSERT(res == S_OK); + + dxgi_factory->Release(); + + // Or swapChain.As(&mSwapChain) + IM_ASSERT(vd->SwapChain == nullptr); + swap_chain->QueryInterface(IID_PPV_ARGS(&vd->SwapChain)); + swap_chain->Release(); + + // Create the render targets + if (vd->SwapChain) + { + D3D12_DESCRIPTOR_HEAP_DESC desc = {}; + desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + desc.NumDescriptors = bd->numFramesInFlight; + desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + desc.NodeMask = 1; + + HRESULT hr = bd->pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&vd->RtvDescHeap)); + IM_ASSERT(hr == S_OK); + + SIZE_T rtv_descriptor_size = bd->pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = vd->RtvDescHeap->GetCPUDescriptorHandleForHeapStart(); + for (UINT i = 0; i < bd->numFramesInFlight; i++) + { + vd->FrameCtx[i].RenderTargetCpuDescriptors = rtv_handle; + rtv_handle.ptr += rtv_descriptor_size; + } + + ID3D12Resource* back_buffer; + for (UINT i = 0; i < bd->numFramesInFlight; i++) + { + IM_ASSERT(vd->FrameCtx[i].RenderTarget == nullptr); + vd->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer)); + bd->pd3dDevice->CreateRenderTargetView(back_buffer, nullptr, vd->FrameCtx[i].RenderTargetCpuDescriptors); + vd->FrameCtx[i].RenderTarget = back_buffer; + } + } + + for (UINT i = 0; i < bd->numFramesInFlight; i++) + ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]); +} + +static void ImGui_WaitForPendingOperations(ImGui_ImplDX12_ViewportData* vd) +{ + HRESULT hr = S_FALSE; + if (vd && vd->CommandQueue && vd->Fence && vd->FenceEvent) + { + hr = vd->CommandQueue->Signal(vd->Fence, ++vd->FenceSignaledValue); + IM_ASSERT(hr == S_OK); + ::WaitForSingleObject(vd->FenceEvent, 0); // Reset any forgotten waits + hr = vd->Fence->SetEventOnCompletion(vd->FenceSignaledValue, vd->FenceEvent); + IM_ASSERT(hr == S_OK); + ::WaitForSingleObject(vd->FenceEvent, INFINITE); + } +} + +static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) +{ + // The main viewport (owned by the application) will always have RendererUserData == 0 since we didn't create the data for it. + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + if (ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData) + { + ImGui_WaitForPendingOperations(vd); + + SafeRelease(vd->CommandQueue); + SafeRelease(vd->CommandList); + SafeRelease(vd->SwapChain); + SafeRelease(vd->RtvDescHeap); + SafeRelease(vd->Fence); + ::CloseHandle(vd->FenceEvent); + vd->FenceEvent = nullptr; + + for (UINT i = 0; i < bd->numFramesInFlight; i++) + { + SafeRelease(vd->FrameCtx[i].RenderTarget); + SafeRelease(vd->FrameCtx[i].CommandAllocator); + ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]); + } + IM_DELETE(vd); + } + viewport->RendererUserData = nullptr; +} + +static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData; + + ImGui_WaitForPendingOperations(vd); + + for (UINT i = 0; i < bd->numFramesInFlight; i++) + SafeRelease(vd->FrameCtx[i].RenderTarget); + + if (vd->SwapChain) + { + ID3D12Resource* back_buffer = nullptr; + vd->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0); + for (UINT i = 0; i < bd->numFramesInFlight; i++) + { + vd->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer)); + bd->pd3dDevice->CreateRenderTargetView(back_buffer, nullptr, vd->FrameCtx[i].RenderTargetCpuDescriptors); + vd->FrameCtx[i].RenderTarget = back_buffer; + } + } +} + +static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*) +{ + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData; + + ImGui_ImplDX12_FrameContext* frame_context = &vd->FrameCtx[vd->FrameIndex % bd->numFramesInFlight]; + UINT back_buffer_idx = vd->SwapChain->GetCurrentBackBufferIndex(); + + const ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); + D3D12_RESOURCE_BARRIER barrier = {}; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.pResource = vd->FrameCtx[back_buffer_idx].RenderTarget; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + + // Draw + ID3D12GraphicsCommandList* cmd_list = vd->CommandList; + + frame_context->CommandAllocator->Reset(); + cmd_list->Reset(frame_context->CommandAllocator, nullptr); + cmd_list->ResourceBarrier(1, &barrier); + cmd_list->OMSetRenderTargets(1, &vd->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, FALSE, nullptr); + if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) + cmd_list->ClearRenderTargetView(vd->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, (float*)&clear_color, 0, nullptr); + cmd_list->SetDescriptorHeaps(1, &bd->pd3dSrvDescHeap); + + ImGui_ImplDX12_RenderDrawData(viewport->DrawData, cmd_list); + + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; + cmd_list->ResourceBarrier(1, &barrier); + cmd_list->Close(); + + vd->CommandQueue->Wait(vd->Fence, vd->FenceSignaledValue); + vd->CommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmd_list); + vd->CommandQueue->Signal(vd->Fence, ++vd->FenceSignaledValue); +} + +static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport, void*) +{ + ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData; + + vd->SwapChain->Present(0, 0); + while (vd->Fence->GetCompletedValue() < vd->FenceSignaledValue) + ::SwitchToThread(); +} + +void ImGui_ImplDX12_InitPlatformInterface() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_CreateWindow = ImGui_ImplDX12_CreateWindow; + platform_io.Renderer_DestroyWindow = ImGui_ImplDX12_DestroyWindow; + platform_io.Renderer_SetWindowSize = ImGui_ImplDX12_SetWindowSize; + platform_io.Renderer_RenderWindow = ImGui_ImplDX12_RenderWindow; + platform_io.Renderer_SwapBuffers = ImGui_ImplDX12_SwapBuffers; +} + +void ImGui_ImplDX12_ShutdownPlatformInterface() +{ + ImGui::DestroyPlatformWindows(); +} + //----------------------------------------------------------------------------- #endif // #ifndef IMGUI_DISABLE diff --git a/neo/libs/imgui/backends/imgui_impl_dx12.h b/neo/libs/imgui/backends/imgui_impl_dx12.h index a57c1694d..f90cb9bc1 100644 --- a/neo/libs/imgui/backends/imgui_impl_dx12.h +++ b/neo/libs/imgui/backends/imgui_impl_dx12.h @@ -5,6 +5,7 @@ // [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // The aim of imgui_impl_dx12.h/.cpp is to be usable in your engine without any modification. // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ diff --git a/neo/libs/imgui/backends/imgui_impl_dx9.cpp b/neo/libs/imgui/backends/imgui_impl_dx9.cpp index 2cc8681a6..d091d04af 100644 --- a/neo/libs/imgui/backends/imgui_impl_dx9.cpp +++ b/neo/libs/imgui/backends/imgui_impl_dx9.cpp @@ -5,6 +5,7 @@ // [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: IMGUI_USE_BGRA_PACKED_COLOR support, as this is the optimal color encoding for DirectX9. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -16,6 +17,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2024-10-07: DirectX9: Changed default texture sampler to Clamp instead of Repeat/Wrap. // 2024-02-12: DirectX9: Using RGBA format when supported by the driver to avoid CPU side conversion. (#6575) // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. @@ -79,6 +81,12 @@ static ImGui_ImplDX9_Data* ImGui_ImplDX9_GetBackendData() return ImGui::GetCurrentContext() ? (ImGui_ImplDX9_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; } +// Forward Declarations +static void ImGui_ImplDX9_InitMultiViewportSupport(); +static void ImGui_ImplDX9_ShutdownMultiViewportSupport(); +static void ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows(); +static void ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows(); + // Functions static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data) { @@ -283,6 +291,11 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) global_vtx_offset += draw_list->VtxBuffer.Size; } + // When using multi-viewports, it appears that there's an odd logic in DirectX9 which prevent subsequent windows + // from rendering until the first window submits at least one draw call, even once. That's our workaround. (see #2560) + if (global_vtx_offset == 0) + bd->pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 0, 0, 0); + // Restore the DX9 transform device->SetTransform(D3DTS_WORLD, &last_world); device->SetTransform(D3DTS_VIEW, &last_view); @@ -322,11 +335,14 @@ bool ImGui_ImplDX9_Init(IDirect3DDevice9* device) io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_dx9"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) bd->pd3dDevice = device; bd->pd3dDevice->AddRef(); bd->HasRgbaSupport = ImGui_ImplDX9_CheckFormatSupport(bd->pd3dDevice, D3DFMT_A8B8G8R8); + ImGui_ImplDX9_InitMultiViewportSupport(); + return true; } @@ -336,11 +352,12 @@ void ImGui_ImplDX9_Shutdown() IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplDX9_ShutdownMultiViewportSupport(); ImGui_ImplDX9_InvalidateDeviceObjects(); if (bd->pd3dDevice) { bd->pd3dDevice->Release(); } io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasViewports); IM_DELETE(bd); } @@ -397,6 +414,7 @@ bool ImGui_ImplDX9_CreateDeviceObjects() return false; if (!ImGui_ImplDX9_CreateFontsTexture()) return false; + ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows(); return true; } @@ -408,6 +426,7 @@ void ImGui_ImplDX9_InvalidateDeviceObjects() if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; } if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; } if (bd->FontTexture) { bd->FontTexture->Release(); bd->FontTexture = nullptr; ImGui::GetIO().Fonts->SetTexID(0); } // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well. + ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows(); } void ImGui_ImplDX9_NewFrame() @@ -419,6 +438,148 @@ void ImGui_ImplDX9_NewFrame() ImGui_ImplDX9_CreateDeviceObjects(); } +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data. +struct ImGui_ImplDX9_ViewportData +{ + IDirect3DSwapChain9* SwapChain; + D3DPRESENT_PARAMETERS d3dpp; + + ImGui_ImplDX9_ViewportData() { SwapChain = nullptr; ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS)); } + ~ImGui_ImplDX9_ViewportData() { IM_ASSERT(SwapChain == nullptr); } +}; + +static void ImGui_ImplDX9_CreateWindow(ImGuiViewport* viewport) +{ + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + ImGui_ImplDX9_ViewportData* vd = IM_NEW(ImGui_ImplDX9_ViewportData)(); + viewport->RendererUserData = vd; + + // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). + // Some backends will leave PlatformHandleRaw == 0, in which case we assume PlatformHandle will contain the HWND. + HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle; + IM_ASSERT(hwnd != 0); + + ZeroMemory(&vd->d3dpp, sizeof(D3DPRESENT_PARAMETERS)); + vd->d3dpp.Windowed = TRUE; + vd->d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + vd->d3dpp.BackBufferWidth = (UINT)viewport->Size.x; + vd->d3dpp.BackBufferHeight = (UINT)viewport->Size.y; + vd->d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; + vd->d3dpp.hDeviceWindow = hwnd; + vd->d3dpp.EnableAutoDepthStencil = FALSE; + vd->d3dpp.AutoDepthStencilFormat = D3DFMT_D16; + vd->d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // Present without vsync + + HRESULT hr = bd->pd3dDevice->CreateAdditionalSwapChain(&vd->d3dpp, &vd->SwapChain); IM_UNUSED(hr); + IM_ASSERT(hr == D3D_OK); + IM_ASSERT(vd->SwapChain != nullptr); +} + +static void ImGui_ImplDX9_DestroyWindow(ImGuiViewport* viewport) +{ + // The main viewport (owned by the application) will always have RendererUserData == 0 since we didn't create the data for it. + if (ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData) + { + if (vd->SwapChain) + vd->SwapChain->Release(); + vd->SwapChain = nullptr; + ZeroMemory(&vd->d3dpp, sizeof(D3DPRESENT_PARAMETERS)); + IM_DELETE(vd); + } + viewport->RendererUserData = nullptr; +} + +static void ImGui_ImplDX9_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData; + if (vd->SwapChain) + { + vd->SwapChain->Release(); + vd->SwapChain = nullptr; + vd->d3dpp.BackBufferWidth = (UINT)size.x; + vd->d3dpp.BackBufferHeight = (UINT)size.y; + HRESULT hr = bd->pd3dDevice->CreateAdditionalSwapChain(&vd->d3dpp, &vd->SwapChain); IM_UNUSED(hr); + IM_ASSERT(hr == D3D_OK); + } +} + +static void ImGui_ImplDX9_RenderWindow(ImGuiViewport* viewport, void*) +{ + ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData(); + ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData; + ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); + + LPDIRECT3DSURFACE9 render_target = nullptr; + LPDIRECT3DSURFACE9 last_render_target = nullptr; + LPDIRECT3DSURFACE9 last_depth_stencil = nullptr; + vd->SwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &render_target); + bd->pd3dDevice->GetRenderTarget(0, &last_render_target); + bd->pd3dDevice->GetDepthStencilSurface(&last_depth_stencil); + bd->pd3dDevice->SetRenderTarget(0, render_target); + bd->pd3dDevice->SetDepthStencilSurface(nullptr); + + if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) + { + D3DCOLOR clear_col_dx = D3DCOLOR_RGBA((int)(clear_color.x*255.0f), (int)(clear_color.y*255.0f), (int)(clear_color.z*255.0f), (int)(clear_color.w*255.0f)); + bd->pd3dDevice->Clear(0, nullptr, D3DCLEAR_TARGET, clear_col_dx, 1.0f, 0); + } + + ImGui_ImplDX9_RenderDrawData(viewport->DrawData); + + // Restore render target + bd->pd3dDevice->SetRenderTarget(0, last_render_target); + bd->pd3dDevice->SetDepthStencilSurface(last_depth_stencil); + render_target->Release(); + last_render_target->Release(); + if (last_depth_stencil) last_depth_stencil->Release(); +} + +static void ImGui_ImplDX9_SwapBuffers(ImGuiViewport* viewport, void*) +{ + ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData; + HRESULT hr = vd->SwapChain->Present(nullptr, nullptr, vd->d3dpp.hDeviceWindow, nullptr, 0); + // Let main application handle D3DERR_DEVICELOST by resetting the device. + IM_ASSERT(SUCCEEDED(hr) || hr == D3DERR_DEVICELOST); +} + +static void ImGui_ImplDX9_InitMultiViewportSupport() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_CreateWindow = ImGui_ImplDX9_CreateWindow; + platform_io.Renderer_DestroyWindow = ImGui_ImplDX9_DestroyWindow; + platform_io.Renderer_SetWindowSize = ImGui_ImplDX9_SetWindowSize; + platform_io.Renderer_RenderWindow = ImGui_ImplDX9_RenderWindow; + platform_io.Renderer_SwapBuffers = ImGui_ImplDX9_SwapBuffers; +} + +static void ImGui_ImplDX9_ShutdownMultiViewportSupport() +{ + ImGui::DestroyPlatformWindows(); +} + +static void ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int i = 1; i < platform_io.Viewports.Size; i++) + if (!platform_io.Viewports[i]->RendererUserData) + ImGui_ImplDX9_CreateWindow(platform_io.Viewports[i]); +} + +static void ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int i = 1; i < platform_io.Viewports.Size; i++) + if (platform_io.Viewports[i]->RendererUserData) + ImGui_ImplDX9_DestroyWindow(platform_io.Viewports[i]); +} + //----------------------------------------------------------------------------- #endif // #ifndef IMGUI_DISABLE diff --git a/neo/libs/imgui/backends/imgui_impl_dx9.h b/neo/libs/imgui/backends/imgui_impl_dx9.h index b693054d9..983d956e9 100644 --- a/neo/libs/imgui/backends/imgui_impl_dx9.h +++ b/neo/libs/imgui/backends/imgui_impl_dx9.h @@ -5,6 +5,7 @@ // [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: IMGUI_USE_BGRA_PACKED_COLOR support, as this is the optimal color encoding for DirectX9. +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/neo/libs/imgui/backends/imgui_impl_glfw.cpp b/neo/libs/imgui/backends/imgui_impl_glfw.cpp index 01a6a5566..7a0b1f54d 100644 --- a/neo/libs/imgui/backends/imgui_impl_glfw.cpp +++ b/neo/libs/imgui/backends/imgui_impl_glfw.cpp @@ -9,6 +9,9 @@ // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values are obsolete since 1.87 and not supported since 1.91.5] // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// Missing features or Issues: +// [ ] Platform: Multi-viewport: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -25,6 +28,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2024-11-05: [Docking] Added Linux workaround for spurious mouse up events emitted while dragging and creating new viewport. (#3158, #7733, #7922) // 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: // - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn // - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn @@ -41,6 +46,7 @@ // 2023-03-16: Inputs: Fixed key modifiers handling on secondary viewports (docking branch). Broken on 2023/01/04. (#6248, #6034) // 2023-03-14: Emscripten: Avoid using glfwGetError() and glfwGetGamepadState() which are not correctly implemented in Emscripten emulation. (#6240) // 2023-02-03: Emscripten: Registering custom low-level mouse wheel handler to get more accurate scrolling impulses on Emscripten. (#4019, #6096) +// 2023-01-18: Handle unsupported glfwGetVideoMode() call on e.g. Emscripten. // 2023-01-04: Inputs: Fixed mods state on Linux when using Alt-GR text input (e.g. German keyboard layout), could lead to broken text input. Revert a 2022/01/17 change were we resumed using mods provided by GLFW, turns out they were faulty. // 2022-11-22: Perform a dummy glfwGetError() read to cancel missing names with glfwGetKeyName(). (#5908) // 2022-10-18: Perform a dummy glfwGetError() read to cancel missing mouse cursors errors. Using GLFW_VERSION_COMBINED directly. (#5785) @@ -125,11 +131,29 @@ // We gather version tests as define in order to easily see which features are version-dependent. #define GLFW_VERSION_COMBINED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 + GLFW_VERSION_REVISION) +#define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_COMBINED >= 3200) // 3.2+ GLFW_FLOATING +#define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_COMBINED >= 3300) // 3.3+ GLFW_HOVERED +#define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwSetWindowOpacity +#define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetMonitorContentScale +#if defined(__EMSCRIPTEN__) || defined(__SWITCH__) // no Vulkan support in GLFW for Emscripten or homebrew Nintendo Switch +#define GLFW_HAS_VULKAN (0) +#else +#define GLFW_HAS_VULKAN (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwCreateWindowSurface +#endif +#define GLFW_HAS_FOCUS_WINDOW (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwFocusWindow +#define GLFW_HAS_FOCUS_ON_SHOW (GLFW_VERSION_COMBINED >= 3300) // 3.3+ GLFW_FOCUS_ON_SHOW +#define GLFW_HAS_MONITOR_WORK_AREA (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetMonitorWorkarea +#define GLFW_HAS_OSX_WINDOW_POS_FIX (GLFW_VERSION_COMBINED >= 3301) // 3.3.1+ Fixed: Resizing window repositions it on MacOS #1553 #ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released? #define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_COMBINED >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR #else #define GLFW_HAS_NEW_CURSORS (0) #endif +#ifdef GLFW_MOUSE_PASSTHROUGH // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2020-07-17 (passthrough) +#define GLFW_HAS_MOUSE_PASSTHROUGH (GLFW_VERSION_COMBINED >= 3400) // 3.4+ GLFW_MOUSE_PASSTHROUGH +#else +#define GLFW_HAS_MOUSE_PASSTHROUGH (0) +#endif #define GLFW_HAS_GAMEPAD_API (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetGamepadState() new api #define GLFW_HAS_GETKEYNAME (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwGetKeyName() #define GLFW_HAS_GETERROR (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetError() @@ -149,9 +173,13 @@ struct ImGui_ImplGlfw_Data double Time; GLFWwindow* MouseWindow; GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT]; + bool MouseIgnoreButtonUpWaitForFocusLoss; + bool MouseIgnoreButtonUp; ImVec2 LastValidMousePos; + GLFWwindow* KeyOwnerWindows[GLFW_KEY_LAST]; bool InstalledCallbacks; bool CallbacksChainForAllWindows; + bool WantUpdateMonitors; #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 const char* CanvasSelector; #endif @@ -184,6 +212,11 @@ static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData() return ImGui::GetCurrentContext() ? (ImGui_ImplGlfw_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; } +// Forward Declarations +static void ImGui_ImplGlfw_UpdateMonitors(); +static void ImGui_ImplGlfw_InitMultiViewportSupport(); +static void ImGui_ImplGlfw_ShutdownMultiViewportSupport(); + // Functions // Not static to allow third-party code to use that if they want to (but undocumented) @@ -337,6 +370,10 @@ void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int acti if (bd->PrevUserCallbackMousebutton != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) bd->PrevUserCallbackMousebutton(window, button, action, mods); + // Workaround for Linux: ignore mouse up events which are following an focus loss following a viewport creation + if (bd->MouseIgnoreButtonUp && action == GLFW_RELEASE) + return; + ImGui_ImplGlfw_UpdateKeyModifiers(window); ImGuiIO& io = ImGui::GetIO(); @@ -404,6 +441,9 @@ void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, i ImGui_ImplGlfw_UpdateKeyModifiers(window); + if (keycode >= 0 && keycode < IM_ARRAYSIZE(bd->KeyOwnerWindows)) + bd->KeyOwnerWindows[keycode] = (action == GLFW_PRESS) ? window : nullptr; + keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode); ImGuiIO& io = ImGui::GetIO(); @@ -418,6 +458,10 @@ void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused) if (bd->PrevUserCallbackWindowFocus != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) bd->PrevUserCallbackWindowFocus(window, focused); + // Workaround for Linux: when losing focus with MouseIgnoreButtonUpWaitForFocusLoss set, we will temporarily ignore subsequent Mouse Up events + bd->MouseIgnoreButtonUp = (bd->MouseIgnoreButtonUpWaitForFocusLoss && focused == 0); + bd->MouseIgnoreButtonUpWaitForFocusLoss = false; + ImGuiIO& io = ImGui::GetIO(); io.AddFocusEvent(focused != 0); } @@ -429,6 +473,13 @@ void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y) bd->PrevUserCallbackCursorPos(window, x, y); ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + int window_x, window_y; + glfwGetWindowPos(window, &window_x, &window_y); + x += window_x; + y += window_y; + } io.AddMousePosEvent((float)x, (float)y); bd->LastValidMousePos = ImVec2((float)x, (float)y); } @@ -467,7 +518,8 @@ void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int) { - // Unused in 'master' branch but 'docking' branch will use this, so we declare it ahead of it so if you have to install callbacks you can install this one too. + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + bd->WantUpdateMonitors = true; } #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 @@ -489,32 +541,7 @@ static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEven #endif #ifdef _WIN32 -// GLFW doesn't allow to distinguish Mouse vs TouchScreen vs Pen. -// Add support for Win32 (based on imgui_impl_win32), because we rely on _TouchScreen info to trickle inputs differently. -static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo() -{ - LPARAM extra_info = ::GetMessageExtraInfo(); - if ((extra_info & 0xFFFFFF80) == 0xFF515700) - return ImGuiMouseSource_Pen; - if ((extra_info & 0xFFFFFF80) == 0xFF515780) - return ImGuiMouseSource_TouchScreen; - return ImGuiMouseSource_Mouse; -} -static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - switch (msg) - { - case WM_MOUSEMOVE: case WM_NCMOUSEMOVE: - case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_LBUTTONUP: - case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_RBUTTONUP: - case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: case WM_MBUTTONUP: - case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: case WM_XBUTTONUP: - ImGui::GetIO().AddMouseSourceEvent(GetMouseSourceFromMessageExtraInfo()); - break; - } - return ::CallWindowProcW(bd->PrevWndProc, hWnd, msg, wParam, lParam); -} +static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); #endif void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) @@ -590,9 +617,16 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw io.BackendPlatformName = "imgui_impl_glfw"; io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) +#ifndef __EMSCRIPTEN__ + io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) +#endif +#if GLFW_HAS_MOUSE_PASSTHROUGH || GLFW_HAS_WINDOW_HOVERED + io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional) +#endif bd->Window = window; bd->Time = 0.0; + bd->WantUpdateMonitors = true; ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); platform_io.Platform_SetClipboardTextFn = [](ImGuiContext*, const char* text) { glfwSetClipboardString(nullptr, text); }; @@ -631,6 +665,11 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw if (install_callbacks) ImGui_ImplGlfw_InstallCallbacks(window); + // Update monitor a first time during init + // (note: monitor callback are broken in GLFW 3.2 and earlier, see github.com/glfw/glfw/issues/784) + ImGui_ImplGlfw_UpdateMonitors(); + glfwSetMonitorCallback(ImGui_ImplGlfw_MonitorCallback); + // Set platform dependent data in viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = (void*)bd->Window; @@ -641,6 +680,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw #else IM_UNUSED(main_viewport); #endif + ImGui_ImplGlfw_InitMultiViewportSupport(); // Windows: register a WndProc hook so we can intercept some messages. #ifdef _WIN32 @@ -691,6 +731,8 @@ void ImGui_ImplGlfw_Shutdown() IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplGlfw_ShutdownMultiViewportSupport(); + if (bd->InstalledCallbacks) ImGui_ImplGlfw_RestoreCallbacks(bd->Window); #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 @@ -710,7 +752,7 @@ void ImGui_ImplGlfw_Shutdown() io.BackendPlatformName = nullptr; io.BackendPlatformUserData = nullptr; - io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); + io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_PlatformHasViewports | ImGuiBackendFlags_HasMouseHoveredViewport); IM_DELETE(bd); } @@ -718,10 +760,15 @@ static void ImGui_ImplGlfw_UpdateMouseData() { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - // (those braces are here to reduce diff with multi-viewports support in 'docking' branch) + ImGuiID mouse_viewport_id = 0; + const ImVec2 mouse_pos_prev = io.MousePos; + for (int n = 0; n < platform_io.Viewports.Size; n++) { - GLFWwindow* window = bd->Window; + ImGuiViewport* viewport = platform_io.Viewports[n]; + GLFWwindow* window = (GLFWwindow*)viewport->PlatformHandle; + #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 const bool is_window_focused = true; #else @@ -730,19 +777,54 @@ static void ImGui_ImplGlfw_UpdateMouseData() if (is_window_focused) { // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user) + // When multi-viewports are enabled, all Dear ImGui positions are same as OS positions. if (io.WantSetMousePos) - glfwSetCursorPos(window, (double)io.MousePos.x, (double)io.MousePos.y); + glfwSetCursorPos(window, (double)(mouse_pos_prev.x - viewport->Pos.x), (double)(mouse_pos_prev.y - viewport->Pos.y)); // (Optional) Fallback to provide mouse position when focused (ImGui_ImplGlfw_CursorPosCallback already provides this when hovered or captured) if (bd->MouseWindow == nullptr) { double mouse_x, mouse_y; glfwGetCursorPos(window, &mouse_x, &mouse_y); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + // Single viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) + // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor) + int window_x, window_y; + glfwGetWindowPos(window, &window_x, &window_y); + mouse_x += window_x; + mouse_y += window_y; + } bd->LastValidMousePos = ImVec2((float)mouse_x, (float)mouse_y); io.AddMousePosEvent((float)mouse_x, (float)mouse_y); } } + + // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering. + // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. + // - [X] GLFW >= 3.3 backend ON WINDOWS ONLY does correctly ignore viewports with the _NoInputs flag (since we implement hit via our WndProc hook) + // On other platforms we rely on the library fallbacking to its own search when reporting a viewport with _NoInputs flag. + // - [!] GLFW <= 3.2 backend CANNOT correctly ignore viewports with the _NoInputs flag, and CANNOT reported Hovered Viewport because of mouse capture. + // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window + // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported + // by the backend, and use its flawed heuristic to guess the viewport behind. + // - [X] GLFW backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). + // FIXME: This is currently only correct on Win32. See what we do below with the WM_NCHITTEST, missing an equivalent for other systems. + // See https://github.com/glfw/glfw/issues/1236 if you want to help in making this a GLFW feature. +#if GLFW_HAS_MOUSE_PASSTHROUGH + const bool window_no_input = (viewport->Flags & ImGuiViewportFlags_NoInputs) != 0; + glfwSetWindowAttrib(window, GLFW_MOUSE_PASSTHROUGH, window_no_input); +#endif +#if GLFW_HAS_MOUSE_PASSTHROUGH || GLFW_HAS_WINDOW_HOVERED + if (glfwGetWindowAttrib(window, GLFW_HOVERED)) + mouse_viewport_id = viewport->ID; +#else + // We cannot use bd->MouseWindow maintained from CursorEnter/Leave callbacks, because it is locked to the window capturing mouse. +#endif } + + if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) + io.AddMouseViewportEvent(mouse_viewport_id); } static void ImGui_ImplGlfw_UpdateMouseCursor() @@ -753,9 +835,10 @@ static void ImGui_ImplGlfw_UpdateMouseCursor() return; ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); - // (those braces are here to reduce diff with multi-viewports support in 'docking' branch) + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int n = 0; n < platform_io.Viewports.Size; n++) { - GLFWwindow* window = bd->Window; + GLFWwindow* window = (GLFWwindow*)platform_io.Viewports[n]->PlatformHandle; if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) { // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor @@ -824,6 +907,50 @@ static void ImGui_ImplGlfw_UpdateGamepads() #undef MAP_ANALOG } +static void ImGui_ImplGlfw_UpdateMonitors() +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + bd->WantUpdateMonitors = false; + + int monitors_count = 0; + GLFWmonitor** glfw_monitors = glfwGetMonitors(&monitors_count); + if (monitors_count == 0) // Preserve existing monitor list if there are none. Happens on macOS sleeping (#5683) + return; + + platform_io.Monitors.resize(0); + for (int n = 0; n < monitors_count; n++) + { + ImGuiPlatformMonitor monitor; + int x, y; + glfwGetMonitorPos(glfw_monitors[n], &x, &y); + const GLFWvidmode* vid_mode = glfwGetVideoMode(glfw_monitors[n]); + if (vid_mode == nullptr) + continue; // Failed to get Video mode (e.g. Emscripten does not support this function) + monitor.MainPos = monitor.WorkPos = ImVec2((float)x, (float)y); + monitor.MainSize = monitor.WorkSize = ImVec2((float)vid_mode->width, (float)vid_mode->height); +#if GLFW_HAS_MONITOR_WORK_AREA + int w, h; + glfwGetMonitorWorkarea(glfw_monitors[n], &x, &y, &w, &h); + if (w > 0 && h > 0) // Workaround a small GLFW issue reporting zero on monitor changes: https://github.com/glfw/glfw/pull/1761 + { + monitor.WorkPos = ImVec2((float)x, (float)y); + monitor.WorkSize = ImVec2((float)w, (float)h); + } +#endif +#if GLFW_HAS_PER_MONITOR_DPI + // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. + float x_scale, y_scale; + glfwGetMonitorContentScale(glfw_monitors[n], &x_scale, &y_scale); + if (x_scale == 0.0f) + continue; // Some accessibility applications are declaring virtual monitors with a DPI of 0, see #7902. + monitor.DpiScale = x_scale; +#endif + monitor.PlatformHandle = (void*)glfw_monitors[n]; // [...] GLFW doc states: "guaranteed to be valid only until the monitor configuration changes" + platform_io.Monitors.push_back(monitor); + } +} + void ImGui_ImplGlfw_NewFrame() { ImGuiIO& io = ImGui::GetIO(); @@ -838,6 +965,8 @@ void ImGui_ImplGlfw_NewFrame() io.DisplaySize = ImVec2((float)w, (float)h); if (w > 0 && h > 0) io.DisplayFramebufferScale = ImVec2((float)display_w / (float)w, (float)display_h / (float)h); + if (bd->WantUpdateMonitors) + ImGui_ImplGlfw_UpdateMonitors(); // Setup time step // (Accept glfwGetTime() not returning a monotonically increasing value. Seems to happens on disconnecting peripherals and probably on VMs and Emscripten, see #6491, #6189, #6114, #3644) @@ -847,6 +976,7 @@ void ImGui_ImplGlfw_NewFrame() io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f); bd->Time = current_time; + bd->MouseIgnoreButtonUp = false; ImGui_ImplGlfw_UpdateMouseData(); ImGui_ImplGlfw_UpdateMouseCursor(); @@ -916,6 +1046,404 @@ void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* window, const char* c } #endif // #ifdef EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 + +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data. +struct ImGui_ImplGlfw_ViewportData +{ + GLFWwindow* Window; + bool WindowOwned; + int IgnoreWindowPosEventFrame; + int IgnoreWindowSizeEventFrame; +#ifdef _WIN32 + WNDPROC PrevWndProc; +#endif + + ImGui_ImplGlfw_ViewportData() { memset((void*)this, 0, sizeof(*this)); IgnoreWindowSizeEventFrame = IgnoreWindowPosEventFrame = -1; } + ~ImGui_ImplGlfw_ViewportData() { IM_ASSERT(Window == nullptr); } +}; + +static void ImGui_ImplGlfw_WindowCloseCallback(GLFWwindow* window) +{ + if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) + viewport->PlatformRequestClose = true; +} + +// GLFW may dispatch window pos/size events after calling glfwSetWindowPos()/glfwSetWindowSize(). +// However: depending on the platform the callback may be invoked at different time: +// - on Windows it appears to be called within the glfwSetWindowPos()/glfwSetWindowSize() call +// - on Linux it is queued and invoked during glfwPollEvents() +// Because the event doesn't always fire on glfwSetWindowXXX() we use a frame counter tag to only +// ignore recent glfwSetWindowXXX() calls. +static void ImGui_ImplGlfw_WindowPosCallback(GLFWwindow* window, int, int) +{ + if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) + { + if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) + { + bool ignore_event = (ImGui::GetFrameCount() <= vd->IgnoreWindowPosEventFrame + 1); + //data->IgnoreWindowPosEventFrame = -1; + if (ignore_event) + return; + } + viewport->PlatformRequestMove = true; + } +} + +static void ImGui_ImplGlfw_WindowSizeCallback(GLFWwindow* window, int, int) +{ + if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle(window)) + { + if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) + { + bool ignore_event = (ImGui::GetFrameCount() <= vd->IgnoreWindowSizeEventFrame + 1); + //data->IgnoreWindowSizeEventFrame = -1; + if (ignore_event) + return; + } + viewport->PlatformRequestResize = true; + } +} + +static void ImGui_ImplGlfw_CreateWindow(ImGuiViewport* viewport) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGui_ImplGlfw_ViewportData* vd = IM_NEW(ImGui_ImplGlfw_ViewportData)(); + viewport->PlatformUserData = vd; + + // Workaround for Linux: ignore mouse up events corresponding to losing focus of the previously focused window (#7733, #3158, #7922) +#ifdef __linux__ + bd->MouseIgnoreButtonUpWaitForFocusLoss = true; +#endif + + // GLFW 3.2 unfortunately always set focus on glfwCreateWindow() if GLFW_VISIBLE is set, regardless of GLFW_FOCUSED + // With GLFW 3.3, the hint GLFW_FOCUS_ON_SHOW fixes this problem + glfwWindowHint(GLFW_VISIBLE, false); + glfwWindowHint(GLFW_FOCUSED, false); +#if GLFW_HAS_FOCUS_ON_SHOW + glfwWindowHint(GLFW_FOCUS_ON_SHOW, false); + #endif + glfwWindowHint(GLFW_DECORATED, (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? false : true); +#if GLFW_HAS_WINDOW_TOPMOST + glfwWindowHint(GLFW_FLOATING, (viewport->Flags & ImGuiViewportFlags_TopMost) ? true : false); +#endif + GLFWwindow* share_window = (bd->ClientApi == GlfwClientApi_OpenGL) ? bd->Window : nullptr; + vd->Window = glfwCreateWindow((int)viewport->Size.x, (int)viewport->Size.y, "No Title Yet", nullptr, share_window); + vd->WindowOwned = true; + viewport->PlatformHandle = (void*)vd->Window; +#ifdef _WIN32 + viewport->PlatformHandleRaw = glfwGetWin32Window(vd->Window); +#elif defined(__APPLE__) + viewport->PlatformHandleRaw = (void*)glfwGetCocoaWindow(vd->Window); +#endif + glfwSetWindowPos(vd->Window, (int)viewport->Pos.x, (int)viewport->Pos.y); + + // Install GLFW callbacks for secondary viewports + glfwSetWindowFocusCallback(vd->Window, ImGui_ImplGlfw_WindowFocusCallback); + glfwSetCursorEnterCallback(vd->Window, ImGui_ImplGlfw_CursorEnterCallback); + glfwSetCursorPosCallback(vd->Window, ImGui_ImplGlfw_CursorPosCallback); + glfwSetMouseButtonCallback(vd->Window, ImGui_ImplGlfw_MouseButtonCallback); + glfwSetScrollCallback(vd->Window, ImGui_ImplGlfw_ScrollCallback); + glfwSetKeyCallback(vd->Window, ImGui_ImplGlfw_KeyCallback); + glfwSetCharCallback(vd->Window, ImGui_ImplGlfw_CharCallback); + glfwSetWindowCloseCallback(vd->Window, ImGui_ImplGlfw_WindowCloseCallback); + glfwSetWindowPosCallback(vd->Window, ImGui_ImplGlfw_WindowPosCallback); + glfwSetWindowSizeCallback(vd->Window, ImGui_ImplGlfw_WindowSizeCallback); + if (bd->ClientApi == GlfwClientApi_OpenGL) + { + glfwMakeContextCurrent(vd->Window); + glfwSwapInterval(0); + } +} + +static void ImGui_ImplGlfw_DestroyWindow(ImGuiViewport* viewport) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) + { + if (vd->WindowOwned) + { +#if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) + HWND hwnd = (HWND)viewport->PlatformHandleRaw; + ::RemovePropA(hwnd, "IMGUI_VIEWPORT"); +#endif + + // Release any keys that were pressed in the window being destroyed and are still held down, + // because we will not receive any release events after window is destroyed. + for (int i = 0; i < IM_ARRAYSIZE(bd->KeyOwnerWindows); i++) + if (bd->KeyOwnerWindows[i] == vd->Window) + ImGui_ImplGlfw_KeyCallback(vd->Window, i, 0, GLFW_RELEASE, 0); // Later params are only used for main viewport, on which this function is never called. + + glfwDestroyWindow(vd->Window); + } + vd->Window = nullptr; + IM_DELETE(vd); + } + viewport->PlatformUserData = viewport->PlatformHandle = nullptr; +} + +static void ImGui_ImplGlfw_ShowWindow(ImGuiViewport* viewport) +{ + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + +#if defined(_WIN32) + // GLFW hack: Hide icon from task bar + HWND hwnd = (HWND)viewport->PlatformHandleRaw; + if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) + { + LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); + ex_style &= ~WS_EX_APPWINDOW; + ex_style |= WS_EX_TOOLWINDOW; + ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); + } + + // GLFW hack: install hook for WM_NCHITTEST message handler +#if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED && defined(_WIN32) + ::SetPropA(hwnd, "IMGUI_VIEWPORT", viewport); + vd->PrevWndProc = (WNDPROC)::GetWindowLongPtrW(hwnd, GWLP_WNDPROC); + ::SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc); +#endif + +#if !GLFW_HAS_FOCUS_ON_SHOW + // GLFW hack: GLFW 3.2 has a bug where glfwShowWindow() also activates/focus the window. + // The fix was pushed to GLFW repository on 2018/01/09 and should be included in GLFW 3.3 via a GLFW_FOCUS_ON_SHOW window attribute. + // See https://github.com/glfw/glfw/issues/1189 + // FIXME-VIEWPORT: Implement same work-around for Linux/OSX in the meanwhile. + if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) + { + ::ShowWindow(hwnd, SW_SHOWNA); + return; + } +#endif +#endif + + glfwShowWindow(vd->Window); +} + +static ImVec2 ImGui_ImplGlfw_GetWindowPos(ImGuiViewport* viewport) +{ + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + int x = 0, y = 0; + glfwGetWindowPos(vd->Window, &x, &y); + return ImVec2((float)x, (float)y); +} + +static void ImGui_ImplGlfw_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) +{ + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + vd->IgnoreWindowPosEventFrame = ImGui::GetFrameCount(); + glfwSetWindowPos(vd->Window, (int)pos.x, (int)pos.y); +} + +static ImVec2 ImGui_ImplGlfw_GetWindowSize(ImGuiViewport* viewport) +{ + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + int w = 0, h = 0; + glfwGetWindowSize(vd->Window, &w, &h); + return ImVec2((float)w, (float)h); +} + +static void ImGui_ImplGlfw_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; +#if __APPLE__ && !GLFW_HAS_OSX_WINDOW_POS_FIX + // Native OS windows are positioned from the bottom-left corner on macOS, whereas on other platforms they are + // positioned from the upper-left corner. GLFW makes an effort to convert macOS style coordinates, however it + // doesn't handle it when changing size. We are manually moving the window in order for changes of size to be based + // on the upper-left corner. + int x, y, width, height; + glfwGetWindowPos(vd->Window, &x, &y); + glfwGetWindowSize(vd->Window, &width, &height); + glfwSetWindowPos(vd->Window, x, y - height + size.y); +#endif + vd->IgnoreWindowSizeEventFrame = ImGui::GetFrameCount(); + glfwSetWindowSize(vd->Window, (int)size.x, (int)size.y); +} + +static void ImGui_ImplGlfw_SetWindowTitle(ImGuiViewport* viewport, const char* title) +{ + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + glfwSetWindowTitle(vd->Window, title); +} + +static void ImGui_ImplGlfw_SetWindowFocus(ImGuiViewport* viewport) +{ +#if GLFW_HAS_FOCUS_WINDOW + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + glfwFocusWindow(vd->Window); +#else + // FIXME: What are the effect of not having this function? At the moment imgui doesn't actually call SetWindowFocus - we set that up ahead, will answer that question later. + (void)viewport; +#endif +} + +static bool ImGui_ImplGlfw_GetWindowFocus(ImGuiViewport* viewport) +{ + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + return glfwGetWindowAttrib(vd->Window, GLFW_FOCUSED) != 0; +} + +static bool ImGui_ImplGlfw_GetWindowMinimized(ImGuiViewport* viewport) +{ + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + return glfwGetWindowAttrib(vd->Window, GLFW_ICONIFIED) != 0; +} + +#if GLFW_HAS_WINDOW_ALPHA +static void ImGui_ImplGlfw_SetWindowAlpha(ImGuiViewport* viewport, float alpha) +{ + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + glfwSetWindowOpacity(vd->Window, alpha); +} +#endif + +static void ImGui_ImplGlfw_RenderWindow(ImGuiViewport* viewport, void*) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + if (bd->ClientApi == GlfwClientApi_OpenGL) + glfwMakeContextCurrent(vd->Window); +} + +static void ImGui_ImplGlfw_SwapBuffers(ImGuiViewport* viewport, void*) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + if (bd->ClientApi == GlfwClientApi_OpenGL) + { + glfwMakeContextCurrent(vd->Window); + glfwSwapBuffers(vd->Window); + } +} + +//-------------------------------------------------------------------------------------------------------- +// Vulkan support (the Vulkan renderer needs to call a platform-side support function to create the surface) +//-------------------------------------------------------------------------------------------------------- + +// Avoid including so we can build without it +#if GLFW_HAS_VULKAN +#ifndef VULKAN_H_ +#define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; +#if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) +#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object; +#else +#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; +#endif +VK_DEFINE_HANDLE(VkInstance) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR) +struct VkAllocationCallbacks; +enum VkResult { VK_RESULT_MAX_ENUM = 0x7FFFFFFF }; +#endif // VULKAN_H_ +extern "C" { extern GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); } +static int ImGui_ImplGlfw_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData; + IM_UNUSED(bd); + IM_ASSERT(bd->ClientApi == GlfwClientApi_Vulkan); + VkResult err = glfwCreateWindowSurface((VkInstance)vk_instance, vd->Window, (const VkAllocationCallbacks*)vk_allocator, (VkSurfaceKHR*)out_vk_surface); + return (int)err; +} +#endif // GLFW_HAS_VULKAN + +static void ImGui_ImplGlfw_InitMultiViewportSupport() +{ + // Register platform interface (will be coupled with a renderer interface) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Platform_CreateWindow = ImGui_ImplGlfw_CreateWindow; + platform_io.Platform_DestroyWindow = ImGui_ImplGlfw_DestroyWindow; + platform_io.Platform_ShowWindow = ImGui_ImplGlfw_ShowWindow; + platform_io.Platform_SetWindowPos = ImGui_ImplGlfw_SetWindowPos; + platform_io.Platform_GetWindowPos = ImGui_ImplGlfw_GetWindowPos; + platform_io.Platform_SetWindowSize = ImGui_ImplGlfw_SetWindowSize; + platform_io.Platform_GetWindowSize = ImGui_ImplGlfw_GetWindowSize; + platform_io.Platform_SetWindowFocus = ImGui_ImplGlfw_SetWindowFocus; + platform_io.Platform_GetWindowFocus = ImGui_ImplGlfw_GetWindowFocus; + platform_io.Platform_GetWindowMinimized = ImGui_ImplGlfw_GetWindowMinimized; + platform_io.Platform_SetWindowTitle = ImGui_ImplGlfw_SetWindowTitle; + platform_io.Platform_RenderWindow = ImGui_ImplGlfw_RenderWindow; + platform_io.Platform_SwapBuffers = ImGui_ImplGlfw_SwapBuffers; +#if GLFW_HAS_WINDOW_ALPHA + platform_io.Platform_SetWindowAlpha = ImGui_ImplGlfw_SetWindowAlpha; +#endif +#if GLFW_HAS_VULKAN + platform_io.Platform_CreateVkSurface = ImGui_ImplGlfw_CreateVkSurface; +#endif + + // Register main window handle (which is owned by the main application, not by us) + // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGui_ImplGlfw_ViewportData* vd = IM_NEW(ImGui_ImplGlfw_ViewportData)(); + vd->Window = bd->Window; + vd->WindowOwned = false; + main_viewport->PlatformUserData = vd; + main_viewport->PlatformHandle = (void*)bd->Window; +} + +static void ImGui_ImplGlfw_ShutdownMultiViewportSupport() +{ + ImGui::DestroyPlatformWindows(); +} + +//----------------------------------------------------------------------------- + +// WndProc hook (declared here because we will need access to ImGui_ImplGlfw_ViewportData) +#ifdef _WIN32 +static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo() +{ + LPARAM extra_info = ::GetMessageExtraInfo(); + if ((extra_info & 0xFFFFFF80) == 0xFF515700) + return ImGuiMouseSource_Pen; + if ((extra_info & 0xFFFFFF80) == 0xFF515780) + return ImGuiMouseSource_TouchScreen; + return ImGuiMouseSource_Mouse; +} +static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + WNDPROC prev_wndproc = bd->PrevWndProc; + ImGuiViewport* viewport = (ImGuiViewport*)::GetPropA(hWnd, "IMGUI_VIEWPORT"); + if (viewport != NULL) + if (ImGui_ImplGlfw_ViewportData* vd = (ImGui_ImplGlfw_ViewportData*)viewport->PlatformUserData) + prev_wndproc = vd->PrevWndProc; + + switch (msg) + { + // GLFW doesn't allow to distinguish Mouse vs TouchScreen vs Pen. + // Add support for Win32 (based on imgui_impl_win32), because we rely on _TouchScreen info to trickle inputs differently. + case WM_MOUSEMOVE: case WM_NCMOUSEMOVE: + case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: case WM_LBUTTONUP: + case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_RBUTTONUP: + case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: case WM_MBUTTONUP: + case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: case WM_XBUTTONUP: + ImGui::GetIO().AddMouseSourceEvent(GetMouseSourceFromMessageExtraInfo()); + break; + + // We have submitted https://github.com/glfw/glfw/pull/1568 to allow GLFW to support "transparent inputs". + // In the meanwhile we implement custom per-platform workarounds here (FIXME-VIEWPORT: Implement same work-around for Linux/OSX!) +#if !GLFW_HAS_MOUSE_PASSTHROUGH && GLFW_HAS_WINDOW_HOVERED + case WM_NCHITTEST: + { + // Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() properly (which is OPTIONAL). + // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging. + // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in + // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system. + if (viewport && (viewport->Flags & ImGuiViewportFlags_NoInputs)) + return HTTRANSPARENT; + break; + } +#endif + } + return ::CallWindowProcW(prev_wndproc, hWnd, msg, wParam, lParam); +} +#endif // #ifdef _WIN32 + //----------------------------------------------------------------------------- #if defined(__clang__) diff --git a/neo/libs/imgui/backends/imgui_impl_glfw.h b/neo/libs/imgui/backends/imgui_impl_glfw.h index 53a22415d..16930d11a 100644 --- a/neo/libs/imgui/backends/imgui_impl_glfw.h +++ b/neo/libs/imgui/backends/imgui_impl_glfw.h @@ -1,6 +1,7 @@ // dear imgui: Platform Backend for GLFW // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) +// (Requires: GLFW 3.1+. Prefer GLFW 3.3+ for full feature support.) // Implemented features: // [X] Platform: Clipboard support. @@ -8,6 +9,9 @@ // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values are obsolete since 1.87 and not supported since 1.91.5] // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// Missing features or Issues: +// [ ] Platform: Multi-viewport: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/neo/libs/imgui/backends/imgui_impl_glut.cpp b/neo/libs/imgui/backends/imgui_impl_glut.cpp index ef7f17922..04591960b 100644 --- a/neo/libs/imgui/backends/imgui_impl_glut.cpp +++ b/neo/libs/imgui/backends/imgui_impl_glut.cpp @@ -13,6 +13,7 @@ // [ ] Platform: Missing mouse cursor shape/visibility support. // [ ] Platform: Missing clipboard support (not supported by Glut). // [ ] Platform: Missing gamepad support. +// [ ] Platform: Missing multi-viewport support (multiple windows). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/neo/libs/imgui/backends/imgui_impl_glut.h b/neo/libs/imgui/backends/imgui_impl_glut.h index 20e77dbce..d5d01a289 100644 --- a/neo/libs/imgui/backends/imgui_impl_glut.h +++ b/neo/libs/imgui/backends/imgui_impl_glut.h @@ -13,6 +13,7 @@ // [ ] Platform: Missing mouse cursor shape/visibility support. // [ ] Platform: Missing clipboard support (not supported by Glut). // [ ] Platform: Missing gamepad support. +// [ ] Platform: Missing multi-viewport support (multiple windows). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/neo/libs/imgui/backends/imgui_impl_metal.h b/neo/libs/imgui/backends/imgui_impl_metal.h index 351c2eff7..dcf96ba0e 100644 --- a/neo/libs/imgui/backends/imgui_impl_metal.h +++ b/neo/libs/imgui/backends/imgui_impl_metal.h @@ -4,6 +4,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'MTLTexture' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/neo/libs/imgui/backends/imgui_impl_metal.mm b/neo/libs/imgui/backends/imgui_impl_metal.mm index 1e0c47f52..b90e50691 100644 --- a/neo/libs/imgui/backends/imgui_impl_metal.mm +++ b/neo/libs/imgui/backends/imgui_impl_metal.mm @@ -4,6 +4,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'MTLTexture' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -15,7 +16,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) -// 2024-01-08: Metal: Fixed memory leaks when using metal-cpp (#8276, #8166) or when using multiple contexts (#7419). +// 2025-XX-XX: Metal: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2025-01-08: Metal: Fixed memory leaks when using metal-cpp (#8276, #8166) or when using multiple contexts (#7419). // 2022-08-23: Metal: Update deprecated property 'sampleCount'->'rasterSampleCount'. // 2022-07-05: Metal: Add dispatch synchronization. // 2022-06-30: Metal: Use __bridge for ARC based systems. @@ -39,6 +41,12 @@ #import #import +// Forward Declarations +static void ImGui_ImplMetal_InitMultiViewportSupport(); +static void ImGui_ImplMetal_ShutdownMultiViewportSupport(); +static void ImGui_ImplMetal_CreateDeviceObjectsForPlatformWindows(); +static void ImGui_ImplMetal_InvalidateDeviceObjectsForPlatformWindows(); + #pragma mark - Support classes // A wrapper around a MTLBuffer object that knows the last time it was reused @@ -133,10 +141,13 @@ bool ImGui_ImplMetal_Init(id device) io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_metal"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) bd->SharedMetalContext = [[MetalContext alloc] init]; bd->SharedMetalContext.device = device; + ImGui_ImplMetal_InitMultiViewportSupport(); + return true; } @@ -145,13 +156,14 @@ void ImGui_ImplMetal_Shutdown() ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); IM_UNUSED(bd); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); + ImGui_ImplMetal_ShutdownMultiViewportSupport(); ImGui_ImplMetal_DestroyDeviceObjects(); ImGui_ImplMetal_DestroyBackendData(); ImGuiIO& io = ImGui::GetIO(); io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasViewports); } void ImGui_ImplMetal_NewFrame(MTLRenderPassDescriptor* renderPassDescriptor) @@ -369,6 +381,7 @@ bool ImGui_ImplMetal_CreateDeviceObjects(id device) depthStencilDescriptor.depthWriteEnabled = NO; depthStencilDescriptor.depthCompareFunction = MTLCompareFunctionAlways; bd->SharedMetalContext.depthStencilState = [device newDepthStencilStateWithDescriptor:depthStencilDescriptor]; + ImGui_ImplMetal_CreateDeviceObjectsForPlatformWindows(); #ifdef IMGUI_IMPL_METAL_CPP [depthStencilDescriptor release]; #endif @@ -380,9 +393,155 @@ void ImGui_ImplMetal_DestroyDeviceObjects() { ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); ImGui_ImplMetal_DestroyFontsTexture(); + ImGui_ImplMetal_InvalidateDeviceObjectsForPlatformWindows(); [bd->SharedMetalContext.renderPipelineStateCache removeAllObjects]; } +#pragma mark - Multi-viewport support + +#import + +#if TARGET_OS_OSX +#import +#endif + +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +struct ImGuiViewportDataMetal +{ + CAMetalLayer* MetalLayer; + id CommandQueue; + MTLRenderPassDescriptor* RenderPassDescriptor; + void* Handle = nullptr; + bool FirstFrame = true; +}; + +static void ImGui_ImplMetal_CreateWindow(ImGuiViewport* viewport) +{ + ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData(); + ImGuiViewportDataMetal* data = IM_NEW(ImGuiViewportDataMetal)(); + viewport->RendererUserData = data; + + // PlatformHandleRaw should always be a NSWindow*, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*). + // Some back-ends will leave PlatformHandleRaw == 0, in which case we assume PlatformHandle will contain the NSWindow*. + void* handle = viewport->PlatformHandleRaw ? viewport->PlatformHandleRaw : viewport->PlatformHandle; + IM_ASSERT(handle != nullptr); + + id device = bd->SharedMetalContext.device; + CAMetalLayer* layer = [CAMetalLayer layer]; + layer.device = device; + layer.framebufferOnly = YES; + layer.pixelFormat = bd->SharedMetalContext.framebufferDescriptor.colorPixelFormat; +#if TARGET_OS_OSX + NSWindow* window = (__bridge NSWindow*)handle; + NSView* view = window.contentView; + view.layer = layer; + view.wantsLayer = YES; +#endif + data->MetalLayer = layer; + data->CommandQueue = [device newCommandQueue]; + data->RenderPassDescriptor = [[MTLRenderPassDescriptor alloc] init]; + data->Handle = handle; +} + +static void ImGui_ImplMetal_DestroyWindow(ImGuiViewport* viewport) +{ + // The main viewport (owned by the application) will always have RendererUserData == 0 since we didn't create the data for it. + if (ImGuiViewportDataMetal* data = (ImGuiViewportDataMetal*)viewport->RendererUserData) + IM_DELETE(data); + viewport->RendererUserData = nullptr; +} + +inline static CGSize MakeScaledSize(CGSize size, CGFloat scale) +{ + return CGSizeMake(size.width * scale, size.height * scale); +} + +static void ImGui_ImplMetal_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGuiViewportDataMetal* data = (ImGuiViewportDataMetal*)viewport->RendererUserData; + data->MetalLayer.drawableSize = MakeScaledSize(CGSizeMake(size.x, size.y), viewport->DpiScale); +} + +static void ImGui_ImplMetal_RenderWindow(ImGuiViewport* viewport, void*) +{ + ImGuiViewportDataMetal* data = (ImGuiViewportDataMetal*)viewport->RendererUserData; + +#if TARGET_OS_OSX + void* handle = viewport->PlatformHandleRaw ? viewport->PlatformHandleRaw : viewport->PlatformHandle; + NSWindow* window = (__bridge NSWindow*)handle; + + // Always render the first frame, regardless of occlusionState, to avoid an initial flicker + if ((window.occlusionState & NSWindowOcclusionStateVisible) == 0 && !data->FirstFrame) + { + // Do not render windows which are completely occluded. Calling -[CAMetalLayer nextDrawable] will hang for + // approximately 1 second if the Metal layer is completely occluded. + return; + } + data->FirstFrame = false; + + viewport->DpiScale = (float)window.backingScaleFactor; + if (data->MetalLayer.contentsScale != viewport->DpiScale) + { + data->MetalLayer.contentsScale = viewport->DpiScale; + data->MetalLayer.drawableSize = MakeScaledSize(window.frame.size, viewport->DpiScale); + } + viewport->DrawData->FramebufferScale = ImVec2(viewport->DpiScale, viewport->DpiScale); +#endif + + id drawable = [data->MetalLayer nextDrawable]; + if (drawable == nil) + return; + + MTLRenderPassDescriptor* renderPassDescriptor = data->RenderPassDescriptor; + renderPassDescriptor.colorAttachments[0].texture = drawable.texture; + renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 0); + if ((viewport->Flags & ImGuiViewportFlags_NoRendererClear) == 0) + renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; + + id commandBuffer = [data->CommandQueue commandBuffer]; + id renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; + ImGui_ImplMetal_RenderDrawData(viewport->DrawData, commandBuffer, renderEncoder); + [renderEncoder endEncoding]; + + [commandBuffer presentDrawable:drawable]; + [commandBuffer commit]; +} + +static void ImGui_ImplMetal_InitMultiViewportSupport() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_CreateWindow = ImGui_ImplMetal_CreateWindow; + platform_io.Renderer_DestroyWindow = ImGui_ImplMetal_DestroyWindow; + platform_io.Renderer_SetWindowSize = ImGui_ImplMetal_SetWindowSize; + platform_io.Renderer_RenderWindow = ImGui_ImplMetal_RenderWindow; +} + +static void ImGui_ImplMetal_ShutdownMultiViewportSupport() +{ + ImGui::DestroyPlatformWindows(); +} + +static void ImGui_ImplMetal_CreateDeviceObjectsForPlatformWindows() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int i = 1; i < platform_io.Viewports.Size; i++) + if (!platform_io.Viewports[i]->RendererUserData) + ImGui_ImplMetal_CreateWindow(platform_io.Viewports[i]); +} + +static void ImGui_ImplMetal_InvalidateDeviceObjectsForPlatformWindows() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int i = 1; i < platform_io.Viewports.Size; i++) + if (platform_io.Viewports[i]->RendererUserData) + ImGui_ImplMetal_DestroyWindow(platform_io.Viewports[i]); +} + #pragma mark - MetalBuffer implementation @implementation MetalBuffer diff --git a/neo/libs/imgui/backends/imgui_impl_opengl2.cpp b/neo/libs/imgui/backends/imgui_impl_opengl2.cpp index f3004bd29..eea0895c0 100644 --- a/neo/libs/imgui/backends/imgui_impl_opengl2.cpp +++ b/neo/libs/imgui/backends/imgui_impl_opengl2.cpp @@ -3,6 +3,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // Missing features or Issues: // [ ] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). @@ -24,6 +25,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2024-10-07: OpenGL: Changed default texture sampler to Clamp instead of Repeat/Wrap. // 2024-06-28: OpenGL: ImGui_ImplOpenGL2_NewFrame() recreates font texture if it has been destroyed by ImGui_ImplOpenGL2_DestroyFontsTexture(). (#7748) // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. @@ -107,9 +109,20 @@ #define glTexParameteri qglTexParameteri #define glVertexPointer qglVertexPointer #define glViewport qglViewport +#define glClearColor qglClearColor +#define glClear qglClear #endif // DG: use qgl +// [Debugging] +//#define IMGUI_IMPL_OPENGL_DEBUG +#ifdef IMGUI_IMPL_OPENGL_DEBUG +#include +#define GL_CALL(_CALL) do { _CALL; GLenum gl_err = glGetError(); if (gl_err != 0) fprintf(stderr, "GL error 0x%x returned from '%s'.\n", gl_err, #_CALL); } while (0) // Call with error check +#else +#define GL_CALL(_CALL) _CALL // Call without error check +#endif +// OpenGL data struct ImGui_ImplOpenGL2_Data { GLuint FontTexture; @@ -124,6 +137,10 @@ static ImGui_ImplOpenGL2_Data* ImGui_ImplOpenGL2_GetBackendData() return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL2_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; } +// Forward Declarations +static void ImGui_ImplOpenGL2_InitMultiViewportSupport(); +static void ImGui_ImplOpenGL2_ShutdownMultiViewportSupport(); + // Functions bool ImGui_ImplOpenGL2_Init() { @@ -135,6 +152,9 @@ bool ImGui_ImplOpenGL2_Init() ImGui_ImplOpenGL2_Data* bd = IM_NEW(ImGui_ImplOpenGL2_Data)(); io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_opengl2"; + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) + + ImGui_ImplOpenGL2_InitMultiViewportSupport(); return true; } @@ -145,9 +165,11 @@ void ImGui_ImplOpenGL2_Shutdown() IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplOpenGL2_ShutdownMultiViewportSupport(); ImGui_ImplOpenGL2_DestroyDeviceObjects(); io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; + io.BackendFlags &= ~ImGuiBackendFlags_RendererHasViewports; IM_DELETE(bd); } @@ -196,7 +218,7 @@ static void ImGui_ImplOpenGL2_SetupRenderState(ImDrawData* draw_data, int fb_wid // Setup viewport, orthographic projection matrix // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. - glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); + GL_CALL(glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height)); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); @@ -343,6 +365,35 @@ void ImGui_ImplOpenGL2_DestroyDeviceObjects() ImGui_ImplOpenGL2_DestroyFontsTexture(); } + +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +static void ImGui_ImplOpenGL2_RenderWindow(ImGuiViewport* viewport, void*) +{ + if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) + { + ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); + glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); + } + ImGui_ImplOpenGL2_RenderDrawData(viewport->DrawData); +} + +static void ImGui_ImplOpenGL2_InitMultiViewportSupport() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_RenderWindow = ImGui_ImplOpenGL2_RenderWindow; +} + +static void ImGui_ImplOpenGL2_ShutdownMultiViewportSupport() +{ + ImGui::DestroyPlatformWindows(); +} + //----------------------------------------------------------------------------- #if defined(__clang__) diff --git a/neo/libs/imgui/backends/imgui_impl_opengl2.h b/neo/libs/imgui/backends/imgui_impl_opengl2.h index 5832a1765..c7b40e077 100644 --- a/neo/libs/imgui/backends/imgui_impl_opengl2.h +++ b/neo/libs/imgui/backends/imgui_impl_opengl2.h @@ -3,6 +3,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // Missing features or Issues: // [ ] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). diff --git a/neo/libs/imgui/backends/imgui_impl_opengl3.cpp b/neo/libs/imgui/backends/imgui_impl_opengl3.cpp index efc1a3c49..0d683a419 100644 --- a/neo/libs/imgui/backends/imgui_impl_opengl3.cpp +++ b/neo/libs/imgui/backends/imgui_impl_opengl3.cpp @@ -6,6 +6,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! // [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!] +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // About WebGL/ES: // - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES. @@ -22,6 +23,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2024-10-07: OpenGL: Changed default texture sampler to Clamp instead of Repeat/Wrap. // 2024-06-28: OpenGL: ImGui_ImplOpenGL3_NewFrame() recreates font texture if it has been destroyed by ImGui_ImplOpenGL3_DestroyFontsTexture(). (#7748) // 2024-05-07: OpenGL: Update loader for Linux to support EGL/GLVND. (#7562) @@ -252,6 +254,10 @@ static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData() return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; } +// Forward Declarations +static void ImGui_ImplOpenGL3_InitMultiViewportSupport(); +static void ImGui_ImplOpenGL3_ShutdownMultiViewportSupport(); + // OpenGL vertex attribute state (for ES 1.0 and ES 2.0 only) #ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY struct ImGui_ImplOpenGL3_VtxAttribState @@ -344,6 +350,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) if (bd->GlVersion >= 320) io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. #endif + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) // Store GLSL version string so we can refer to it later in case we recreate shaders. // Note: GLSL version is NOT the same as GL version. Leave this to nullptr if unsure. @@ -384,6 +391,8 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) } #endif + ImGui_ImplOpenGL3_InitMultiViewportSupport(); + return true; } @@ -393,10 +402,11 @@ void ImGui_ImplOpenGL3_Shutdown() IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplOpenGL3_ShutdownMultiViewportSupport(); ImGui_ImplOpenGL3_DestroyDeviceObjects(); io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasViewports); IM_DELETE(bd); } @@ -956,6 +966,34 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects() ImGui_ImplOpenGL3_DestroyFontsTexture(); } +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +static void ImGui_ImplOpenGL3_RenderWindow(ImGuiViewport* viewport, void*) +{ + if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) + { + ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); + glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); + } + ImGui_ImplOpenGL3_RenderDrawData(viewport->DrawData); +} + +static void ImGui_ImplOpenGL3_InitMultiViewportSupport() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_RenderWindow = ImGui_ImplOpenGL3_RenderWindow; +} + +static void ImGui_ImplOpenGL3_ShutdownMultiViewportSupport() +{ + ImGui::DestroyPlatformWindows(); +} + //----------------------------------------------------------------------------- #if defined(__GNUC__) diff --git a/neo/libs/imgui/backends/imgui_impl_opengl3.h b/neo/libs/imgui/backends/imgui_impl_opengl3.h index 5de51cfdd..d56213044 100644 --- a/neo/libs/imgui/backends/imgui_impl_opengl3.h +++ b/neo/libs/imgui/backends/imgui_impl_opengl3.h @@ -6,6 +6,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! // [x] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset) [Desktop OpenGL only!] +// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // About WebGL/ES: // - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES. diff --git a/neo/libs/imgui/backends/imgui_impl_osx.h b/neo/libs/imgui/backends/imgui_impl_osx.h index 2e7eabb2b..08bfe76e6 100644 --- a/neo/libs/imgui/backends/imgui_impl_osx.h +++ b/neo/libs/imgui/backends/imgui_impl_osx.h @@ -10,6 +10,10 @@ // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: IME support. +// [x] Platform: Multi-viewport / platform windows. +// Issues: +// [ ] Platform: Multi-viewport: Window size not correctly reported when enabling io.ConfigViewportsNoDecoration +// [ ] Platform: Multi-viewport: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/neo/libs/imgui/backends/imgui_impl_osx.mm b/neo/libs/imgui/backends/imgui_impl_osx.mm index c2a5f6378..5d067f524 100644 --- a/neo/libs/imgui/backends/imgui_impl_osx.mm +++ b/neo/libs/imgui/backends/imgui_impl_osx.mm @@ -10,6 +10,10 @@ // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: IME support. +// [x] Platform: Multi-viewport / platform windows. +// Issues: +// [ ] Platform: Multi-viewport: Window size not correctly reported when enabling io.ConfigViewportsNoDecoration +// [ ] Platform: Multi-viewport: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -29,6 +33,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-XX-XX: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2025-01-20: Removed notification observer when shutting down. (#8331) // 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: // - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn // - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn @@ -96,6 +102,9 @@ static inline CFTimeInterval GetMachAbsoluteTimeInSeconds() { return (CFTimeInterval)(double)(clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1e9); } // Forward Declarations +static void ImGui_ImplOSX_InitMultiViewportSupport(); +static void ImGui_ImplOSX_ShutdownMultiViewportSupport(); +static void ImGui_ImplOSX_UpdateMonitors(); static void ImGui_ImplOSX_AddTrackingArea(NSView* _Nonnull view); static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view); @@ -142,9 +151,25 @@ - (void)updateImePosWithView:(NSView *)view NSWindow* window = view.window; if (!window) return; - NSRect contentRect = [window contentRectForFrameRect:window.frame]; - NSRect rect = NSMakeRect(_posX, contentRect.size.height - _posY, 0, 0); - _imeRect = [window convertRectToScreen:rect]; + + ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + NSRect frame = window.frame; + NSRect contentRect = window.contentLayoutRect; + if (window.styleMask & NSWindowStyleMaskFullSizeContentView) // No title bar windows should be considered. + contentRect = frame; + + NSRect firstScreenFrame = NSScreen.screens[0].frame; + _imeRect = NSMakeRect(_posX, _posY, 0, 0); + _imeRect.origin.y = firstScreenFrame.size.height - _imeRect.size.height - _imeRect.origin.y; // Opposite of ConvertNSRect() + } + else + { + NSRect contentRect = [window contentRectForFrameRect:window.frame]; + NSRect rect = NSMakeRect(_posX, contentRect.size.height - _posY, 0, 0); + _imeRect = [window convertRectToScreen:rect]; + } } - (void)viewDidMoveToWindow @@ -239,6 +264,7 @@ @interface ImGuiObserver : NSObject - (void)onApplicationBecomeActive:(NSNotification*)aNotification; - (void)onApplicationBecomeInactive:(NSNotification*)aNotification; +- (void)displaysDidChange:(NSNotification*)aNotification; @end @@ -256,6 +282,11 @@ - (void)onApplicationBecomeInactive:(NSNotification*)aNotification io.AddFocusEvent(false); } +- (void)displaysDidChange:(NSNotification*)aNotification +{ + ImGui_ImplOSX_UpdateMonitors(); +} + @end // Functions @@ -410,11 +441,15 @@ bool ImGui_ImplOSX_Init(NSView* view) io.BackendPlatformName = "imgui_impl_osx"; io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) //io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) + //io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional) bd->Observer = [ImGuiObserver new]; bd->Window = view.window ?: NSApp.orderedWindows.firstObject; ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (__bridge_retained void*)bd->Window; + ImGui_ImplOSX_UpdateMonitors(); + ImGui_ImplOSX_InitMultiViewportSupport(); // Load cursors. Some of them are undocumented. bd->MouseCursorHidden = false; @@ -497,6 +532,7 @@ void ImGui_ImplOSX_Shutdown() ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData(); IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); + [[NSNotificationCenter defaultCenter] removeObserver:bd->Observer]; bd->Observer = nullptr; if (bd->Monitor != nullptr) { @@ -504,12 +540,12 @@ void ImGui_ImplOSX_Shutdown() bd->Monitor = nullptr; } + ImGui_ImplOSX_ShutdownMultiViewportSupport(); ImGui_ImplOSX_DestroyBackendData(); - ImGuiIO& io = ImGui::GetIO(); io.BackendPlatformName = nullptr; io.BackendPlatformUserData = nullptr; - io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasGamepad); + io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_PlatformHasViewports); } static void ImGui_ImplOSX_UpdateMouseCursor() @@ -686,14 +722,23 @@ static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) if (event.type == NSEventTypeMouseMoved || event.type == NSEventTypeLeftMouseDragged || event.type == NSEventTypeRightMouseDragged || event.type == NSEventTypeOtherMouseDragged) { - NSPoint mousePoint = event.locationInWindow; - if (event.window == nil) - mousePoint = [[view window] convertPointFromScreen:mousePoint]; - mousePoint = [view convertPoint:mousePoint fromView:nil]; - if ([view isFlipped]) - mousePoint = NSMakePoint(mousePoint.x, mousePoint.y); + NSPoint mousePoint; + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + mousePoint = NSEvent.mouseLocation; + mousePoint.y = CGDisplayPixelsHigh(kCGDirectMainDisplay) - mousePoint.y; // Normalize y coordinate to top-left of main display. + } else - mousePoint = NSMakePoint(mousePoint.x, view.bounds.size.height - mousePoint.y); + { + mousePoint = event.locationInWindow; + if (event.window == nil) + mousePoint = [[view window] convertPointFromScreen:mousePoint]; + mousePoint = [view convertPoint:mousePoint fromView:nil]; // Convert to local coordinates of view + if ([view isFlipped]) + mousePoint = NSMakePoint(mousePoint.x, mousePoint.y); + else + mousePoint = NSMakePoint(mousePoint.x, view.bounds.size.height - mousePoint.y); + } io.AddMouseSourceEvent(GetMouseSource(event)); io.AddMousePosEvent((float)mousePoint.x, (float)mousePoint.y); return io.WantCaptureMouse; @@ -821,6 +866,294 @@ static void ImGui_ImplOSX_AddTrackingArea(NSView* _Nonnull view) }]; } +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +struct ImGuiViewportDataOSX +{ + NSWindow* Window; + bool WindowOwned; + + ImGuiViewportDataOSX() { WindowOwned = false; } + ~ImGuiViewportDataOSX() { IM_ASSERT(Window == nil); } +}; + +@interface ImGui_ImplOSX_Window: NSWindow +@end + +@implementation ImGui_ImplOSX_Window + +- (BOOL)canBecomeKeyWindow +{ + return YES; +} + +@end + +static void ConvertNSRect(NSRect* r) +{ + NSRect firstScreenFrame = NSScreen.screens[0].frame; + IM_ASSERT(firstScreenFrame.origin.x == 0 && firstScreenFrame.origin.y == 0); + r->origin.y = firstScreenFrame.size.height - r->origin.y - r->size.height; +} + +static void ImGui_ImplOSX_CreateWindow(ImGuiViewport* viewport) +{ + ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData(); + ImGuiViewportDataOSX* data = IM_NEW(ImGuiViewportDataOSX)(); + viewport->PlatformUserData = data; + + NSScreen* screen = bd->Window.screen; + NSRect rect = NSMakeRect(viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y); + ConvertNSRect(&rect); + + NSWindowStyleMask styleMask = 0; + if (viewport->Flags & ImGuiViewportFlags_NoDecoration) + styleMask |= NSWindowStyleMaskBorderless; + else + styleMask |= NSWindowStyleMaskTitled | NSWindowStyleMaskResizable | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable; + + NSWindow* window = [[ImGui_ImplOSX_Window alloc] initWithContentRect:rect + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:YES + screen:screen]; + if (viewport->Flags & ImGuiViewportFlags_TopMost) + [window setLevel:NSFloatingWindowLevel]; + + window.title = @"Untitled"; + window.opaque = YES; + + KeyEventResponder* view = [[KeyEventResponder alloc] initWithFrame:rect]; + if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6 && ceil(NSAppKitVersionNumber) < NSAppKitVersionNumber10_15) + [view setWantsBestResolutionOpenGLSurface:YES]; + + window.contentView = view; + + data->Window = window; + data->WindowOwned = true; + viewport->PlatformRequestResize = false; + viewport->PlatformHandle = viewport->PlatformHandleRaw = (__bridge_retained void*)window; +} + +static void ImGui_ImplOSX_DestroyWindow(ImGuiViewport* viewport) +{ + NSWindow* window = (__bridge_transfer NSWindow*)viewport->PlatformHandleRaw; + window = nil; + + if (ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData) + { + NSWindow* window = data->Window; + if (window != nil && data->WindowOwned) + { + window.contentView = nil; + window.contentViewController = nil; + [window orderOut:nil]; + } + data->Window = nil; + IM_DELETE(data); + } + viewport->PlatformUserData = viewport->PlatformHandle = viewport->PlatformHandleRaw = nullptr; +} + +static void ImGui_ImplOSX_ShowWindow(ImGuiViewport* viewport) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + + if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) + [data->Window orderFront:nil]; + else + [data->Window makeKeyAndOrderFront:nil]; + + [data->Window setIsVisible:YES]; +} + +static ImVec2 ImGui_ImplOSX_GetWindowPos(ImGuiViewport* viewport) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + + NSWindow* window = data->Window; + NSRect frame = window.frame; + NSRect contentRect = window.contentLayoutRect; + if (window.styleMask & NSWindowStyleMaskFullSizeContentView) // No title bar windows should be considered. + contentRect = frame; + + NSRect firstScreenFrame = NSScreen.screens[0].frame; + return ImVec2(frame.origin.x, firstScreenFrame.size.height - frame.origin.y - contentRect.size.height); +} + +static void ImGui_ImplOSX_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + + NSWindow* window = data->Window; + NSSize size = window.frame.size; + + NSRect r = NSMakeRect(pos.x, pos.y, size.width, size.height); + ConvertNSRect(&r); + [window setFrameOrigin:r.origin]; +} + +static ImVec2 ImGui_ImplOSX_GetWindowSize(ImGuiViewport* viewport) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + + NSWindow* window = data->Window; + NSSize size = window.contentLayoutRect.size; + return ImVec2(size.width, size.height); +} + +static void ImGui_ImplOSX_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + + NSWindow* window = data->Window; + NSRect rect = window.frame; + rect.origin.y -= (size.y - rect.size.height); + rect.size.width = size.x; + rect.size.height = size.y; + [window setFrame:rect display:YES]; +} + +static void ImGui_ImplOSX_SetWindowFocus(ImGuiViewport* viewport) +{ + ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData(); + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + [data->Window makeKeyAndOrderFront:bd->Window]; +} + +static bool ImGui_ImplOSX_GetWindowFocus(ImGuiViewport* viewport) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + + return data->Window.isKeyWindow; +} + +static bool ImGui_ImplOSX_GetWindowMinimized(ImGuiViewport* viewport) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + + return data->Window.isMiniaturized; +} + +static void ImGui_ImplOSX_SetWindowTitle(ImGuiViewport* viewport, const char* title) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + + data->Window.title = [NSString stringWithUTF8String:title]; +} + +static void ImGui_ImplOSX_SetWindowAlpha(ImGuiViewport* viewport, float alpha) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + IM_ASSERT(alpha >= 0.0f && alpha <= 1.0f); + + data->Window.alphaValue = alpha; +} + +static float ImGui_ImplOSX_GetWindowDpiScale(ImGuiViewport* viewport) +{ + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)viewport->PlatformUserData; + IM_ASSERT(data->Window != 0); + + return data->Window.backingScaleFactor; +} + +static void ImGui_ImplOSX_UpdateMonitors() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Monitors.resize(0); + + NSRect firstScreenFrame = NSScreen.screens[0].frame; + IM_ASSERT(firstScreenFrame.origin.x == 0 && firstScreenFrame.origin.y == 0); + + for (NSScreen* screen in NSScreen.screens) + { + NSRect frame = screen.frame; + NSRect visibleFrame = screen.visibleFrame; + ConvertNSRect(&frame); + ConvertNSRect(&visibleFrame); + + ImGuiPlatformMonitor imgui_monitor; + imgui_monitor.MainPos = ImVec2(frame.origin.x, frame.origin.y); + imgui_monitor.MainSize = ImVec2(frame.size.width, frame.size.height); + imgui_monitor.WorkPos = ImVec2(visibleFrame.origin.x, visibleFrame.origin.y); + imgui_monitor.WorkSize = ImVec2(visibleFrame.size.width, visibleFrame.size.height); + imgui_monitor.DpiScale = screen.backingScaleFactor; + imgui_monitor.PlatformHandle = (__bridge_retained void*)screen; + + platform_io.Monitors.push_back(imgui_monitor); + } +} + +static void ImGui_ImplOSX_InitMultiViewportSupport() +{ + ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData(); + + // Register platform interface (will be coupled with a renderer interface) + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Platform_CreateWindow = ImGui_ImplOSX_CreateWindow; + platform_io.Platform_DestroyWindow = ImGui_ImplOSX_DestroyWindow; + platform_io.Platform_ShowWindow = ImGui_ImplOSX_ShowWindow; + platform_io.Platform_SetWindowPos = ImGui_ImplOSX_SetWindowPos; + platform_io.Platform_GetWindowPos = ImGui_ImplOSX_GetWindowPos; + platform_io.Platform_SetWindowSize = ImGui_ImplOSX_SetWindowSize; + platform_io.Platform_GetWindowSize = ImGui_ImplOSX_GetWindowSize; + platform_io.Platform_SetWindowFocus = ImGui_ImplOSX_SetWindowFocus; + platform_io.Platform_GetWindowFocus = ImGui_ImplOSX_GetWindowFocus; + platform_io.Platform_GetWindowMinimized = ImGui_ImplOSX_GetWindowMinimized; + platform_io.Platform_SetWindowTitle = ImGui_ImplOSX_SetWindowTitle; + platform_io.Platform_SetWindowAlpha = ImGui_ImplOSX_SetWindowAlpha; + platform_io.Platform_GetWindowDpiScale = ImGui_ImplOSX_GetWindowDpiScale; // FIXME-DPI + + // Register main window handle (which is owned by the main application, not by us) + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGuiViewportDataOSX* data = IM_NEW(ImGuiViewportDataOSX)(); + data->Window = bd->Window; + data->WindowOwned = false; + main_viewport->PlatformUserData = data; + main_viewport->PlatformHandle = (__bridge void*)bd->Window; + + [NSNotificationCenter.defaultCenter addObserver:bd->Observer + selector:@selector(displaysDidChange:) + name:NSApplicationDidChangeScreenParametersNotification + object:nil]; +} + +static void ImGui_ImplOSX_ShutdownMultiViewportSupport() +{ + ImGui_ImplOSX_Data* bd = ImGui_ImplOSX_GetBackendData(); + [NSNotificationCenter.defaultCenter removeObserver:bd->Observer + name:NSApplicationDidChangeScreenParametersNotification + object:nil]; + bd->Observer = nullptr; + bd->Window = nullptr; + if (bd->Monitor != nullptr) + { + [NSEvent removeMonitor:bd->Monitor]; + bd->Monitor = nullptr; + } + + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGuiViewportDataOSX* data = (ImGuiViewportDataOSX*)main_viewport->PlatformUserData; + IM_DELETE(data); + main_viewport->PlatformUserData = nullptr; + ImGui::DestroyPlatformWindows(); +} + //----------------------------------------------------------------------------- #endif // #ifndef IMGUI_DISABLE diff --git a/neo/libs/imgui/backends/imgui_impl_sdl2.cpp b/neo/libs/imgui/backends/imgui_impl_sdl2.cpp index 0aaf9830a..527012693 100644 --- a/neo/libs/imgui/backends/imgui_impl_sdl2.cpp +++ b/neo/libs/imgui/backends/imgui_impl_sdl2.cpp @@ -10,6 +10,10 @@ // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: Basic IME support. App needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!. +// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// Missing features or Issues: +// [ ] Platform: Multi-viewport: Minimized windows seems to break mouse wheel events (at least under Windows). +// [ ] Platform: Multi-viewport: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -21,6 +25,8 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2025-01-20: Made ImGui_ImplSDL2_SetGamepadMode(ImGui_ImplSDL2_GamepadMode_Manual) accept an empty array. // 2024-10-24: Emscripten: from SDL 2.30.9, SDL_EVENT_MOUSE_WHEEL event doesn't require dividing by 100.0f. // 2024-09-09: use SDL_Vulkan_GetDrawableSize() when available. (#7967, #3190) // 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: @@ -58,7 +64,7 @@ // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-08-17: Calling io.AddFocusEvent() on SDL_WINDOWEVENT_FOCUS_GAINED/SDL_WINDOWEVENT_FOCUS_LOST. // 2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using SDL_GetMouseFocus() + SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, requires SDL 2.0.5+) -// 2021-06-29: *BREAKING CHANGE* Removed 'SDL_Window* window' parameter to ImGui_ImplSDL2_NewFrame() which was unnecessary. +// 2021-06:29: *BREAKING CHANGE* Removed 'SDL_Window* window' parameter to ImGui_ImplSDL2_NewFrame() which was unnecessary. // 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). // 2021-03-22: Rework global mouse pos availability check listing supported platforms explicitly, effectively fixing mouse access on Raspberry Pi. (#2837, #3950) // 2020-05-25: Misc: Report a zero display-size when window is minimized, to be consistent with other backends. @@ -99,6 +105,7 @@ #endif // SDL +// (the multi-viewports feature requires SDL features supported from SDL 2.0.4+. SDL 2.0.5+ is highly recommended) #include #include #ifdef __APPLE__ @@ -113,9 +120,17 @@ #else #define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 0 #endif +#define SDL_HAS_WINDOW_ALPHA SDL_VERSION_ATLEAST(2,0,5) +#define SDL_HAS_ALWAYS_ON_TOP SDL_VERSION_ATLEAST(2,0,5) +#define SDL_HAS_USABLE_DISPLAY_BOUNDS SDL_VERSION_ATLEAST(2,0,5) +#define SDL_HAS_PER_MONITOR_DPI SDL_VERSION_ATLEAST(2,0,4) #define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) +#define SDL_HAS_DISPLAY_EVENT SDL_VERSION_ATLEAST(2,0,9) +#define SDL_HAS_SHOW_WINDOW_ACTIVATION_HINT SDL_VERSION_ATLEAST(2,0,18) #if SDL_HAS_VULKAN #include +#else +static const Uint32 SDL_WINDOW_VULKAN = 0x10000000; #endif // SDL Data @@ -126,6 +141,8 @@ struct ImGui_ImplSDL2_Data SDL_Renderer* Renderer; Uint64 Time; char* ClipboardTextData; + bool UseVulkan; + bool WantUpdateMonitors; // Mouse handling Uint32 MouseWindowID; @@ -134,6 +151,7 @@ struct ImGui_ImplSDL2_Data SDL_Cursor* MouseLastCursor; int MouseLastLeaveFrame; bool MouseCanUseGlobalState; + bool MouseCanReportHoveredViewport; // This is hard to use/unreliable on SDL so we'll set ImGuiBackendFlags_HasMouseHoveredViewport dynamically based on state. // Gamepad handling ImVector Gamepads; @@ -152,6 +170,11 @@ static ImGui_ImplSDL2_Data* ImGui_ImplSDL2_GetBackendData() return ImGui::GetCurrentContext() ? (ImGui_ImplSDL2_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; } +// Forward Declarations +static void ImGui_ImplSDL2_UpdateMonitors(); +static void ImGui_ImplSDL2_InitMultiViewportSupport(SDL_Window* window, void* sdl_gl_context); +static void ImGui_ImplSDL2_ShutdownMultiViewportSupport(); + // Functions static const char* ImGui_ImplSDL2_GetClipboardText(ImGuiContext*) { @@ -168,13 +191,13 @@ static void ImGui_ImplSDL2_SetClipboardText(ImGuiContext*, const char* text) } // Note: native IME will only display if user calls SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1") _before_ SDL_CreateWindow(). -static void ImGui_ImplSDL2_PlatformSetImeData(ImGuiContext*, ImGuiViewport*, ImGuiPlatformImeData* data) +static void ImGui_ImplSDL2_PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data) { if (data->WantVisible) { SDL_Rect r; - r.x = (int)data->InputPos.x; - r.y = (int)data->InputPos.y; + r.x = (int)(data->InputPos.x - viewport->Pos.x); + r.y = (int)(data->InputPos.y - viewport->Pos.y + data->InputLineHeight); r.w = 1; r.h = (int)data->InputLineHeight; SDL_SetTextInputRect(&r); @@ -323,15 +346,13 @@ static void ImGui_ImplSDL2_UpdateKeyModifiers(SDL_Keymod sdl_key_mods) static ImGuiViewport* ImGui_ImplSDL2_GetViewportForWindowID(Uint32 window_id) { - ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); - return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : nullptr; + return ImGui::FindViewportByPlatformHandle((void*)(intptr_t)window_id); } // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. -// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field. bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); @@ -345,6 +366,13 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) if (ImGui_ImplSDL2_GetViewportForWindowID(event->motion.windowID) == nullptr) return false; ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + int window_x, window_y; + SDL_GetWindowPosition(SDL_GetWindowFromID(event->motion.windowID), &window_x, &window_y); + mouse_pos.x += window_x; + mouse_pos.y += window_y; + } io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); io.AddMousePosEvent(mouse_pos.x, mouse_pos.y); return true; @@ -406,10 +434,21 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) io.SetKeyEventNativeData(key, event->key.keysym.sym, event->key.keysym.scancode, event->key.keysym.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions. return true; } +#if SDL_HAS_DISPLAY_EVENT + case SDL_DISPLAYEVENT: + { + // 2.0.26 has SDL_DISPLAYEVENT_CONNECTED/SDL_DISPLAYEVENT_DISCONNECTED/SDL_DISPLAYEVENT_ORIENTATION, + // so change of DPI/Scaling are not reflected in this event. (SDL3 has it) + bd->WantUpdateMonitors = true; + return true; + } +#endif case SDL_WINDOWEVENT: { - if (ImGui_ImplSDL2_GetViewportForWindowID(event->window.windowID) == nullptr) + ImGuiViewport* viewport = ImGui_ImplSDL2_GetViewportForWindowID(event->window.windowID); + if (viewport == NULL) return false; + // - When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right. // - However we won't get a correct LEAVE event for a captured window. // - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late, @@ -425,8 +464,14 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) bd->MouseLastLeaveFrame = ImGui::GetFrameCount() + 1; if (window_event == SDL_WINDOWEVENT_FOCUS_GAINED) io.AddFocusEvent(true); - else if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST) + else if (window_event == SDL_WINDOWEVENT_FOCUS_LOST) io.AddFocusEvent(false); + else if (window_event == SDL_WINDOWEVENT_CLOSE) + viewport->PlatformRequestClose = true; + else if (window_event == SDL_WINDOWEVENT_MOVED) + viewport->PlatformRequestMove = true; + else if (window_event == SDL_WINDOWEVENT_RESIZED) + viewport->PlatformRequestResize = true; return true; } case SDL_CONTROLLERDEVICEADDED: @@ -464,13 +509,23 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void ImGui_ImplSDL2_Data* bd = IM_NEW(ImGui_ImplSDL2_Data)(); io.BackendPlatformUserData = (void*)bd; io.BackendPlatformName = "imgui_impl_sdl2"; - io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) - io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) + io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + if (mouse_can_use_global_state) + io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) bd->Window = window; bd->WindowID = SDL_GetWindowID(window); bd->Renderer = renderer; + + // SDL on Linux/OSX doesn't report events for unfocused windows (see https://github.com/ocornut/imgui/issues/4960) + // We will use 'MouseCanReportHoveredViewport' to set 'ImGuiBackendFlags_HasMouseHoveredViewport' dynamically each frame. bd->MouseCanUseGlobalState = mouse_can_use_global_state; +#ifndef __APPLE__ + bd->MouseCanReportHoveredViewport = bd->MouseCanUseGlobalState; +#else + bd->MouseCanReportHoveredViewport = false; +#endif ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText; @@ -481,6 +536,9 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplSDL2_EmscriptenOpenURL(url); return true; }; #endif + // Update monitor a first time during init + ImGui_ImplSDL2_UpdateMonitors(); + // Gamepad handling bd->GamepadMode = ImGui_ImplSDL2_GamepadMode_AutoFirst; bd->WantUpdateGamepadsList = true; @@ -533,7 +591,11 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0"); #endif - (void)sdl_gl_context; // Unused in 'master' branch. + // We need SDL_CaptureMouse(), SDL_GetGlobalMouseState() from SDL 2.0.4+ to support multiple viewports. + // We left the call to ImGui_ImplSDL2_InitMultiViewportSupport() outside of #ifdef to avoid unused-function warnings. + if (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) + ImGui_ImplSDL2_InitMultiViewportSupport(window, sdl_gl_context); + return true; } @@ -547,7 +609,11 @@ bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window) #if !SDL_HAS_VULKAN IM_ASSERT(0 && "Unsupported"); #endif - return ImGui_ImplSDL2_Init(window, nullptr, nullptr); + if (!ImGui_ImplSDL2_Init(window, nullptr, nullptr)) + return false; + ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); + bd->UseVulkan = true; + return true; } bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window) @@ -581,6 +647,8 @@ void ImGui_ImplSDL2_Shutdown() IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDL2_ShutdownMultiViewportSupport(); + if (bd->ClipboardTextData) SDL_free(bd->ClipboardTextData); for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) @@ -589,10 +657,11 @@ void ImGui_ImplSDL2_Shutdown() io.BackendPlatformName = nullptr; io.BackendPlatformUserData = nullptr; - io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); + io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_PlatformHasViewports | ImGuiBackendFlags_HasMouseHoveredViewport); IM_DELETE(bd); } +// This code is incredibly messy because some of the functions we need for full viewport support are not available in SDL < 2.0.4. static void ImGui_ImplSDL2_UpdateMouseData() { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); @@ -603,25 +672,56 @@ static void ImGui_ImplSDL2_UpdateMouseData() // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside SDL_CaptureMouse((bd->MouseButtonsDown != 0) ? SDL_TRUE : SDL_FALSE); SDL_Window* focused_window = SDL_GetKeyboardFocus(); - const bool is_app_focused = (bd->Window == focused_window); + const bool is_app_focused = (focused_window && (bd->Window == focused_window || ImGui_ImplSDL2_GetViewportForWindowID(SDL_GetWindowID(focused_window)) != NULL)); #else + SDL_Window* focused_window = bd->Window; const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only #endif + if (is_app_focused) { // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user) if (io.WantSetMousePos) - SDL_WarpMouseInWindow(bd->Window, (int)io.MousePos.x, (int)io.MousePos.y); + { +#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + SDL_WarpMouseGlobal((int)io.MousePos.x, (int)io.MousePos.y); + else +#endif + SDL_WarpMouseInWindow(bd->Window, (int)io.MousePos.x, (int)io.MousePos.y); + } // (Optional) Fallback to provide mouse position when focused (SDL_MOUSEMOTION already provides this when hovered or captured) if (bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0) { - int window_x, window_y, mouse_x_global, mouse_y_global; - SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global); - SDL_GetWindowPosition(bd->Window, &window_x, &window_y); - io.AddMousePosEvent((float)(mouse_x_global - window_x), (float)(mouse_y_global - window_y)); + // Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) + // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor) + int mouse_x, mouse_y, window_x, window_y; + SDL_GetGlobalMouseState(&mouse_x, &mouse_y); + if (!(io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + { + SDL_GetWindowPosition(focused_window, &window_x, &window_y); + mouse_x -= window_x; + mouse_y -= window_y; + } + io.AddMousePosEvent((float)mouse_x, (float)mouse_y); } } + + // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering. + // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. + // - [!] SDL backend does NOT correctly ignore viewports with the _NoInputs flag. + // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window + // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported + // by the backend, and use its flawed heuristic to guess the viewport behind. + // - [X] SDL backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). + if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) + { + ImGuiID mouse_viewport_id = 0; + if (ImGuiViewport* mouse_viewport = ImGui_ImplSDL2_GetViewportForWindowID(bd->MouseWindowID)) + mouse_viewport_id = mouse_viewport->ID; + io.AddMouseViewportEvent(mouse_viewport_id); + } } static void ImGui_ImplSDL2_UpdateMouseCursor() @@ -665,7 +765,7 @@ void ImGui_ImplSDL2_SetGamepadMode(ImGui_ImplSDL2_GamepadMode mode, struct _SDL_ ImGui_ImplSDL2_CloseGamepads(); if (mode == ImGui_ImplSDL2_GamepadMode_Manual) { - IM_ASSERT(manual_gamepads_array != nullptr && manual_gamepads_count > 0); + IM_ASSERT(manual_gamepads_array != nullptr || manual_gamepads_count <= 0); for (int n = 0; n < manual_gamepads_count; n++) bd->Gamepads.push_back(manual_gamepads_array[n]); } @@ -755,6 +855,43 @@ static void ImGui_ImplSDL2_UpdateGamepads() ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickDown, SDL_CONTROLLER_AXIS_RIGHTY, +thumb_dead_zone, +32767); } +// FIXME: Note that doesn't update with DPI/Scaling change only as SDL2 doesn't have an event for it (SDL3 has). +static void ImGui_ImplSDL2_UpdateMonitors() +{ + ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Monitors.resize(0); + bd->WantUpdateMonitors = false; + int display_count = SDL_GetNumVideoDisplays(); + for (int n = 0; n < display_count; n++) + { + // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. + ImGuiPlatformMonitor monitor; + SDL_Rect r; + SDL_GetDisplayBounds(n, &r); + monitor.MainPos = monitor.WorkPos = ImVec2((float)r.x, (float)r.y); + monitor.MainSize = monitor.WorkSize = ImVec2((float)r.w, (float)r.h); +#if SDL_HAS_USABLE_DISPLAY_BOUNDS + SDL_GetDisplayUsableBounds(n, &r); + monitor.WorkPos = ImVec2((float)r.x, (float)r.y); + monitor.WorkSize = ImVec2((float)r.w, (float)r.h); +#endif +#if SDL_HAS_PER_MONITOR_DPI + // FIXME-VIEWPORT: On MacOS SDL reports actual monitor DPI scale, ignoring OS configuration. We may want to set + // DpiScale to cocoa_window.backingScaleFactor here. + float dpi = 0.0f; + if (!SDL_GetDisplayDPI(n, &dpi, nullptr, nullptr)) + { + if (dpi <= 0.0f) + continue; // Some accessibility applications are declaring virtual monitors with a DPI of 0, see #7902. + monitor.DpiScale = dpi / 96.0f; + } +#endif + monitor.PlatformHandle = (void*)(intptr_t)n; + platform_io.Monitors.push_back(monitor); + } +} + void ImGui_ImplSDL2_NewFrame() { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); @@ -779,6 +916,10 @@ void ImGui_ImplSDL2_NewFrame() if (w > 0 && h > 0) io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h); + // Update monitors + if (bd->WantUpdateMonitors) + ImGui_ImplSDL2_UpdateMonitors(); + // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) // (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens in VMs and Emscripten, see #6189, #6114, #3644) static Uint64 frequency = SDL_GetPerformanceFrequency(); @@ -795,6 +936,13 @@ void ImGui_ImplSDL2_NewFrame() io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); } + // Our io.AddMouseViewportEvent() calls will only be valid when not capturing. + // Technically speaking testing for 'bd->MouseButtonsDown == 0' would be more rigorous, but testing for payload reduces noise and potential side-effects. + if (bd->MouseCanReportHoveredViewport && ImGui::GetDragDropPayload() == nullptr) + io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; + else + io.BackendFlags &= ~ImGuiBackendFlags_HasMouseHoveredViewport; + ImGui_ImplSDL2_UpdateMouseData(); ImGui_ImplSDL2_UpdateMouseCursor(); @@ -802,6 +950,256 @@ void ImGui_ImplSDL2_NewFrame() ImGui_ImplSDL2_UpdateGamepads(); } +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data. +struct ImGui_ImplSDL2_ViewportData +{ + SDL_Window* Window; + Uint32 WindowID; + bool WindowOwned; + SDL_GLContext GLContext; + + ImGui_ImplSDL2_ViewportData() { Window = nullptr; WindowID = 0; WindowOwned = false; GLContext = nullptr; } + ~ImGui_ImplSDL2_ViewportData() { IM_ASSERT(Window == nullptr && GLContext == nullptr); } +}; + +static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) +{ + ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); + ImGui_ImplSDL2_ViewportData* vd = IM_NEW(ImGui_ImplSDL2_ViewportData)(); + viewport->PlatformUserData = vd; + + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGui_ImplSDL2_ViewportData* main_viewport_data = (ImGui_ImplSDL2_ViewportData*)main_viewport->PlatformUserData; + + // Share GL resources with main context + bool use_opengl = (main_viewport_data->GLContext != nullptr); + SDL_GLContext backup_context = nullptr; + if (use_opengl) + { + backup_context = SDL_GL_GetCurrentContext(); + SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); + SDL_GL_MakeCurrent(main_viewport_data->Window, main_viewport_data->GLContext); + } + + Uint32 sdl_flags = 0; + sdl_flags |= use_opengl ? SDL_WINDOW_OPENGL : (bd->UseVulkan ? SDL_WINDOW_VULKAN : 0); + sdl_flags |= SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_ALLOW_HIGHDPI; + sdl_flags |= SDL_WINDOW_HIDDEN; + sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0; + sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE; +#if !defined(_WIN32) + // See SDL hack in ImGui_ImplSDL2_ShowWindow(). + sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) ? SDL_WINDOW_SKIP_TASKBAR : 0; +#endif +#if SDL_HAS_ALWAYS_ON_TOP + sdl_flags |= (viewport->Flags & ImGuiViewportFlags_TopMost) ? SDL_WINDOW_ALWAYS_ON_TOP : 0; +#endif + vd->Window = SDL_CreateWindow("No Title Yet", (int)viewport->Pos.x, (int)viewport->Pos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); + vd->WindowOwned = true; + if (use_opengl) + { + vd->GLContext = SDL_GL_CreateContext(vd->Window); + SDL_GL_SetSwapInterval(0); + } + if (use_opengl && backup_context) + SDL_GL_MakeCurrent(vd->Window, backup_context); + + viewport->PlatformHandle = (void*)(intptr_t)SDL_GetWindowID(vd->Window); + viewport->PlatformHandleRaw = nullptr; + SDL_SysWMinfo info; + SDL_VERSION(&info.version); + if (SDL_GetWindowWMInfo(vd->Window, &info)) + { +#if defined(SDL_VIDEO_DRIVER_WINDOWS) + viewport->PlatformHandleRaw = info.info.win.window; +#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA) + viewport->PlatformHandleRaw = (void*)info.info.cocoa.window; +#endif + } +} + +static void ImGui_ImplSDL2_DestroyWindow(ImGuiViewport* viewport) +{ + if (ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData) + { + if (vd->GLContext && vd->WindowOwned) + SDL_GL_DeleteContext(vd->GLContext); + if (vd->Window && vd->WindowOwned) + SDL_DestroyWindow(vd->Window); + vd->GLContext = nullptr; + vd->Window = nullptr; + IM_DELETE(vd); + } + viewport->PlatformUserData = viewport->PlatformHandle = nullptr; +} + +static void ImGui_ImplSDL2_ShowWindow(ImGuiViewport* viewport) +{ + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; +#if defined(_WIN32) && !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP || WINAPI_FAMILY == WINAPI_FAMILY_GAMES)) + HWND hwnd = (HWND)viewport->PlatformHandleRaw; + + // SDL hack: Hide icon from task bar + // Note: SDL 2.0.6+ has a SDL_WINDOW_SKIP_TASKBAR flag which is supported under Windows but the way it create the window breaks our seamless transition. + if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) + { + LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); + ex_style &= ~WS_EX_APPWINDOW; + ex_style |= WS_EX_TOOLWINDOW; + ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); + } +#endif + +#if SDL_HAS_SHOW_WINDOW_ACTIVATION_HINT + SDL_SetHint(SDL_HINT_WINDOW_NO_ACTIVATION_WHEN_SHOWN, (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) ? "1" : "0"); +#elif defined(_WIN32) + // SDL hack: SDL always activate/focus windows :/ + if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) + { + ::ShowWindow(hwnd, SW_SHOWNA); + return; + } +#endif + SDL_ShowWindow(vd->Window); +} + +static ImVec2 ImGui_ImplSDL2_GetWindowPos(ImGuiViewport* viewport) +{ + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + int x = 0, y = 0; + SDL_GetWindowPosition(vd->Window, &x, &y); + return ImVec2((float)x, (float)y); +} + +static void ImGui_ImplSDL2_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) +{ + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + SDL_SetWindowPosition(vd->Window, (int)pos.x, (int)pos.y); +} + +static ImVec2 ImGui_ImplSDL2_GetWindowSize(ImGuiViewport* viewport) +{ + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + int w = 0, h = 0; + SDL_GetWindowSize(vd->Window, &w, &h); + return ImVec2((float)w, (float)h); +} + +static void ImGui_ImplSDL2_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + SDL_SetWindowSize(vd->Window, (int)size.x, (int)size.y); +} + +static void ImGui_ImplSDL2_SetWindowTitle(ImGuiViewport* viewport, const char* title) +{ + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + SDL_SetWindowTitle(vd->Window, title); +} + +#if SDL_HAS_WINDOW_ALPHA +static void ImGui_ImplSDL2_SetWindowAlpha(ImGuiViewport* viewport, float alpha) +{ + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + SDL_SetWindowOpacity(vd->Window, alpha); +} +#endif + +static void ImGui_ImplSDL2_SetWindowFocus(ImGuiViewport* viewport) +{ + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + SDL_RaiseWindow(vd->Window); +} + +static bool ImGui_ImplSDL2_GetWindowFocus(ImGuiViewport* viewport) +{ + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + return (SDL_GetWindowFlags(vd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; +} + +static bool ImGui_ImplSDL2_GetWindowMinimized(ImGuiViewport* viewport) +{ + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + return (SDL_GetWindowFlags(vd->Window) & SDL_WINDOW_MINIMIZED) != 0; +} + +static void ImGui_ImplSDL2_RenderWindow(ImGuiViewport* viewport, void*) +{ + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + if (vd->GLContext) + SDL_GL_MakeCurrent(vd->Window, vd->GLContext); +} + +static void ImGui_ImplSDL2_SwapBuffers(ImGuiViewport* viewport, void*) +{ + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + if (vd->GLContext) + { + SDL_GL_MakeCurrent(vd->Window, vd->GLContext); + SDL_GL_SwapWindow(vd->Window); + } +} + +// Vulkan support (the Vulkan renderer needs to call a platform-side support function to create the surface) +// SDL is graceful enough to _not_ need so we can safely include this. +#if SDL_HAS_VULKAN +#include +static int ImGui_ImplSDL2_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface) +{ + ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData; + (void)vk_allocator; + SDL_bool ret = SDL_Vulkan_CreateSurface(vd->Window, (VkInstance)vk_instance, (VkSurfaceKHR*)out_vk_surface); + return ret ? 0 : 1; // ret ? VK_SUCCESS : VK_NOT_READY +} +#endif // SDL_HAS_VULKAN + +static void ImGui_ImplSDL2_InitMultiViewportSupport(SDL_Window* window, void* sdl_gl_context) +{ + // Register platform interface (will be coupled with a renderer interface) + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Platform_CreateWindow = ImGui_ImplSDL2_CreateWindow; + platform_io.Platform_DestroyWindow = ImGui_ImplSDL2_DestroyWindow; + platform_io.Platform_ShowWindow = ImGui_ImplSDL2_ShowWindow; + platform_io.Platform_SetWindowPos = ImGui_ImplSDL2_SetWindowPos; + platform_io.Platform_GetWindowPos = ImGui_ImplSDL2_GetWindowPos; + platform_io.Platform_SetWindowSize = ImGui_ImplSDL2_SetWindowSize; + platform_io.Platform_GetWindowSize = ImGui_ImplSDL2_GetWindowSize; + platform_io.Platform_SetWindowFocus = ImGui_ImplSDL2_SetWindowFocus; + platform_io.Platform_GetWindowFocus = ImGui_ImplSDL2_GetWindowFocus; + platform_io.Platform_GetWindowMinimized = ImGui_ImplSDL2_GetWindowMinimized; + platform_io.Platform_SetWindowTitle = ImGui_ImplSDL2_SetWindowTitle; + platform_io.Platform_RenderWindow = ImGui_ImplSDL2_RenderWindow; + platform_io.Platform_SwapBuffers = ImGui_ImplSDL2_SwapBuffers; +#if SDL_HAS_WINDOW_ALPHA + platform_io.Platform_SetWindowAlpha = ImGui_ImplSDL2_SetWindowAlpha; +#endif +#if SDL_HAS_VULKAN + platform_io.Platform_CreateVkSurface = ImGui_ImplSDL2_CreateVkSurface; +#endif + + // Register main window handle (which is owned by the main application, not by us) + // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGui_ImplSDL2_ViewportData* vd = IM_NEW(ImGui_ImplSDL2_ViewportData)(); + vd->Window = window; + vd->WindowID = SDL_GetWindowID(window); + vd->WindowOwned = false; + vd->GLContext = sdl_gl_context; + main_viewport->PlatformUserData = vd; + main_viewport->PlatformHandle = (void*)(intptr_t)vd->WindowID; +} + +static void ImGui_ImplSDL2_ShutdownMultiViewportSupport() +{ + ImGui::DestroyPlatformWindows(); +} + //----------------------------------------------------------------------------- #if defined(__clang__) diff --git a/neo/libs/imgui/backends/imgui_impl_sdl2.h b/neo/libs/imgui/backends/imgui_impl_sdl2.h index 3c4775697..008794cf0 100644 --- a/neo/libs/imgui/backends/imgui_impl_sdl2.h +++ b/neo/libs/imgui/backends/imgui_impl_sdl2.h @@ -9,6 +9,10 @@ // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. // [X] Platform: Basic IME support. App needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!. +// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// Missing features or Issues: +// [ ] Platform: Multi-viewport: Minimized windows seems to break mouse wheel events (at least under Windows). +// [ ] Platform: Multi-viewport: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/neo/libs/imgui/backends/imgui_impl_sdl3.cpp b/neo/libs/imgui/backends/imgui_impl_sdl3.cpp index b8e4dec5e..3e4d5bccd 100644 --- a/neo/libs/imgui/backends/imgui_impl_sdl3.cpp +++ b/neo/libs/imgui/backends/imgui_impl_sdl3.cpp @@ -1,16 +1,17 @@ -// dear imgui: Platform Backend for SDL3 (*EXPERIMENTAL*) -// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) +// dear imgui: Platform Backend for SDL3 +// This needs to be used along with a Renderer (e.g. SDL_GPU, DirectX11, OpenGL3, Vulkan..) // (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) -// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) - // Implemented features: // [X] Platform: Clipboard support. // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen. // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5] // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. -// [X] Platform: IME support. +// [x] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable' -> the OS animation effect when window gets created/destroyed is problematic. SDL2 backend doesn't have issue. +// Missing features or Issues: +// [ ] Platform: Multi-viewport: Minimized windows seems to break mouse wheel events (at least under Windows). +// [x] Platform: IME support. Position somehow broken in SDL3 + app needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -22,7 +23,10 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2025-01-20: Made ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode_Manual) accept an empty array. // 2024-10-24: Emscripten: SDL_EVENT_MOUSE_WHEEL event doesn't require dividing by 100.0f on Emscripten. +// 2024-09-11: (Docking) Added support for viewport->ParentViewportId field to support parenting at OS level. (#7973) // 2024-09-03: Update for SDL3 api changes: SDL_GetGamepads() memory ownership revert. (#7918, #7898, #7807) // 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO: // - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn @@ -93,6 +97,8 @@ struct ImGui_ImplSDL3_Data SDL_Renderer* Renderer; Uint64 Time; char* ClipboardTextData; + bool UseVulkan; + bool WantUpdateMonitors; // IME handling SDL_Window* ImeWindow; @@ -104,11 +110,12 @@ struct ImGui_ImplSDL3_Data SDL_Cursor* MouseLastCursor; int MousePendingLeaveFrame; bool MouseCanUseGlobalState; + bool MouseCanReportHoveredViewport; // This is hard to use/unreliable on SDL so we'll set ImGuiBackendFlags_HasMouseHoveredViewport dynamically based on state. // Gamepad handling - ImVector Gamepads; + ImVector Gamepads; ImGui_ImplSDL3_GamepadMode GamepadMode; - bool WantUpdateGamepadsList; + bool WantUpdateGamepadsList; ImGui_ImplSDL3_Data() { memset((void*)this, 0, sizeof(*this)); } }; @@ -122,6 +129,11 @@ static ImGui_ImplSDL3_Data* ImGui_ImplSDL3_GetBackendData() return ImGui::GetCurrentContext() ? (ImGui_ImplSDL3_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; } +// Forward Declarations +static void ImGui_ImplSDL3_UpdateMonitors(); +static void ImGui_ImplSDL3_InitMultiViewportSupport(SDL_Window* window, void* sdl_gl_context); +static void ImGui_ImplSDL3_ShutdownMultiViewportSupport(); + // Functions static const char* ImGui_ImplSDL3_GetClipboardText(ImGuiContext*) { @@ -151,8 +163,8 @@ static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* view if (data->WantVisible) { SDL_Rect r; - r.x = (int)data->InputPos.x; - r.y = (int)data->InputPos.y; + r.x = (int)(data->InputPos.x - viewport->Pos.x); + r.y = (int)(data->InputPos.y - viewport->Pos.y + data->InputLineHeight); r.w = 1; r.h = (int)data->InputLineHeight; SDL_SetTextInputArea(window, &r, 0); @@ -305,18 +317,15 @@ static void ImGui_ImplSDL3_UpdateKeyModifiers(SDL_Keymod sdl_key_mods) io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & SDL_KMOD_GUI) != 0); } - static ImGuiViewport* ImGui_ImplSDL3_GetViewportForWindowID(SDL_WindowID window_id) { - ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); - return (window_id == bd->WindowID) ? ImGui::GetMainViewport() : nullptr; + return ImGui::FindViewportByPlatformHandle((void*)(intptr_t)window_id); } // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. -// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field. bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) { ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); @@ -330,6 +339,13 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) if (ImGui_ImplSDL3_GetViewportForWindowID(event->motion.windowID) == nullptr) return false; ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + int window_x, window_y; + SDL_GetWindowPosition(SDL_GetWindowFromID(event->motion.windowID), &window_x, &window_y); + mouse_pos.x += window_x; + mouse_pos.y += window_y; + } io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse); io.AddMousePosEvent(mouse_pos.x, mouse_pos.y); return true; @@ -383,6 +399,15 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) io.SetKeyEventNativeData(key, event->key.key, event->key.scancode, event->key.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions. return true; } + case SDL_EVENT_DISPLAY_ORIENTATION: + case SDL_EVENT_DISPLAY_ADDED: + case SDL_EVENT_DISPLAY_REMOVED: + case SDL_EVENT_DISPLAY_MOVED: + case SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED: + { + bd->WantUpdateMonitors = true; + return true; + } case SDL_EVENT_WINDOW_MOUSE_ENTER: { if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr) @@ -410,6 +435,21 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) io.AddFocusEvent(event->type == SDL_EVENT_WINDOW_FOCUS_GAINED); return true; } + case SDL_EVENT_WINDOW_CLOSE_REQUESTED: + case SDL_EVENT_WINDOW_MOVED: + case SDL_EVENT_WINDOW_RESIZED: + { + ImGuiViewport* viewport = ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID); + if (viewport == NULL) + return false; + if (event->type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) + viewport->PlatformRequestClose = true; + if (event->type == SDL_EVENT_WINDOW_MOVED) + viewport->PlatformRequestMove = true; + if (event->type == SDL_EVENT_WINDOW_RESIZED) + viewport->PlatformRequestResize = true; + return true; + } case SDL_EVENT_GAMEPAD_ADDED: case SDL_EVENT_GAMEPAD_REMOVED: { @@ -455,17 +495,30 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void io.BackendPlatformName = "imgui_impl_sdl3"; io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + if (mouse_can_use_global_state) + io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) bd->Window = window; bd->WindowID = SDL_GetWindowID(window); bd->Renderer = renderer; + + // SDL on Linux/OSX doesn't report events for unfocused windows (see https://github.com/ocornut/imgui/issues/4960) + // We will use 'MouseCanReportHoveredViewport' to set 'ImGuiBackendFlags_HasMouseHoveredViewport' dynamically each frame. bd->MouseCanUseGlobalState = mouse_can_use_global_state; +#ifndef __APPLE__ + bd->MouseCanReportHoveredViewport = bd->MouseCanUseGlobalState; +#else + bd->MouseCanReportHoveredViewport = false; +#endif ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText; platform_io.Platform_GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText; platform_io.Platform_SetImeDataFn = ImGui_ImplSDL3_PlatformSetImeData; + // Update monitor a first time during init + ImGui_ImplSDL3_UpdateMonitors(); + // Gamepad handling bd->GamepadMode = ImGui_ImplSDL3_GamepadMode_AutoFirst; bd->WantUpdateGamepadsList = true; @@ -491,27 +544,35 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void // (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application. // It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click: // you can ignore SDL_EVENT_MOUSE_BUTTON_DOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED) -#ifdef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); -#endif // From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710) -#ifdef SDL_HINT_MOUSE_AUTO_CAPTURE SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0"); -#endif + + // SDL 3.x : see https://github.com/libsdl-org/SDL/issues/6659 + SDL_SetHint("SDL_BORDERLESS_WINDOWED_STYLE", "0"); + + // We need SDL_CaptureMouse(), SDL_GetGlobalMouseState() from SDL 2.0.4+ to support multiple viewports. + // We left the call to ImGui_ImplSDL3_InitPlatformInterface() outside of #ifdef to avoid unused-function warnings. + if (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) + ImGui_ImplSDL3_InitMultiViewportSupport(window, sdl_gl_context); return true; } +// Should technically be a SDL_GLContext but due to typedef it is sane to keep it void* in public interface. bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context) { - IM_UNUSED(sdl_gl_context); // Viewport branch will need this. return ImGui_ImplSDL3_Init(window, nullptr, sdl_gl_context); } bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window) { - return ImGui_ImplSDL3_Init(window, nullptr, nullptr); + if (!ImGui_ImplSDL3_Init(window, nullptr, nullptr)) + return false; + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + bd->UseVulkan = true; + return true; } bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window) @@ -550,6 +611,8 @@ void ImGui_ImplSDL3_Shutdown() IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplSDL3_ShutdownMultiViewportSupport(); + if (bd->ClipboardTextData) SDL_free(bd->ClipboardTextData); for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) @@ -558,10 +621,11 @@ void ImGui_ImplSDL3_Shutdown() io.BackendPlatformName = nullptr; io.BackendPlatformUserData = nullptr; - io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); + io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_PlatformHasViewports | ImGuiBackendFlags_HasMouseHoveredViewport); IM_DELETE(bd); } +// This code is incredibly messy because some of the functions we need for full viewport support are not available in SDL < 2.0.4. static void ImGui_ImplSDL3_UpdateMouseData() { ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); @@ -572,7 +636,7 @@ static void ImGui_ImplSDL3_UpdateMouseData() // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside SDL_CaptureMouse(bd->MouseButtonsDown != 0); SDL_Window* focused_window = SDL_GetKeyboardFocus(); - const bool is_app_focused = (bd->Window == focused_window); + const bool is_app_focused = (focused_window && (bd->Window == focused_window || ImGui_ImplSDL3_GetViewportForWindowID(SDL_GetWindowID(focused_window)) != NULL)); #else SDL_Window* focused_window = bd->Window; const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only @@ -581,19 +645,47 @@ static void ImGui_ImplSDL3_UpdateMouseData() { // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user) if (io.WantSetMousePos) - SDL_WarpMouseInWindow(bd->Window, io.MousePos.x, io.MousePos.y); + { +#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + SDL_WarpMouseGlobal(io.MousePos.x, io.MousePos.y); + else +#endif + SDL_WarpMouseInWindow(bd->Window, io.MousePos.x, io.MousePos.y); + } // (Optional) Fallback to provide mouse position when focused (SDL_EVENT_MOUSE_MOTION already provides this when hovered or captured) if (bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0) { // Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) - float mouse_x_global, mouse_y_global; + // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor) + float mouse_x, mouse_y; int window_x, window_y; - SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global); - SDL_GetWindowPosition(focused_window, &window_x, &window_y); - io.AddMousePosEvent(mouse_x_global - window_x, mouse_y_global - window_y); + SDL_GetGlobalMouseState(&mouse_x, &mouse_y); + if (!(io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + { + SDL_GetWindowPosition(focused_window, &window_x, &window_y); + mouse_x -= window_x; + mouse_y -= window_y; + } + io.AddMousePosEvent((float)mouse_x, (float)mouse_y); } } + + // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering. + // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. + // - [!] SDL backend does NOT correctly ignore viewports with the _NoInputs flag. + // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window + // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported + // by the backend, and use its flawed heuristic to guess the viewport behind. + // - [X] SDL backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). + if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) + { + ImGuiID mouse_viewport_id = 0; + if (ImGuiViewport* mouse_viewport = ImGui_ImplSDL3_GetViewportForWindowID(bd->MouseWindowID)) + mouse_viewport_id = mouse_viewport->ID; + io.AddMouseViewportEvent(mouse_viewport_id); + } } static void ImGui_ImplSDL3_UpdateMouseCursor() @@ -637,7 +729,7 @@ void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad* ImGui_ImplSDL3_CloseGamepads(); if (mode == ImGui_ImplSDL3_GamepadMode_Manual) { - IM_ASSERT(manual_gamepads_array != nullptr && manual_gamepads_count > 0); + IM_ASSERT(manual_gamepads_array != nullptr || manual_gamepads_count <= 0); for (int n = 0; n < manual_gamepads_count; n++) bd->Gamepads.push_back(manual_gamepads_array[n]); } @@ -728,6 +820,38 @@ static void ImGui_ImplSDL3_UpdateGamepads() ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickDown, SDL_GAMEPAD_AXIS_RIGHTY, +thumb_dead_zone, +32767); } +static void ImGui_ImplSDL3_UpdateMonitors() +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Monitors.resize(0); + bd->WantUpdateMonitors = false; + + int display_count; + SDL_DisplayID* displays = SDL_GetDisplays(&display_count); + for (int n = 0; n < display_count; n++) + { + // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime. + SDL_DisplayID display_id = displays[n]; + ImGuiPlatformMonitor monitor; + SDL_Rect r; + SDL_GetDisplayBounds(display_id, &r); + monitor.MainPos = monitor.WorkPos = ImVec2((float)r.x, (float)r.y); + monitor.MainSize = monitor.WorkSize = ImVec2((float)r.w, (float)r.h); + SDL_GetDisplayUsableBounds(display_id, &r); + monitor.WorkPos = ImVec2((float)r.x, (float)r.y); + monitor.WorkSize = ImVec2((float)r.w, (float)r.h); + // FIXME-VIEWPORT: On MacOS SDL reports actual monitor DPI scale, ignoring OS configuration. We may want to set + // DpiScale to cocoa_window.backingScaleFactor here. + monitor.DpiScale = SDL_GetDisplayContentScale(display_id); + monitor.PlatformHandle = (void*)(intptr_t)n; + if (monitor.DpiScale <= 0.0f) + continue; // Some accessibility applications are declaring virtual monitors with a DPI of 0, see #7902. + platform_io.Monitors.push_back(monitor); + } + SDL_free(displays); +} + void ImGui_ImplSDL3_NewFrame() { ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); @@ -745,6 +869,10 @@ void ImGui_ImplSDL3_NewFrame() if (w > 0 && h > 0) io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h); + // Update monitors + if (bd->WantUpdateMonitors) + ImGui_ImplSDL3_UpdateMonitors(); + // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) // (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens in VMs and Emscripten, see #6189, #6114, #3644) static Uint64 frequency = SDL_GetPerformanceFrequency(); @@ -761,6 +889,13 @@ void ImGui_ImplSDL3_NewFrame() io.AddMousePosEvent(-FLT_MAX, -FLT_MAX); } + // Our io.AddMouseViewportEvent() calls will only be valid when not capturing. + // Technically speaking testing for 'bd->MouseButtonsDown == 0' would be more rigorous, but testing for payload reduces noise and potential side-effects. + if (bd->MouseCanReportHoveredViewport && ImGui::GetDragDropPayload() == nullptr) + io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; + else + io.BackendFlags &= ~ImGuiBackendFlags_HasMouseHoveredViewport; + ImGui_ImplSDL3_UpdateMouseData(); ImGui_ImplSDL3_UpdateMouseCursor(); @@ -768,6 +903,255 @@ void ImGui_ImplSDL3_NewFrame() ImGui_ImplSDL3_UpdateGamepads(); } +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data. +struct ImGui_ImplSDL3_ViewportData +{ + SDL_Window* Window; + SDL_Window* ParentWindow; + Uint32 WindowID; + bool WindowOwned; + SDL_GLContext GLContext; + + ImGui_ImplSDL3_ViewportData() { Window = ParentWindow = nullptr; WindowID = 0; WindowOwned = false; GLContext = nullptr; } + ~ImGui_ImplSDL3_ViewportData() { IM_ASSERT(Window == nullptr && GLContext == nullptr); } +}; + +static SDL_Window* ImGui_ImplSDL3_GetSDLWindowFromViewportID(ImGuiID viewport_id) +{ + if (viewport_id != 0) + if (ImGuiViewport* viewport = ImGui::FindViewportByID(viewport_id)) + { + SDL_WindowID window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle; + return SDL_GetWindowFromID(window_id); + } + return nullptr; +} + +static void ImGui_ImplSDL3_CreateWindow(ImGuiViewport* viewport) +{ + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + ImGui_ImplSDL3_ViewportData* vd = IM_NEW(ImGui_ImplSDL3_ViewportData)(); + viewport->PlatformUserData = vd; + + vd->ParentWindow = ImGui_ImplSDL3_GetSDLWindowFromViewportID(viewport->ParentViewportId); + + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGui_ImplSDL3_ViewportData* main_viewport_data = (ImGui_ImplSDL3_ViewportData*)main_viewport->PlatformUserData; + + // Share GL resources with main context + bool use_opengl = (main_viewport_data->GLContext != nullptr); + SDL_GLContext backup_context = nullptr; + if (use_opengl) + { + backup_context = SDL_GL_GetCurrentContext(); + SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); + SDL_GL_MakeCurrent(main_viewport_data->Window, main_viewport_data->GLContext); + } + + SDL_WindowFlags sdl_flags = 0; + sdl_flags |= SDL_WINDOW_HIDDEN; + sdl_flags |= use_opengl ? SDL_WINDOW_OPENGL : (bd->UseVulkan ? SDL_WINDOW_VULKAN : 0); + sdl_flags |= SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_HIGH_PIXEL_DENSITY; + sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0; + sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE; + sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) ? SDL_WINDOW_UTILITY : 0; + sdl_flags |= (viewport->Flags & ImGuiViewportFlags_TopMost) ? SDL_WINDOW_ALWAYS_ON_TOP : 0; + vd->Window = SDL_CreateWindow("No Title Yet", (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); + SDL_SetWindowParent(vd->Window, vd->ParentWindow); + SDL_SetWindowPosition(vd->Window, (int)viewport->Pos.x, (int)viewport->Pos.y); + vd->WindowOwned = true; + if (use_opengl) + { + vd->GLContext = SDL_GL_CreateContext(vd->Window); + SDL_GL_SetSwapInterval(0); + } + if (use_opengl && backup_context) + SDL_GL_MakeCurrent(vd->Window, backup_context); + + ImGui_ImplSDL3_SetupPlatformHandles(viewport, vd->Window); +} + +static void ImGui_ImplSDL3_DestroyWindow(ImGuiViewport* viewport) +{ + if (ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData) + { + if (vd->GLContext && vd->WindowOwned) + SDL_GL_DestroyContext(vd->GLContext); + if (vd->Window && vd->WindowOwned) + SDL_DestroyWindow(vd->Window); + vd->GLContext = nullptr; + vd->Window = nullptr; + IM_DELETE(vd); + } + viewport->PlatformUserData = viewport->PlatformHandle = nullptr; +} + +static void ImGui_ImplSDL3_ShowWindow(ImGuiViewport* viewport) +{ + ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData; +#if defined(_WIN32) && !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP || WINAPI_FAMILY == WINAPI_FAMILY_GAMES)) + HWND hwnd = (HWND)viewport->PlatformHandleRaw; + + // SDL hack: Show icon in task bar (#7989) + // Note: SDL_WINDOW_UTILITY can be used to control task bar visibility, but on Windows, it does not affect child windows. + if (!(viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon)) + { + LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE); + ex_style |= WS_EX_APPWINDOW; + ex_style &= ~WS_EX_TOOLWINDOW; + ::ShowWindow(hwnd, SW_HIDE); + ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style); + } +#endif + + SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN, (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) ? "0" : "1"); + SDL_ShowWindow(vd->Window); +} + +static void ImGui_ImplSDL3_UpdateWindow(ImGuiViewport* viewport) +{ + ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData; + + // Update SDL3 parent if it changed _after_ creation. + // This is for advanced apps that are manipulating ParentViewportID manually. + SDL_Window* new_parent = ImGui_ImplSDL3_GetSDLWindowFromViewportID(viewport->ParentViewportId); + if (new_parent != vd->ParentWindow) + { + vd->ParentWindow = new_parent; + SDL_SetWindowParent(vd->Window, vd->ParentWindow); + } +} + +static ImVec2 ImGui_ImplSDL3_GetWindowPos(ImGuiViewport* viewport) +{ + ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData; + int x = 0, y = 0; + SDL_GetWindowPosition(vd->Window, &x, &y); + return ImVec2((float)x, (float)y); +} + +static void ImGui_ImplSDL3_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) +{ + ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData; + SDL_SetWindowPosition(vd->Window, (int)pos.x, (int)pos.y); +} + +static ImVec2 ImGui_ImplSDL3_GetWindowSize(ImGuiViewport* viewport) +{ + ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData; + int w = 0, h = 0; + SDL_GetWindowSize(vd->Window, &w, &h); + return ImVec2((float)w, (float)h); +} + +static void ImGui_ImplSDL3_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData; + SDL_SetWindowSize(vd->Window, (int)size.x, (int)size.y); +} + +static void ImGui_ImplSDL3_SetWindowTitle(ImGuiViewport* viewport, const char* title) +{ + ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData; + SDL_SetWindowTitle(vd->Window, title); +} + +static void ImGui_ImplSDL3_SetWindowAlpha(ImGuiViewport* viewport, float alpha) +{ + ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData; + SDL_SetWindowOpacity(vd->Window, alpha); +} + +static void ImGui_ImplSDL3_SetWindowFocus(ImGuiViewport* viewport) +{ + ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData; + SDL_RaiseWindow(vd->Window); +} + +static bool ImGui_ImplSDL3_GetWindowFocus(ImGuiViewport* viewport) +{ + ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData; + return (SDL_GetWindowFlags(vd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; +} + +static bool ImGui_ImplSDL3_GetWindowMinimized(ImGuiViewport* viewport) +{ + ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData; + return (SDL_GetWindowFlags(vd->Window) & SDL_WINDOW_MINIMIZED) != 0; +} + +static void ImGui_ImplSDL3_RenderWindow(ImGuiViewport* viewport, void*) +{ + ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData; + if (vd->GLContext) + SDL_GL_MakeCurrent(vd->Window, vd->GLContext); +} + +static void ImGui_ImplSDL3_SwapBuffers(ImGuiViewport* viewport, void*) +{ + ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData; + if (vd->GLContext) + { + SDL_GL_MakeCurrent(vd->Window, vd->GLContext); + SDL_GL_SwapWindow(vd->Window); + } +} + +// Vulkan support (the Vulkan renderer needs to call a platform-side support function to create the surface) +// SDL is graceful enough to _not_ need so we can safely include this. +#include +static int ImGui_ImplSDL3_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface) +{ + ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData; + (void)vk_allocator; + bool ret = SDL_Vulkan_CreateSurface(vd->Window, (VkInstance)vk_instance, (VkAllocationCallbacks*)vk_allocator, (VkSurfaceKHR*)out_vk_surface); + return ret ? 0 : 1; // ret ? VK_SUCCESS : VK_NOT_READY +} + +static void ImGui_ImplSDL3_InitMultiViewportSupport(SDL_Window* window, void* sdl_gl_context) +{ + // Register platform interface (will be coupled with a renderer interface) + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Platform_CreateWindow = ImGui_ImplSDL3_CreateWindow; + platform_io.Platform_DestroyWindow = ImGui_ImplSDL3_DestroyWindow; + platform_io.Platform_ShowWindow = ImGui_ImplSDL3_ShowWindow; + platform_io.Platform_UpdateWindow = ImGui_ImplSDL3_UpdateWindow; + platform_io.Platform_SetWindowPos = ImGui_ImplSDL3_SetWindowPos; + platform_io.Platform_GetWindowPos = ImGui_ImplSDL3_GetWindowPos; + platform_io.Platform_SetWindowSize = ImGui_ImplSDL3_SetWindowSize; + platform_io.Platform_GetWindowSize = ImGui_ImplSDL3_GetWindowSize; + platform_io.Platform_SetWindowFocus = ImGui_ImplSDL3_SetWindowFocus; + platform_io.Platform_GetWindowFocus = ImGui_ImplSDL3_GetWindowFocus; + platform_io.Platform_GetWindowMinimized = ImGui_ImplSDL3_GetWindowMinimized; + platform_io.Platform_SetWindowTitle = ImGui_ImplSDL3_SetWindowTitle; + platform_io.Platform_RenderWindow = ImGui_ImplSDL3_RenderWindow; + platform_io.Platform_SwapBuffers = ImGui_ImplSDL3_SwapBuffers; + platform_io.Platform_SetWindowAlpha = ImGui_ImplSDL3_SetWindowAlpha; + platform_io.Platform_CreateVkSurface = ImGui_ImplSDL3_CreateVkSurface; + + // Register main window handle (which is owned by the main application, not by us) + // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGui_ImplSDL3_ViewportData* vd = IM_NEW(ImGui_ImplSDL3_ViewportData)(); + vd->Window = window; + vd->WindowID = SDL_GetWindowID(window); + vd->WindowOwned = false; + vd->GLContext = (SDL_GLContext)sdl_gl_context; + main_viewport->PlatformUserData = vd; + main_viewport->PlatformHandle = (void*)(intptr_t)vd->WindowID; +} + +static void ImGui_ImplSDL3_ShutdownMultiViewportSupport() +{ + ImGui::DestroyPlatformWindows(); +} + //----------------------------------------------------------------------------- #if defined(__clang__) diff --git a/neo/libs/imgui/backends/imgui_impl_sdl3.h b/neo/libs/imgui/backends/imgui_impl_sdl3.h index ff0e2aed3..064e1f076 100644 --- a/neo/libs/imgui/backends/imgui_impl_sdl3.h +++ b/neo/libs/imgui/backends/imgui_impl_sdl3.h @@ -1,16 +1,17 @@ -// dear imgui: Platform Backend for SDL3 (*EXPERIMENTAL*) -// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) +// dear imgui: Platform Backend for SDL3 +// This needs to be used along with a Renderer (e.g. SDL_GPU, DirectX11, OpenGL3, Vulkan..) // (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.) -// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) - // Implemented features: // [X] Platform: Clipboard support. // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen. // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values are obsolete since 1.87 and not supported since 1.91.5] // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. -// [X] Platform: IME support. +// [x] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable' -> the OS animation effect when window gets created/destroyed is problematic. SDL2 backend doesn't have issue. +// Missing features or Issues: +// [ ] Platform: Multi-viewport: Minimized windows seems to break mouse wheel events (at least under Windows). +// [x] Platform: IME support. Position somehow broken in SDL3 + app needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/neo/libs/imgui/backends/imgui_impl_sdlgpu3.cpp b/neo/libs/imgui/backends/imgui_impl_sdlgpu3.cpp index 7eb5eeb08..0fe7ac9cf 100644 --- a/neo/libs/imgui/backends/imgui_impl_sdlgpu3.cpp +++ b/neo/libs/imgui/backends/imgui_impl_sdlgpu3.cpp @@ -4,6 +4,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use simply cast a reference to your SDL_GPUTextureSamplerBinding to ImTextureID. // [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// Missing features: +// [ ] Renderer: Multi-viewport support (multiple windows). // The aim of imgui_impl_sdlgpu3.h/.cpp is to be usable in your engine without any modification. // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ @@ -21,13 +23,16 @@ // Calling the function is MANDATORY, otherwise the ImGui will not upload neither the vertex nor the index buffer for the GPU. See imgui_impl_sdlgpu3.cpp for more info. // CHANGELOG -// 2025-01-09: SDL_Gpu: Added the SDL_GPU3 backend. +// 2025-01-16: Renamed ImGui_ImplSDLGPU3_InitInfo::GpuDevice to Device. +// 2025-01-09: SDL_GPU: Added the SDL_GPU3 backend. #include "imgui.h" #ifndef IMGUI_DISABLE #include "imgui_impl_sdlgpu3.h" #include "imgui_impl_sdlgpu3_shaders.h" +// SDL_GPU Data + // Reusable buffers used for rendering 1 current in-flight frame, for ImGui_ImplSDLGPU3_RenderDrawData() struct ImGui_ImplSDLGPU3_FrameData { @@ -37,10 +42,9 @@ struct ImGui_ImplSDLGPU3_FrameData uint32_t IndexBufferSize = 0; }; -// SDL_GPU Data struct ImGui_ImplSDLGPU3_Data { - ImGui_ImplSDLGPU3_InitInfo GPUInitInfo; + ImGui_ImplSDLGPU3_InitInfo InitInfo; // Graphics pipeline & shaders SDL_GPUShader* VertexShader = nullptr; @@ -57,8 +61,6 @@ struct ImGui_ImplSDLGPU3_Data }; // Forward Declarations -static bool ImGui_ImplSDLGPU3_CreateDeviceObjects(); -static void ImGui_ImplSDLGPU3_DestroyDeviceObjects(); static void ImGui_ImplSDLGPU3_DestroyFrameData(); //----------------------------------------------------------------------------- @@ -89,8 +91,8 @@ static void ImGui_ImplSDLGPU3_SetupRenderState(ImDrawData* draw_data, SDL_GPUGra SDL_GPUBufferBinding index_buffer_binding = {}; index_buffer_binding.buffer = fd->IndexBuffer; index_buffer_binding.offset = 0; - SDL_BindGPUVertexBuffers(render_pass,0,&vertex_buffer_binding,1); - SDL_BindGPUIndexBuffer(render_pass,&index_buffer_binding,sizeof(ImDrawIdx) == 2 ? SDL_GPU_INDEXELEMENTSIZE_16BIT : SDL_GPU_INDEXELEMENTSIZE_32BIT); + SDL_BindGPUVertexBuffers(render_pass,0, &vertex_buffer_binding, 1); + SDL_BindGPUIndexBuffer(render_pass, &index_buffer_binding, sizeof(ImDrawIdx) == 2 ? SDL_GPU_INDEXELEMENTSIZE_16BIT : SDL_GPU_INDEXELEMENTSIZE_32BIT); } // Setup viewport @@ -101,7 +103,7 @@ static void ImGui_ImplSDLGPU3_SetupRenderState(ImDrawData* draw_data, SDL_GPUGra viewport.h = (float)fb_height; viewport.min_depth = 0.0f; viewport.max_depth = 1.0f; - SDL_SetGPUViewport(render_pass,&viewport); + SDL_SetGPUViewport(render_pass, &viewport); // Setup scale and translation // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. @@ -116,16 +118,17 @@ static void ImGui_ImplSDLGPU3_SetupRenderState(ImDrawData* draw_data, SDL_GPUGra static void CreateOrResizeBuffer(SDL_GPUBuffer** buffer, uint32_t* old_size, uint32_t new_size, SDL_GPUBufferUsageFlags usage) { ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); - ImGui_ImplSDLGPU3_InitInfo* v = &bd->GPUInitInfo; + ImGui_ImplSDLGPU3_InitInfo* v = &bd->InitInfo; - SDL_WaitForGPUIdle(v->GpuDevice); - SDL_ReleaseGPUBuffer(v->GpuDevice, *buffer); + // Even though this is fairly rarely called. + SDL_WaitForGPUIdle(v->Device); + SDL_ReleaseGPUBuffer(v->Device, *buffer); SDL_GPUBufferCreateInfo buffer_info = {}; buffer_info.usage = usage; buffer_info.size = new_size; buffer_info.props = 0; - *buffer = SDL_CreateGPUBuffer(v->GpuDevice, &buffer_info); + *buffer = SDL_CreateGPUBuffer(v->Device, &buffer_info); *old_size = new_size; IM_ASSERT(*buffer != nullptr && "Failed to create GPU Buffer, call SDL_GetError() for more information"); } @@ -142,7 +145,7 @@ void Imgui_ImplSDLGPU3_PrepareDrawData(ImDrawData* draw_data, SDL_GPUCommandBuff return; ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); - ImGui_ImplSDLGPU3_InitInfo* v = &bd->GPUInitInfo; + ImGui_ImplSDLGPU3_InitInfo* v = &bd->InitInfo; ImGui_ImplSDLGPU3_FrameData* fd = &bd->MainWindowFrameData; uint32_t vertex_size = draw_data->TotalVtxCount * sizeof(ImDrawVert); @@ -160,13 +163,13 @@ void Imgui_ImplSDLGPU3_PrepareDrawData(ImDrawData* draw_data, SDL_GPUCommandBuff index_transferbuffer_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; index_transferbuffer_info.size = index_size; - SDL_GPUTransferBuffer* vertex_transferbuffer = SDL_CreateGPUTransferBuffer(v->GpuDevice, &vertex_transferbuffer_info); + SDL_GPUTransferBuffer* vertex_transferbuffer = SDL_CreateGPUTransferBuffer(v->Device, &vertex_transferbuffer_info); IM_ASSERT(vertex_transferbuffer != nullptr && "Failed to create the vertex transfer buffer, call SDL_GetError() for more information"); - SDL_GPUTransferBuffer* index_transferbuffer = SDL_CreateGPUTransferBuffer(v->GpuDevice, &index_transferbuffer_info); + SDL_GPUTransferBuffer* index_transferbuffer = SDL_CreateGPUTransferBuffer(v->Device, &index_transferbuffer_info); IM_ASSERT(index_transferbuffer != nullptr && "Failed to create the index transfer buffer, call SDL_GetError() for more information"); - ImDrawVert* vtx_dst = (ImDrawVert*)SDL_MapGPUTransferBuffer(v->GpuDevice, vertex_transferbuffer, true); - ImDrawIdx* idx_dst = (ImDrawIdx*)SDL_MapGPUTransferBuffer(v->GpuDevice, index_transferbuffer, true); + ImDrawVert* vtx_dst = (ImDrawVert*)SDL_MapGPUTransferBuffer(v->Device, vertex_transferbuffer, true); + ImDrawIdx* idx_dst = (ImDrawIdx*)SDL_MapGPUTransferBuffer(v->Device, index_transferbuffer, true); for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* draw_list = draw_data->CmdLists[n]; @@ -175,8 +178,8 @@ void Imgui_ImplSDLGPU3_PrepareDrawData(ImDrawData* draw_data, SDL_GPUCommandBuff vtx_dst += draw_list->VtxBuffer.Size; idx_dst += draw_list->IdxBuffer.Size; } - SDL_UnmapGPUTransferBuffer(v->GpuDevice, vertex_transferbuffer); - SDL_UnmapGPUTransferBuffer(v->GpuDevice, index_transferbuffer); + SDL_UnmapGPUTransferBuffer(v->Device, vertex_transferbuffer); + SDL_UnmapGPUTransferBuffer(v->Device, index_transferbuffer); SDL_GPUTransferBufferLocation vertex_buffer_location = {}; vertex_buffer_location.offset = 0; @@ -196,11 +199,11 @@ void Imgui_ImplSDLGPU3_PrepareDrawData(ImDrawData* draw_data, SDL_GPUCommandBuff index_buffer_region.size = index_size; SDL_GPUCopyPass* copy_pass = SDL_BeginGPUCopyPass(command_buffer); - SDL_UploadToGPUBuffer(copy_pass, &vertex_buffer_location, &vertex_buffer_region,true); - SDL_UploadToGPUBuffer(copy_pass, &index_buffer_location, &index_buffer_region,true); + SDL_UploadToGPUBuffer(copy_pass, &vertex_buffer_location, &vertex_buffer_region, true); + SDL_UploadToGPUBuffer(copy_pass, &index_buffer_location, &index_buffer_region, true); SDL_EndGPUCopyPass(copy_pass); - SDL_ReleaseGPUTransferBuffer(v->GpuDevice, index_transferbuffer); - SDL_ReleaseGPUTransferBuffer(v->GpuDevice, vertex_transferbuffer); + SDL_ReleaseGPUTransferBuffer(v->Device, index_transferbuffer); + SDL_ReleaseGPUTransferBuffer(v->Device, vertex_transferbuffer); } void ImGui_ImplSDLGPU3_RenderDrawData(ImDrawData* draw_data, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass, SDL_GPUGraphicsPipeline* pipeline) @@ -278,16 +281,16 @@ void ImGui_ImplSDLGPU3_RenderDrawData(ImDrawData* draw_data, SDL_GPUCommandBuffe SDL_SetGPUScissor(render_pass, &scissor_rect); } -bool ImGui_ImplSDLGPU3_CreateFontsTexture() +void ImGui_ImplSDLGPU3_CreateFontsTexture() { ImGuiIO& io = ImGui::GetIO(); ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); - ImGui_ImplSDLGPU3_InitInfo* v = &bd->GPUInitInfo; + ImGui_ImplSDLGPU3_InitInfo* v = &bd->InitInfo; // Destroy existing texture (if any) if (bd->FontTexture) { - SDL_WaitForGPUIdle(v->GpuDevice); + SDL_WaitForGPUIdle(v->Device); ImGui_ImplSDLGPU3_DestroyFontsTexture(); } @@ -308,7 +311,7 @@ bool ImGui_ImplSDLGPU3_CreateFontsTexture() texture_info.num_levels = 1; texture_info.sample_count = SDL_GPU_SAMPLECOUNT_1; - bd->FontTexture = SDL_CreateGPUTexture(v->GpuDevice, &texture_info); + bd->FontTexture = SDL_CreateGPUTexture(v->Device, &texture_info); IM_ASSERT(bd->FontTexture && "Failed to create font texture, call SDL_GetError() for more info"); } @@ -317,39 +320,37 @@ bool ImGui_ImplSDLGPU3_CreateFontsTexture() // Create all the upload structures and upload: { - SDL_GPUTransferBufferCreateInfo font_transferbuffer_info = {}; - font_transferbuffer_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; - font_transferbuffer_info.size = upload_size; + SDL_GPUTransferBufferCreateInfo transferbuffer_info = {}; + transferbuffer_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; + transferbuffer_info.size = upload_size; - SDL_GPUTransferBuffer* font_transferbuffer = SDL_CreateGPUTransferBuffer(v->GpuDevice, &font_transferbuffer_info); - IM_ASSERT(font_transferbuffer != nullptr && "Failed to create font transfer buffer, call SDL_GetError() for more information"); + SDL_GPUTransferBuffer* transferbuffer = SDL_CreateGPUTransferBuffer(v->Device, &transferbuffer_info); + IM_ASSERT(transferbuffer != nullptr && "Failed to create font transfer buffer, call SDL_GetError() for more information"); - void* texture_ptr = SDL_MapGPUTransferBuffer(v->GpuDevice, font_transferbuffer, false); + void* texture_ptr = SDL_MapGPUTransferBuffer(v->Device, transferbuffer, false); memcpy(texture_ptr, pixels, upload_size); - SDL_UnmapGPUTransferBuffer(v->GpuDevice, font_transferbuffer); + SDL_UnmapGPUTransferBuffer(v->Device, transferbuffer); - SDL_GPUTextureTransferInfo font_transfer_info = {}; - font_transfer_info.offset = 0; - font_transfer_info.transfer_buffer = font_transferbuffer; + SDL_GPUTextureTransferInfo transfer_info = {}; + transfer_info.offset = 0; + transfer_info.transfer_buffer = transferbuffer; - SDL_GPUTextureRegion font_texture_region = {}; - font_texture_region.texture = bd->FontTexture; - font_texture_region.w = width; - font_texture_region.h = height; - font_texture_region.d = 1; + SDL_GPUTextureRegion texture_region = {}; + texture_region.texture = bd->FontTexture; + texture_region.w = width; + texture_region.h = height; + texture_region.d = 1; - SDL_GPUCommandBuffer* cmd = SDL_AcquireGPUCommandBuffer(v->GpuDevice); + SDL_GPUCommandBuffer* cmd = SDL_AcquireGPUCommandBuffer(v->Device); SDL_GPUCopyPass* copy_pass = SDL_BeginGPUCopyPass(cmd); - SDL_UploadToGPUTexture(copy_pass, &font_transfer_info, &font_texture_region, false); + SDL_UploadToGPUTexture(copy_pass, &transfer_info, &texture_region, false); SDL_EndGPUCopyPass(copy_pass); SDL_SubmitGPUCommandBuffer(cmd); - SDL_ReleaseGPUTransferBuffer(v->GpuDevice, font_transferbuffer); + SDL_ReleaseGPUTransferBuffer(v->Device, transferbuffer); } // Store our identifier io.Fonts->SetTexID((ImTextureID)&bd->FontBinding); - - return true; } // You probably never need to call this, as it is called by ImGui_ImplSDLGPU3_CreateFontsTexture() and ImGui_ImplSDLGPU3_Shutdown(). @@ -357,10 +358,10 @@ void ImGui_ImplSDLGPU3_DestroyFontsTexture() { ImGuiIO& io = ImGui::GetIO(); ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); - ImGui_ImplSDLGPU3_InitInfo* v = &bd->GPUInitInfo; + ImGui_ImplSDLGPU3_InitInfo* v = &bd->InitInfo; if (bd->FontTexture) { - SDL_ReleaseGPUTexture(v->GpuDevice, bd->FontTexture); + SDL_ReleaseGPUTexture(v->Device, bd->FontTexture); bd->FontBinding.texture = nullptr; bd->FontTexture = nullptr; } @@ -371,9 +372,9 @@ static void Imgui_ImplSDLGPU3_CreateShaders() { // Create the shader modules ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); - ImGui_ImplSDLGPU3_InitInfo* v = &bd->GPUInitInfo; + ImGui_ImplSDLGPU3_InitInfo* v = &bd->InitInfo; - const char* driver = SDL_GetGPUDeviceDriver(v->GpuDevice); + const char* driver = SDL_GetGPUDeviceDriver(v->Device); SDL_GPUShaderCreateInfo vertex_shader_info = {}; vertex_shader_info.entrypoint = "main"; @@ -422,8 +423,8 @@ static void Imgui_ImplSDLGPU3_CreateShaders() fragment_shader_info.code_size = sizeof(metallib_fragment); } #endif - bd->VertexShader = SDL_CreateGPUShader(v->GpuDevice, &vertex_shader_info); - bd->FragmentShader = SDL_CreateGPUShader(v->GpuDevice, &fragment_shader_info); + bd->VertexShader = SDL_CreateGPUShader(v->Device, &vertex_shader_info); + bd->FragmentShader = SDL_CreateGPUShader(v->Device, &fragment_shader_info); IM_ASSERT(bd->VertexShader != nullptr && "Failed to create vertex shader, call SDL_GetError() for more information"); IM_ASSERT(bd->FragmentShader != nullptr && "Failed to create fragment shader, call SDL_GetError() for more information"); } @@ -431,7 +432,7 @@ static void Imgui_ImplSDLGPU3_CreateShaders() static void ImGui_ImplSDLGPU3_CreateGraphicsPipeline() { ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); - ImGui_ImplSDLGPU3_InitInfo* v = &bd->GPUInitInfo; + ImGui_ImplSDLGPU3_InitInfo* v = &bd->InitInfo; Imgui_ImplSDLGPU3_CreateShaders(); SDL_GPUVertexBufferDescription vertex_buffer_desc[1]; @@ -507,14 +508,14 @@ static void ImGui_ImplSDLGPU3_CreateGraphicsPipeline() pipeline_info.depth_stencil_state = depth_stencil_state; pipeline_info.target_info = target_info; - bd->Pipeline = SDL_CreateGPUGraphicsPipeline(v->GpuDevice, &pipeline_info); + bd->Pipeline = SDL_CreateGPUGraphicsPipeline(v->Device, &pipeline_info); IM_ASSERT(bd->Pipeline != nullptr && "Failed to create graphics pipeline, call SDL_GetError() for more information"); } -bool ImGui_ImplSDLGPU3_CreateDeviceObjects() +void ImGui_ImplSDLGPU3_CreateDeviceObjects() { ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); - ImGui_ImplSDLGPU3_InitInfo* v = &bd->GPUInitInfo; + ImGui_ImplSDLGPU3_InitInfo* v = &bd->InitInfo; if (!bd->FontSampler) { @@ -529,27 +530,26 @@ bool ImGui_ImplSDLGPU3_CreateDeviceObjects() sampler_info.mip_lod_bias = 0.0f; sampler_info.min_lod = -1000.0f; sampler_info.max_lod = 1000.0f; - sampler_info.enable_anisotropy = true; + sampler_info.enable_anisotropy = false; sampler_info.max_anisotropy = 1.0f; sampler_info.enable_compare = false; - bd->FontSampler = SDL_CreateGPUSampler(v->GpuDevice, &sampler_info); + bd->FontSampler = SDL_CreateGPUSampler(v->Device, &sampler_info); bd->FontBinding.sampler = bd->FontSampler; IM_ASSERT(bd->FontSampler != nullptr && "Failed to create font sampler, call SDL_GetError() for more information"); } ImGui_ImplSDLGPU3_CreateGraphicsPipeline(); - - return true; + ImGui_ImplSDLGPU3_CreateFontsTexture(); } void ImGui_ImplSDLGPU3_DestroyFrameData() { ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); - ImGui_ImplSDLGPU3_InitInfo* v = &bd->GPUInitInfo; + ImGui_ImplSDLGPU3_InitInfo* v = &bd->InitInfo; - SDL_ReleaseGPUBuffer(v->GpuDevice, bd->MainWindowFrameData.VertexBuffer); - SDL_ReleaseGPUBuffer(v->GpuDevice, bd->MainWindowFrameData.IndexBuffer); + SDL_ReleaseGPUBuffer(v->Device, bd->MainWindowFrameData.VertexBuffer); + SDL_ReleaseGPUBuffer(v->Device, bd->MainWindowFrameData.IndexBuffer); bd->MainWindowFrameData.VertexBuffer = nullptr; bd->MainWindowFrameData.IndexBuffer = nullptr; bd->MainWindowFrameData.VertexBufferSize = 0; @@ -559,15 +559,15 @@ void ImGui_ImplSDLGPU3_DestroyFrameData() void ImGui_ImplSDLGPU3_DestroyDeviceObjects() { ImGui_ImplSDLGPU3_Data* bd = ImGui_ImplSDLGPU3_GetBackendData(); - ImGui_ImplSDLGPU3_InitInfo* v = &bd->GPUInitInfo; + ImGui_ImplSDLGPU3_InitInfo* v = &bd->InitInfo; ImGui_ImplSDLGPU3_DestroyFrameData(); ImGui_ImplSDLGPU3_DestroyFontsTexture(); - if (bd->VertexShader) { SDL_ReleaseGPUShader(v->GpuDevice, bd->VertexShader); bd->VertexShader = nullptr;} - if (bd->FragmentShader) { SDL_ReleaseGPUShader(v->GpuDevice, bd->FragmentShader); bd->FragmentShader = nullptr;} - if (bd->FontSampler) { SDL_ReleaseGPUSampler(v->GpuDevice, bd->FontSampler); bd->FontSampler = nullptr;} - if (bd->Pipeline) { SDL_ReleaseGPUGraphicsPipeline(v->GpuDevice, bd->Pipeline); bd->Pipeline = nullptr;} + if (bd->VertexShader) { SDL_ReleaseGPUShader(v->Device, bd->VertexShader); bd->VertexShader = nullptr;} + if (bd->FragmentShader) { SDL_ReleaseGPUShader(v->Device, bd->FragmentShader); bd->FragmentShader = nullptr;} + if (bd->FontSampler) { SDL_ReleaseGPUSampler(v->Device, bd->FontSampler); bd->FontSampler = nullptr;} + if (bd->Pipeline) { SDL_ReleaseGPUGraphicsPipeline(v->Device, bd->Pipeline); bd->Pipeline = nullptr;} } bool ImGui_ImplSDLGPU3_Init(ImGui_ImplSDLGPU3_InitInfo* info) @@ -582,10 +582,10 @@ bool ImGui_ImplSDLGPU3_Init(ImGui_ImplSDLGPU3_InitInfo* info) io.BackendRendererName = "imgui_impl_sdlgpu3"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. - IM_ASSERT(info->GpuDevice != nullptr); + IM_ASSERT(info->Device != nullptr); IM_ASSERT(info->ColorTargetFormat != SDL_GPU_TEXTUREFORMAT_INVALID); - bd->GPUInitInfo = *info; + bd->InitInfo = *info; ImGui_ImplSDLGPU3_CreateDeviceObjects(); diff --git a/neo/libs/imgui/backends/imgui_impl_sdlgpu3.h b/neo/libs/imgui/backends/imgui_impl_sdlgpu3.h index ff9c751c8..f46ba25bf 100644 --- a/neo/libs/imgui/backends/imgui_impl_sdlgpu3.h +++ b/neo/libs/imgui/backends/imgui_impl_sdlgpu3.h @@ -4,6 +4,8 @@ // Implemented features: // [X] Renderer: User texture binding. Use simply cast a reference to your SDL_GPUTextureSamplerBinding to ImTextureID. // [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. +// Missing features: +// [ ] Renderer: Multi-viewport support (multiple windows). // The aim of imgui_impl_sdlgpu3.h/.cpp is to be usable in your engine without any modification. // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ @@ -29,18 +31,21 @@ // - Remember to set ColorTargetFormat to the correct format. If you're rendering to the swapchain, call SDL_GetGPUSwapchainTextureFormat to query the right value struct ImGui_ImplSDLGPU3_InitInfo { - SDL_GPUDevice* GpuDevice = nullptr; + SDL_GPUDevice* Device = nullptr; SDL_GPUTextureFormat ColorTargetFormat = SDL_GPU_TEXTUREFORMAT_INVALID; SDL_GPUSampleCount MSAASamples = SDL_GPU_SAMPLECOUNT_1; }; // Follow "Getting Started" link and check examples/ folder to learn about using backends! -IMGUI_IMPL_API bool ImGui_ImplSDLGPU3_Init(ImGui_ImplSDLGPU3_InitInfo* info); -IMGUI_IMPL_API void ImGui_ImplSDLGPU3_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplSDLGPU3_NewFrame(); -IMGUI_IMPL_API void Imgui_ImplSDLGPU3_PrepareDrawData(ImDrawData* draw_data, SDL_GPUCommandBuffer* command_buffer); -IMGUI_IMPL_API void ImGui_ImplSDLGPU3_RenderDrawData(ImDrawData* draw_data, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass, SDL_GPUGraphicsPipeline* pipeline = nullptr); -IMGUI_IMPL_API bool ImGui_ImplSDLGPU3_CreateFontsTexture(); -IMGUI_IMPL_API void ImGui_ImplSDLGPU3_DestroyFontsTexture(); +IMGUI_IMPL_API bool ImGui_ImplSDLGPU3_Init(ImGui_ImplSDLGPU3_InitInfo* info); +IMGUI_IMPL_API void ImGui_ImplSDLGPU3_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplSDLGPU3_NewFrame(); +IMGUI_IMPL_API void Imgui_ImplSDLGPU3_PrepareDrawData(ImDrawData* draw_data, SDL_GPUCommandBuffer* command_buffer); +IMGUI_IMPL_API void ImGui_ImplSDLGPU3_RenderDrawData(ImDrawData* draw_data, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass, SDL_GPUGraphicsPipeline* pipeline = nullptr); + +IMGUI_IMPL_API void ImGui_ImplSDLGPU3_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplSDLGPU3_DestroyDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplSDLGPU3_CreateFontsTexture(); +IMGUI_IMPL_API void ImGui_ImplSDLGPU3_DestroyFontsTexture(); #endif // #ifndef IMGUI_DISABLE diff --git a/neo/libs/imgui/backends/imgui_impl_sdlrenderer2.cpp b/neo/libs/imgui/backends/imgui_impl_sdlrenderer2.cpp index 6d0ee564f..ef5f8584d 100644 --- a/neo/libs/imgui/backends/imgui_impl_sdlrenderer2.cpp +++ b/neo/libs/imgui/backends/imgui_impl_sdlrenderer2.cpp @@ -1,16 +1,20 @@ // dear imgui: Renderer Backend for SDL_Renderer for SDL2 // (Requires: SDL 2.0.17+) -// Note how SDL_Renderer is an _optional_ component of SDL2. -// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. -// If your application will want to render any non trivial amount of graphics other than UI, -// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and -// it might be difficult to step out of those boundaries. +// Note that SDL_Renderer is an _optional_ component of SDL2, which IMHO is now largely obsolete. +// For a multi-platform app consider using other technologies: +// - SDL3+SDL_GPU: SDL_GPU is SDL3 new graphics abstraction API. You will need to update to SDL3. +// - SDL2+DirectX, SDL2+OpenGL, SDL2+Vulkan: combine SDL with dedicated renderers. +// If your application wants to render any non trivial amount of graphics other than UI, +// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user +// and it might be difficult to step out of those boundaries. // Implemented features: // [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// Missing features: +// [ ] Renderer: Multi-viewport support (multiple windows). // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -21,6 +25,7 @@ // - Introduction, links and more at the top of imgui.cpp // CHANGELOG +// 2025-01-18: Use endian-dependent RGBA32 texture format, to match SDL_Color. // 2024-10-09: Expose selected render state in ImGui_ImplSDLRenderer2_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks. // 2024-05-14: *BREAKING CHANGE* ImGui_ImplSDLRenderer3_RenderDrawData() requires SDL_Renderer* passed as parameter. // 2023-05-30: Renamed imgui_impl_sdlrenderer.h/.cpp to imgui_impl_sdlrenderer2.h/.cpp to accommodate for upcoming SDL3. @@ -228,7 +233,7 @@ bool ImGui_ImplSDLRenderer2_CreateFontsTexture() // Upload texture to graphics system // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) - bd->FontTexture = SDL_CreateTexture(bd->Renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, height); + bd->FontTexture = SDL_CreateTexture(bd->Renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, width, height); if (bd->FontTexture == nullptr) { SDL_Log("error creating texture"); diff --git a/neo/libs/imgui/backends/imgui_impl_sdlrenderer2.h b/neo/libs/imgui/backends/imgui_impl_sdlrenderer2.h index 7aed18d21..f5b8172a4 100644 --- a/neo/libs/imgui/backends/imgui_impl_sdlrenderer2.h +++ b/neo/libs/imgui/backends/imgui_impl_sdlrenderer2.h @@ -1,16 +1,20 @@ // dear imgui: Renderer Backend for SDL_Renderer for SDL2 // (Requires: SDL 2.0.17+) -// Note how SDL_Renderer is an _optional_ component of SDL2. -// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. -// If your application will want to render any non trivial amount of graphics other than UI, -// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and -// it might be difficult to step out of those boundaries. +// Note that SDL_Renderer is an _optional_ component of SDL2, which IMHO is now largely obsolete. +// For a multi-platform app consider using other technologies: +// - SDL3+SDL_GPU: SDL_GPU is SDL3 new graphics abstraction API. You will need to update to SDL3. +// - SDL2+DirectX, SDL2+OpenGL, SDL2+Vulkan: combine SDL with dedicated renderers. +// If your application wants to render any non trivial amount of graphics other than UI, +// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user +// and it might be difficult to step out of those boundaries. // Implemented features: // [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// Missing features: +// [ ] Renderer: Multi-viewport support (multiple windows). // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/neo/libs/imgui/backends/imgui_impl_sdlrenderer3.cpp b/neo/libs/imgui/backends/imgui_impl_sdlrenderer3.cpp index 1bcf7a228..d76eebea5 100644 --- a/neo/libs/imgui/backends/imgui_impl_sdlrenderer3.cpp +++ b/neo/libs/imgui/backends/imgui_impl_sdlrenderer3.cpp @@ -1,18 +1,20 @@ // dear imgui: Renderer Backend for SDL_Renderer for SDL3 -// (Requires: SDL 3.0.0+) +// (Requires: SDL 3.1.8+) -// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) - -// Note how SDL_Renderer is an _optional_ component of SDL3. -// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. -// If your application will want to render any non trivial amount of graphics other than UI, -// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and -// it might be difficult to step out of those boundaries. +// Note that SDL_Renderer is an _optional_ component of SDL3, which IMHO is now largely obsolete. +// For a multi-platform app consider using other technologies: +// - SDL3+SDL_GPU: SDL_GPU is SDL3 new graphics abstraction API. +// - SDL3+DirectX, SDL3+OpenGL, SDL3+Vulkan: combine SDL with dedicated renderers. +// If your application wants to render any non trivial amount of graphics other than UI, +// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user +// and it might be difficult to step out of those boundaries. // Implemented features: // [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// Missing features: +// [ ] Renderer: Multi-viewport support (multiple windows). // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -23,6 +25,7 @@ // - Introduction, links and more at the top of imgui.cpp // CHANGELOG +// 2025-01-18: Use endian-dependent RGBA32 texture format, to match SDL_Color. // 2024-10-09: Expose selected render state in ImGui_ImplSDLRenderer3_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks. // 2024-07-01: Update for SDL3 api changes: SDL_RenderGeometryRaw() uint32 version was removed (SDL#9009). // 2024-05-14: *BREAKING CHANGE* ImGui_ImplSDLRenderer3_RenderDrawData() requires SDL_Renderer* passed as parameter. @@ -247,7 +250,7 @@ bool ImGui_ImplSDLRenderer3_CreateFontsTexture() // Upload texture to graphics system // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) - bd->FontTexture = SDL_CreateTexture(bd->Renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, height); + bd->FontTexture = SDL_CreateTexture(bd->Renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, width, height); if (bd->FontTexture == nullptr) { SDL_Log("error creating texture"); diff --git a/neo/libs/imgui/backends/imgui_impl_sdlrenderer3.h b/neo/libs/imgui/backends/imgui_impl_sdlrenderer3.h index 3a7a51ee5..f79aa1bc4 100644 --- a/neo/libs/imgui/backends/imgui_impl_sdlrenderer3.h +++ b/neo/libs/imgui/backends/imgui_impl_sdlrenderer3.h @@ -1,18 +1,20 @@ // dear imgui: Renderer Backend for SDL_Renderer for SDL3 -// (Requires: SDL 3.0.0+) +// (Requires: SDL 3.1.8+) -// (**IMPORTANT: SDL 3.0.0 is NOT YET RELEASED AND CURRENTLY HAS A FAST CHANGING API. THIS CODE BREAKS OFTEN AS SDL3 CHANGES.**) - -// Note how SDL_Renderer is an _optional_ component of SDL3. -// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX. -// If your application will want to render any non trivial amount of graphics other than UI, -// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and -// it might be difficult to step out of those boundaries. +// Note that SDL_Renderer is an _optional_ component of SDL3, which IMHO is now largely obsolete. +// For a multi-platform app consider using other technologies: +// - SDL3+SDL_GPU: SDL_GPU is SDL3 new graphics abstraction API. +// - SDL3+DirectX, SDL3+OpenGL, SDL3+Vulkan: combine SDL with dedicated renderers. +// If your application wants to render any non trivial amount of graphics other than UI, +// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user +// and it might be difficult to step out of those boundaries. // Implemented features: // [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// Missing features: +// [ ] Renderer: Multi-viewport support (multiple windows). // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/neo/libs/imgui/backends/imgui_impl_vulkan.cpp b/neo/libs/imgui/backends/imgui_impl_vulkan.cpp index 940ea2d79..03e1f8de5 100644 --- a/neo/libs/imgui/backends/imgui_impl_vulkan.cpp +++ b/neo/libs/imgui/backends/imgui_impl_vulkan.cpp @@ -5,6 +5,7 @@ // [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Call ImGui_ImplVulkan_AddTexture() to register one. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions. // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// [x] Renderer: Multi-viewport / platform windows. With issues (flickering when creating a new viewport). // The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification. // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ @@ -26,6 +27,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2025-01-09: Vulkan: Added IMGUI_IMPL_VULKAN_MINIMUM_IMAGE_SAMPLER_POOL_SIZE to clarify how many image sampler descriptors are expected to be available in descriptor pool. (#6642) // 2025-01-06: Vulkan: Added more ImGui_ImplVulkanH_XXXX helper functions to simplify our examples. // 2024-12-11: Vulkan: Fixed setting VkSwapchainCreateInfoKHR::preTransform for platforms not supporting VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR. (#8222) @@ -104,6 +106,7 @@ void ImGui_ImplVulkan_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulka void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulkan_WindowRenderBuffers* buffers, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd, const VkAllocationCallbacks* allocator); +void ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(VkDevice device, const VkAllocationCallbacks* allocator); void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator); @@ -120,15 +123,18 @@ static bool g_FunctionsLoaded = true; IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateCommandBuffers) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateDescriptorSets) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkAcquireNextImageKHR) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkBeginCommandBuffer) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkBindBufferMemory) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkBindImageMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBeginRenderPass) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindDescriptorSets) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindIndexBuffer) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindPipeline) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindVertexBuffers) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdCopyBufferToImage) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdDrawIndexed) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdEndRenderPass) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdPipelineBarrier) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdPushConstants) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdSetScissor) \ @@ -179,13 +185,17 @@ static bool g_FunctionsLoaded = true; IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceFormatsKHR) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfacePresentModesKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceSupportKHR) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetSwapchainImagesKHR) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkMapMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkQueuePresentKHR) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkQueueSubmit) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkQueueWaitIdle) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkResetCommandPool) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkResetFences) \ IMGUI_VULKAN_FUNC_MAP_MACRO(vkUnmapMemory) \ - IMGUI_VULKAN_FUNC_MAP_MACRO(vkUpdateDescriptorSets) + IMGUI_VULKAN_FUNC_MAP_MACRO(vkUpdateDescriptorSets) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkWaitForFences) // Define function pointers #define IMGUI_VULKAN_FUNC_DEF(func) static PFN_##func func; @@ -216,7 +226,7 @@ struct ImGui_ImplVulkan_WindowRenderBuffers { uint32_t Index; uint32_t Count; - ImGui_ImplVulkan_FrameRenderBuffers* FrameRenderBuffers; + ImVector FrameRenderBuffers; }; struct ImGui_ImplVulkan_Texture @@ -229,6 +239,20 @@ struct ImGui_ImplVulkan_Texture ImGui_ImplVulkan_Texture() { memset((void*)this, 0, sizeof(*this)); } }; +// For multi-viewport support: +// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data. +struct ImGui_ImplVulkan_ViewportData +{ + ImGui_ImplVulkanH_Window Window; // Used by secondary viewports only + ImGui_ImplVulkan_WindowRenderBuffers RenderBuffers; // Used by all viewports + bool WindowOwned; + bool SwapChainNeedRebuild; // Flag when viewport swapchain resized in the middle of processing a frame + bool SwapChainSuboptimal; // Flag when VK_SUBOPTIMAL_KHR was returned. + + ImGui_ImplVulkan_ViewportData() { WindowOwned = SwapChainNeedRebuild = SwapChainSuboptimal = false; memset(&RenderBuffers, 0, sizeof(RenderBuffers)); } + ~ImGui_ImplVulkan_ViewportData() { } +}; + // Vulkan data struct ImGui_ImplVulkan_Data { @@ -237,7 +261,8 @@ struct ImGui_ImplVulkan_Data VkPipelineCreateFlags PipelineCreateFlags; VkDescriptorSetLayout DescriptorSetLayout; VkPipelineLayout PipelineLayout; - VkPipeline Pipeline; + VkPipeline Pipeline; // pipeline for main render pass (created by app) + VkPipeline PipelineForViewports; // pipeline for secondary viewports (created by backend) VkShaderModule ShaderModuleVert; VkShaderModule ShaderModuleFrag; VkDescriptorPool DescriptorPool; @@ -262,6 +287,10 @@ struct ImGui_ImplVulkan_Data // SHADERS //----------------------------------------------------------------------------- +// Forward Declarations +static void ImGui_ImplVulkan_InitMultiViewportSupport(); +static void ImGui_ImplVulkan_ShutdownMultiViewportSupport(); + // backends/vulkan/glsl_shader.vert, compiled with: // # glslangValidator -V -x -o glsl_shader.vert.u32 glsl_shader.vert /* @@ -499,14 +528,16 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm if (pipeline == VK_NULL_HANDLE) pipeline = bd->Pipeline; - // Allocate array to store enough vertex/index buffers - ImGui_ImplVulkan_WindowRenderBuffers* wrb = &bd->MainWindowRenderBuffers; - if (wrb->FrameRenderBuffers == nullptr) + // Allocate array to store enough vertex/index buffers. Each unique viewport gets its own storage. + ImGui_ImplVulkan_ViewportData* viewport_renderer_data = (ImGui_ImplVulkan_ViewportData*)draw_data->OwnerViewport->RendererUserData; + IM_ASSERT(viewport_renderer_data != nullptr); + ImGui_ImplVulkan_WindowRenderBuffers* wrb = &viewport_renderer_data->RenderBuffers; + if (wrb->FrameRenderBuffers.Size == 0) { wrb->Index = 0; wrb->Count = v->ImageCount; - wrb->FrameRenderBuffers = (ImGui_ImplVulkan_FrameRenderBuffers*)IM_ALLOC(sizeof(ImGui_ImplVulkan_FrameRenderBuffers) * wrb->Count); - memset((void*)wrb->FrameRenderBuffers, 0, sizeof(ImGui_ImplVulkan_FrameRenderBuffers) * wrb->Count); + wrb->FrameRenderBuffers.resize(wrb->Count); + memset((void*)wrb->FrameRenderBuffers.Data, 0, wrb->FrameRenderBuffers.size_in_bytes()); } IM_ASSERT(wrb->Count == v->ImageCount); wrb->Index = (wrb->Index + 1) % wrb->Count; @@ -1065,7 +1096,7 @@ void ImGui_ImplVulkan_DestroyDeviceObjects() { ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; - ImGui_ImplVulkan_DestroyWindowRenderBuffers(v->Device, &bd->MainWindowRenderBuffers, v->Allocator); + ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(v->Device, v->Allocator); ImGui_ImplVulkan_DestroyFontsTexture(); if (bd->TexCommandBuffer) { vkFreeCommandBuffers(v->Device, bd->TexCommandPool, 1, &bd->TexCommandBuffer); bd->TexCommandBuffer = VK_NULL_HANDLE; } @@ -1076,9 +1107,19 @@ void ImGui_ImplVulkan_DestroyDeviceObjects() if (bd->DescriptorSetLayout) { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayout, v->Allocator); bd->DescriptorSetLayout = VK_NULL_HANDLE; } if (bd->PipelineLayout) { vkDestroyPipelineLayout(v->Device, bd->PipelineLayout, v->Allocator); bd->PipelineLayout = VK_NULL_HANDLE; } if (bd->Pipeline) { vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); bd->Pipeline = VK_NULL_HANDLE; } + if (bd->PipelineForViewports) { vkDestroyPipeline(v->Device, bd->PipelineForViewports, v->Allocator); bd->PipelineForViewports = VK_NULL_HANDLE; } if (bd->DescriptorPool) { vkDestroyDescriptorPool(v->Device, bd->DescriptorPool, v->Allocator); bd->DescriptorPool = VK_NULL_HANDLE; } } +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING +static void ImGui_ImplVulkan_LoadDynamicRenderingFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data) +{ + // Manually load those two (see #5446) + ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR = reinterpret_cast(loader_func("vkCmdBeginRenderingKHR", user_data)); + ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR = reinterpret_cast(loader_func("vkCmdEndRenderingKHR", user_data)); +} +#endif + bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data) { // Load function pointers @@ -1094,9 +1135,7 @@ bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const ch #undef IMGUI_VULKAN_FUNC_LOAD #ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING - // Manually load those two (see #5446) - ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR = reinterpret_cast(loader_func("vkCmdBeginRenderingKHR", user_data)); - ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR = reinterpret_cast(loader_func("vkCmdEndRenderingKHR", user_data)); + ImGui_ImplVulkan_LoadDynamicRenderingFunctions(loader_func, user_data); #endif #else IM_UNUSED(loader_func); @@ -1115,8 +1154,7 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info) { #ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING #ifndef IMGUI_IMPL_VULKAN_USE_LOADER - ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR = reinterpret_cast(vkGetInstanceProcAddr(info->Instance, "vkCmdBeginRenderingKHR")); - ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR = reinterpret_cast(vkGetInstanceProcAddr(info->Instance, "vkCmdEndRenderingKHR")); + ImGui_ImplVulkan_LoadDynamicRenderingFunctions([](const char* function_name, void* user_data) { return vkGetInstanceProcAddr((VkInstance)user_data, function_name); }, (void*)info->Instance); #endif IM_ASSERT(ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR != nullptr); IM_ASSERT(ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR != nullptr); @@ -1134,6 +1172,7 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info) io.BackendRendererUserData = (void*)bd; io.BackendRendererName = "imgui_impl_vulkan"; io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) IM_ASSERT(info->Instance != VK_NULL_HANDLE); IM_ASSERT(info->PhysicalDevice != VK_NULL_HANDLE); @@ -1152,6 +1191,12 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info) ImGui_ImplVulkan_CreateDeviceObjects(); + // Our render function expect RendererUserData to be storing the window render buffer we need (for the main viewport we won't use ->Window) + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + main_viewport->RendererUserData = IM_NEW(ImGui_ImplVulkan_ViewportData)(); + + ImGui_ImplVulkan_InitMultiViewportSupport(); + return true; } @@ -1161,10 +1206,21 @@ void ImGui_ImplVulkan_Shutdown() IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + // First destroy objects in all viewports ImGui_ImplVulkan_DestroyDeviceObjects(); + + // Manually delete main viewport render data in-case we haven't initialized for viewports + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + if (ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)main_viewport->RendererUserData) + IM_DELETE(vd); + main_viewport->RendererUserData = nullptr; + + // Clean up windows + ImGui_ImplVulkan_ShutdownMultiViewportSupport(); + io.BackendRendererName = nullptr; io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; + io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasViewports); IM_DELETE(bd); } @@ -1184,10 +1240,12 @@ void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) if (bd->VulkanInitInfo.MinImageCount == min_image_count) return; + IM_ASSERT(0); // FIXME-VIEWPORT: Unsupported. Need to recreate all swap chains! ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; VkResult err = vkDeviceWaitIdle(v->Device); check_vk_result(err); - ImGui_ImplVulkan_DestroyWindowRenderBuffers(v->Device, &bd->MainWindowRenderBuffers, v->Allocator); + ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(v->Device, v->Allocator); + bd->VulkanInitInfo.MinImageCount = min_image_count; } @@ -1250,8 +1308,7 @@ void ImGui_ImplVulkan_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulk { for (uint32_t n = 0; n < buffers->Count; n++) ImGui_ImplVulkan_DestroyFrameRenderBuffers(device, &buffers->FrameRenderBuffers[n], allocator); - IM_FREE(buffers->FrameRenderBuffers); - buffers->FrameRenderBuffers = nullptr; + buffers->FrameRenderBuffers.clear(); buffers->Index = 0; buffers->Count = 0; } @@ -1460,15 +1517,11 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator); for (uint32_t i = 0; i < wd->SemaphoreCount; i++) ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator); - IM_FREE(wd->Frames); - IM_FREE(wd->FrameSemaphores); - wd->Frames = nullptr; - wd->FrameSemaphores = nullptr; + wd->Frames.clear(); + wd->FrameSemaphores.clear(); wd->ImageCount = 0; if (wd->RenderPass) vkDestroyRenderPass(device, wd->RenderPass, allocator); - if (wd->Pipeline) - vkDestroyPipeline(device, wd->Pipeline, allocator); // If min image count was not specified, request different count of images dependent on selected present mode if (min_image_count == 0) @@ -1518,12 +1571,11 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->ImageCount, backbuffers); check_vk_result(err); - IM_ASSERT(wd->Frames == nullptr && wd->FrameSemaphores == nullptr); wd->SemaphoreCount = wd->ImageCount + 1; - wd->Frames = (ImGui_ImplVulkanH_Frame*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_Frame) * wd->ImageCount); - wd->FrameSemaphores = (ImGui_ImplVulkanH_FrameSemaphores*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_FrameSemaphores) * wd->SemaphoreCount); - memset((void*)wd->Frames, 0, sizeof(wd->Frames[0]) * wd->ImageCount); - memset((void*)wd->FrameSemaphores, 0, sizeof(wd->FrameSemaphores[0]) * wd->SemaphoreCount); + wd->Frames.resize(wd->ImageCount); + wd->FrameSemaphores.resize(wd->SemaphoreCount); + memset(wd->Frames.Data, 0, wd->Frames.size_in_bytes()); + memset(wd->FrameSemaphores.Data, 0, wd->FrameSemaphores.size_in_bytes()); for (uint32_t i = 0; i < wd->ImageCount; i++) wd->Frames[i].Backbuffer = backbuffers[i]; } @@ -1621,6 +1673,7 @@ void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevic IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); (void)instance; ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count); + //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline, g_VulkanInitInfo.Subpass); ImGui_ImplVulkanH_CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator); } @@ -1633,11 +1686,8 @@ void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator); for (uint32_t i = 0; i < wd->SemaphoreCount; i++) ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator); - IM_FREE(wd->Frames); - IM_FREE(wd->FrameSemaphores); - wd->Frames = nullptr; - wd->FrameSemaphores = nullptr; - vkDestroyPipeline(device, wd->Pipeline, allocator); + wd->Frames.clear(); + wd->FrameSemaphores.clear(); vkDestroyRenderPass(device, wd->RenderPass, allocator); vkDestroySwapchainKHR(device, wd->Swapchain, allocator); vkDestroySurfaceKHR(instance, wd->Surface, allocator); @@ -1665,6 +1715,296 @@ void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH fsd->ImageAcquiredSemaphore = fsd->RenderCompleteSemaphore = VK_NULL_HANDLE; } +void ImGui_ImplVulkanH_DestroyAllViewportsRenderBuffers(VkDevice device, const VkAllocationCallbacks* allocator) +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int n = 0; n < platform_io.Viewports.Size; n++) + if (ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)platform_io.Viewports[n]->RendererUserData) + ImGui_ImplVulkan_DestroyWindowRenderBuffers(device, &vd->RenderBuffers, allocator); +} + +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_ViewportData* vd = IM_NEW(ImGui_ImplVulkan_ViewportData)(); + viewport->RendererUserData = vd; + ImGui_ImplVulkanH_Window* wd = &vd->Window; + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + + // Create surface + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + VkResult err = (VkResult)platform_io.Platform_CreateVkSurface(viewport, (ImU64)v->Instance, (const void*)v->Allocator, (ImU64*)&wd->Surface); + check_vk_result(err); + + // Check for WSI support + VkBool32 res; + vkGetPhysicalDeviceSurfaceSupportKHR(v->PhysicalDevice, v->QueueFamily, wd->Surface, &res); + if (res != VK_TRUE) + { + IM_ASSERT(0); // Error: no WSI support on physical device + return; + } + + // Select Surface Format + ImVector requestSurfaceImageFormats; +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + for (uint32_t n = 0; n < v->PipelineRenderingCreateInfo.colorAttachmentCount; n++) + requestSurfaceImageFormats.push_back(v->PipelineRenderingCreateInfo.pColorAttachmentFormats[n]); +#endif + const VkFormat defaultFormats[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; + for (VkFormat format : defaultFormats) + requestSurfaceImageFormats.push_back(format); + + const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; + wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(v->PhysicalDevice, wd->Surface, requestSurfaceImageFormats.Data, (size_t)requestSurfaceImageFormats.Size, requestSurfaceColorSpace); + + // Select Present Mode + // FIXME-VULKAN: Even thought mailbox seems to get us maximum framerate with a single window, it halves framerate with a second window etc. (w/ Nvidia and SDK 1.82.1) + VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR }; + wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(v->PhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes)); + //printf("[vulkan] Secondary window selected PresentMode = %d\n", wd->PresentMode); + + // Create SwapChain, RenderPass, Framebuffer, etc. + wd->ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; + wd->UseDynamicRendering = v->UseDynamicRendering; + ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, wd, v->QueueFamily, v->Allocator, (int)viewport->Size.x, (int)viewport->Size.y, v->MinImageCount); + vd->WindowOwned = true; + + // Create pipeline (shared by all secondary viewports) + if (bd->PipelineForViewports == VK_NULL_HANDLE) + ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &bd->PipelineForViewports, 0); +} + +static void ImGui_ImplVulkan_DestroyWindow(ImGuiViewport* viewport) +{ + // The main viewport (owned by the application) will always have RendererUserData == 0 since we didn't create the data for it. + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + if (ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData) + { + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + if (vd->WindowOwned) + ImGui_ImplVulkanH_DestroyWindow(v->Instance, v->Device, &vd->Window, v->Allocator); + ImGui_ImplVulkan_DestroyWindowRenderBuffers(v->Device, &vd->RenderBuffers, v->Allocator); + IM_DELETE(vd); + } + viewport->RendererUserData = nullptr; +} + +static void ImGui_ImplVulkan_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData; + if (vd == nullptr) // This is nullptr for the main viewport (which is left to the user/app to handle) + return; + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + vd->Window.ClearEnable = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? false : true; + ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, &vd->Window, v->QueueFamily, v->Allocator, (int)size.x, (int)size.y, v->MinImageCount); +} + +static void ImGui_ImplVulkan_RenderWindow(ImGuiViewport* viewport, void*) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData; + ImGui_ImplVulkanH_Window* wd = &vd->Window; + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + VkResult err; + + if (vd->SwapChainNeedRebuild || vd->SwapChainSuboptimal) + { + ImGui_ImplVulkanH_CreateOrResizeWindow(v->Instance, v->PhysicalDevice, v->Device, wd, v->QueueFamily, v->Allocator, (int)viewport->Size.x, (int)viewport->Size.y, v->MinImageCount); + vd->SwapChainNeedRebuild = vd->SwapChainSuboptimal = false; + } + + ImGui_ImplVulkanH_Frame* fd = nullptr; + ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[wd->SemaphoreIndex]; + { + { + err = vkAcquireNextImageKHR(v->Device, wd->Swapchain, UINT64_MAX, fsd->ImageAcquiredSemaphore, VK_NULL_HANDLE, &wd->FrameIndex); + if (err == VK_ERROR_OUT_OF_DATE_KHR) + { + vd->SwapChainNeedRebuild = true; // Since we are not going to swap this frame anyway, it's ok that recreation happens on next frame. + return; + } + if (err == VK_SUBOPTIMAL_KHR) + vd->SwapChainSuboptimal = true; + else + check_vk_result(err); + fd = &wd->Frames[wd->FrameIndex]; + } + for (;;) + { + err = vkWaitForFences(v->Device, 1, &fd->Fence, VK_TRUE, 100); + if (err == VK_SUCCESS) break; + if (err == VK_TIMEOUT) continue; + check_vk_result(err); + } + { + err = vkResetCommandPool(v->Device, fd->CommandPool, 0); + check_vk_result(err); + VkCommandBufferBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + err = vkBeginCommandBuffer(fd->CommandBuffer, &info); + check_vk_result(err); + } + { + ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); + memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float)); + } +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + if (v->UseDynamicRendering) + { + // Transition swapchain image to a layout suitable for drawing. + VkImageMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + barrier.image = fd->Backbuffer; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(fd->CommandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + + VkRenderingAttachmentInfo attachmentInfo = {}; + attachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR; + attachmentInfo.imageView = fd->BackbufferView; + attachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachmentInfo.resolveMode = VK_RESOLVE_MODE_NONE; + attachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachmentInfo.clearValue = wd->ClearValue; + + VkRenderingInfo renderingInfo = {}; + renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO_KHR; + renderingInfo.renderArea.extent.width = wd->Width; + renderingInfo.renderArea.extent.height = wd->Height; + renderingInfo.layerCount = 1; + renderingInfo.viewMask = 0; + renderingInfo.colorAttachmentCount = 1; + renderingInfo.pColorAttachments = &attachmentInfo; + + ImGuiImplVulkanFuncs_vkCmdBeginRenderingKHR(fd->CommandBuffer, &renderingInfo); + } + else +#endif + { + VkRenderPassBeginInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + info.renderPass = wd->RenderPass; + info.framebuffer = fd->Framebuffer; + info.renderArea.extent.width = wd->Width; + info.renderArea.extent.height = wd->Height; + info.clearValueCount = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? 0 : 1; + info.pClearValues = (viewport->Flags & ImGuiViewportFlags_NoRendererClear) ? nullptr : &wd->ClearValue; + vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE); + } + } + + ImGui_ImplVulkan_RenderDrawData(viewport->DrawData, fd->CommandBuffer, bd->PipelineForViewports); + + { +#ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING + if (v->UseDynamicRendering) + { + ImGuiImplVulkanFuncs_vkCmdEndRenderingKHR(fd->CommandBuffer); + + // Transition image to a layout suitable for presentation + VkImageMemoryBarrier barrier = {}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + barrier.image = fd->Backbuffer; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(fd->CommandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); + } + else +#endif + { + vkCmdEndRenderPass(fd->CommandBuffer); + } + { + VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkSubmitInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + info.waitSemaphoreCount = 1; + info.pWaitSemaphores = &fsd->ImageAcquiredSemaphore; + info.pWaitDstStageMask = &wait_stage; + info.commandBufferCount = 1; + info.pCommandBuffers = &fd->CommandBuffer; + info.signalSemaphoreCount = 1; + info.pSignalSemaphores = &fsd->RenderCompleteSemaphore; + + err = vkEndCommandBuffer(fd->CommandBuffer); + check_vk_result(err); + err = vkResetFences(v->Device, 1, &fd->Fence); + check_vk_result(err); + err = vkQueueSubmit(v->Queue, 1, &info, fd->Fence); + check_vk_result(err); + } + } +} + +static void ImGui_ImplVulkan_SwapBuffers(ImGuiViewport* viewport, void*) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_ViewportData* vd = (ImGui_ImplVulkan_ViewportData*)viewport->RendererUserData; + ImGui_ImplVulkanH_Window* wd = &vd->Window; + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + + if (vd->SwapChainNeedRebuild) // Frame data became invalid in the middle of rendering + return; + + VkResult err; + uint32_t present_index = wd->FrameIndex; + + ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[wd->SemaphoreIndex]; + VkPresentInfoKHR info = {}; + info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + info.waitSemaphoreCount = 1; + info.pWaitSemaphores = &fsd->RenderCompleteSemaphore; + info.swapchainCount = 1; + info.pSwapchains = &wd->Swapchain; + info.pImageIndices = &present_index; + err = vkQueuePresentKHR(v->Queue, &info); + if (err == VK_ERROR_OUT_OF_DATE_KHR) + { + vd->SwapChainNeedRebuild = true; + return; + } + if (err == VK_SUBOPTIMAL_KHR) + vd->SwapChainSuboptimal = true; + else + check_vk_result(err); + wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores +} + +void ImGui_ImplVulkan_InitMultiViewportSupport() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + IM_ASSERT(platform_io.Platform_CreateVkSurface != nullptr && "Platform needs to setup the CreateVkSurface handler."); + platform_io.Renderer_CreateWindow = ImGui_ImplVulkan_CreateWindow; + platform_io.Renderer_DestroyWindow = ImGui_ImplVulkan_DestroyWindow; + platform_io.Renderer_SetWindowSize = ImGui_ImplVulkan_SetWindowSize; + platform_io.Renderer_RenderWindow = ImGui_ImplVulkan_RenderWindow; + platform_io.Renderer_SwapBuffers = ImGui_ImplVulkan_SwapBuffers; +} + +void ImGui_ImplVulkan_ShutdownMultiViewportSupport() +{ + ImGui::DestroyPlatformWindows(); +} + //----------------------------------------------------------------------------- #endif // #ifndef IMGUI_DISABLE diff --git a/neo/libs/imgui/backends/imgui_impl_vulkan.h b/neo/libs/imgui/backends/imgui_impl_vulkan.h index 3bd994864..d90f00311 100644 --- a/neo/libs/imgui/backends/imgui_impl_vulkan.h +++ b/neo/libs/imgui/backends/imgui_impl_vulkan.h @@ -5,6 +5,7 @@ // [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Call ImGui_ImplVulkan_AddTexture() to register one. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions. // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// [x] Renderer: Multi-viewport / platform windows. With issues (flickering when creating a new viewport). // The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification. // IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ @@ -161,8 +162,8 @@ struct ImGui_ImplVulkanH_Frame; struct ImGui_ImplVulkanH_Window; // Helpers -IMGUI_IMPL_API void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wnd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); -IMGUI_IMPL_API void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wnd, const VkAllocationCallbacks* allocator); +IMGUI_IMPL_API void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); +IMGUI_IMPL_API void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator); IMGUI_IMPL_API VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space); IMGUI_IMPL_API VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count); IMGUI_IMPL_API VkPhysicalDevice ImGui_ImplVulkanH_SelectPhysicalDevice(VkInstance instance); @@ -199,7 +200,6 @@ struct ImGui_ImplVulkanH_Window VkSurfaceFormatKHR SurfaceFormat; VkPresentModeKHR PresentMode; VkRenderPass RenderPass; - VkPipeline Pipeline; // The window pipeline may uses a different VkRenderPass than the one passed in ImGui_ImplVulkan_InitInfo bool UseDynamicRendering; bool ClearEnable; VkClearValue ClearValue; @@ -207,8 +207,8 @@ struct ImGui_ImplVulkanH_Window uint32_t ImageCount; // Number of simultaneous in-flight frames (returned by vkGetSwapchainImagesKHR, usually derived from min_image_count) uint32_t SemaphoreCount; // Number of simultaneous in-flight frames + 1, to be able to use it in vkAcquireNextImageKHR uint32_t SemaphoreIndex; // Current set of swapchain wait semaphores we're using (needs to be distinct from per frame data) - ImGui_ImplVulkanH_Frame* Frames; - ImGui_ImplVulkanH_FrameSemaphores* FrameSemaphores; + ImVector Frames; + ImVector FrameSemaphores; ImGui_ImplVulkanH_Window() { diff --git a/neo/libs/imgui/backends/imgui_impl_wgpu.cpp b/neo/libs/imgui/backends/imgui_impl_wgpu.cpp index 2f34c0e0b..e414a1281 100644 --- a/neo/libs/imgui/backends/imgui_impl_wgpu.cpp +++ b/neo/libs/imgui/backends/imgui_impl_wgpu.cpp @@ -6,6 +6,8 @@ // [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// Missing features: +// [ ] Renderer: Multi-viewport support (multiple windows). Not meaningful on the web. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/neo/libs/imgui/backends/imgui_impl_wgpu.h b/neo/libs/imgui/backends/imgui_impl_wgpu.h index 7efb02afe..3262ac5ea 100644 --- a/neo/libs/imgui/backends/imgui_impl_wgpu.h +++ b/neo/libs/imgui/backends/imgui_impl_wgpu.h @@ -13,6 +13,8 @@ // [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID! // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. +// Missing features: +// [ ] Renderer: Multi-viewport support (multiple windows). Not meaningful on the web. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/neo/libs/imgui/backends/imgui_impl_win32.cpp b/neo/libs/imgui/backends/imgui_impl_win32.cpp index f1cf6eccc..3c7f7b398 100644 --- a/neo/libs/imgui/backends/imgui_impl_win32.cpp +++ b/neo/libs/imgui/backends/imgui_impl_win32.cpp @@ -7,6 +7,7 @@ // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values are obsolete since 1.87 and not supported since 1.91.5] // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. @@ -21,6 +22,10 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2024-11-21: [Docking] Fixed a crash when multiple processes are running with multi-viewports, caused by misusage of GetProp(). (#8162, #8069) +// 2024-10-28: [Docking] Rely on property stored inside HWND to retrieve context/viewport, should facilitate attempt to use this for parallel contexts. (#8069) +// 2024-09-16: [Docking] Inputs: fixed an issue where a viewport destroyed while clicking would hog mouse tracking and temporary lead to incorrect update of HoveredWindow. (#7971) // 2024-07-08: Inputs: Fixed ImGuiMod_Super being mapped to VK_APPS instead of VK_LWIN||VK_RWIN. (#7768) // 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys. // 2023-09-25: Inputs: Synthesize key-down event on key-up for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit it (same behavior as GLFW/SDL). @@ -103,6 +108,11 @@ typedef DWORD(WINAPI* PFN_XInputGetState)(DWORD, XINPUT_STATE*); #pragma GCC diagnostic ignored "-Wcast-function-type" // warning: cast between incompatible function types (for loader) #endif +// Forward Declarations +static void ImGui_ImplWin32_InitMultiViewportSupport(bool platform_has_own_dc); +static void ImGui_ImplWin32_ShutdownMultiViewportSupport(); +static void ImGui_ImplWin32_UpdateMonitors(); + struct ImGui_ImplWin32_Data { HWND hWnd; @@ -113,6 +123,7 @@ struct ImGui_ImplWin32_Data INT64 TicksPerSecond; ImGuiMouseCursor LastMouseCursor; UINT32 KeyboardCodePage; + bool WantUpdateMonitors; #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD bool HasGamepad; @@ -167,6 +178,8 @@ static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc) io.BackendPlatformName = "imgui_impl_win32"; io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) + io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional) bd->hWnd = (HWND)hwnd; bd->TicksPerSecond = perf_frequency; @@ -174,10 +187,17 @@ static bool ImGui_ImplWin32_InitEx(void* hwnd, bool platform_has_own_dc) bd->LastMouseCursor = ImGuiMouseCursor_COUNT; ImGui_ImplWin32_UpdateKeyboardCodePage(io); - // Set platform dependent data in viewport + // Update monitor a first time during init + ImGui_ImplWin32_UpdateMonitors(); + + // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (void*)bd->hWnd; - IM_UNUSED(platform_has_own_dc); // Used in 'docking' branch + + // Be aware that GetPropA()/SetPropA() may be accessed from other processes. + // So as we store a pointer in IMGUI_CONTEXT we need to make sure we only call GetPropA() on windows owned by our process. + ::SetPropA(bd->hWnd, "IMGUI_CONTEXT", ImGui::GetCurrentContext()); + ImGui_ImplWin32_InitMultiViewportSupport(platform_has_own_dc); // Dynamically load XInput library #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD @@ -220,6 +240,9 @@ void ImGui_ImplWin32_Shutdown() IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); + ::SetPropA(bd->hWnd, "IMGUI_CONTEXT", nullptr); + ImGui_ImplWin32_ShutdownMultiViewportSupport(); + // Unload XInput library #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD if (bd->XInputDLL) @@ -228,7 +251,7 @@ void ImGui_ImplWin32_Shutdown() io.BackendPlatformName = nullptr; io.BackendPlatformUserData = nullptr; - io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); + io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_PlatformHasViewports | ImGuiBackendFlags_HasMouseHoveredViewport); IM_DELETE(bd); } @@ -298,32 +321,69 @@ static void ImGui_ImplWin32_UpdateKeyModifiers(ImGuiIO& io) io.AddKeyEvent(ImGuiMod_Super, IsVkDown(VK_LWIN) || IsVkDown(VK_RWIN)); } -static void ImGui_ImplWin32_UpdateMouseData(ImGuiIO& io) +static ImGuiViewport* ImGui_ImplWin32_FindViewportByPlatformHandle(ImGuiPlatformIO& platform_io, HWND hwnd) +{ + // We cannot use ImGui::FindViewportByPlatformHandle() because it doesn't take a context. + // When called from ImGui_ImplWin32_WndProcHandler_PlatformWindow() we don't assume that context is bound. + //return ImGui::FindViewportByPlatformHandle((void*)hwnd); + for (ImGuiViewport* viewport : platform_io.Viewports) + if (viewport->PlatformHandle == hwnd) + return viewport; + return nullptr; +} + +// This code supports multi-viewports (multiple OS Windows mapped into different Dear ImGui viewports) +// Because of that, it is a little more complicated than your typical single-viewport binding code! +static void ImGui_ImplWin32_UpdateMouseData(ImGuiIO& io, ImGuiPlatformIO& platform_io) { ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(io); IM_ASSERT(bd->hWnd != 0); + POINT mouse_screen_pos; + bool has_mouse_screen_pos = ::GetCursorPos(&mouse_screen_pos) != 0; + HWND focused_window = ::GetForegroundWindow(); - const bool is_app_focused = (focused_window == bd->hWnd); + const bool is_app_focused = (focused_window && (focused_window == bd->hWnd || ::IsChild(focused_window, bd->hWnd) || ImGui_ImplWin32_FindViewportByPlatformHandle(platform_io, focused_window))); if (is_app_focused) { // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user) + // When multi-viewports are enabled, all Dear ImGui positions are same as OS positions. if (io.WantSetMousePos) { POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; - if (::ClientToScreen(bd->hWnd, &pos)) - ::SetCursorPos(pos.x, pos.y); + if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0) + ::ClientToScreen(focused_window, &pos); + ::SetCursorPos(pos.x, pos.y); } // (Optional) Fallback to provide mouse position when focused (WM_MOUSEMOVE already provides this when hovered or captured) // This also fills a short gap when clicking non-client area: WM_NCMOUSELEAVE -> modal OS move -> gap -> WM_NCMOUSEMOVE - if (!io.WantSetMousePos && bd->MouseTrackedArea == 0) + if (!io.WantSetMousePos && bd->MouseTrackedArea == 0 && has_mouse_screen_pos) { - POINT pos; - if (::GetCursorPos(&pos) && ::ScreenToClient(bd->hWnd, &pos)) - io.AddMousePosEvent((float)pos.x, (float)pos.y); + // Single viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window) + // (This is the position you can get with ::GetCursorPos() + ::ScreenToClient() or WM_MOUSEMOVE.) + // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor) + // (This is the position you can get with ::GetCursorPos() or WM_MOUSEMOVE + ::ClientToScreen(). In theory adding viewport->Pos to a client position would also be the same.) + POINT mouse_pos = mouse_screen_pos; + if (!(io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) + ::ScreenToClient(bd->hWnd, &mouse_pos); + io.AddMousePosEvent((float)mouse_pos.x, (float)mouse_pos.y); } } + + // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering. + // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic. + // - [X] Win32 backend correctly ignore viewports with the _NoInputs flag (here using ::WindowFromPoint with WM_NCHITTEST + HTTRANSPARENT in WndProc does that) + // Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window + // for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported + // by the backend, and use its flawed heuristic to guess the viewport behind. + // - [X] Win32 backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target). + ImGuiID mouse_viewport_id = 0; + if (has_mouse_screen_pos) + if (HWND hovered_hwnd = ::WindowFromPoint(mouse_screen_pos)) + if (ImGuiViewport* viewport = ImGui_ImplWin32_FindViewportByPlatformHandle(platform_io, hovered_hwnd)) + mouse_viewport_id = viewport->ID; + io.AddMouseViewportEvent(mouse_viewport_id); } // Gamepad navigation mapping @@ -384,16 +444,50 @@ static void ImGui_ImplWin32_UpdateGamepads(ImGuiIO& io) #endif } +static BOOL CALLBACK ImGui_ImplWin32_UpdateMonitors_EnumFunc(HMONITOR monitor, HDC, LPRECT, LPARAM) +{ + MONITORINFO info = {}; + info.cbSize = sizeof(MONITORINFO); + if (!::GetMonitorInfo(monitor, &info)) + return TRUE; + ImGuiPlatformMonitor imgui_monitor; + imgui_monitor.MainPos = ImVec2((float)info.rcMonitor.left, (float)info.rcMonitor.top); + imgui_monitor.MainSize = ImVec2((float)(info.rcMonitor.right - info.rcMonitor.left), (float)(info.rcMonitor.bottom - info.rcMonitor.top)); + imgui_monitor.WorkPos = ImVec2((float)info.rcWork.left, (float)info.rcWork.top); + imgui_monitor.WorkSize = ImVec2((float)(info.rcWork.right - info.rcWork.left), (float)(info.rcWork.bottom - info.rcWork.top)); + imgui_monitor.DpiScale = ImGui_ImplWin32_GetDpiScaleForMonitor(monitor); + imgui_monitor.PlatformHandle = (void*)monitor; + if (imgui_monitor.DpiScale <= 0.0f) + return TRUE; // Some accessibility applications are declaring virtual monitors with a DPI of 0, see #7902. + ImGuiPlatformIO& io = ImGui::GetPlatformIO(); + if (info.dwFlags & MONITORINFOF_PRIMARY) + io.Monitors.push_front(imgui_monitor); + else + io.Monitors.push_back(imgui_monitor); + return TRUE; +} + +static void ImGui_ImplWin32_UpdateMonitors() +{ + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + ImGui::GetPlatformIO().Monitors.resize(0); + ::EnumDisplayMonitors(nullptr, nullptr, ImGui_ImplWin32_UpdateMonitors_EnumFunc, 0); + bd->WantUpdateMonitors = false; +} + void ImGui_ImplWin32_NewFrame() { ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized? Did you call ImGui_ImplWin32_Init()?"); ImGuiIO& io = ImGui::GetIO(); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); // Setup display size (every frame to accommodate for window resizing) RECT rect = { 0, 0, 0, 0 }; ::GetClientRect(bd->hWnd, &rect); io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); + if (bd->WantUpdateMonitors) + ImGui_ImplWin32_UpdateMonitors(); // Setup time step INT64 current_time = 0; @@ -402,7 +496,7 @@ void ImGui_ImplWin32_NewFrame() bd->Time = current_time; // Update OS mouse position - ImGui_ImplWin32_UpdateMouseData(io); + ImGui_ImplWin32_UpdateMouseData(io, platform_io); // Process workarounds for known Windows key handling issues ImGui_ImplWin32_ProcessKeyEventsWorkarounds(io); @@ -618,8 +712,11 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandlerEx(HWND hwnd, UINT msg, WPA bd->MouseTrackedArea = area; } POINT mouse_pos = { (LONG)GET_X_LPARAM(lParam), (LONG)GET_Y_LPARAM(lParam) }; - if (msg == WM_NCMOUSEMOVE && ::ScreenToClient(hwnd, &mouse_pos) == FALSE) // WM_NCMOUSEMOVE are provided in absolute coordinates. - return 0; + bool want_absolute_pos = (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) != 0; + if (msg == WM_MOUSEMOVE && want_absolute_pos) // WM_MOUSEMOVE are client-relative coordinates. + ::ClientToScreen(hwnd, &mouse_pos); + if (msg == WM_NCMOUSEMOVE && !want_absolute_pos) // WM_NCMOUSEMOVE are absolute coordinates. + ::ScreenToClient(hwnd, &mouse_pos); io.AddMouseSourceEvent(mouse_source); io.AddMousePosEvent((float)mouse_pos.x, (float)mouse_pos.y); return 0; @@ -765,6 +862,9 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandlerEx(HWND hwnd, UINT msg, WPA bd->WantUpdateHasGamepad = true; #endif return 0; + case WM_DISPLAYCHANGE: + bd->WantUpdateMonitors = true; + return 0; } return 0; } @@ -829,6 +929,10 @@ typedef DPI_AWARENESS_CONTEXT(WINAPI* PFN_SetThreadDpiAwarenessContext)(DPI_AWAR // Helper function to enable DPI awareness without setting up a manifest void ImGui_ImplWin32_EnableDpiAwareness() { + // Make sure monitors will be updated with latest correct scaling + if (ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData()) + bd->WantUpdateMonitors = true; + if (_IsWindows10OrGreater()) { static HINSTANCE user32_dll = ::LoadLibraryA("user32.dll"); // Reference counted per-process @@ -928,6 +1032,381 @@ void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd) } } +//--------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. +// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. +//-------------------------------------------------------------------------------------------------------- + +// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data. +struct ImGui_ImplWin32_ViewportData +{ + HWND Hwnd; + HWND HwndParent; + bool HwndOwned; + DWORD DwStyle; + DWORD DwExStyle; + + ImGui_ImplWin32_ViewportData() { Hwnd = HwndParent = nullptr; HwndOwned = false; DwStyle = DwExStyle = 0; } + ~ImGui_ImplWin32_ViewportData() { IM_ASSERT(Hwnd == nullptr); } +}; + +static void ImGui_ImplWin32_GetWin32StyleFromViewportFlags(ImGuiViewportFlags flags, DWORD* out_style, DWORD* out_ex_style) +{ + if (flags & ImGuiViewportFlags_NoDecoration) + *out_style = WS_POPUP; + else + *out_style = WS_OVERLAPPEDWINDOW; + + if (flags & ImGuiViewportFlags_NoTaskBarIcon) + *out_ex_style = WS_EX_TOOLWINDOW; + else + *out_ex_style = WS_EX_APPWINDOW; + + if (flags & ImGuiViewportFlags_TopMost) + *out_ex_style |= WS_EX_TOPMOST; +} + +static HWND ImGui_ImplWin32_GetHwndFromViewportID(ImGuiID viewport_id) +{ + if (viewport_id != 0) + if (ImGuiViewport* viewport = ImGui::FindViewportByID(viewport_id)) + return (HWND)viewport->PlatformHandle; + return nullptr; +} + +static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) +{ + ImGui_ImplWin32_ViewportData* vd = IM_NEW(ImGui_ImplWin32_ViewportData)(); + viewport->PlatformUserData = vd; + + // Select style and parent window + ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &vd->DwStyle, &vd->DwExStyle); + vd->HwndParent = ImGui_ImplWin32_GetHwndFromViewportID(viewport->ParentViewportId); + + // Create window + RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) }; + ::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle); + vd->Hwnd = ::CreateWindowExW( + vd->DwExStyle, L"ImGui Platform", L"Untitled", vd->DwStyle, // Style, class name, window name + rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, // Window area + vd->HwndParent, nullptr, ::GetModuleHandle(nullptr), nullptr); // Owner window, Menu, Instance, Param + vd->HwndOwned = true; + viewport->PlatformRequestResize = false; + viewport->PlatformHandle = viewport->PlatformHandleRaw = vd->Hwnd; + + // Secondary viewports store their imgui context + ::SetPropA(vd->Hwnd, "IMGUI_CONTEXT", ImGui::GetCurrentContext()); +} + +static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport) +{ + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + if (ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData) + { + if (::GetCapture() == vd->Hwnd) + { + // Transfer capture so if we started dragging from a window that later disappears, we'll still receive the MOUSEUP event. + ::ReleaseCapture(); + ::SetCapture(bd->hWnd); + } + if (vd->Hwnd && vd->HwndOwned) + ::DestroyWindow(vd->Hwnd); + vd->Hwnd = nullptr; + IM_DELETE(vd); + } + viewport->PlatformUserData = viewport->PlatformHandle = nullptr; +} + +static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport) +{ + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); + + // ShowParent() also brings parent to front, which is not always desirable, + // so we temporarily disable parenting. (#7354) + if (vd->HwndParent != NULL) + ::SetWindowLongPtr(vd->Hwnd, GWLP_HWNDPARENT, (LONG_PTR)nullptr); + + if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) + ::ShowWindow(vd->Hwnd, SW_SHOWNA); + else + ::ShowWindow(vd->Hwnd, SW_SHOW); + + // Restore + if (vd->HwndParent != NULL) + ::SetWindowLongPtr(vd->Hwnd, GWLP_HWNDPARENT, (LONG_PTR)vd->HwndParent); +} + +static void ImGui_ImplWin32_UpdateWindow(ImGuiViewport* viewport) +{ + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); + + // Update Win32 parent if it changed _after_ creation + // Unlike style settings derived from configuration flags, this is more likely to change for advanced apps that are manipulating ParentViewportID manually. + HWND new_parent = ImGui_ImplWin32_GetHwndFromViewportID(viewport->ParentViewportId); + if (new_parent != vd->HwndParent) + { + // Win32 windows can either have a "Parent" (for WS_CHILD window) or an "Owner" (which among other thing keeps window above its owner). + // Our Dear Imgui-side concept of parenting only mostly care about what Win32 call "Owner". + // The parent parameter of CreateWindowEx() sets up Parent OR Owner depending on WS_CHILD flag. In our case an Owner as we never use WS_CHILD. + // Calling ::SetParent() here would be incorrect: it will create a full child relation, alter coordinate system and clipping. + // Calling ::SetWindowLongPtr() with GWLP_HWNDPARENT seems correct although poorly documented. + // https://devblogs.microsoft.com/oldnewthing/20100315-00/?p=14613 + vd->HwndParent = new_parent; + ::SetWindowLongPtr(vd->Hwnd, GWLP_HWNDPARENT, (LONG_PTR)vd->HwndParent); + } + + // (Optional) Update Win32 style if it changed _after_ creation. + // Generally they won't change unless configuration flags are changed, but advanced uses (such as manually rewriting viewport flags) make this useful. + DWORD new_style; + DWORD new_ex_style; + ImGui_ImplWin32_GetWin32StyleFromViewportFlags(viewport->Flags, &new_style, &new_ex_style); + + // Only reapply the flags that have been changed from our point of view (as other flags are being modified by Windows) + if (vd->DwStyle != new_style || vd->DwExStyle != new_ex_style) + { + // (Optional) Update TopMost state if it changed _after_ creation + bool top_most_changed = (vd->DwExStyle & WS_EX_TOPMOST) != (new_ex_style & WS_EX_TOPMOST); + HWND insert_after = top_most_changed ? ((viewport->Flags & ImGuiViewportFlags_TopMost) ? HWND_TOPMOST : HWND_NOTOPMOST) : 0; + UINT swp_flag = top_most_changed ? 0 : SWP_NOZORDER; + + // Apply flags and position (since it is affected by flags) + vd->DwStyle = new_style; + vd->DwExStyle = new_ex_style; + ::SetWindowLong(vd->Hwnd, GWL_STYLE, vd->DwStyle); + ::SetWindowLong(vd->Hwnd, GWL_EXSTYLE, vd->DwExStyle); + RECT rect = { (LONG)viewport->Pos.x, (LONG)viewport->Pos.y, (LONG)(viewport->Pos.x + viewport->Size.x), (LONG)(viewport->Pos.y + viewport->Size.y) }; + ::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle); // Client to Screen + ::SetWindowPos(vd->Hwnd, insert_after, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, swp_flag | SWP_NOACTIVATE | SWP_FRAMECHANGED); + ::ShowWindow(vd->Hwnd, SW_SHOWNA); // This is necessary when we alter the style + viewport->PlatformRequestMove = viewport->PlatformRequestResize = true; + } +} + +static ImVec2 ImGui_ImplWin32_GetWindowPos(ImGuiViewport* viewport) +{ + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); + POINT pos = { 0, 0 }; + ::ClientToScreen(vd->Hwnd, &pos); + return ImVec2((float)pos.x, (float)pos.y); +} + +static void ImGui_ImplWin32_UpdateWin32StyleFromWindow(ImGuiViewport* viewport) +{ + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + vd->DwStyle = ::GetWindowLongW(vd->Hwnd, GWL_STYLE); + vd->DwExStyle = ::GetWindowLongW(vd->Hwnd, GWL_EXSTYLE); +} + +static void ImGui_ImplWin32_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos) +{ + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); + RECT rect = { (LONG)pos.x, (LONG)pos.y, (LONG)pos.x, (LONG)pos.y }; + if (viewport->Flags & ImGuiViewportFlags_OwnedByApp) + ImGui_ImplWin32_UpdateWin32StyleFromWindow(viewport); // Not our window, poll style before using + ::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle); + ::SetWindowPos(vd->Hwnd, nullptr, rect.left, rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); +} + +static ImVec2 ImGui_ImplWin32_GetWindowSize(ImGuiViewport* viewport) +{ + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); + RECT rect; + ::GetClientRect(vd->Hwnd, &rect); + return ImVec2(float(rect.right - rect.left), float(rect.bottom - rect.top)); +} + +static void ImGui_ImplWin32_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); + RECT rect = { 0, 0, (LONG)size.x, (LONG)size.y }; + if (viewport->Flags & ImGuiViewportFlags_OwnedByApp) + ImGui_ImplWin32_UpdateWin32StyleFromWindow(viewport); // Not our window, poll style before using + ::AdjustWindowRectEx(&rect, vd->DwStyle, FALSE, vd->DwExStyle); // Client to Screen + ::SetWindowPos(vd->Hwnd, nullptr, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); +} + +static void ImGui_ImplWin32_SetWindowFocus(ImGuiViewport* viewport) +{ + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); + ::BringWindowToTop(vd->Hwnd); + ::SetForegroundWindow(vd->Hwnd); + ::SetFocus(vd->Hwnd); +} + +static bool ImGui_ImplWin32_GetWindowFocus(ImGuiViewport* viewport) +{ + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); + return ::GetForegroundWindow() == vd->Hwnd; +} + +static bool ImGui_ImplWin32_GetWindowMinimized(ImGuiViewport* viewport) +{ + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); + return ::IsIconic(vd->Hwnd) != 0; +} + +static void ImGui_ImplWin32_SetWindowTitle(ImGuiViewport* viewport, const char* title) +{ + // ::SetWindowTextA() doesn't properly handle UTF-8 so we explicitely convert our string. + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); + int n = ::MultiByteToWideChar(CP_UTF8, 0, title, -1, nullptr, 0); + ImVector title_w; + title_w.resize(n); + ::MultiByteToWideChar(CP_UTF8, 0, title, -1, title_w.Data, n); + ::SetWindowTextW(vd->Hwnd, title_w.Data); +} + +static void ImGui_ImplWin32_SetWindowAlpha(ImGuiViewport* viewport, float alpha) +{ + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); + IM_ASSERT(alpha >= 0.0f && alpha <= 1.0f); + if (alpha < 1.0f) + { + DWORD ex_style = ::GetWindowLongW(vd->Hwnd, GWL_EXSTYLE) | WS_EX_LAYERED; + ::SetWindowLongW(vd->Hwnd, GWL_EXSTYLE, ex_style); + ::SetLayeredWindowAttributes(vd->Hwnd, 0, (BYTE)(255 * alpha), LWA_ALPHA); + } + else + { + DWORD ex_style = ::GetWindowLongW(vd->Hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED; + ::SetWindowLongW(vd->Hwnd, GWL_EXSTYLE, ex_style); + } +} + +static float ImGui_ImplWin32_GetWindowDpiScale(ImGuiViewport* viewport) +{ + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->Hwnd != 0); + return ImGui_ImplWin32_GetDpiScaleForHwnd(vd->Hwnd); +} + +// FIXME-DPI: Testing DPI related ideas +static void ImGui_ImplWin32_OnChangedViewport(ImGuiViewport* viewport) +{ + (void)viewport; +#if 0 + ImGuiStyle default_style; + //default_style.WindowPadding = ImVec2(0, 0); + //default_style.WindowBorderSize = 0.0f; + //default_style.ItemSpacing.y = 3.0f; + //default_style.FramePadding = ImVec2(0, 0); + default_style.ScaleAllSizes(viewport->DpiScale); + ImGuiStyle& style = ImGui::GetStyle(); + style = default_style; +#endif +} + +namespace ImGui { extern ImGuiIO& GetIOEx(ImGuiContext*); extern ImGuiPlatformIO& GetPlatformIOEx(ImGuiContext*); } + +static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + // Allow secondary viewport WndProc to be called regardless of current context + ImGuiContext* ctx = (ImGuiContext*)::GetPropA(hWnd, "IMGUI_CONTEXT"); + if (ctx == NULL) + return DefWindowProc(hWnd, msg, wParam, lParam); // unlike ImGui_ImplWin32_WndProcHandler() we are called directly by Windows, we can't just return 0. + + ImGuiIO& io = ImGui::GetIOEx(ctx); + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIOEx(ctx); + LRESULT result = 0; + if (ImGui_ImplWin32_WndProcHandlerEx(hWnd, msg, wParam, lParam, io)) + result = true; + else if (ImGuiViewport* viewport = ImGui_ImplWin32_FindViewportByPlatformHandle(platform_io, hWnd)) + { + switch (msg) + { + case WM_CLOSE: + viewport->PlatformRequestClose = true; + break; + case WM_MOVE: + viewport->PlatformRequestMove = true; + break; + case WM_SIZE: + viewport->PlatformRequestResize = true; + break; + case WM_MOUSEACTIVATE: + if (viewport->Flags & ImGuiViewportFlags_NoFocusOnClick) + result = MA_NOACTIVATE; + break; + case WM_NCHITTEST: + // Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() correctly. (which is optional). + // The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging. + // If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in + // your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system. + if (viewport->Flags & ImGuiViewportFlags_NoInputs) + result = HTTRANSPARENT; + break; + } + } + if (result == 0) + result = DefWindowProc(hWnd, msg, wParam, lParam); + return result; +} + +static void ImGui_ImplWin32_InitMultiViewportSupport(bool platform_has_own_dc) +{ + WNDCLASSEXW wcex; + wcex.cbSize = sizeof(WNDCLASSEXW); + wcex.style = CS_HREDRAW | CS_VREDRAW | (platform_has_own_dc ? CS_OWNDC : 0); + wcex.lpfnWndProc = ImGui_ImplWin32_WndProcHandler_PlatformWindow; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = ::GetModuleHandle(nullptr); + wcex.hIcon = nullptr; + wcex.hCursor = nullptr; + wcex.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1); + wcex.lpszMenuName = nullptr; + wcex.lpszClassName = L"ImGui Platform"; + wcex.hIconSm = nullptr; + ::RegisterClassExW(&wcex); + + ImGui_ImplWin32_UpdateMonitors(); + + // Register platform interface (will be coupled with a renderer interface) + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Platform_CreateWindow = ImGui_ImplWin32_CreateWindow; + platform_io.Platform_DestroyWindow = ImGui_ImplWin32_DestroyWindow; + platform_io.Platform_ShowWindow = ImGui_ImplWin32_ShowWindow; + platform_io.Platform_SetWindowPos = ImGui_ImplWin32_SetWindowPos; + platform_io.Platform_GetWindowPos = ImGui_ImplWin32_GetWindowPos; + platform_io.Platform_SetWindowSize = ImGui_ImplWin32_SetWindowSize; + platform_io.Platform_GetWindowSize = ImGui_ImplWin32_GetWindowSize; + platform_io.Platform_SetWindowFocus = ImGui_ImplWin32_SetWindowFocus; + platform_io.Platform_GetWindowFocus = ImGui_ImplWin32_GetWindowFocus; + platform_io.Platform_GetWindowMinimized = ImGui_ImplWin32_GetWindowMinimized; + platform_io.Platform_SetWindowTitle = ImGui_ImplWin32_SetWindowTitle; + platform_io.Platform_SetWindowAlpha = ImGui_ImplWin32_SetWindowAlpha; + platform_io.Platform_UpdateWindow = ImGui_ImplWin32_UpdateWindow; + platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; // FIXME-DPI + platform_io.Platform_OnChangedViewport = ImGui_ImplWin32_OnChangedViewport; // FIXME-DPI + + // Register main window handle (which is owned by the main application, not by us) + // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. + ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + ImGui_ImplWin32_ViewportData* vd = IM_NEW(ImGui_ImplWin32_ViewportData)(); + vd->Hwnd = bd->hWnd; + vd->HwndOwned = false; + main_viewport->PlatformUserData = vd; +} + +static void ImGui_ImplWin32_ShutdownMultiViewportSupport() +{ + ::UnregisterClass(_T("ImGui Platform"), ::GetModuleHandle(nullptr)); + ImGui::DestroyPlatformWindows(); +} + //--------------------------------------------------------------------------------------------------------- #if defined(__GNUC__) diff --git a/neo/libs/imgui/backends/imgui_impl_win32.h b/neo/libs/imgui/backends/imgui_impl_win32.h index 083fe385f..6584ff8fd 100644 --- a/neo/libs/imgui/backends/imgui_impl_win32.h +++ b/neo/libs/imgui/backends/imgui_impl_win32.h @@ -7,6 +7,7 @@ // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values are obsolete since 1.87 and not supported since 1.91.5] // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. diff --git a/neo/libs/imgui/docs/CHANGELOG.txt b/neo/libs/imgui/docs/CHANGELOG.txt index 462de35fb..ede5c1ba8 100644 --- a/neo/libs/imgui/docs/CHANGELOG.txt +++ b/neo/libs/imgui/docs/CHANGELOG.txt @@ -35,6 +35,86 @@ HOW TO UPDATE? and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users. - Please report any issue! +----------------------------------------------------------------------- + VERSION 1.91.8 WIP (In Progress) +----------------------------------------------------------------------- + +Breaking changes: + +- ColorEdit, ColorPicker: redesigned how alpha is displayed in the preview square. (#8335, #1578, #346) + - Removed ImGuiColorEditFlags_AlphaPreview (made value 0): it is now the default behavior. + - Prior to 1.91.8: alpha was made opaque in the preview by default _unless_ using ImGuiColorEditFlags_AlphaPreview. + - We now display the preview as transparent by default. You can use ImGuiColorEditFlags_AlphaOpaque to use old behavior. + - The new flags may be combined better and allow finer controls: + - ImGuiColorEditFlags_AlphaOpaque: disable alpha in the preview, but alpha value still editable. + - ImGuiColorEditFlags_AlphaNoBg: disable rendering a checkerboard background behind transparent color. + - ImGuiColorEditFlags_AlphaPreviewHalf: display half opaque / half transparent preview. +- Backends: SDLGPU3: Renamed ImGui_ImplSDLGPU3_InitInfo::GpuDevice to Device + for consistency. (#8163, #7998, #7988) + +Other changes: + +- imgui_freetype: fixed issue where glyph advances would incorrectly be + snapped to pixels. Effectively it would only be noticeable when hinting + is disabled with ImGuiFreeTypeBuilderFlags_NoHinting, as hinting itself + snaps glyph advances. +- Inputs: added IsMouseReleasedWithDelay() helper. (#8337, #8320) + Use if you absolutely need to distinguish single-click from double-clicks + by introducing a delay. This is a very rarely used UI idiom, but some apps + use this: e.g. MS Explorer single-click on an icon triggers a rename. + Generally use with 'delay >= io.MouseDoubleClickTime' + combine with a + 'io.MouseClickedLastCount == 1' check. +- Windows: legacy SetWindowFontScale() is properly inherited by nested child + windows. Note that an upcoming major release should make this obsolete, + but in the meanwhile it works better now. (#2701, #8138, #1018) +- Windows, Style: Fixed small rendering issues with menu bar, resize grip and + scrollbar when using thick border sizes. (#8267, #7887) +- Windows: Fixed IsItemXXXX() functions not working on append-version of EndChild(). (#8350) + Also made some of the fields accessible after BeginChild() to match Begin() logic. +- Tables, Menus: Fixed using BeginTable() in menu layer (any menu bar). (#8355) + It previously overrode the current layer back to main layer, which caused an issue + with MainMenuBar attempted to release focus when leaving the menu layer. +- ColorEdit, ColorPicker: Fixed alpha preview broken in 1.91.7. (#8336, #8241). [@PathogenDavid] +- Tabs, Style: reworked selected overline rendering to better accommodate + for rounded tabs. Reduced default thickness (style.TabBarOverlineSize), + increased default rounding (style.TabRounding). (#8334) [@Kian738, @ocornut] + styles as the current look is not right (but ImGuiCol_TabSelectedOverline stays the same). +- Debug Tools: Tweaked font preview. +- ImDrawList: texture baked storage for thick line reduced from ~64x64 to ~32x32. (#3245) +- Fonts: IndexLookup[] table hold 16-bit values even in ImWchar32 mode, + as it accounts for number of glyphs in same font. This is favorable to + CalcTextSize() calls touching less memory. +- Fonts: OversampleH/OversampleV defaults to 0 for automatic selection. + - OversampleH == 0 --> use 1 or 2 depending on font size and use of PixelSnapH. + - OversampleV == 0 --> always use 1. + This also +- ImFontAtlas: made calling ClearFonts() call ClearInputData(), as calling + one without the other is never correct. (#8174, #6556, #6336, #4723) +- Examples: DirectX12: Reduced number of frame in flight from 3 to 2 in + provided example, to reduce latency. +- Backends+Examples: Vulkan: better handle VK_SUBOPTIMAL_KHR being returned by + vkAcquireNextImageKHR() or vkQueuePresentKHR(). (#7825, #7831) [@NostraMagister] +- Backends: SDL2: removed assert preventing using ImGui_ImplSDL2_SetGamepadMode() + with ImGui_ImplSDL2_GamepadMode_Manual and an empty array. (#8329) +- Backends: SDL3: removed assert preventing using ImGui_ImplSDL3_SetGamepadMode() + with ImGui_ImplSDL3_GamepadMode_Manual and an empty array. (#8329) +- Backends: SDLGPU3: Exposed ImGui_ImplSDLGPU3_CreateDeviceObjects()/_DestroyDeviceObjects(). + Removed return value from ImGui_ImplSDLGPU3_CreateFontsTexture(). (#8163, #7998, #7988) +- Backends: SDL_Renderer2/3: Use endian-dependent RGBA32 texture format, to match + SDL_Color. (#8327) [@dkosmari] +- Backends: DirectX12: Texture upload use the command queue provided in + ImGui_ImplDX12_InitInfo instead of creating its own. +- Backends: OSX: Removed notification observer when shutting down. (#8331) [@jrachele] + +Docking+Viewports Branch: + +- Docking: Fixed an issue in 1.91.7 where using legacy ImGuiWindowFlags_NavFlattened + flag (instead of ImGuiChildFlags_NavFlattened) in docking branch would conflict + with internal docking flags. (#8357) [@DanielGibson] +- Backends: SDL3: new viewport windows are created with the SDL_WINDOW_HIDDEN flag + before calling SDL_ShowWindow(). (#8328) [@PathogenDavid] + + ----------------------------------------------------------------------- VERSION 1.91.7 (Released 2025-01-14) ----------------------------------------------------------------------- @@ -79,6 +159,8 @@ Other changes: the label (not only the highlight/frame) also spans all columns. This is useful for table rows where you know nothing else is submitted. (#8318, #3565) Obviously best used with ImGuiTableFlags_NoBordersInBodyUntilResize. +- Selectable: Fixed horizontal label alignment when combined with using + ImGuiSelectableFlags_SpanAllColumns. (#8338) - Drags: Added ImGuiSliderFlags_NoSpeedTweaks flag to disable keyboard modifiers altering the tweak speed. Useful if you want to alter tweak speed yourself based on your own logic. (#8223) @@ -93,7 +175,7 @@ Other changes: - Demo: Added label edition to Property Editor demo + fix an ID issue. (#8266) [@moritz-h] - Misc: Fixed misc/cpp/imgui_stdlib.h/.cpp not supporting IMGUI_DISABLE. (#8294) [@juur] - Misc: Fixed MinGW builds not using UTF-8 friendly _wfopen(). (#8300) -- Backends: SDL_GPU for SDL3: Added backend for SDL_GPU! (#8163, #7998, #7988) [@DeltaW0x]. +- Backends: SDLGPU3 for SDL3: Added backend for SDL_GPU! (#8163, #7998, #7988) [@DeltaW0x]. - Backends: SDL3: Added ImGui_ImplSDL3_InitForSDLGPU() for consistency, even though it is currently not doing anything particular. (#8163, #7998, #7988) - Backends: Allegro5: Avoid calling al_set_mouse_cursor() repeatedly since it appears @@ -166,6 +248,15 @@ Other changes: - Examples: Win32+DX12: Using a basic free-list allocator to manage multiple SRV descriptors. +Docking+Viewports Branch: + +- Docking: Added an assert to clarify that ImGuiDockNodeFlags_CentralNode flag + (from internals) does not need to be passed to DockSpace(), as it causes general + havoc. (#8145) +- Backends: Win32: Fixed a crash/regression in 1.91.5 when running two processes + with multi-viewports (was using GetProp() to query property which could have + belonged to another process). (#8162, #8069) [@sammyfreg, @ocornut] + ----------------------------------------------------------------------- VERSION 1.91.5 (Released 2024-11-07) @@ -224,6 +315,15 @@ Other changes: - Examples: Android+OpenGL: Using ALooper_pollOnce() instead of ALooper_pollAll() which has been deprecated. (#8013) [@feather179] +Docking+Viewports Branch: + +- Backends: GLFW: added Linux workaround for spurious mouse up events emitted while dragging + and creating new viewports. Generally they would be interrupting a dragging operations. + (#3158, #7733, #7922) [@rokups, @ocornut] +- Docking: fixed using ImGuiDockNodeFlags_KeepAliveOnly with DockSpaceOverViewport(): + the normally invisible space did erroneously claim mouse hover and could be potentially + focused. (#8125) [@kcbanner] + ----------------------------------------------------------------------- VERSION 1.91.4 (Released 2024-10-18) @@ -313,6 +413,11 @@ Other changes: - Backends: DX9, DX10, DX11, DX12, OpenGL, Vulkan, WGPU: Changed default texture sampler to Clamp instead of Repeat/Wrap. (#7468, #7511, #5999, #5502, #7230) +Docking+Viewports Branch: + +- Backends: changed all backends to allow enabling ImGuiConfigFlags_ViewportsEnable + after initialization. (#5371) + ----------------------------------------------------------------------- VERSION 1.91.3 (Released 2024-10-04) @@ -376,6 +481,13 @@ Other changes: - Backends: WebGPU: Fixed DAWN api change using WGPUStringView in WGPUShaderSourceWGSL. (#8009, #8010) [@blitz-research] +Docking+Viewports Branch: + +- Backends: SDL2, SDL3: Fixed building for UWP platforms. (#8008) +- Backends: Win32: Use ResisterClassW()/CreateWindowExW() for secondary viewports, to + ensure correct IME input even if the backend was compiled in MBCS mode. (#7979, #5725) + + ----------------------------------------------------------------------- VERSION 1.91.2 (Released 2024-09-19) ----------------------------------------------------------------------- @@ -436,6 +548,18 @@ Other changes: - Backends: WebGPU: Added support for optional IMGUI_IMPL_WEBGPU_BACKEND_DAWN / IMGUI_IMPL_WEBGPU_BACKEND_WGPU defines to handle ever-changing native implementations. (#7977, #7969, #6602, #6188, #7523) [@acgaudette] +Docking+Viewports Branch: + +- Viewports: fixed an issue where a window manually constrained to the main viewport while crossing + over main viewport bounds isn't translated properly. (#7985) +- Backends: SDL2, SDL3, Win32: ensure that ImGuiPlatformMonitor list is available after backend Init call. (#7995) +- Backends: Win32: fixed direct calls to platform_io.Platform_SetWindowPos()/Platform_SetWindowSize() + on windows created by application (typically main viewport). +- Backends: Win32: fixed an issue where a viewport destroyed while clicking would hog + mouse tracking and temporary lead to incorrect update of HoveredWindow. (#7971) +- Backends: SDL3: added support for viewport->ParentViewportId field to support parenting + windows at OS level. (#7973, #7989) [@RT2code] + ----------------------------------------------------------------------- VERSION 1.91.1 (Released 2024-09-04) ----------------------------------------------------------------------- @@ -519,6 +643,16 @@ Other changes: to handle minimization without burning CPU or GPU by running unthrottled code. (#7844) - Examples: SDL3: Update for API changes: SDL_Init() returns 0 on failure. +Docking+Viewports Branch: + +- Viewports: added optional platform_io.Platform_GetWindowWorkAreaInsets() hook + to allow backends to alter the default per-viewport work-area. (#7823) +- Backends: don't report monitors with DpiScale of 0, which seemed to be reported for + virtual monitors instead by accessibility drivers. (#7902) [@nicolasnoble, @ocornut] +- Backends: SDL2, SDL3: using SDL_HINT_WINDOW_NO_ACTIVATION_WHEN_SHOWN to support the + ImGuiViewportFlags_NoFocusOnAppearing flag, instead of using a Win32-specific hack. + (#7896) [@RT2Code] + ----------------------------------------------------------------------- VERSION 1.91.0 (Released 2024-07-30) ----------------------------------------------------------------------- @@ -678,6 +812,16 @@ Other changes: - Backends: GLFW+Emscripten: Fixed Emscripten warning when using mouse wheel on some setups "Unable to preventDefault inside passive event listener". (#7647, #7600) [@ypujante] +Docking+Viewports Branch: + +- Viewports: Always update fallback monitor to primary monitor if there's one. +- Backends: OSX: Fixed NSAppKitVersion version limit for setWantsBestResolutionOpenGLSurface + usage. (#7814) [@YGXXD] +- Backends: SDL3: Fixed a bug preventing ImGuiViewportFlags_NoFocusOnAppearing support from + working (Windows only). +- Backends: Vulkan: ImGui_ImplVulkan_SwapBuffers() used by secondary viewports still proceeds + increasing frame counters on VK_SUBOPTIMAL_KHR. (#7825, #3881) [@NostraMagister] + ----------------------------------------------------------------------- VERSION 1.90.9 (Released 2024-07-01) @@ -759,6 +903,19 @@ Other changes: returning VK_SUBOPTIMAL_KHR, which doesn't seem to happen on Wayland. (#7671) [@AndreiNego, @ocornut] +Docking+Viewports Branch: + +- Windows, Menus: Fixed an issue where the size of sub-menu in their own viewport + would be erroneously clamped to the size of main viewport. (#7730) +- Merged GetBackgroundDrawList() and GetBackgroundDrawList(ImGuiViewport* viewport) + api entry points into a same one GetBackgroundDrawList(ImGuiViewport* viewport = NULL); +- Merged GetForegroundDrawList() and GetForegroundDrawList(ImGuiViewport* viewport) + api entry points into a same one GetForegroundDrawList(ImGuiViewport* viewport = NULL); +- Backends: SDL3: Update for introduction of SDL_GLContext from void*. (#7701, #7702) + [@bcsanches] +- Backends: Win32: Secondary viewports WndProc handler retrieve/set imgui context from + the HWND, allowing WndProc dispatch to work in multi-context setups. + ----------------------------------------------------------------------- VERSION 1.90.8 (Released 2024-06-06) @@ -909,6 +1066,15 @@ versions of IsKeyPressed(), IsKeyChordPressed(), IsMouseClicked() prior to this those API public. Only past users of imgui_internal.h with the extra parameters will be affected. Added asserts for valid flags in various functions to detect _some_ misuses, BUT NOT ALL. +Docking+Viewports Branch: + +- Docking: *BREAKING* changed signature of DockSpaceOverViewport() to allow passing + an explicit dockspace id if desired. (#7611) + Before: DockSpaceOverViewport(const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, ...); + After: DockSpaceOverViewport(ImGuiID dockspace_id = 0, const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, ...); + Simply add a leading 0 to your existing calls to DockSpaceOverViewport() if you have any. +- Tables: resizing border hit-rect scales according to current monitor dpi scale. + ----------------------------------------------------------------------- VERSION 1.90.6 (Released 2024-05-08) @@ -967,6 +1133,18 @@ Other changes: - Examples: GLFW+WebGPU: Added support for WebGPU-native/Dawn (#7435, #7132) [@eliasdaler, @Zelif] - Examples: GLFW+WebGPU: Renamed example_emscripten_wgpu/ to example_glfw_wgpu/. (#7435, #7132) +Docking+Viewports Branch: + +- Docking: when io.ConfigDockingWithShift is enabled, fixed help tooltip erroneously + reading SetNextWindowXXX() data. (#6709, #4643, #7491) [@ocornut, @cfillion] +- Viewports: fixed outer-right edge of MenuBar clipping rectangle off by one when window + is located on a monitor with negative coordinates. (#6861, #2884) [@cfillion] +- Backends: Vulkan: reworked swap-chain resize handling for secondary viewports, fix for + typical Linux setups. (#2626, #3390, #3758, #7508, #7513) [@RoryO, @InsideBSITheSecond] +- Backends: Vulkan: create a custom pipeline for secondary viewports. Fixes issues + when user created main viewport uses a different renderpass. (#6325, #6305, #7398, + #3459, #3253, #3522) [@skaman, @FunMiles] + ----------------------------------------------------------------------- VERSION 1.90.5 (Released 2024-04-11) @@ -1023,6 +1201,11 @@ Other changes: Adding a link to the crawlable version, even though it is not intended for humans, to increase its search rank. +Docking+Viewports Branch: + +- Backends: Win32: made it so that an appearing viewport showing up doesn't bring + its parent to front. (#7354) + ----------------------------------------------------------------------- VERSION 1.90.4 (Released 2024-02-22) @@ -1056,6 +1239,13 @@ Other changes: - Demo: Custom Rendering: better demonstrate PathArcTo(), PathBezierQuadraticCurveTo(), PathBezierCubicCurveTo(), PathStroke(), PathFillConvex() functions. +Docking+Viewports Branch: + +- Backends: GLFW: enable ImGuiBackendFlags_HasMouseHoveredViewport support with GLFW 3.3.x, + as required specs for it was relaxed in 1.87. This enable better viewport hovering detection + without GLFW 3.4's GLFW_MOUSE_PASSTHROUGH, with less reliance on dear imgui's own heuristic + of platform windows order. (#7316) + ----------------------------------------------------------------------- VERSION 1.90.3 (Released 2024-02-14) @@ -1144,6 +1334,18 @@ Other changes: Often requested in some form (#6962, #5219, #3290, #4627, #5054, #3878, #2881, #1506, #1216, #968), and useful for interactive completion/suggestions popups (#2057, #718) +Docking+Viewports Branch: + +- Added ImGuiWindowClass::FocusRouteParentWindowId as a way to connect the focus route between + a tool window to a parent document window, so that Shortcuts in the documents are routed when the + tool is focused (regardless of whether the tool is docked or in a floating viewport, etc.) (#6798) +- Added ImGuiDockNodeFlags_DockedWindowsInFocusRoute to automatically make a dockspace connect + the focus route of its docked window. This is provided a convenience in case you have windows + where a connection is not explicit. (#6798) +- Viewports: Fixed moving accross monitors when io.ConfigWindowsMoveFromTitleBarOnly is set. (#7299, #3071) +- Backends: OSX: Fixed not submitting Monitors info when viewports are not enabled, leading to + missing e.g. DpiScale info. (#7257) [@actboy168] + ----------------------------------------------------------------------- VERSION 1.90.1 (Released 2024-01-10) @@ -1248,6 +1450,15 @@ Other changes: (#6751) [@Traveller23, @ypujante] - Examples: SDL3: Minor fixes following recent SDL3 in-progress development. +Docking+Viewports Branch: + +- Windows: Fixed ImGuiCol_TitleBg/Active alpha being used for viewport-owned windows. (#7181) [@PathogenDavid] +- Backends: DX12: Changed swapchain scaling mode to DXGI_SCALING_NONE to reduce artifacts as + queued frames aren't synchronized with platform window resizes. (#7152, #7153) [@SuperWangKai] +- Backends: OSX: Fixed monitor and window position by correctly transforming Y origin on multi-viewports + multi-monitor setups. (#7028, #7101, #6009, #6432) [@dmirty-kuzmenko, @734vin] +- Backends: OSX: Fixed IME position in multi-monitor multi-viewports setups. (#7028) [@734vin] + ----------------------------------------------------------------------- VERSION 1.90.0 (Released 2023-11-15) @@ -1442,6 +1653,8 @@ Other changes: Fixed leaks, and added ImGui_ImplVulkan_DestroyFontsTexture() (probably no need to call this directly). (#6943, #6715, #6327, #3743, #4618) [@helynranta, @thomasherzog, @guybrush77, @albin-johansson, @MiroKaku, @benbatya-fb, @ocornut] +- Backends: Vulkan: use specified ColorAttachmentFormat when creating a secondary window when enabling + UseDynamicRendering option. (#6999, #5446, #5037) [@raaaviol] - Backends: GLFW: Clear emscripten's MouseWheel callback before shutdown. (#6790, #6096, #4019) [@halx99] - Backends: GLFW: Added support for F13 to F24 function keys. (#6891) - Backends: SDL2, SDL3: Added support for F13 to F24 function keys, AppBack, AppForward. (#6891) @@ -1459,6 +1672,31 @@ Other changes: - Examples: GLFW+Vulkan, SDL+Vulkan: Simplified and removed code due to backend improvements. - Internals: Renamed ImFloor() to ImTrunc(). Renamed ImFloorSigned() to ImFloor(). (#6861) +Docking+Viewports Branch: + +- Viewports: Fixed window inner clipping rectangle off by one when window is located on a monitor + with negative coordinates. While it is expected that other small issues with arise from this + situation, at the moment we are fixing the most noticeable one. (#6861, #2884) [@Vuhdo, @alektron] +- Docking: revised undocking to reduce accidental whole-node undocking: + - cannot undock a whole node by dragging from empty space in tab-bar. + - can undock whole node by dragging from window/collapse menu button. + - can undock single window by dragging from its tab. + - can still move (but not undock) whole node or whole hierarchy when node is part of a + floating hierarchy. + - added tooltip when hovering the collapse/window menu button, to faciliate understanding + that whole dock node may be undocked or grabbed from here. +- Docking: Fixed an issue leading to incorrect restoration of selected tab in dock nodes that + don't carry the currently focused window. (#2304) +- Docking: added ImGuiDockNodeFlags_NoUndocking. (#2999, #6823, #6780, #3492) +- Docking: renamed ImGuiDockNodeFlags_NoSplit to ImGuiDockNodeFlags_NoDockingSplit. +- Docking: renamed ImGuiDockNodeFlags_NoDockingInCentralNode to ImGuiDockNodeFlags_NoDockingOverCentralNode. +- Docking: Internals: renamed ImGuiDockNodeFlags_NoDockingSplitMe to ImGuiDockNodeFlags_NoDockingSplit. +- Docking: Fixed a bug where ClassId compare tests (when using SetNextWindowClass) on success would + prevent further filter from running, namely the one that prevent docking over a popup. +- Backends: GLFW: Fixed an assertion in situation where the WndProc handler is different between + main and secondary viewport (may happen due to third-party hooks). (#6889) +- Backends: DX9: Fixed incorrect assert triggering on reopening session with minimized windows. (#3424) + ----------------------------------------------------------------------- VERSION 1.89.9 (Released 2023-09-04) @@ -1516,6 +1754,11 @@ Other changes: - Examples: Emscripten+WebGPU: Fixed WGPUInstance creation process + use preferred framebuffer format. (#6640, #6748) [@smileorigin] +Docking+Viewports Branch: + +- Docking: when io.ConfigDockingWithShift is enabled, staying stationary while moving + a window displays an help tooltip to increase affordance. (#6709, #4643) + ----------------------------------------------------------------------- VERSION 1.89.8 (Released 2023-08-01) @@ -1584,6 +1827,16 @@ Other changes: - Examples: Vulkan: Creating minimal descriptor pools to fit only what is needed by example. (#6642) [@SaschaWillem] +Docking+Viewports Branch: + +- Docking, Style: resizing separators use same colors as window borders (ImGuiCol_Border) + for consistency. With default styles it doesn't make a big difference. (#2522) [@rmitton] + In the future if we promote using thick value for inner/outer docking padding we may + need to introduce new colors for it. +- Docking: added style.DockingSeparatorSize, ImGuiStyleVar_DockingSeparatorSize. Now + also scaled by style.ScaleAllSizes(). (#3481, #4721, #2522) [@PossiblyAShrub, @wobbier] +- Docking: fixed rendering of docked-window scrollbar above outer border. (#2522) + ----------------------------------------------------------------------- VERSION 1.89.7 (Released 2023-07-04) @@ -1679,6 +1932,23 @@ Other changes: - Examples: Win32+OpenGL3: Changed DefWindowProc() to DefWindowProcW() to match other examples and support the example app being compiled without UNICODE. (#6516, #5725, #5961, #5975) [@yenixing] +Docking+Viewports Branch: + +- Viewports+Docking: Fixed extraneous viewport+platform-window recreation in various + combination of showing or hiding windows, docking with/without split, undocking. + While with some backends and without OS decorations, some extraneous window recreation + were visibly not noticeable, they would typically become noticeable when enabling + OS decorations on those windows (e.g. Windows title bar fade-in/animation). +- Viewports: Closing a viewport via OS/platform means (e.g. OS close button or task-bar menu), + mark all windows in this viewport as closed. +- Docking: Fixed one-frame flickering on reappearing windows binding to a dock node + where a later-submitted window was already bound. +- Docking: Fixed dragging from title-bar empty space (regression from 1.88 related to + keeping ID alive when calling low-level ButtonBehavior() directly). (#5181, #2645) +- Docking: [Internal] DockBuilderDockWindow() API calls don't clear docking order + if the target node is same as existing one. +- Backends: Win32: Added support for changing ParentViewportID after viewport creation. + ----------------------------------------------------------------------- VERSION 1.89.6 (Released 2023-05-31) @@ -1748,6 +2018,23 @@ Other changes: - Examples: Added SDL3+SDL_Renderer example. (#6286) - Examples: Updated all Visual Studio projects and batches to use /utf-8 argument. +Docking+Viewports Branch: + +- Viewports: Fixed platform-side focus (e.g. Alt+Tab) from leading to accidental + closure of Modal windows. Regression from 1.89.5. (#6357, #6299) +- Viewports: Fixed loss of imgui-side focus when dragging a secondary viewport back in + main viewport, due to platform-side handling changes. Regression from 1.89.5 (#6299) +- Viewports: Avoid applying imgui-side focus when focus change is due to a viewport + destruction. Fixes erroneous popup closure on closing a previous popup. (#6462, #6299) +- Viewports: Added void* ImGuiPlatformMonitor::PlatformHandle field (backend-dependant), + for usage by user code. +- Backends: GLFW: Preserve monitor list when there are no monitor, may briefly + happen when recovering from macOS sleeping mode. (#5683) [@Guistac] +- Backends: SDL2: Update monitor list when receiving a display event. (#6348) + Note however that SDL2 currently doesn't have an event for a DPI/Scaling change, + so monitor data won't be updated in this situation. +- Backends: SDL3: Update monitor list when receiving a display event. (#6348) + ----------------------------------------------------------------------- VERSION 1.89.5 (Released 2023-04-13) @@ -1805,7 +2092,6 @@ Other changes: is to other IME function such as SDL_SetTextInputRect(). (#6306, #6071, #1953) - Backends: GLFW: Added support on Win32 only for io.AddMouseSourceEvent() to discriminate Mouse/TouchScreen/Pen. (#2334, #2702) -- Backends: GLFW: Fixed key modifiers handling on secondary viewports. (#6248, #6034) [@aiekick] - Backends: Android: Added support for io.AddMouseSourceEvent() to discriminate Mouse/TouchScreen/Pen. (#6315) [@PathogenDavid] - Backends: OSX: Added support for io.AddMouseSourceEvent() to discriminate Mouse/Pen. @@ -1821,6 +2107,16 @@ Other changes: - TestSuite: Added variety of new regression tests and improved/amended existing ones in imgui_test_engine/ repo. [@PathogenDavid, @ocornut] +Docking+Viewports Branch: + +- Viewports: Setting focus from Platform/OS (e.g. via decoration, or Alt-Tab) sets corresponding + focus at Dear ImGui level (generally last focused window in the viewport). (#6299) +- Docking: Fixed using GetItemXXX() or IsItemXXX() functions after a DockSpace(). (#6217) +- Backends: GLFW: Fixed key modifiers handling on secondary viewports. (#6248, #6034) [@aiekick] +- Backends: GLFW: Fixed Emscripten erroneously enabling multi-viewport support, leading to assert. (#5683) +- Backends: SDL2/SDL3: Fixed IME text input rectangle position with viewports. (#6071, #1953) +- Backends: SDL3: Fixed for compilation with multi-viewports. (#6255) [@P3RK4N] + ----------------------------------------------------------------------- VERSION 1.89.4 (Released 2023-03-14) @@ -1895,7 +2191,6 @@ Other changes: and ImGuiConfigFlags_NavEnableGamepad by default. (#787) - Internals: Misc tweaks to facilitate applying an explicit-context patch. (#5856) [@Dragnalith] - ----------------------------------------------------------------------- VERSION 1.89.3 (Released 2023-02-14) ----------------------------------------------------------------------- @@ -1996,6 +2291,10 @@ Other changes: - Examples: SDL2+SDL_Renderer: Added call to SDL_RenderSetScale() to fix display on a Retina display (albeit lower-res as our other unmodified examples). (#6121, #6065, #5931). +Docking+Viewports Branch: + +- Backends: GLFW: Handle unsupported glfwGetVideoMode() for Emscripten. (#6096) + ----------------------------------------------------------------------- VERSION 1.89.2 (Released 2023-01-05) @@ -2046,6 +2345,17 @@ All changes: - Backends: Vulkan: Fixed sampler passed to ImGui_ImplVulkan_AddTexture() not being honored as we were using an immutable sampler. (#5502, #6001, #914) [@martin-ejdestig, @rytisss] +Docking+Viewports Branch: + +- Docking: Internals: fixed DockBuilderCopyDockSpace() crashing when windows not in the + remapping list are docked on the left or top side of a split. (#6035) +- Docking: fixed DockSpace() with ImGuiDockNodeFlags_KeepAliveOnly marking current window + as written to, even if it doesn't technically submit an item. This allow using KeepAliveOnly + from any window location. (#6037) +- Backends: OSX: fixed typo in ImGui_ImplOSX_GetWindowSize that would cause issues when resiing + from OS decorations, if they are enabled on secondary viewports. (#6009) [@sivu] +- Backends: Metal: fixed secondary viewport rendering. (#6015) [@dmirty-kuzmenko] + ----------------------------------------------------------------------- VERSION 1.89.1 (Released 2022-11-24) @@ -2072,6 +2382,13 @@ Other changes: - Backends: GLFW: cancel out errors emitted by glfwGetKeyName() when a name is missing. (#5908) - Backends: WebGPU: fixed validation error with default depth buffer settings. (#5869, #5914) [@kdchambers] +Docking+Viewports Branch: + +- Viewports: Fixed collapsed windows setting ImGuiViewportFlags_NoRendererClear without + making title bar color opaque, leading to potential texture/fb garbage being visible. + Right now as we don't fully support transparent viewports (#2766), so we turn that + 'TitleBgCollapsed' color opaque just lke we do for 'WindowBG' on uncollapsed windows. + ----------------------------------------------------------------------- VERSION 1.89 (Released 2022-11-15) @@ -2263,6 +2580,23 @@ Other Changes: - Backends: Vulkan: Added experimental ImGui_ImplVulkan_RemoveTexture() for api symmetry. (#914, #5738). - Backends: WebGPU: fixed rendering when a depth buffer is enabled. (#5869) [@brainlag] +Docking+Viewports Branch: + +- Docking: Fixed incorrect focus highlight on docking node when focusing a menu. (#5702) +- Docking, Nav: Fixed using gamepad/keyboard navigation not being able enter menu layer when + it only contained the standard Collapse/Close buttons and no actual menu. (#5463, #4792) +- Docking: Fixed regression introduced in v1.87 when docked window content not rendered + while switching between with CTRL+Tab. [@rokups] +- Docking: Fixed amending into an existing tab bar from rendering invisible items. (#5515) +- Docking: Made spacing between dock nodes not a dropping gap. When hovering it only + outer-docking drop markers are visible. +- Docking+Viewports: Fixed undocking window node causing parent viewports to become unresponsive + in certain situation (e.g. hidden tab bar). (#5503) [@rokups] +- Backends: SDL: Fixed building backend under non-OSX Apple targets (e.g. iPhone). (#5665) +- Backends: SDL: Fixed drag'n drop crossing a viewport border losing mouse coordinates. (#5710, #5012) +- Backends: GLFW: Fixed leftover static variable preventing from changing or + reinitializing backend while application is running. (#4616, #5434) [@rtoumazet] + ----------------------------------------------------------------------- VERSION 1.88 (Released 2022-06-21) @@ -2397,6 +2731,26 @@ Other Changes: - Examples: Emscripten+WebGPU: Fix building for latest WebGPU specs. (#3632) - Examples: OSX+Metal, OSX+OpenGL: Removed now-unnecessary calls to ImGui_ImplOSX_HandleEvent(). (#4821) +Docking+Viewports Branch: + +- Docking: Fixed floating docked nodes not being clamped into viewport workrect to stay reachable + when io.ConfigWindowsMoveFromTitleBarOnly is true and multi-viewports are disabled. (#5044) +- Docking: Fixed a regression where moving window would be interrupted after undocking a tab + when io.ConfigDockingAlwaysTabBar is true. (#5324) [@rokups] +- Docking: Fixed incorrect focus highlight on docking node when focusing empty central node + or a child window which was manually injected into a dockspace window. +- Docking, Modal: Fixed a crash when opening popup from a parent which is being docked on the same frame. (#5401) +- Viewports: Fixed an issue where MouseViewport was lagging by a frame when using 1.87 Input Queue. + A common side-effect would be that when releasing a window drag the underlying window would highlight + for a frame. (#5837, #4921) [@cfillion] +- Viewports: Fixed translating a host viewport from briefly altering the size of AlwaysAutoResize windows. (#5057) +- Viewports: Fixed main viewport size not matching ImDrawData::DisplaySize for one frame during resize + when multi-viewports are disabled. (#4900) +- Backends: SDL: Fixed dragging out main viewport broken on some SDL setups. (#5012) [@rokups] +- Backends: OSX: Added support for multi-viewports. [@stuartcarnie, @metarutaiga] (#4821, #2778) +- Backends: Metal: Added support for multi-viewports. [@stuartcarnie, @metarutaiga] (#4821, #2778) +- Examples: OSX+Metal, SDL+Metal, GLFW+Metal: Added support for multi-viewports. [@rokups] + ----------------------------------------------------------------------- VERSION 1.87 (Released 2022-02-07) @@ -2540,6 +2894,29 @@ Other Changes: - Backends: WebGPU: Fixed incorrect size parameters in wgpuRenderPassEncoderSetIndexBuffer() and wgpuRenderPassEncoderSetVertexBuffer() calls. (#4891) [@FeepsDev] +Docking+Viewports Branch: + +- Docking: Fixed a CTRL+TAB crash when aiming at an empty docked window. (#4792) +- Docking: Tabs use their own identifier instead of the Window identifier. + (This will invalidate some stored .ini data such as last selected tab, sorry!) +- Docking: Fixed size constraints not working on single window holding on a dock id (still doesn't work on docked windows). +- Docking: Fixed CTRL+TAB back into a docked window not selecting menu layer when no item are on main layer. +- Viewports, IO: Added io.AddMouseViewportEvent() function to queue hovered viewport change (when known by backend). +- Viewports: Relaxed specs for backend supporting ImGuiBackendFlags_HasMouseHoveredViewport: it is now _optional_ + for the backend to have to ignore viewports with the _NoInputs flag when call io.AddMouseViewportEvent(). It is + much better if they can (Win32 and GLFW 3.3+ backends can, SDL and GLFW 3.2 backends cannot, they are lacking data). + A concrete example is: when dragging a viewport for docking, the viewport is marked with _NoInputs to allow us + to pick the target viewports for docking. If the backend reports a viewport with _NoInputs when calling the + io.AddMouseViewportEvent() function, then Dear ImGui will revert to its flawed heuristic to find the viewport under. + By lowering those specs, we allow the SDL and more backend to support this, only relying on the heuristic in a few + drag and drop situations rather that relying on it everywhere. +- Viewports: Fixed a CTRL+TAB crash with viewports enabled when the window list needs to appears in + its own viewport (regression from 1.86). (#4023, #787) +- Viewports: Fixed active InputText() from preventing viewports to merge. (#4212) +- Backends: SDL: Added support for ImGuiBackendFlags_HasMouseHoveredViewport now that its specs have been lowered. +- (Breaking) Removed ImGuiPlatformIO::Platform_SetImeInputPos() in favor of newly standardized + io.SetPlatformImeDataFn() function. Should not affect more than default backends. + ----------------------------------------------------------------------- VERSION 1.86 (Released 2021-12-22) @@ -2638,6 +3015,24 @@ Other Changes: wgpuRenderPassEncoderSetIndexBuffer() functions as validation layers appears to not do what the in-flux specs says. (#4766) [@meshula] +Docking+Viewports Branch: + +- Docking: Revert removal of io.ConfigDockingWithShift config option (removed in 1.83). (#4643) +- Docking: Fixed a bug undocking windows docked into a non-visible or _KeepAliveOnly dockspace + when unrelated windows submitted before the dockspace have dynamic visibility. (#4757) +- Docking, Style: Docked windows honor ImGuiCol_WindowBg. (#2700, #2539) +- Docking, Style: Docked windows honor display their border properly. (#2522) +- Docking: Fixed incorrectly rounded tab bars for dock node that are not at the top of their dock tree. +- Docking: Fixed single-frame node pos/size inconsistencies when window stop or start being submitted. +- Docking: Prevent docking any window created above a popup/modal. (#4317) +- Viewports: Made it possible to explicitly assign ImGuiWindowClass::ParentViewportId to 0 in order + to ensure a window is not parented. Previously this would use the global default (which might be 0, + but not always as it would depend on io.ConfigViewportsNoDefaultParent). (#3152, #2871) +- Viewports: Fixed tooltip in own viewport over modal from being incorrectly dimmed. (#4729) +- Viewports: Fixed CTRL+TAB highlight outline on docked windows not always fitting in host viewport. +- Backends: Made it possible to shutdown default Platform Backends before the Renderer backends. (#4656) +- Disabled: Fixed nested BeginDisabled()/EndDisabled() bug in Docking branch due to bad merge. (#4655, #4452, #4453, #4462) + ----------------------------------------------------------------------- VERSION 1.85 (Released 2021-10-12) @@ -2656,6 +3051,10 @@ Breaking Changes: Can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead but it's not very useful in practice, and the only use of it in the demo was illfit. Using 'GetContentRegionAvail().x' is generally a better choice. +- (Docking branch) IsWindowFocused() and IsWindowHovered() with only the _ChildWindows flag + and without the _RootWindow flag used to leak docking hierarchy, so a docked window would + return as the child of the window hosting the dockspace. This was inconsistent and incorrect + with other behaviors so we fixed it. Added a new _DockHierarchy flag to opt-in this behavior. Other Changes: @@ -2729,6 +3128,29 @@ Other Changes: - Backends: All renderers: Normalize clipping rect handling across backends. (#4464) - Examples: Added SDL + SDL_Renderer example in "examples/example_sdl_sdlrenderer/" folder. (#3926) [@1bsyl] +Docking+Viewports Branch: + +- IsWindowFocused: Fixed using ImGuiFocusedFlags_ChildWindows (without _RootWindow) from leaking the + docking hierarchy. Added ImGuiFocusedFlags_DockHierarchy flag to consider docking hierarchy in the test. +- IsWindowHovered: Fixed using ImGuiHoveredFlags_ChildWindows (without _RootWindow) from leaking the + docking hierarchy. Added ImGuiHoveredFlags_DockHierarchy flag to consider docking hierarchy in the test. +- Nav: Fixed an issue with losing focus on docked windows when pressing Alt while keyboard navigation + is disabled. (#4547, #4439) [@PathogenDavid] +- Docking: Fixed IsItemHovered() and functions depending on it (e.g. BeginPopupContextItem()) when + called after Begin() on a docked window (broken 2021/03/04). (#3851) +- Docking: Improved resizing system so that non-central zone are better at keeping their fixed size. + The algorithm is still not handling the allocation of size ideally for nested sibling, but it got better. +- Docking: Fixed settings load issue when mouse wheeling. (#4310) +- Docking: Fixed manually created floating node with a central node from not hiding when windows are gone. +- Docking + Drag and Drop: Fixed using BeginDragDropSource() or BeginDragDropTarget() inside a Begin() + that returned false because the window is docked. (#4515) +- Viewports: Fixed a crash while a window owning its viewport disappear while being dragged. + It would manifest when e.g. reconfiguring dock nodes while dragging. +- Viewports: Fixed unnecessary creation of temporary viewports when multiple docked windows + got reassigned to a new node (created mid-frame) which already has a HostWindow. +- Viewports: Fixed window with viewport ini data immediately merged into a host viewport from + leaving a temporary viewport alive for a frame (would leak into backend). + ----------------------------------------------------------------------- VERSION 1.84.2 (Released 2021-08-23) @@ -2865,6 +3287,25 @@ Other Changes: - Examples: Updated all .vcxproj to VS2015 (toolset v140) to facilitate usage with vcpkg. - Examples: SDL2: Accommodate for vcpkg install having headers in SDL2/SDL.h vs SDL.h. +Docking+Viewports Branch: + +- Docking: Clicking on the right-most close button of a docking node closes all windows. (#4186) +- Docking: Fix IsWindowAppearing() and ImGuiCond_Appearing on docked windows. (#4177, #3982, #1497, #1061) +- Docking: Fix crash using DockBuilderRemoveNode() in some situations. (#3111, #3179, #3203, #4295) [@hsimyu] +- Docking: Fix crash when a dock node gets re-qualified as dockspace>floating>dockspace, which tends to happen + when incorrectly calling DockBuilderAddNode() without ImGuiDockNodeFlags_Dockspace and using it as a Dockspace + on the next frame after the floating window hosting the node has been automatically created. (#3203, #4295) +- Docking: Reworked node flags saving/inheritance so that flags enforced by docked windows via the + DockNodeFlagsOverrideSet mechanism are are not left in empty dockspace nodes once the windows gets undocked. + (#4292, #3834, #3633, #3521, #3492, #3335, #2999, #2648) +- Docking: (Internal/Experimental) Removed DockNodeFlagsOverrideClear flags from ImGuiWindowClass as + it is ambiguous how to apply them and we haven't got a use out of them yet. +- Docking: Fixed ImGuiWindowFlags_UnsavedDocument clipping label in docked windows when there are + no close button. (#5745) +- Viewports: Fix popup/tooltip created without a parent window from being given a ParentViewportId value + from the implicit/fallback window. (#4236, #2409) +- Backends: Vulkan: Fix the use of the incorrect fence for secondary viewports. (#4208) [@FunMiles] + ----------------------------------------------------------------------- VERSION 1.83 (Released 2021-05-24) @@ -2949,6 +3390,22 @@ Other Changes: - Docs: Improvements to description of using colored glyphs/emojis. (#4169, #3369) - Docs: Improvements to minor mistakes in documentation comments (#3923) [@ANF-Studios] +Docking+Viewports Branch: + +- [Breaking] Removed io.ConfigDockingWithShift config option. Behavior always equivalent to having the + option set to false (dock/undock by default, hold shift to avoid docking). (#2109) +- Docking: DockSpace() returns its node ID. +- Docking: Dockspace() never draws a background. (#3924) +- Docking: Undocking nodes/windows covering most of the monitor max their size down to 90% to ease manipulations. +- Docking: Docking node tab bar honors ItemInnerSpacing.x before first tab. (#4130) +- Docking: Tweak rendering and alignment of dock node menu marker. (#4130) +- Docking: Fixed restoring of tab order within a dockspace or a split node. +- Docking: Fixed reappearing docked windows with no close button showing a tab with extraneous space for one frame. +- Docking: Fixed multiple simultaneously reappearing window from appearing undocked for one frame. +- Viewports: Hotfix for crash in monitor array access, caused by 4b9bc4902. (#3967) +- Backends, Viewports: GLFW: Add a workaround for stuck keys after closing a GLFW window (#3837). +- Backends, Viewports: Vulkan: Rebuild swapchain on VK_SUBOPTIMAL_KHR. (#3881) + ----------------------------------------------------------------------- VERSION 1.82 (Released 2021-02-15) @@ -3044,6 +3501,13 @@ Other Changes: scheduled builds builds are not required. [@rokups] - Log/Capture: Added LogTextV, a va_list variant of LogText. [@PathogenDavid] +Docking+Viewports Branch: + +- Viewports: Fix setting of ImGuiViewportFlags_NoRendererClear. (#3213) +- Viewports: Added GetViewportPlatformMonitor() with a safety net to keep code portable. +- Viewports, Backends: SDL: Fix missing ImGuiBackendFlags_HasSetMousePos flag in docking branch. +- Viewports, Backends: GLFW: Fix application of WantSetMousePos. (#1542, #787) + ----------------------------------------------------------------------- VERSION 1.81 (Released 2021-02-10) @@ -3125,6 +3589,17 @@ Other Changes: - Examples: WebGPU: Added Emscripten+WebGPU example. (#3632) [@bfierz] - Backends: GLFW: Added ImGui_ImplGlfw_InitForOther() initialization call to use with non OpenGL API. (#3632) +Docking+Viewports Branch: + +- Docking: Fix losing docking information on closed windows for which the hosting node was split. (#3716) [@GamingMinds-DanielC] +- Docking: Fix gap in hit test hole when using ImGuiDockNodeFlags_PassthruCentralNode touching the edge of a viewport. (#3733) +- Viewports: (Breaking) removed ImGuiPlatformIO::MainViewport which is now pretty much unused and duplicate + (and misleading as we will evolve the concept). +- Viewports: (Breaking) turned ImGuiViewport::GetWorkPos(), ImGuiViewport::GetWorkSize() into regular fields + (WorkPos, WorkSize) before exposing in master branch. +- Viewports: Fix issue inferring viewport z-order when new popups gets created. (#3734) + Metrics updates. +- Viewports, Backends: Vulkan: handle VK_ERROR_OUT_OF_DATE_KHR when resizing secondary viewport (#3766, #3758) + ----------------------------------------------------------------------- VERSION 1.80 (Released 2021-01-21) @@ -3244,6 +3719,14 @@ Other Changes: - Docs: Split examples/README.txt into docs/BACKENDS.md and docs/EXAMPLES.md, and improved them. - Docs: Consistently renamed all occurrences of "binding" and "back-end" to "backend" in comments and docs. +Docking+Viewports Branch: + +- Docking: Docked windows honor change of tab and text colors. (#2771) +- Docking: Support for appending into existing tab-bar made to work with Docking + internal helper DockNodeBeginAmendTabBar(). +- Docking: Added experimental TabItemFlagsOverrideSet to ImGuiWindowClass. +- Viewports: Fixed incorrect whitening of popups above a modal if both use their own viewport. +- Viewports: Backends: Vulkan: Fixed build, removed extraneous pipeline creation. (#3459, #3579) + ----------------------------------------------------------------------- VERSION 1.79 (Released 2020-10-08) @@ -3338,6 +3821,21 @@ Other Changes: - Examples: DX12: Added '#define ImTextureID ImU64' in project and build files to also allow building on 32-bit systems. Added project to default Visual Studio solution file. (#301) +Docking+Viewports Branch: + +- Docking: DockSpace() emits ItemSize() properly (useful when not filling all space). +- Docking: Fixed docking while hovering a child window. (#3420) broken by 85a661d. Improve metrics debugging. +- Docking: Fix honoring payload filter with overlapping nodes (we incorrectly over-relied on g.HoveredDockNode + when making change for #3398). +- Docking: Fix handling of WindowMenuButtonPosition == ImGuiDir_None in Docking Nodes. (#3499) +- Viewports: Fixed a rare edge-case if the window targeted by CTRL+Tab stops being rendered. +- Viewports, Backends: DX12: Make secondary viewport format match main viewport one (#3462) {@BeastLe9enD] +- Viewports: Backends: Vulkan: Removed unused shader code. Fix leaks. Avoid unnecessary pipeline creation for main + viewport. (#3459) + Add ImGui_ImplVulkanH_CreateWindowSwapChain in ImGui_ImplVulkanH_CreateOrResizeWindow(). +- Viewports: Backends: DirectX9: Recover from D3DERR_DEVICELOST on secondary viewports. (#3424) +- Viewports, Backends: Win32: Fix toggling of ImGuiViewportFlags_TopMost (#3477) [@Kodokuna] +- Viewports: Backends: GLFW: Workaround for cases where glfwGetMonitorWorkarea fails (#3457) [@dougbinks] + ----------------------------------------------------------------------- VERSION 1.78 (Released 2020-08-18) @@ -3439,6 +3937,23 @@ Other Changes: - Examples: Vulkan: Fixed GLFW+Vulkan and SDL+Vulkan clear color not being set. (#3390) [@RoryO] - CI: Emscripten has stopped their support for their fastcomp backend, switching to latest sdk [@Xipiryon] +Docking+Viewports Branch: + +- Docking: Made DockBuilderAddNode() automatically call DockBuilderRemoveNode(). (#3399, #2109) +- Docking: Storing HoveredDockNode in context which can be useful for easily detecting e.g. hovering an + empty node. (#3398) +- Docking: Fixed docking overlay bits appearing at (0,0), because of 43bd80a. Most typically noticeable + when disabling multi-viewport. +- Docking: Workaround recovery for node created without the _DockSpace flags later becoming a DockSpace. (#3340) +- Docking: Rework size allocations to recover when there's no enough room for nodes + do not hold on + _WantLockSizeOnce forever. (#3328) +- Docking: Rework size allocation to allow user code to override node sizes. Not all edge cases will be + properly handled but this is a step toward toolbar emitting size constraints. +- Docking: Added experimental flags to perform more docking filtering and disable resize per axis. + Designed for toolbar patterns. +- Viewports, Backends, GLFW: Use GLFW_MOUSE_PASSTHROUGH when available. +- Viewports, Backends: DX12: Fixed issue on shutdown when viewports are disabled. (#3347) + ----------------------------------------------------------------------- VERSION 1.77 (Released 2020-06-29) @@ -3530,6 +4045,17 @@ Other Changes: - Examples: Apple: Fixed example_apple_metal and example_apple_opengl2 using imgui_impl_osx.mm not forwarding right and center mouse clicks. (#3260) [@nburrus] +Docking+Viewports Branch: + +- Viewports: Don't set ImGuiViewportFlags_NoRendererClear when ImGuiWindowFlags_NoBackground is set. (#3213) +- Viewports: Report minimized viewports as zero DisplaySize to be consistent with main branch. (#1542) +- Docking, Settings: Allow reload of settings data at runtime. (#2573) +- Backends, GLFW: Fix windows resizing incorrectly on Linux due to GLFW firing window positioning + callbacks on next frame after window is resized manually. (#2117) +- Backends: DX12: Fix OBJECT_DELETED_WHILE_STILL_IN_USE on viewport resizing. (#3210) +- Backends: DX12: Fix for crash caused by early resource release. (#3121) +- Backends, Win32: Request monitor update when DPI awareness is enabled to make sure they have the correct DPI settings. + ----------------------------------------------------------------------- VERSION 1.76 (Released 2020-04-12) @@ -3602,6 +4128,20 @@ Other Changes: - Examples: SDL+DX11: Fixed resizing main window. (#3057) [@joeslay] - Examples: Added SDL+Metal example application. (#3017) [@coding-jackalope] +Docking+Viewports Branch: + +- Docking: Fixed assert preventing dockspace from being created instead a hidden tab. (#3101) +- Viewports: Fixed secondary viewports accidentally merging into a minimized host viewport. (#3118) +- Viewports, Docking: Added per-viewport work area system for e.g. menu-bars. Fixed DockspaceOverViewport() + and demo code (overlay etc) accordingly. (#3035, #2889, #2474, #1542, #2109) +- Viewports: Improve menu positioning in multi-monitor setups. [@rokups] +- Viewports: Software mouse cursor is also scaled by current DpiScale. (amend #939) +- Viewports: Avoid manually clipping resize grips and borders, which messes up with automation ability + to locate those items. Also simpler and more standard. +- Viewports: Fix for UWP in the imgui_impl_win32.cpp IME handler. (#2895, #2892). +- Viewports: Bunch of extra of comments to facilitate setting up multi-viewports. +- Viewports, GLFW: Avoid using window positioning workaround for GLFW 3.3+ versions that have it fixed. + ----------------------------------------------------------------------- VERSION 1.75 (Released 2020-02-10) @@ -3700,6 +4240,21 @@ Other Changes: - Examples: Metal: Wrapped main loop in @autoreleasepool block to ensure allocations get freed even if underlying system event loop gets paused due to app nap. (#2910, #2917) [@bear24rw] +Docking+Viewports Branch: + +- Docking + Nav: Fixed messed up Ctrl+Tab order with docked windows. +- Docking + Nav: Fixed failing to restore NavId when refocusing a child within a docked window. +- Docking + Nav: Fixed failing to restore NavId when refocusing due to missing nav window (when + it stops being submitted). +- Docking: Fixed a bug where the tab bar of a hidden dockspace would keep requesting focus. (#2960) +- Docking: Added experimental DockNodeFlagsOverrideSet/DockNodeFlagsOverrideClear flags in ImGuiWindowClass + (currently experimenting with toolbar idioms). +- Viewports: Fix resizing viewport-owning windows when mouse pos is outside the InnerClipRect + (can happen with OS decoration enabled). +- Viewports: Preserve last known size for minimized main viewport to be consistent with secondary viewports. +- Backends: SDL: Honor NoTaskBarIcon flag under non Win32 OS. (#2117) +- Backends: GLFW, SDL: Platform monitors declared properly even if multi-viewport is not enabled. + ----------------------------------------------------------------------- VERSION 1.74 (Released 2019-11-25) @@ -3779,6 +4334,14 @@ Other Changes: - CI: Set up a bunch of continuous-integration tests using GitHub Actions. We now compile many of the example applications on Windows, Linux, MacOS, iOS, Emscripten. Removed Travis integration. (#2865) [@rokups] +Docking+Viewports Branch: + +- Docking: Can undock from the small triangle button. (#2109,. #2645) +- Docking: Fixed node->HasCloseButton not honoring ImGuiDockNodeFlags_NoCloseButton in a floating node, + leading to empty space at the right of tab-bars with those flags. (#2109) +- Docking: Made docked windows not use style.ChildRounding. +- Multi-viewports: Added multi-viewport support in the DX12 back-end. (#2851) [@obfuscate] + ----------------------------------------------------------------------- VERSION 1.73 (Released 2019-09-24) @@ -3841,6 +4404,28 @@ Other Changes: - Misc: Updated stb_rect_pack.h from 0.99 to 1.00 (fixes by @rygorous: off-by-1 bug in best-fit heuristic, fix handling of rectangles too large to fit inside texture). (#2762) [@tido64] +Docking+Viewports Branch: + +- Docking: Fix BeginDocked() path that creates node so that SetNextWindowDockID() doesn't immediately discard the node. (#2109) +- Docking: Fix for node created at the same time as windows that are still resizing (typically with + io.ConfigDockingAlwaysTabBar) to not be zero/min sized. (#2109). The fix delays their visibility by one frame, + which is not ideal but not very problematic as the .ini data gets populated after that. +- Docking: Fix a crash that could occur with a malformed ini file (DockNode Parent value pointing to a missing node). +- Viewport: Fix modal/popup window being stuck in unowned hidden viewport associated to fallback window without stealing + it back. Fix modal reference viewport when opened outside of another window. (#1542) +- Viewport: Modals don't need to set ImGuiViewportFlags_NoFocusOnClick, this also mitigate the issue described by #2445, + which becomes particularly bad with unfocused modal. (#1542) +- Viewport: better case case where host window gets moved and resized simultaneous (toggling maximized state). + There's no perfect solution there, than using io.ConfigViewportsNoAutoMerge = false. (#1542) +- Viewport, Docking: Fixed incorrect assignment of IsFallbackWindow which would tag dock node host windows created + in NewFrame() as such, messing with popup viewport inheritance. +- Viewport: Fixed issue where resize grip would display as hovered while mouse is still off the OS bounds so a click + would miss it and focus the OS window behind expected one. (#1542) +- Viewport: Fix to allow multiple shutdown / calls to DestroyPlatformWindows(). (#2769) +- Viewport: Backends: GLFW: Fix setting window size on macOS (#2767, #2117) [@rokups] +- Viewport: Backends: GLFW+Linux: Fix window having incorrect size after uncollapse. (#2756, #2117) [@rokups] +- Viewport: Backends: DirectX9: Workaround for windows not refreshing when main viewport has no draw call. (#2560) + ----------------------------------------------------------------------- VERSION 1.72b (Released 2019-07-31) @@ -3943,6 +4528,25 @@ Other Changes: (#2482, #2632) [@josiahmanson] - Examples: Added SDL2+DirectX11 example application. (#2632, #2612, #2482) [@vincenthamm] +Docking+Viewports Branch: + +- Docking: Making it possible to undock a node by clicking on the tab bar / title bar for the node. (#2645). +- Docking: Explicitly inhibit constraint when docked for now. Fix clipping issue related to constraints. (#2690). +- Docking: Fixed dragging/resizing from OS decoration not marking settings as dirty. +- Docking: Renamed io.ConfigDockingTabBarOnSingleWindows to io.ConfigDockingAlwaysTabBar. + Added ImGuiWindowClass::DockingAlwaysTabBar to set on individual windows. +- Docking: Perform simple check: assert if Docking or Viewport are enabled exactly on frame 1 (instead of frame 0 + or later), which is a common user error leading to loss of .ini data on load. +- Docking: Fix so that an appearing window making a dock node reappear won't have a zero-size on its first frame. +- Docking: Fixed using ImGuiDockNodeFlags_AutoHideTabBar with io.ConfigDockingTabBarOnSingleWindows. +- Docking: Added ImGuiDockNode to .natvis file. +- Docking: Fixed support for large meshes in GetBackgroundDrawList(), GetForegroundDrawList(). (#2638) +- Viewport: Fix monitor dpi info not being copied to main viewport when multi-viewports are not enabled. (#2621, #1676) +- Viewport: Refactored ImGuiWindowClass's ViewportFlagsOverrideMask + ViewportFlagsOverrideValue into + ViewportFlagsOverrideSet + ViewportFlagsOverrideClear which appears easier to grasp. (#1542) +- Viewport: Added ImGuiViewportFlags_NoAutoMerge to prevent merging into host viewport in a per-window basis + via the ImGuiWindowClass override mechanism. (#1542) + ----------------------------------------------------------------------- VERSION 1.71 (Released 2019-06-12) @@ -4282,6 +4886,7 @@ Breaking Changes: - Renamed ImFontAtlas::GlyphRangesBuilder to ImFontGlyphRangesBuilder. Kept redirection typedef (will obsolete). Other Changes: + - Added BETA api for Tab Bar/Tabs widgets: (#261, #351) - Added BeginTabBar(), EndTabBar(), BeginTabItem(), EndTabItem(), SetTabItemClosed() API. - Added ImGuiTabBarFlags flags for BeginTabBar(). diff --git a/neo/libs/imgui/docs/FONTS.md b/neo/libs/imgui/docs/FONTS.md index c451af61c..e36afdf81 100644 --- a/neo/libs/imgui/docs/FONTS.md +++ b/neo/libs/imgui/docs/FONTS.md @@ -110,8 +110,6 @@ ImGui::PopFont(); **For advanced options create a ImFontConfig structure and pass it to the AddFont() function (it will be copied internally):** ```cpp ImFontConfig config; -config.OversampleH = 2; -config.OversampleV = 1; config.GlyphExtraSpacing.x = 1.0f; ImFont* font = io.Fonts->AddFontFromFileTTF("font.ttf", size_pixels, &config); ``` diff --git a/neo/libs/imgui/docs/TODO.txt b/neo/libs/imgui/docs/TODO.txt index 2a42874cd..3aec732a2 100644 --- a/neo/libs/imgui/docs/TODO.txt +++ b/neo/libs/imgui/docs/TODO.txt @@ -127,7 +127,35 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - separator: width, thickness, centering (#1643, #2657) - splitter: formalize the splitter idiom into an official api (we want to handle n-way split) (#319) - - docking: merge docking branch (#2109) + - docking: B: ordering currently held in tab bar should be implicitly held by windows themselves (also see #2304) + - docking: B- tab bar: the order/focus restoring code could be part of TabBar and not DockNode? (#8) + - docking: B~ rework code to be able to lazily create tab bar instance in a single place. The _Unsorted tab flag could be replacing a trailing-counter in DockNode? + - docking: B~ fully track windows/settings reference in dock nodes. perhaps find a representation that allows facilitate use of dock builder functions. + - docking: B~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete). this is mostly working but the DockBuilderXXX api are not exposed/finished. + - docking: B: when docking outer, perform size locking on neighbors nodes the same way we do it with splitters, so other nodes are not resized. + - docking: B~ central node resizing behavior incorrect. + - docking: B: changing title font/style per-window is not supported as dock nodes are created in NewFrame. + - docking: B- dock node inside its own viewports creates 1 temporary viewport per window on startup before ditching them (doesn't affect the user nor request platform windows to be created, but unnecessary) + - docking: B- resize sibling locking behavior may be less desirable if we merged same-axis sibling in a same node level? + - docking: B- single visible node part of a hidden split hierarchy (OnlyNodeWithWindows != NULL) should show a normal title bar (not a tab bar) + - docking: B~ SetNextWindowDock() calls (with conditional) -> defer everything to DockContextUpdate (repro: Documents->[X]Windows->Dock 1 elsewhere->Click Redock All + - docking: B~ tidy up tab list popup buttons features (available with manual tab-bar, see ImGuiTabBarFlags_NoTabListPopupButton code, not used by docking nodes) + - docking: B- SetNextWindowDockId(0) with a second Begin() in the frame will asserts + - docking: B: resize grip drawn in host window typically appears under scrollbar. + - docking: B: resize grip auto-resize on multiple node hierarchy doesn't make much sense or should be improved? + - docking: B- SetNextWindowFocus() doesn't seem to apply if the window is hidden this frame, need repro (#4) + - docking: B- resizing a dock tree small currently has glitches (overlapping collapse and close button, etc.) + - docking: B- dpi: look at interaction with the hi-dpi and multi-dpi stuff. + - docking: B- tab bar: appearing on first frame with a dumb layout would do less harm that not appearing? (when behind dynamic branch) or store titles + render in EndTabBar() + - docking: B- tab bar: make selected tab always shows its full title? + - docking: B- hide close button on single tab bar? + - docking: B- nav: design interactions so nav controls can dock/undock + - docking: B- dockspace: flag to lock the dock tree and/or sizes (ImGuiDockNodeFlags_Locked?) + - docking: B- reintroduce collapsing a floating dock node. also collapsing a docked dock node! + - docking: B- allow dragging a non-floating dock node by clicking on the title-bar-looking section (not just the collapse/menu button) + - docking: B- option to remember undocked window size? (instead of keeping their docked size) (relate to #2104) + - docking: C- nav: CTRL+TAB highlighting tabs shows the mismatch between focus-stack and tab-order (not visible in VS because it doesn't highlight the tabs) + - docking: C- after a dock/undock, the Scrollbar Status update in Begin() should use an updated e.g. size_y_for_scrollbars to avoid a 1 frame scrollbar flicker. - tabs: "there is currently a problem because TabItem() will try to submit their own tooltip after 0.50 second, and this will have the effect of making your tooltip flicker once." -> tooltip priority work (WIP branch) - tabs: make EndTabBar fail if users doesn't respect BeginTabBar return value, for consistency/future-proofing. @@ -299,6 +327,20 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i - nav/windowing: Resizing window will currently fail with certain types of resizing constraints/callback applied - focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622) + - viewport: make it possible to have no main/hosting viewport + - viewport: We set ImGuiViewportFlags_NoFocusOnAppearing in a way that is required for GLFW/SDL binding, but could be handled better without + on a custom e.g. Win32 bindings. It prevents newly dragged-out viewports from taking the focus, which makes ALT+F4 more ambiguous. + - viewport: not focusing newly undocked viewport means clicking back on previous one doesn't bring OS window to front. + - viewport: with platform decoration enabled, platform may force constraint (e.g. minimum size) + - viewport: use getfocus/setfocus api to synchronize imgui<>platform focus better (e.g imgui-side ctrl-tab can focus os window, OS initial setup and alt-tab can focus imgui window etc.) + - viewport: store per-viewport/monitor DPI in .ini file so an application reload or main window changing DPI on reload can be properly patched for. + - viewport: implicit/fallback Debug window can hog a zombie viewport (harmless, noisy?) > could at least clear out the reference on a per session basis? + - viewport: need to clarify how to use GetMousePos() from a user point of view. + - platform: glfw: no support for ImGuiBackendFlags_HasMouseHoveredViewport. + - platform: sdl: no support for ImGuiBackendFlags_HasMouseHoveredViewport. maybe we could use SDL_GetMouseFocus() / SDL_WINDOW_MOUSE_FOCUS if imgui could fallback on its heuristic when NoInputs is set + - platform: sdl: no refresh of monitor/display (SDL doesn't seem to have an event for it). + - platform: sdl: multi-viewport + minimized window seems to break mouse wheel events (at least under Win32). + - inputs: support track pad style scrolling & slider edit. - inputs/io: backspace and arrows in the context of a text input could use system repeat rate. - inputs/io: clarify/standardize/expose repeat rate and repeat delays (#1808) diff --git a/neo/libs/imgui/examples/example_allegro5/main.cpp b/neo/libs/imgui/examples/example_allegro5/main.cpp index 3ca061cc6..298c8451d 100644 --- a/neo/libs/imgui/examples/example_allegro5/main.cpp +++ b/neo/libs/imgui/examples/example_allegro5/main.cpp @@ -40,6 +40,7 @@ int main(int, char**) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking // Setup Dear ImGui style ImGui::StyleColorsDark(); diff --git a/neo/libs/imgui/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj b/neo/libs/imgui/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj index 4bb4fc288..3ebf9ccf9 100644 --- a/neo/libs/imgui/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj +++ b/neo/libs/imgui/examples/example_apple_metal/example_apple_metal.xcodeproj/project.pbxproj @@ -7,7 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 050450AB2768052600AB6805 /* imgui_tables.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5079822D257677DB0038A28D /* imgui_tables.cpp */; }; + 050450AD276863B000AB6805 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 050450AC276863B000AB6805 /* QuartzCore.framework */; }; 05318E0F274C397200A8DE2E /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05318E0E274C397200A8DE2E /* GameController.framework */; }; + 05A275442773BEA20084EF39 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05A275432773BEA20084EF39 /* QuartzCore.framework */; }; 07A82ED82139413D0078D120 /* imgui_widgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07A82ED72139413C0078D120 /* imgui_widgets.cpp */; }; 07A82ED92139418F0078D120 /* imgui_widgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 07A82ED72139413C0078D120 /* imgui_widgets.cpp */; }; 5079822E257677DB0038A28D /* imgui_tables.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5079822D257677DB0038A28D /* imgui_tables.cpp */; }; @@ -33,7 +36,11 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 050450AC276863B000AB6805 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 05318E0E274C397200A8DE2E /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; }; + 05A2754027728F5B0084EF39 /* imgui_impl_metal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imgui_impl_metal.h; path = ../../backends/imgui_impl_metal.h; sourceTree = ""; }; + 05A2754127728F5B0084EF39 /* imgui_impl_osx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imgui_impl_osx.h; path = ../../backends/imgui_impl_osx.h; sourceTree = ""; }; + 05A275432773BEA20084EF39 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.2.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; }; 07A82ED62139413C0078D120 /* imgui_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = imgui_internal.h; path = ../../imgui_internal.h; sourceTree = ""; }; 07A82ED72139413C0078D120 /* imgui_widgets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_widgets.cpp; path = ../../imgui_widgets.cpp; sourceTree = ""; }; 5079822D257677DB0038A28D /* imgui_tables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_tables.cpp; path = ../../imgui_tables.cpp; sourceTree = ""; }; @@ -66,6 +73,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05A275442773BEA20084EF39 /* QuartzCore.framework in Frameworks */, 8309BD8F253CCAAA0045E2A1 /* UIKit.framework in Frameworks */, 83BBE9E720EB46BD00295997 /* MetalKit.framework in Frameworks */, 83BBE9E520EB46B900295997 /* Metal.framework in Frameworks */, @@ -76,6 +84,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 050450AD276863B000AB6805 /* QuartzCore.framework in Frameworks */, 8309BDC6253CCCFE0045E2A1 /* AppKit.framework in Frameworks */, 83BBE9EC20EB471700295997 /* MetalKit.framework in Frameworks */, 05318E0F274C397200A8DE2E /* GameController.framework in Frameworks */, @@ -136,6 +145,8 @@ 83BBE9E320EB46B800295997 /* Frameworks */ = { isa = PBXGroup; children = ( + 050450AC276863B000AB6805 /* QuartzCore.framework */, + 05A275432773BEA20084EF39 /* QuartzCore.framework */, 05318E0E274C397200A8DE2E /* GameController.framework */, 8309BDC5253CCCFE0045E2A1 /* AppKit.framework */, 8309BD8E253CCAAA0045E2A1 /* UIKit.framework */, @@ -153,7 +164,9 @@ isa = PBXGroup; children = ( 5079822D257677DB0038A28D /* imgui_tables.cpp */, + 05A2754027728F5B0084EF39 /* imgui_impl_metal.h */, 8309BDB5253CCC9D0045E2A1 /* imgui_impl_metal.mm */, + 05A2754127728F5B0084EF39 /* imgui_impl_osx.h */, 8309BDB6253CCC9D0045E2A1 /* imgui_impl_osx.mm */, 83BBEA0420EB54E700295997 /* imconfig.h */, 83BBEA0320EB54E700295997 /* imgui.cpp */, @@ -268,9 +281,9 @@ 8309BDBB253CCCAD0045E2A1 /* imgui_impl_metal.mm in Sources */, 83BBEA0920EB54E700295997 /* imgui.cpp in Sources */, 83BBEA0720EB54E700295997 /* imgui_demo.cpp in Sources */, - 83BBEA0520EB54E700295997 /* imgui_draw.cpp in Sources */, + 83BBEA0520EB54E700295997 /* imgui_draw.cpp in Sources */, 5079822E257677DB0038A28D /* imgui_tables.cpp in Sources */, - 07A82ED82139413D0078D120 /* imgui_widgets.cpp in Sources */, + 07A82ED82139413D0078D120 /* imgui_widgets.cpp in Sources */, 8309BDA5253CCC070045E2A1 /* main.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -281,10 +294,10 @@ files = ( 8309BDBE253CCCB60045E2A1 /* imgui_impl_metal.mm in Sources */, 8309BDBF253CCCB60045E2A1 /* imgui_impl_osx.mm in Sources */, - 83BBEA0A20EB54E700295997 /* imgui.cpp in Sources */, - 83BBEA0820EB54E700295997 /* imgui_demo.cpp in Sources */, - 83BBEA0620EB54E700295997 /* imgui_draw.cpp in Sources */, - 5079822E257677DB0038A28D /* imgui_tables.cpp in Sources */, + 83BBEA0A20EB54E700295997 /* imgui.cpp in Sources */, + 83BBEA0820EB54E700295997 /* imgui_demo.cpp in Sources */, + 83BBEA0620EB54E700295997 /* imgui_draw.cpp in Sources */, + 050450AB2768052600AB6805 /* imgui_tables.cpp in Sources */, 07A82ED92139418F0078D120 /* imgui_widgets.cpp in Sources */, 8309BDA8253CCC080045E2A1 /* main.mm in Sources */, ); diff --git a/neo/libs/imgui/examples/example_apple_metal/main.mm b/neo/libs/imgui/examples/example_apple_metal/main.mm index 109ef6153..d184dd6eb 100644 --- a/neo/libs/imgui/examples/example_apple_metal/main.mm +++ b/neo/libs/imgui/examples/example_apple_metal/main.mm @@ -60,11 +60,21 @@ -(instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullabl ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Renderer backend ImGui_ImplMetal_Init(_device); @@ -195,6 +205,13 @@ -(void)drawInMTKView:(MTKView*)view // Present [commandBuffer presentDrawable:view.currentDrawable]; [commandBuffer commit]; + + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } } -(void)mtkView:(MTKView*)view drawableSizeWillChange:(CGSize)size diff --git a/neo/libs/imgui/examples/example_apple_opengl2/main.mm b/neo/libs/imgui/examples/example_apple_opengl2/main.mm index 815c0f72e..11829a820 100644 --- a/neo/libs/imgui/examples/example_apple_opengl2/main.mm +++ b/neo/libs/imgui/examples/example_apple_opengl2/main.mm @@ -47,11 +47,21 @@ -(void)initialize ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer backends ImGui_ImplOSX_Init(self); ImGui_ImplOpenGL2_Init(); @@ -136,6 +146,13 @@ -(void)updateAndDrawDemoView ImGui_ImplOpenGL2_RenderDrawData(draw_data); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + // Present [[self openGLContext] flushBuffer]; diff --git a/neo/libs/imgui/examples/example_glfw_metal/main.mm b/neo/libs/imgui/examples/example_glfw_metal/main.mm index e9bc63acb..2f346ffb8 100644 --- a/neo/libs/imgui/examples/example_glfw_metal/main.mm +++ b/neo/libs/imgui/examples/example_glfw_metal/main.mm @@ -33,11 +33,21 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows // Setup style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. @@ -69,7 +79,7 @@ int main(int, char**) id commandQueue = [device newCommandQueue]; // Setup Platform/Renderer backends - ImGui_ImplGlfw_InitForOpenGL(window, true); + ImGui_ImplGlfw_InitForOther(window, true); ImGui_ImplMetal_Init(device); NSWindow *nswin = glfwGetCocoaWindow(window); @@ -157,6 +167,13 @@ int main(int, char**) ImGui::Render(); ImGui_ImplMetal_RenderDrawData(ImGui::GetDrawData(), commandBuffer, renderEncoder); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + [renderEncoder popDebugGroup]; [renderEncoder endEncoding]; diff --git a/neo/libs/imgui/examples/example_glfw_opengl2/main.cpp b/neo/libs/imgui/examples/example_glfw_opengl2/main.cpp index 1fcec2b2a..df14dca69 100644 --- a/neo/libs/imgui/examples/example_glfw_opengl2/main.cpp +++ b/neo/libs/imgui/examples/example_glfw_opengl2/main.cpp @@ -52,11 +52,23 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL2_Init(); @@ -155,6 +167,17 @@ int main(int, char**) ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); //glUseProgram(last_program); + // Update and Render additional Platform Windows + // (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere. + // For this specific demo app we could also call glfwMakeContextCurrent(window) directly) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + GLFWwindow* backup_current_context = glfwGetCurrentContext(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + glfwMakeContextCurrent(backup_current_context); + } + glfwMakeContextCurrent(window); glfwSwapBuffers(window); } diff --git a/neo/libs/imgui/examples/example_glfw_opengl3/main.cpp b/neo/libs/imgui/examples/example_glfw_opengl3/main.cpp index e5f29d405..d02bb092e 100644 --- a/neo/libs/imgui/examples/example_glfw_opengl3/main.cpp +++ b/neo/libs/imgui/examples/example_glfw_opengl3/main.cpp @@ -83,11 +83,23 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForOpenGL(window, true); #ifdef __EMSCRIPTEN__ @@ -190,6 +202,17 @@ int main(int, char**) glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + // Update and Render additional Platform Windows + // (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere. + // For this specific demo app we could also call glfwMakeContextCurrent(window) directly) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + GLFWwindow* backup_current_context = glfwGetCurrentContext(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + glfwMakeContextCurrent(backup_current_context); + } + glfwSwapBuffers(window); } #ifdef __EMSCRIPTEN__ diff --git a/neo/libs/imgui/examples/example_glfw_vulkan/main.cpp b/neo/libs/imgui/examples/example_glfw_vulkan/main.cpp index b61dcaae0..9eb87f02b 100644 --- a/neo/libs/imgui/examples/example_glfw_vulkan/main.cpp +++ b/neo/libs/imgui/examples/example_glfw_vulkan/main.cpp @@ -61,7 +61,7 @@ static void glfw_error_callback(int error, const char* description) } static void check_vk_result(VkResult err) { - if (err == 0) + if (err == VK_SUCCESS) return; fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err); if (err < 0) @@ -263,17 +263,15 @@ static void CleanupVulkanWindow() static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) { - VkResult err; - VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; - err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); + VkResult err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - { g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) return; - } - check_vk_result(err); + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; { @@ -342,11 +340,11 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) info.pImageIndices = &wd->FrameIndex; VkResult err = vkQueuePresentKHR(g_Queue, &info); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - { g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) return; - } - check_vk_result(err); + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores } @@ -390,11 +388,23 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForVulkan(window, true); ImGui_ImplVulkan_InitInfo init_info = {}; @@ -505,17 +515,25 @@ int main(int, char**) // Rendering ImGui::Render(); - ImDrawData* draw_data = ImGui::GetDrawData(); - const bool is_minimized = (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f); - if (!is_minimized) + ImDrawData* main_draw_data = ImGui::GetDrawData(); + const bool main_is_minimized = (main_draw_data->DisplaySize.x <= 0.0f || main_draw_data->DisplaySize.y <= 0.0f); + wd->ClearValue.color.float32[0] = clear_color.x * clear_color.w; + wd->ClearValue.color.float32[1] = clear_color.y * clear_color.w; + wd->ClearValue.color.float32[2] = clear_color.z * clear_color.w; + wd->ClearValue.color.float32[3] = clear_color.w; + if (!main_is_minimized) + FrameRender(wd, main_draw_data); + + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - wd->ClearValue.color.float32[0] = clear_color.x * clear_color.w; - wd->ClearValue.color.float32[1] = clear_color.y * clear_color.w; - wd->ClearValue.color.float32[2] = clear_color.z * clear_color.w; - wd->ClearValue.color.float32[3] = clear_color.w; - FrameRender(wd, draw_data); - FramePresent(wd); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); } + + // Present Main Platform Window + if (!main_is_minimized) + FramePresent(wd); } // Cleanup diff --git a/neo/libs/imgui/examples/example_glfw_wgpu/main.cpp b/neo/libs/imgui/examples/example_glfw_wgpu/main.cpp index f510987ed..723b69da4 100644 --- a/neo/libs/imgui/examples/example_glfw_wgpu/main.cpp +++ b/neo/libs/imgui/examples/example_glfw_wgpu/main.cpp @@ -93,6 +93,7 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking // Setup Dear ImGui style ImGui::StyleColorsDark(); diff --git a/neo/libs/imgui/examples/example_glut_opengl2/main.cpp b/neo/libs/imgui/examples/example_glut_opengl2/main.cpp index 58539ca5f..4edae6f19 100644 --- a/neo/libs/imgui/examples/example_glut_opengl2/main.cpp +++ b/neo/libs/imgui/examples/example_glut_opengl2/main.cpp @@ -61,6 +61,7 @@ int main(int argc, char** argv) ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking // Setup Dear ImGui style ImGui::StyleColorsDark(); @@ -78,7 +79,6 @@ int main(int argc, char** argv) // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. ImGui_ImplGLUT_InstallFuncs(); - // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. diff --git a/neo/libs/imgui/examples/example_sdl2_directx11/main.cpp b/neo/libs/imgui/examples/example_sdl2_directx11/main.cpp index 47c852d27..7b7de21c6 100644 --- a/neo/libs/imgui/examples/example_sdl2_directx11/main.cpp +++ b/neo/libs/imgui/examples/example_sdl2_directx11/main.cpp @@ -71,11 +71,23 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer backends ImGui_ImplSDL2_InitForD3D(window); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); @@ -181,6 +193,13 @@ int main(int, char**) g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, clear_color_with_alpha); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + g_pSwapChain->Present(1, 0); // Present with vsync //g_pSwapChain->Present(0, 0); // Present without vsync } diff --git a/neo/libs/imgui/examples/example_sdl2_metal/main.mm b/neo/libs/imgui/examples/example_sdl2_metal/main.mm index d54812710..5cd704382 100644 --- a/neo/libs/imgui/examples/example_sdl2_metal/main.mm +++ b/neo/libs/imgui/examples/example_sdl2_metal/main.mm @@ -24,11 +24,21 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows // Setup style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. @@ -168,6 +178,13 @@ int main(int, char**) ImGui::Render(); ImGui_ImplMetal_RenderDrawData(ImGui::GetDrawData(), commandBuffer, renderEncoder); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + [renderEncoder popDebugGroup]; [renderEncoder endEncoding]; diff --git a/neo/libs/imgui/examples/example_sdl2_opengl2/main.cpp b/neo/libs/imgui/examples/example_sdl2_opengl2/main.cpp index a70edd225..4c537c376 100644 --- a/neo/libs/imgui/examples/example_sdl2_opengl2/main.cpp +++ b/neo/libs/imgui/examples/example_sdl2_opengl2/main.cpp @@ -57,11 +57,23 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer backends ImGui_ImplSDL2_InitForOpenGL(window, gl_context); ImGui_ImplOpenGL2_Init(); @@ -160,6 +172,19 @@ int main(int, char**) glClear(GL_COLOR_BUFFER_BIT); //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData()); + + // Update and Render additional Platform Windows + // (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere. + // For this specific demo app we could also call SDL_GL_MakeCurrent(window, gl_context) directly) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + SDL_Window* backup_current_window = SDL_GL_GetCurrentWindow(); + SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + SDL_GL_MakeCurrent(backup_current_window, backup_current_context); + } + SDL_GL_SwapWindow(window); } diff --git a/neo/libs/imgui/examples/example_sdl2_opengl3/main.cpp b/neo/libs/imgui/examples/example_sdl2_opengl3/main.cpp index 51add5c1f..4fc75c33b 100644 --- a/neo/libs/imgui/examples/example_sdl2_opengl3/main.cpp +++ b/neo/libs/imgui/examples/example_sdl2_opengl3/main.cpp @@ -97,11 +97,23 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer backends ImGui_ImplSDL2_InitForOpenGL(window, gl_context); ImGui_ImplOpenGL3_Init(glsl_version); @@ -207,6 +219,19 @@ int main(int, char**) glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + // Update and Render additional Platform Windows + // (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere. + // For this specific demo app we could also call SDL_GL_MakeCurrent(window, gl_context) directly) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + SDL_Window* backup_current_window = SDL_GL_GetCurrentWindow(); + SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + SDL_GL_MakeCurrent(backup_current_window, backup_current_context); + } + SDL_GL_SwapWindow(window); } #ifdef __EMSCRIPTEN__ diff --git a/neo/libs/imgui/examples/example_sdl2_sdlrenderer2/main.cpp b/neo/libs/imgui/examples/example_sdl2_sdlrenderer2/main.cpp index 31fa3f9bf..e922873ad 100644 --- a/neo/libs/imgui/examples/example_sdl2_sdlrenderer2/main.cpp +++ b/neo/libs/imgui/examples/example_sdl2_sdlrenderer2/main.cpp @@ -59,6 +59,7 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking // Setup Dear ImGui style ImGui::StyleColorsDark(); diff --git a/neo/libs/imgui/examples/example_sdl2_vulkan/main.cpp b/neo/libs/imgui/examples/example_sdl2_vulkan/main.cpp index b82ed0b7a..15cd6556e 100644 --- a/neo/libs/imgui/examples/example_sdl2_vulkan/main.cpp +++ b/neo/libs/imgui/examples/example_sdl2_vulkan/main.cpp @@ -49,7 +49,7 @@ static bool g_SwapChainRebuild = false; static void check_vk_result(VkResult err) { - if (err == 0) + if (err == VK_SUCCESS) return; fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err); if (err < 0) @@ -217,7 +217,7 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); // Select Present Mode -#ifdef APP_UNLIMITED_FRAME_RATE +#ifdef APP_USE_UNLIMITED_FRAME_RATE VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR }; #else VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR }; @@ -251,17 +251,15 @@ static void CleanupVulkanWindow() static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) { - VkResult err; - VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; - err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); + VkResult err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - { g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) return; - } - check_vk_result(err); + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; { @@ -330,11 +328,11 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) info.pImageIndices = &wd->FrameIndex; VkResult err = vkQueuePresentKHR(g_Queue, &info); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - { g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) return; - } - check_vk_result(err); + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores } @@ -390,11 +388,23 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer backends ImGui_ImplSDL2_InitForVulkan(window); ImGui_ImplVulkan_InitInfo init_info = {}; @@ -514,17 +524,25 @@ int main(int, char**) // Rendering ImGui::Render(); - ImDrawData* draw_data = ImGui::GetDrawData(); - const bool is_minimized = (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f); - if (!is_minimized) + ImDrawData* main_draw_data = ImGui::GetDrawData(); + const bool main_is_minimized = (main_draw_data->DisplaySize.x <= 0.0f || main_draw_data->DisplaySize.y <= 0.0f); + wd->ClearValue.color.float32[0] = clear_color.x * clear_color.w; + wd->ClearValue.color.float32[1] = clear_color.y * clear_color.w; + wd->ClearValue.color.float32[2] = clear_color.z * clear_color.w; + wd->ClearValue.color.float32[3] = clear_color.w; + if (!main_is_minimized) + FrameRender(wd, main_draw_data); + + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - wd->ClearValue.color.float32[0] = clear_color.x * clear_color.w; - wd->ClearValue.color.float32[1] = clear_color.y * clear_color.w; - wd->ClearValue.color.float32[2] = clear_color.z * clear_color.w; - wd->ClearValue.color.float32[3] = clear_color.w; - FrameRender(wd, draw_data); - FramePresent(wd); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); } + + // Present Main Platform Window + if (!main_is_minimized) + FramePresent(wd); } // Cleanup diff --git a/neo/libs/imgui/examples/example_sdl3_opengl3/main.cpp b/neo/libs/imgui/examples/example_sdl3_opengl3/main.cpp index 5520a7da9..c22f04861 100644 --- a/neo/libs/imgui/examples/example_sdl3_opengl3/main.cpp +++ b/neo/libs/imgui/examples/example_sdl3_opengl3/main.cpp @@ -93,11 +93,23 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer backends ImGui_ImplSDL3_InitForOpenGL(window, gl_context); ImGui_ImplOpenGL3_Init(glsl_version); @@ -203,6 +215,19 @@ int main(int, char**) glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + // Update and Render additional Platform Windows + // (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere. + // For this specific demo app we could also call SDL_GL_MakeCurrent(window, gl_context) directly) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + SDL_Window* backup_current_window = SDL_GL_GetCurrentWindow(); + SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + SDL_GL_MakeCurrent(backup_current_window, backup_current_context); + } + SDL_GL_SwapWindow(window); } #ifdef __EMSCRIPTEN__ diff --git a/neo/libs/imgui/examples/example_sdl3_sdlgpu3/main.cpp b/neo/libs/imgui/examples/example_sdl3_sdlgpu3/main.cpp index f025c537d..dbc7759ba 100644 --- a/neo/libs/imgui/examples/example_sdl3_sdlgpu3/main.cpp +++ b/neo/libs/imgui/examples/example_sdl3_sdlgpu3/main.cpp @@ -7,7 +7,7 @@ // - Introduction, links and more at the top of imgui.cpp // Important note to the reader who wish to integrate imgui_impl_sdlgpu3.cpp/.h in their own engine/app. -// - Unline other backends, the user must call the function Imgui_ImplSDLGPU_PrepareDrawData BEFORE issuing a SDL_GPURenderPass containing ImGui_ImplSDLGPU_RenderDrawData. +// - Unlike other backends, the user must call the function Imgui_ImplSDLGPU_PrepareDrawData() BEFORE issuing a SDL_GPURenderPass containing ImGui_ImplSDLGPU_RenderDrawData. // Calling the function is MANDATORY, otherwise the ImGui will not upload neither the vertex nor the index buffer for the GPU. See imgui_impl_sdlgpu3.cpp for more info. #include "imgui.h" @@ -54,6 +54,7 @@ int main(int, char**) printf("Error: SDL_ClaimWindowForGPUDevice(): %s\n", SDL_GetError()); return -1; } + SDL_SetGPUSwapchainParameters(gpu_device, window, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_MAILBOX); // Setup Dear ImGui context IMGUI_CHECKVERSION(); @@ -69,7 +70,7 @@ int main(int, char**) // Setup Platform/Renderer backends ImGui_ImplSDL3_InitForSDLGPU(window); ImGui_ImplSDLGPU3_InitInfo init_info = {}; - init_info.GpuDevice = gpu_device; + init_info.Device = gpu_device; init_info.ColorTargetFormat = SDL_GetGPUSwapchainTextureFormat(gpu_device, window); init_info.MSAASamples = SDL_GPU_SAMPLECOUNT_1; ImGui_ImplSDLGPU3_Init(&init_info); @@ -140,7 +141,7 @@ int main(int, char**) ImGui::Checkbox("Another Window", &show_another_window); ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f - ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color + ImGui::ColorEdit4("clear color", (float*)&clear_color); // Edit 3 floats representing a color if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) counter++; @@ -179,7 +180,7 @@ int main(int, char**) // Setup and start a render pass SDL_GPUColorTargetInfo target_info = {}; target_info.texture = swapchain_texture; - target_info.clear_color = SDL_FColor{ clear_color.x,clear_color.y,clear_color.z,clear_color.w }; + target_info.clear_color = SDL_FColor { clear_color.x, clear_color.y, clear_color.z, clear_color.w }; target_info.load_op = SDL_GPU_LOADOP_CLEAR; target_info.store_op = SDL_GPU_STOREOP_STORE; target_info.mip_level = 0; @@ -196,6 +197,7 @@ int main(int, char**) // Submit the command buffer SDL_SubmitGPUCommandBuffer(command_buffer); } + // Cleanup SDL_WaitForGPUIdle(gpu_device); ImGui_ImplSDL3_Shutdown(); diff --git a/neo/libs/imgui/examples/example_sdl3_sdlrenderer3/main.cpp b/neo/libs/imgui/examples/example_sdl3_sdlrenderer3/main.cpp index 4b9b86e9b..0e8331ed1 100644 --- a/neo/libs/imgui/examples/example_sdl3_sdlrenderer3/main.cpp +++ b/neo/libs/imgui/examples/example_sdl3_sdlrenderer3/main.cpp @@ -55,6 +55,7 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking // Setup Dear ImGui style ImGui::StyleColorsDark(); diff --git a/neo/libs/imgui/examples/example_sdl3_vulkan/main.cpp b/neo/libs/imgui/examples/example_sdl3_vulkan/main.cpp index e36f87194..5e0412c3a 100644 --- a/neo/libs/imgui/examples/example_sdl3_vulkan/main.cpp +++ b/neo/libs/imgui/examples/example_sdl3_vulkan/main.cpp @@ -54,7 +54,7 @@ static bool g_SwapChainRebuild = false; static void check_vk_result(VkResult err) { - if (err == 0) + if (err == VK_SUCCESS) return; fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err); if (err < 0) @@ -222,7 +222,7 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace); // Select Present Mode -#ifdef APP_UNLIMITED_FRAME_RATE +#ifdef APP_USE_UNLIMITED_FRAME_RATE VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR }; #else VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR }; @@ -256,17 +256,15 @@ static void CleanupVulkanWindow() static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) { - VkResult err; - VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; - err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); + VkResult err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - { g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) return; - } - check_vk_result(err); + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; { @@ -335,11 +333,11 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) info.pImageIndices = &wd->FrameIndex; VkResult err = vkQueuePresentKHR(g_Queue, &info); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - { g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) return; - } - check_vk_result(err); + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores } @@ -394,11 +392,23 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer backends ImGui_ImplSDL3_InitForVulkan(window); ImGui_ImplVulkan_InitInfo init_info = {}; @@ -518,17 +528,25 @@ int main(int, char**) // Rendering ImGui::Render(); - ImDrawData* draw_data = ImGui::GetDrawData(); - const bool is_minimized = (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f); - if (!is_minimized) + ImDrawData* main_draw_data = ImGui::GetDrawData(); + const bool main_is_minimized = (main_draw_data->DisplaySize.x <= 0.0f || main_draw_data->DisplaySize.y <= 0.0f); + wd->ClearValue.color.float32[0] = clear_color.x * clear_color.w; + wd->ClearValue.color.float32[1] = clear_color.y * clear_color.w; + wd->ClearValue.color.float32[2] = clear_color.z * clear_color.w; + wd->ClearValue.color.float32[3] = clear_color.w; + if (!main_is_minimized) + FrameRender(wd, main_draw_data); + + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - wd->ClearValue.color.float32[0] = clear_color.x * clear_color.w; - wd->ClearValue.color.float32[1] = clear_color.y * clear_color.w; - wd->ClearValue.color.float32[2] = clear_color.z * clear_color.w; - wd->ClearValue.color.float32[3] = clear_color.w; - FrameRender(wd, draw_data); - FramePresent(wd); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); } + + // Present Main Platform Window + if (!main_is_minimized) + FramePresent(wd); } // Cleanup diff --git a/neo/libs/imgui/examples/example_win32_directx10/main.cpp b/neo/libs/imgui/examples/example_win32_directx10/main.cpp index 21198da9a..a9e5e31fb 100644 --- a/neo/libs/imgui/examples/example_win32_directx10/main.cpp +++ b/neo/libs/imgui/examples/example_win32_directx10/main.cpp @@ -54,11 +54,23 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer backends ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX10_Init(g_pd3dDevice); @@ -167,6 +179,13 @@ int main(int, char**) g_pd3dDevice->ClearRenderTargetView(g_mainRenderTargetView, clear_color_with_alpha); ImGui_ImplDX10_RenderDrawData(ImGui::GetDrawData()); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + // Present HRESULT hr = g_pSwapChain->Present(1, 0); // Present with vsync //HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync @@ -186,7 +205,6 @@ int main(int, char**) } // Helper functions - bool CreateDeviceD3D(HWND hWnd) { // Setup swap chain diff --git a/neo/libs/imgui/examples/example_win32_directx11/main.cpp b/neo/libs/imgui/examples/example_win32_directx11/main.cpp index 5285df102..d25213bfd 100644 --- a/neo/libs/imgui/examples/example_win32_directx11/main.cpp +++ b/neo/libs/imgui/examples/example_win32_directx11/main.cpp @@ -54,11 +54,28 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; + //io.ConfigViewportsNoDefaultParent = true; + //io.ConfigDockingAlwaysTabBar = true; + //io.ConfigDockingTransparentPayload = true; + //io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts; // FIXME-DPI: Experimental. THIS CURRENTLY DOESN'T WORK AS EXPECTED. DON'T USE IN USER APP! + //io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; // FIXME-DPI: Experimental. // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer backends ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); @@ -167,6 +184,13 @@ int main(int, char**) g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, clear_color_with_alpha); ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + // Present HRESULT hr = g_pSwapChain->Present(1, 0); // Present with vsync //HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync @@ -186,7 +210,6 @@ int main(int, char**) } // Helper functions - bool CreateDeviceD3D(HWND hWnd) { // Setup swap chain @@ -241,6 +264,10 @@ void CleanupRenderTarget() if (g_mainRenderTargetView) { g_mainRenderTargetView->Release(); g_mainRenderTargetView = nullptr; } } +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 // From Windows SDK 8.1+ headers +#endif + // Forward declare message handler from imgui_impl_win32.cpp extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); @@ -269,6 +296,15 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) case WM_DESTROY: ::PostQuitMessage(0); return 0; + case WM_DPICHANGED: + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) + { + //const int dpi = HIWORD(wParam); + //printf("WM_DPICHANGED to %d (%.0f%%)\n", dpi, (float)dpi / 96.0f * 100.0f); + const RECT* suggested_rect = (RECT*)lParam; + ::SetWindowPos(hWnd, nullptr, suggested_rect->left, suggested_rect->top, suggested_rect->right - suggested_rect->left, suggested_rect->bottom - suggested_rect->top, SWP_NOZORDER | SWP_NOACTIVATE); + } + break; } return ::DefWindowProcW(hWnd, msg, wParam, lParam); } diff --git a/neo/libs/imgui/examples/example_win32_directx12/main.cpp b/neo/libs/imgui/examples/example_win32_directx12/main.cpp index 392ba18aa..d0a3a4282 100644 --- a/neo/libs/imgui/examples/example_win32_directx12/main.cpp +++ b/neo/libs/imgui/examples/example_win32_directx12/main.cpp @@ -23,8 +23,8 @@ #endif // Config for example app -static const int APP_NUM_FRAMES_IN_FLIGHT = 3; -static const int APP_NUM_BACK_BUFFERS = 3; +static const int APP_NUM_FRAMES_IN_FLIGHT = 2; +static const int APP_NUM_BACK_BUFFERS = 2; static const int APP_SRV_HEAP_SIZE = 64; struct FrameContext @@ -133,11 +133,23 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer backends ImGui_ImplWin32_Init(hwnd); @@ -272,6 +284,13 @@ int main(int, char**) g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&g_pd3dCommandList); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + // Present HRESULT hr = g_pSwapChain->Present(1, 0); // Present with vsync //HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync @@ -298,7 +317,6 @@ int main(int, char**) } // Helper functions - bool CreateDeviceD3D(HWND hWnd) { // Setup swap chain diff --git a/neo/libs/imgui/examples/example_win32_directx9/main.cpp b/neo/libs/imgui/examples/example_win32_directx9/main.cpp index 422248bcb..8063636d7 100644 --- a/neo/libs/imgui/examples/example_win32_directx9/main.cpp +++ b/neo/libs/imgui/examples/example_win32_directx9/main.cpp @@ -52,11 +52,23 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer backends ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX9_Init(g_pd3dDevice); @@ -177,6 +189,14 @@ int main(int, char**) ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData()); g_pd3dDevice->EndScene(); } + + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + HRESULT result = g_pd3dDevice->Present(nullptr, nullptr, nullptr, nullptr); if (result == D3DERR_DEVICELOST) g_DeviceLost = true; @@ -195,7 +215,6 @@ int main(int, char**) } // Helper functions - bool CreateDeviceD3D(HWND hWnd) { if ((g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == nullptr) @@ -231,6 +250,10 @@ void ResetDevice() ImGui_ImplDX9_CreateDeviceObjects(); } +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 // From Windows SDK 8.1+ headers +#endif + // Forward declare message handler from imgui_impl_win32.cpp extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); @@ -259,6 +282,15 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) case WM_DESTROY: ::PostQuitMessage(0); return 0; + case WM_DPICHANGED: + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) + { + //const int dpi = HIWORD(wParam); + //printf("WM_DPICHANGED to %d (%.0f%%)\n", dpi, (float)dpi / 96.0f * 100.0f); + const RECT* suggested_rect = (RECT*)lParam; + ::SetWindowPos(hWnd, nullptr, suggested_rect->left, suggested_rect->top, suggested_rect->right - suggested_rect->left, suggested_rect->bottom - suggested_rect->top, SWP_NOZORDER | SWP_NOACTIVATE); + } + break; } return ::DefWindowProcW(hWnd, msg, wParam, lParam); } diff --git a/neo/libs/imgui/examples/example_win32_opengl3/main.cpp b/neo/libs/imgui/examples/example_win32_opengl3/main.cpp index 8ecd27a20..ffc6e3da0 100644 --- a/neo/libs/imgui/examples/example_win32_opengl3/main.cpp +++ b/neo/libs/imgui/examples/example_win32_opengl3/main.cpp @@ -33,6 +33,42 @@ void CleanupDeviceWGL(HWND hWnd, WGL_WindowData* data); void ResetDeviceWGL(); LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +// Support function for multi-viewports +// Unlike most other backend combination, we need specific hooks to combine Win32+OpenGL. +// We could in theory decide to support Win32-specific code in OpenGL backend via e.g. an hypothetical ImGui_ImplOpenGL3_InitForRawWin32(). +static void Hook_Renderer_CreateWindow(ImGuiViewport* viewport) +{ + assert(viewport->RendererUserData == NULL); + + WGL_WindowData* data = IM_NEW(WGL_WindowData); + CreateDeviceWGL((HWND)viewport->PlatformHandle, data); + viewport->RendererUserData = data; +} + +static void Hook_Renderer_DestroyWindow(ImGuiViewport* viewport) +{ + if (viewport->RendererUserData != NULL) + { + WGL_WindowData* data = (WGL_WindowData*)viewport->RendererUserData; + CleanupDeviceWGL((HWND)viewport->PlatformHandle, data); + IM_DELETE(data); + viewport->RendererUserData = NULL; + } +} + +static void Hook_Platform_RenderWindow(ImGuiViewport* viewport, void*) +{ + // Activate the platform window DC in the OpenGL rendering context + if (WGL_WindowData* data = (WGL_WindowData*)viewport->RendererUserData) + wglMakeCurrent(data->hDC, g_hRC); +} + +static void Hook_Renderer_SwapBuffers(ImGuiViewport* viewport, void*) +{ + if (WGL_WindowData* data = (WGL_WindowData*)viewport->RendererUserData) + ::SwapBuffers(data->hDC); +} + // Main code int main(int, char**) { @@ -62,15 +98,39 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer backends ImGui_ImplWin32_InitForOpenGL(hwnd); ImGui_ImplOpenGL3_Init(); + // Win32+GL needs specific hooks for viewport, as there are specific things needed to tie Win32 and GL api. + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + IM_ASSERT(platform_io.Renderer_CreateWindow == NULL); + IM_ASSERT(platform_io.Renderer_DestroyWindow == NULL); + IM_ASSERT(platform_io.Renderer_SwapBuffers == NULL); + IM_ASSERT(platform_io.Platform_RenderWindow == NULL); + platform_io.Renderer_CreateWindow = Hook_Renderer_CreateWindow; + platform_io.Renderer_DestroyWindow = Hook_Renderer_DestroyWindow; + platform_io.Renderer_SwapBuffers = Hook_Renderer_SwapBuffers; + platform_io.Platform_RenderWindow = Hook_Platform_RenderWindow; + } + // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. @@ -163,6 +223,16 @@ int main(int, char**) glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + + // Restore the OpenGL rendering context to the main window DC, since platform windows might have changed it. + wglMakeCurrent(g_MainWindow.hDC, g_hRC); + } + // Present ::SwapBuffers(g_MainWindow.hDC); } diff --git a/neo/libs/imgui/examples/example_win32_vulkan/main.cpp b/neo/libs/imgui/examples/example_win32_vulkan/main.cpp index c6c138695..c2004ada6 100644 --- a/neo/libs/imgui/examples/example_win32_vulkan/main.cpp +++ b/neo/libs/imgui/examples/example_win32_vulkan/main.cpp @@ -50,7 +50,7 @@ static bool g_SwapChainRebuild = false; static void check_vk_result(VkResult err) { - if (err == 0) + if (err == VK_SUCCESS) return; fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err); if (err < 0) @@ -252,17 +252,15 @@ static void CleanupVulkanWindow() static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data) { - VkResult err; - VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore; - err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); + VkResult err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - { g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) return; - } - check_vk_result(err); + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; { @@ -331,16 +329,26 @@ static void FramePresent(ImGui_ImplVulkanH_Window* wd) info.pImageIndices = &wd->FrameIndex; VkResult err = vkQueuePresentKHR(g_Queue, &info); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) - { g_SwapChainRebuild = true; + if (err == VK_ERROR_OUT_OF_DATE_KHR) return; - } - check_vk_result(err); + if (err != VK_SUBOPTIMAL_KHR) + check_vk_result(err); wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; // Now we can use the next set of semaphores } LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +// FIXME: This code would ideally be inside imgui_impl_win32.cpp, it would create a dependency on Vulkan headers in imgui_impl_win32.cpp +static int ImGui_ImplWin32_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface) +{ + VkWin32SurfaceCreateInfoKHR createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + createInfo.hwnd = (HWND)viewport->PlatformHandleRaw; + createInfo.hinstance = ::GetModuleHandle(nullptr); + return (int)vkCreateWin32SurfaceKHR((VkInstance)vk_instance, &createInfo, (VkAllocationCallbacks*)vk_allocator, (VkSurfaceKHR*)out_vk_surface); +} + // Main code int main(int, char**) { @@ -381,13 +389,27 @@ int main(int, char**) ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoTaskBarIcons; + //io.ConfigFlags |= ImGuiConfigFlags_ViewportsNoMerge; // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer backends ImGui_ImplWin32_Init(hwnd); + ImGui::GetPlatformIO().Platform_CreateVkSurface = ImGui_ImplWin32_CreateVkSurface; + ImGui_ImplVulkan_InitInfo init_info = {}; init_info.Instance = g_Instance; init_info.PhysicalDevice = g_PhysicalDevice; @@ -487,17 +509,25 @@ int main(int, char**) // Rendering ImGui::Render(); - ImDrawData* draw_data = ImGui::GetDrawData(); - const bool is_minimized = (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f); - if (!is_minimized) + ImDrawData* main_draw_data = ImGui::GetDrawData(); + const bool main_is_minimized = (main_draw_data->DisplaySize.x <= 0.0f || main_draw_data->DisplaySize.y <= 0.0f); + wd->ClearValue.color.float32[0] = clear_color.x * clear_color.w; + wd->ClearValue.color.float32[1] = clear_color.y * clear_color.w; + wd->ClearValue.color.float32[2] = clear_color.z * clear_color.w; + wd->ClearValue.color.float32[3] = clear_color.w; + if (!main_is_minimized) + FrameRender(wd, main_draw_data); + + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { - wd->ClearValue.color.float32[0] = clear_color.x * clear_color.w; - wd->ClearValue.color.float32[1] = clear_color.y * clear_color.w; - wd->ClearValue.color.float32[2] = clear_color.z * clear_color.w; - wd->ClearValue.color.float32[3] = clear_color.w; - FrameRender(wd, draw_data); - FramePresent(wd); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); } + + // Present Main Platform Window + if (!main_is_minimized) + FramePresent(wd); } // Cleanup diff --git a/neo/libs/imgui/imgui.cpp b/neo/libs/imgui/imgui.cpp index 63dbd1fe8..01180d032 100644 --- a/neo/libs/imgui/imgui.cpp +++ b/neo/libs/imgui/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.7 +// dear imgui, v1.91.8 WIP // (main code and documentation) // Help: @@ -92,6 +92,7 @@ CODE // [SECTION] SETTINGS // [SECTION] LOCALIZATION // [SECTION] VIEWPORTS, PLATFORM WINDOWS +// [SECTION] DOCKING // [SECTION] PLATFORM DEPENDENT HELPERS // [SECTION] METRICS/DEBUGGER WINDOW // [SECTION] DEBUG LOG WINDOW @@ -430,6 +431,16 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. +(Docking/Viewport Branch) + - 2025/XX/XX (1.XXXX) - when multi-viewports are enabled, all positions will be in your natural OS coordinates space. It means that: + - reference to hard-coded positions such as in SetNextWindowPos(ImVec2(0,0)) are probably not what you want anymore. + you may use GetMainViewport()->Pos to offset hard-coded positions, e.g. SetNextWindowPos(GetMainViewport()->Pos) + - likewise io.MousePos and GetMousePos() will use OS coordinates. + If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos. + + - 2025/01/22 (1.91.8) - removed ImGuiColorEditFlags_AlphaPreview (made value 0): it is now the default behavior. + prior to 1.91.8: alpha was made opaque in the preview by default _unless_ using ImGuiColorEditFlags_AlphaPreview. We now display the preview as transparent by default. You can use ImGuiColorEditFlags_AlphaOpaque to use old behavior. + the new flags (ImGuiColorEditFlags_AlphaOpaque, ImGuiColorEditFlags_AlphaNoBg + existing ImGuiColorEditFlags_AlphaPreviewHalf) may be combined better and allow finer controls: - 2025/01/14 (1.91.7) - renamed ImGuiTreeNodeFlags_SpanTextWidth to ImGuiTreeNodeFlags_SpanLabelWidth for consistency with other names. Kept redirection enum (will obsolete). (#6937) - 2024/11/27 (1.91.6) - changed CRC32 table from CRC32-adler to CRC32c polynomial in order to be compatible with the result of SSE 4.2 instructions. As a result, old .ini data may be partially lost (docking and tables information particularly). @@ -522,6 +533,9 @@ CODE - new: IsMouseClicked(ImGuiMouseButton button, ImGuiInputFlags flags, ImGuiID owner_id = 0); for various reasons those changes makes sense. They are being made because making some of those API public. only past users of imgui_internal.h with the extra parameters will be affected. Added asserts for valid flags in various functions to detect _some_ misuses, BUT NOT ALL. + - 2024/05/21 (1.90.7) - docking: changed signature of DockSpaceOverViewport() to add explicit dockspace id if desired. pass 0 to use old behavior. (#7611) + - old: DockSpaceOverViewport(const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, ...); + - new: DockSpaceOverViewport(ImGuiID dockspace_id = 0, const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, ...); - 2024/05/16 (1.90.7) - inputs: on macOS X, Cmd and Ctrl keys are now automatically swapped by io.AddKeyEvent() as this naturally align with how macOS X uses those keys. - it shouldn't really affect you unless you had custom shortcut swapping in place for macOS X apps. - removed ImGuiMod_Shortcut which was previously dynamically remapping to Ctrl or Cmd/Super. It is now unnecessary to specific cross-platform idiomatic shortcuts. (#2343, #4084, #5923, #456) @@ -1176,6 +1190,9 @@ static const ImVec2 TOOLTIP_DEFAULT_OFFSET_MOUSE = ImVec2(16, 10); // Multi static const ImVec2 TOOLTIP_DEFAULT_OFFSET_TOUCH = ImVec2(0, -20); // Multiplied by g.Style.MouseCursorScale static const ImVec2 TOOLTIP_DEFAULT_PIVOT_TOUCH = ImVec2(0.5f, 1.0f); // Multiplied by g.Style.MouseCursorScale +// Docking +static const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA = 0.50f; // For use with io.ConfigDockingTransparentPayload. Apply to Viewport _or_ WindowBg in host viewport. + //------------------------------------------------------------------------- // [SECTION] FORWARD DECLARATIONS //------------------------------------------------------------------------- @@ -1253,10 +1270,22 @@ static void RenderWindowTitleBarContents(ImGuiWindow* window, const static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col); static void RenderDimmedBackgrounds(); static void SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect); +static void SetLastItemDataForChildWindowItem(ImGuiWindow* window, const ImRect& rect); // Viewports const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHashStr("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. +static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags); +static void DestroyViewport(ImGuiViewportP* viewport); static void UpdateViewportsNewFrame(); +static void UpdateViewportsEndFrame(); +static void WindowSelectViewport(ImGuiWindow* window); +static void WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_window_in_stack); +static bool UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport); +static bool UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window); +static bool GetWindowAlwaysWantOwnViewport(ImGuiWindow* window); +static int FindPlatformMonitorForPos(const ImVec2& pos); +static int FindPlatformMonitorForRect(const ImRect& r); +static void UpdateViewportPlatformMonitor(ImGuiViewportP* viewport); } @@ -1334,11 +1363,11 @@ ImGuiStyle::ImGuiStyle() GrabMinSize = 12.0f; // Minimum width/height of a grab box for slider/scrollbar GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero. - TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. + TabRounding = 5.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. TabBorderSize = 0.0f; // Thickness of border around tabs. TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. TabBarBorderSize = 1.0f; // Thickness of tab-bar separator, which takes on the tab active color to denote focus. - TabBarOverlineSize = 2.0f; // Thickness of tab-bar overline, which highlights the selected tab-bar. + TabBarOverlineSize = 1.0f; // Thickness of tab-bar overline, which highlights the selected tab-bar. TableAngledHeadersAngle = 35.0f * (IM_PI / 180.0f); // Angle of angled headers (supported values range from -50 degrees to +50 degrees). TableAngledHeadersTextAlign = ImVec2(0.5f,0.0f);// Alignment of angled headers within the cell ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. @@ -1349,6 +1378,7 @@ ImGuiStyle::ImGuiStyle() SeparatorTextPadding = ImVec2(20.0f,3.f);// Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y. DisplayWindowPadding = ImVec2(19,19); // Window position are clamped to be visible within the display area or monitors by at least this amount. Only applies to regular windows. DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. + DockingSeparatorSize = 2.0f; // Thickness of resizing border between docked windows MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. AntiAliasedLines = true; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. AntiAliasedLinesUseTex = true; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). @@ -1393,6 +1423,7 @@ void ImGuiStyle::ScaleAllSizes(float scale_factor) TabMinWidthForCloseButton = (TabMinWidthForCloseButton != FLT_MAX) ? ImTrunc(TabMinWidthForCloseButton * scale_factor) : FLT_MAX; TabBarOverlineSize = ImTrunc(TabBarOverlineSize * scale_factor); SeparatorTextPadding = ImTrunc(SeparatorTextPadding * scale_factor); + DockingSeparatorSize = ImTrunc(DockingSeparatorSize * scale_factor); DisplayWindowPadding = ImTrunc(DisplayWindowPadding * scale_factor); DisplaySafeAreaPadding = ImTrunc(DisplaySafeAreaPadding * scale_factor); MouseCursorScale = ImTrunc(MouseCursorScale * scale_factor); @@ -1429,6 +1460,18 @@ ImGuiIO::ImGuiIO() ConfigNavCursorVisibleAuto = true; ConfigNavCursorVisibleAlways = false; + // Docking options (when ImGuiConfigFlags_DockingEnable is set) + ConfigDockingNoSplit = false; + ConfigDockingWithShift = false; + ConfigDockingAlwaysTabBar = false; + ConfigDockingTransparentPayload = false; + + // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set) + ConfigViewportsNoAutoMerge = false; + ConfigViewportsNoTaskBarIcon = false; + ConfigViewportsNoDecoration = true; + ConfigViewportsNoDefaultParent = false; + // Miscellaneous options MouseDrawCursor = false; #ifdef __APPLE__ @@ -1791,6 +1834,27 @@ void ImGuiIO::AddMouseSourceEvent(ImGuiMouseSource source) g.InputEventsNextMouseSource = source; } +void ImGuiIO::AddMouseViewportEvent(ImGuiID viewport_id) +{ + IM_ASSERT(Ctx != NULL); + ImGuiContext& g = *Ctx; + //IM_ASSERT(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport); + if (!AppAcceptingEvents) + return; + + // Filter duplicate + const ImGuiInputEvent* latest_event = FindLatestInputEvent(&g, ImGuiInputEventType_MouseViewport); + const ImGuiID latest_viewport_id = latest_event ? latest_event->MouseViewport.HoveredViewportID : g.IO.MouseHoveredViewport; + if (latest_viewport_id == viewport_id) + return; + + ImGuiInputEvent e; + e.Type = ImGuiInputEventType_MouseViewport; + e.Source = ImGuiInputSource_Mouse; + e.MouseViewport.HoveredViewportID = viewport_id; + g.InputEventsQueue.push_back(e); +} + void ImGuiIO::AddFocusEvent(bool focused) { IM_ASSERT(Ctx != NULL); @@ -3360,6 +3424,11 @@ void ImGui::PopStyleColor(int count) } } +static const ImGuiCol GWindowDockStyleColors[ImGuiWindowDockStyleCol_COUNT] = +{ + ImGuiCol_Text, ImGuiCol_TabHovered, ImGuiCol_Tab, ImGuiCol_TabSelected, ImGuiCol_TabSelectedOverline, ImGuiCol_TabDimmed, ImGuiCol_TabDimmedSelected, ImGuiCol_TabDimmedSelectedOverline, +}; + static const ImGuiDataVarInfo GStyleVarInfo[] = { { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha @@ -3395,6 +3464,7 @@ static const ImGuiDataVarInfo GStyleVarInfo[] = { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, SeparatorTextBorderSize)}, // ImGuiStyleVar_SeparatorTextBorderSize { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextAlign) }, // ImGuiStyleVar_SeparatorTextAlign { ImGuiDataType_Float, 2, (ImU32)offsetof(ImGuiStyle, SeparatorTextPadding) }, // ImGuiStyleVar_SeparatorTextPadding + { ImGuiDataType_Float, 1, (ImU32)offsetof(ImGuiStyle, DockingSeparatorSize) }, // ImGuiStyleVar_DockingSeparatorSize }; const ImGuiDataVarInfo* ImGui::GetStyleVarInfo(ImGuiStyleVar idx) @@ -3526,6 +3596,8 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx) case ImGuiCol_TabDimmed: return "TabDimmed"; case ImGuiCol_TabDimmedSelected: return "TabDimmedSelected"; case ImGuiCol_TabDimmedSelectedOverline: return "TabDimmedSelectedOverline"; + case ImGuiCol_DockingPreview: return "DockingPreview"; + case ImGuiCol_DockingEmptyBg: return "DockingEmptyBg"; case ImGuiCol_PlotLines: return "PlotLines"; case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered"; case ImGuiCol_PlotHistogram: return "PlotHistogram"; @@ -3789,7 +3861,7 @@ void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCurso if (!font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2])) continue; const ImVec2 pos = base_pos - offset; - const float scale = base_scale; + const float scale = base_scale * viewport->DpiScale; if (!viewport->GetMainRect().Overlaps(ImRect(pos, pos + ImVec2(size.x + 2, size.y + 2) * scale))) continue; ImDrawList* draw_list = GetForegroundDrawList(viewport); @@ -3873,6 +3945,9 @@ static const ImGuiLocEntry GLocalizationEntriesEnUS[] = { ImGuiLocKey_WindowingUntitled, "(Untitled)" }, { ImGuiLocKey_OpenLink_s, "Open '%s'" }, { ImGuiLocKey_CopyLink, "Copy Link###CopyLink" }, + { ImGuiLocKey_DockingHideTabBar, "Hide tab bar###HideTabBar" }, + { ImGuiLocKey_DockingHoldShiftToDock, "Hold SHIFT to enable Docking window." }, + { ImGuiLocKey_DockingDragToUndockOrMoveNode,"Click and drag to move or undock whole node." }, }; ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) @@ -3881,13 +3956,14 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) InputTextState.Ctx = this; Initialized = false; + ConfigFlagsCurrFrame = ConfigFlagsLastFrame = ImGuiConfigFlags_None; FontAtlasOwnedByContext = shared_font_atlas ? false : true; Font = NULL; FontSize = FontBaseSize = FontScale = CurrentDpiScale = 0.0f; IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)(); Time = 0.0f; FrameCount = 0; - FrameCountEnded = FrameCountRendered = -1; + FrameCountEnded = FrameCountPlatformEnded = FrameCountRendered = -1; WithinEndChildID = 0; WithinFrameScope = WithinFrameScopeWithImplicitWindow = false; GcCompactAll = false; @@ -3945,6 +4021,12 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) CurrentItemFlags = ImGuiItemFlags_None; DebugShowGroupRects = false; + CurrentViewport = NULL; + MouseViewport = MouseLastHoveredViewport = NULL; + PlatformLastFocusedViewportId = 0; + ViewportCreatedCount = PlatformWindowsCreatedCount = 0; + ViewportFocusedStampCount = 0; + NavCursorVisible = false; NavHighlightItemUnderNav = false; NavMousePosDirty = false; @@ -4040,6 +4122,9 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) PlatformImeData.InputPos = ImVec2(0.0f, 0.0f); PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission + PlatformImeViewport = 0; + + DockNodeWindowMenuHandler = NULL; SettingsLoaded = false; SettingsDirtyTimer = 0.0f; @@ -4076,6 +4161,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) DebugItemPickerBreakId = 0; DebugFlashStyleColorTime = 0.0f; DebugFlashStyleColorIdx = ImGuiCol_COUNT; + DebugHoveredDockNode = NULL; // Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations DebugBreakInWindow = 0; @@ -4122,8 +4208,13 @@ void ImGui::Initialize() // Create default viewport ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID; + viewport->Idx = 0; + viewport->PlatformWindowCreated = true; + viewport->Flags = ImGuiViewportFlags_OwnedByApp; g.Viewports.push_back(viewport); g.TempBuffer.resize(1024 * 3 + 1, 0); + g.ViewportCreatedCount++; + g.PlatformIO.Viewports.push_back(g.Viewports[0]); // Build KeysMayBeCharInput[] lookup table (1 bool per named key) for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) @@ -4134,6 +4225,8 @@ void ImGui::Initialize() g.KeysMayBeCharInput.SetBit(key); #ifdef IMGUI_HAS_DOCK + // Initialize Docking + DockContextInitialize(&g); #endif g.Initialized = true; @@ -4163,6 +4256,12 @@ void ImGui::Shutdown() if (g.SettingsLoaded && g.IO.IniFilename != NULL) SaveIniSettingsToDisk(g.IO.IniFilename); + // Destroy platform windows + DestroyPlatformWindows(); + + // Shutdown extensions + DockContextShutdown(&g); + CallContextHooks(&g, ImGuiContextHookType_Shutdown); // Clear everything else @@ -4186,6 +4285,7 @@ void ImGui::Shutdown() g.BeginPopupStack.clear(); g.TreeNodeStack.clear(); + g.CurrentViewport = g.MouseViewport = g.MouseLastHoveredViewport = NULL; g.Viewports.clear_delete(); g.TabBars.Clear(); @@ -4267,21 +4367,28 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* ctx, const char* name) : DrawListInst(NUL NameBufLen = (int)strlen(name) + 1; ID = ImHashStr(name); IDStack.push_back(ID); + ViewportAllowPlatformMonitorExtend = -1; + ViewportPos = ImVec2(FLT_MAX, FLT_MAX); MoveId = GetID("#MOVE"); + TabId = GetID("#TAB"); ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); AutoFitFramesX = AutoFitFramesY = -1; AutoPosLastDirection = ImGuiDir_None; - SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = 0; + SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = SetWindowDockAllowFlags = 0; SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); LastFrameActive = -1; + LastFrameJustFocused = -1; LastTimeActive = -1.0f; - FontWindowScale = 1.0f; + FontRefSize = 0.0f; + FontWindowScale = FontWindowScaleParents = FontDpiScale = 1.0f; SettingsOffset = -1; + DockOrder = -1; DrawList = &DrawListInst; DrawList->_OwnerName = Name; DrawList->_Data = &Ctx->DrawListSharedData; NavPreferredScoringPosRel[0] = NavPreferredScoringPosRel[1] = ImVec2(FLT_MAX, FLT_MAX); + IM_PLACEMENT_NEW(&WindowClass) ImGuiWindowClass(); } ImGuiWindow::~ImGuiWindow() @@ -4297,7 +4404,6 @@ static void SetCurrentWindow(ImGuiWindow* window) g.CurrentWindow = window; g.StackSizesInBeginForCurrentWindow = g.CurrentWindow ? &g.CurrentWindowStack.back().StackSizesInBegin : NULL; g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL; - g.CurrentDpiScale = 1.0f; // FIXME-DPI: WIP this is modified in docking if (window) { g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); @@ -4453,8 +4559,8 @@ bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flag // FIXME-OPT: This could be cached/stored within the window. ImGuiContext& g = *GImGui; if (g.NavWindow) - if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow) - if (focused_root_window->WasActive && focused_root_window != window->RootWindow) + if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindowDockTree) + if (focused_root_window->WasActive && focused_root_window != window->RootWindowDockTree) { // For the purpose of those flags we differentiate "standard popup" from "modal popup" // NB: The 'else' is important because Modal windows are also Popups. @@ -4469,6 +4575,12 @@ bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flag if (!IsWindowWithinBeginStackOf(window->RootWindow, focused_root_window)) return false; } + + // Filter by viewport + if (window->Viewport != g.MouseViewport) + if (g.MovingWindow == NULL || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree) + return false; + return true; } @@ -4533,7 +4645,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) const ImGuiID id = g.LastItemData.ID; if ((flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) == 0) if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) - if (g.ActiveId != window->MoveId) + if (g.ActiveId != window->MoveId && g.ActiveId != window->TabId) return false; // Test if interactions on this window are blocked by an active popup or modal. @@ -4691,15 +4803,30 @@ bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id) // This is also inlined in ItemAdd() // Note: if ImGuiItemStatusFlags_HasDisplayRect is set, user needs to set g.LastItemData.DisplayRect. -void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags item_flags, const ImRect& item_rect) +void ImGui::SetLastItemData(ImGuiID item_id, ImGuiItemFlags item_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect) { ImGuiContext& g = *GImGui; g.LastItemData.ID = item_id; - g.LastItemData.ItemFlags = in_flags; - g.LastItemData.StatusFlags = item_flags; + g.LastItemData.ItemFlags = item_flags; + g.LastItemData.StatusFlags = status_flags; g.LastItemData.Rect = g.LastItemData.NavRect = item_rect; } +static void ImGui::SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect) +{ + ImGuiContext& g = *GImGui; + if (window->DockIsActive) + SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DC.DockTabItemStatusFlags, window->DC.DockTabItemRect); + else + SetLastItemData(window->MoveId, g.CurrentItemFlags, window->DC.WindowItemStatusFlags, rect); +} + +static void ImGui::SetLastItemDataForChildWindowItem(ImGuiWindow* window, const ImRect& rect) +{ + ImGuiContext& g = *GImGui; + SetLastItemData(window->ChildId, g.CurrentItemFlags, window->DC.ChildItemStatusFlags, rect); +} + float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) { if (wrap_pos_x < 0.0f) @@ -4809,6 +4936,13 @@ ImGuiPlatformIO& ImGui::GetPlatformIO() return GImGui->PlatformIO; } +// This variant exists to facilitate backends experimenting with multi-threaded parallel context. (#8069, #6293, #5856) +ImGuiPlatformIO& ImGui::GetPlatformIOEx(ImGuiContext* ctx) +{ + IM_ASSERT(ctx != NULL); + return ctx->PlatformIO; +} + // Pass this to your backend rendering function! Valid after Render() and until the next call to NewFrame() ImDrawData* ImGui::GetDrawData() { @@ -4853,26 +4987,18 @@ static ImDrawList* GetViewportBgFgDrawList(ImGuiViewportP* viewport, size_t draw ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport) { + if (viewport == NULL) + viewport = GImGui->CurrentWindow->Viewport; return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 0, "##Background"); } -ImDrawList* ImGui::GetBackgroundDrawList() -{ - ImGuiContext& g = *GImGui; - return GetBackgroundDrawList(g.Viewports[0]); -} - ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport) { + if (viewport == NULL) + viewport = GImGui->CurrentWindow->Viewport; return GetViewportBgFgDrawList((ImGuiViewportP*)viewport, 1, "##Foreground"); } -ImDrawList* ImGui::GetForegroundDrawList() -{ - ImGuiContext& g = *GImGui; - return GetForegroundDrawList(g.Viewports[0]); -} - ImDrawListSharedData* ImGui::GetDrawListSharedData() { return &GImGui->DrawListSharedData; @@ -4888,17 +5014,43 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) SetActiveID(window->MoveId, window); if (g.IO.ConfigNavCursorVisibleAuto) g.NavCursorVisible = false; - g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos; + g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindowDockTree->Pos; g.ActiveIdNoClearOnFocusLoss = true; SetActiveIdUsingAllKeyboardKeys(); bool can_move_window = true; - if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove)) + if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoMove)) can_move_window = false; + if (ImGuiDockNode* node = window->DockNodeAsHost) + if (node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove)) + can_move_window = false; if (can_move_window) g.MovingWindow = window; } +// We use 'undock == false' when dragging from title bar to allow moving groups of floating nodes without undocking them. +void ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock) +{ + ImGuiContext& g = *GImGui; + bool can_undock_node = false; + if (undock && node != NULL && node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove) == 0 && (node->MergedFlags & ImGuiDockNodeFlags_NoUndocking) == 0) + { + // Can undock if: + // - part of a hierarchy with more than one visible node (if only one is visible, we'll just move the root window) + // - part of a dockspace node hierarchy: so we can undock the last single visible node too. Undocking from a fixed/central node will create a new node and copy windows. + ImGuiDockNode* root_node = DockNodeGetRootNode(node); + if (root_node->OnlyNodeWithWindows != node || root_node->CentralNode != NULL) // -V1051 PVS-Studio thinks node should be root_node and is wrong about that. + can_undock_node = true; + } + + const bool clicked = IsMouseClicked(0); + const bool dragging = IsMouseDragging(0); + if (can_undock_node && dragging) + DockContextQueueUndockNode(&g, node); // Will lead to DockNodeStartMouseMovingWindow() -> StartMouseMovingWindow() being called next frame + else if (!can_undock_node && (clicked || dragging) && g.MovingWindow != window) + StartMouseMovingWindow(window); +} + // Handle mouse moving window // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing() // FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId. @@ -4912,16 +5064,43 @@ void ImGui::UpdateMouseMovingWindowNewFrame() // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window). // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency. KeepAliveID(g.ActiveId); - IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow); - ImGuiWindow* moving_window = g.MovingWindow->RootWindow; - if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos)) + IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindowDockTree); + ImGuiWindow* moving_window = g.MovingWindow->RootWindowDockTree; + + // When a window stop being submitted while being dragged, it may will its viewport until next Begin() + const bool window_disappared = (!moving_window->WasActive && !moving_window->Active); + if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos) && !window_disappared) { ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; - SetWindowPos(moving_window, pos, ImGuiCond_Always); + if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y) + { + SetWindowPos(moving_window, pos, ImGuiCond_Always); + if (moving_window->Viewport && moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window. + { + moving_window->Viewport->Pos = pos; + moving_window->Viewport->UpdateWorkRect(); + } + } FocusWindow(g.MovingWindow); } else { + if (!window_disappared) + { + // Try to merge the window back into the main viewport. + // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports) + if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) + UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport); + + // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button. + if (moving_window->Viewport && !IsDragDropPayloadBeingAccepted()) + g.MouseViewport = moving_window->Viewport; + + // Clear the NoInput window flag set by the Viewport system + if (moving_window->Viewport) + moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; + } + g.MovingWindow = NULL; ClearActiveID(); } @@ -4952,7 +5131,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() return; // Click on empty space to focus window and start moving - // (after we're done with all our widgets) + // (after we're done with all our widgets, so e.g. clicking on docking tab-bar which have set HoveredId already and not get us here!) if (g.IO.MouseClicked[0]) { // Handle the edge case of a popup being closed while clicking in its empty space. @@ -4966,7 +5145,7 @@ void ImGui::UpdateMouseMovingWindowEndFrame() // Cancel moving if clicked outside of title bar if (g.IO.ConfigWindowsMoveFromTitleBarOnly) - if (!(root_window->Flags & ImGuiWindowFlags_NoTitleBar)) + if (!(root_window->Flags & ImGuiWindowFlags_NoTitleBar) || root_window->DockIsActive) if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) g.MovingWindow = NULL; @@ -4995,6 +5174,29 @@ void ImGui::UpdateMouseMovingWindowEndFrame() } } +// This is called during NewFrame()->UpdateViewportsNewFrame() only. +// Need to keep in sync with SetWindowPos() +static void TranslateWindow(ImGuiWindow* window, const ImVec2& delta) +{ + window->Pos += delta; + window->ClipRect.Translate(delta); + window->OuterRectClipped.Translate(delta); + window->InnerRect.Translate(delta); + window->DC.CursorPos += delta; + window->DC.CursorStartPos += delta; + window->DC.CursorMaxPos += delta; + window->DC.IdealMaxPos += delta; +} + +static void ScaleWindow(ImGuiWindow* window, float scale) +{ + ImVec2 origin = window->Viewport->Pos; + window->Pos = ImFloor((window->Pos - origin) * scale + origin); + window->Size = ImTrunc(window->Size * scale); + window->SizeFull = ImTrunc(window->SizeFull * scale); + window->ContentSize = ImTrunc(window->ContentSize * scale); +} + static bool IsWindowActiveAndVisible(ImGuiWindow* window) { return (window->Active) && (!window->Hidden); @@ -5016,11 +5218,12 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms. bool clear_hovered_windows = false; FindHoveredWindowEx(g.IO.MousePos, false, &g.HoveredWindow, &g.HoveredWindowUnderMovingWindow); + IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseViewport); g.HoveredWindowBeforeClear = g.HoveredWindow; // Modal windows prevents mouse from hovering behind them. ImGuiWindow* modal_window = GetTopMostPopupModal(); - if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window)) + if (modal_window && g.HoveredWindow && !IsWindowWithinBeginStackOf(g.HoveredWindow->RootWindow, modal_window)) // FIXME-MERGE: RootWindowDockTree ? clear_hovered_windows = true; // Disabled mouse hovering (we don't currently clear MousePos, we could) @@ -5086,6 +5289,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() } // Called once a frame. Followed by SetCurrentFont() which sets up the remaining data. +// FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal! static void SetupDrawListSharedData() { ImGuiContext& g = *GImGui; @@ -5120,7 +5324,9 @@ void ImGui::NewFrame() CallContextHooks(&g, ImGuiContextHookType_NewFramePre); // Check and assert for various common IO and Configuration mistakes + g.ConfigFlagsLastFrame = g.ConfigFlagsCurrFrame; ErrorCheckNewFrameSanityChecks(); + g.ConfigFlagsCurrFrame = g.IO.ConfigFlags; // Load settings on first frame, save settings when modified (after a delay) UpdateSettings(); @@ -5147,6 +5353,7 @@ void ImGui::NewFrame() UpdateViewportsNewFrame(); // Setup current font and draw list shared data + // FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal! g.IO.Fonts->Locked = true; SetupDrawListSharedData(); SetCurrentFont(GetDefaultFont()); @@ -5154,7 +5361,10 @@ void ImGui::NewFrame() // Mark rendering data as invalid to prevent user who may have a handle on it to use it. for (ImGuiViewportP* viewport : g.Viewports) + { + viewport->DrawData = NULL; viewport->DrawDataP.Valid = false; + } // Drag and drop keep the source ID alive so even if the source disappear our state is consistent if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId) @@ -5265,6 +5475,10 @@ void ImGui::NewFrame() // Update mouse input state UpdateMouseInputs(); + // Undocking + // (needs to be before UpdateMouseMovingWindowNewFrame so the window is already offset and following the mouse on the detaching frame) + DockContextNewFrameUpdateUndocking(&g); + // Mark all windows as not visible and compact unused memory. IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size); const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer; @@ -5329,6 +5543,9 @@ void ImGui::NewFrame() g.CurrentItemFlags = g.ItemFlagsStack.back(); g.GroupStack.resize(0); + // Docking + DockContextNewFrameUpdateDocking(&g); + // [DEBUG] Update debug features #ifndef IMGUI_DISABLE_DEBUG_TOOLS UpdateDebugToolItemPicker(); @@ -5402,7 +5619,8 @@ static void AddWindowToSortBuffer(ImVector* out_sorted_windows, Im static void AddWindowToDrawData(ImGuiWindow* window, int layer) { ImGuiContext& g = *GImGui; - ImGuiViewportP* viewport = g.Viewports[0]; + ImGuiViewportP* viewport = window->Viewport; + IM_ASSERT(viewport != NULL); g.IO.MetricsRenderWindows++; if (window->DrawList->_Splitter._Count > 1) window->DrawList->ChannelsMerge(); // Merge if user forgot to merge back. Also required in Docking branch for ImGuiWindowFlags_DockNodeHost windows. @@ -5446,17 +5664,25 @@ static void InitViewportDrawData(ImGuiViewportP* viewport) ImGuiIO& io = ImGui::GetIO(); ImDrawData* draw_data = &viewport->DrawDataP; + viewport->DrawData = draw_data; // Make publicly accessible viewport->DrawDataBuilder.Layers[0] = &draw_data->CmdLists; viewport->DrawDataBuilder.Layers[1] = &viewport->DrawDataBuilder.LayerData1; viewport->DrawDataBuilder.Layers[0]->resize(0); viewport->DrawDataBuilder.Layers[1]->resize(0); + // When minimized, we report draw_data->DisplaySize as zero to be consistent with non-viewport mode, + // and to allow applications/backends to easily skip rendering. + // FIXME: Note that we however do NOT attempt to report "zero drawlist / vertices" into the ImDrawData structure. + // This is because the work has been done already, and its wasted! We should fix that and add optimizations for + // it earlier in the pipeline, rather than pretend to hide the data at the end of the pipeline. + const bool is_minimized = (viewport->Flags & ImGuiViewportFlags_IsMinimized) != 0; + draw_data->Valid = true; draw_data->CmdListsCount = 0; draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; draw_data->DisplayPos = viewport->Pos; - draw_data->DisplaySize = viewport->Size; - draw_data->FramebufferScale = io.DisplayFramebufferScale; + draw_data->DisplaySize = is_minimized ? ImVec2(0.0f, 0.0f) : viewport->Size; + draw_data->FramebufferScale = io.DisplayFramebufferScale; // FIXME-VIEWPORT: This may vary on a per-monitor/viewport basis? draw_data->OwnerViewport = viewport; } @@ -5482,20 +5708,28 @@ void ImGui::PopClipRect() window->ClipRect = window->DrawList->_ClipRectStack.back(); } +static ImGuiWindow* FindFrontMostVisibleChildWindow(ImGuiWindow* window) +{ + for (int n = window->DC.ChildWindows.Size - 1; n >= 0; n--) + if (IsWindowActiveAndVisible(window->DC.ChildWindows[n])) + return FindFrontMostVisibleChildWindow(window->DC.ChildWindows[n]); + return window; +} + static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col) { if ((col & IM_COL32_A_MASK) == 0) return; - ImGuiViewportP* viewport = (ImGuiViewportP*)GetMainViewport(); + ImGuiViewportP* viewport = window->Viewport; ImRect viewport_rect = viewport->GetMainRect(); // Draw behind window by moving the draw command at the FRONT of the draw list { - // We've already called AddWindowToDrawData() which called DrawList->ChannelsMerge() on DockNodeHost windows, - // and draw list have been trimmed already, hence the explicit recreation of a draw command if missing. + // Draw list have been trimmed already, hence the explicit recreation of a draw command if missing. // FIXME: This is creating complication, might be simpler if we could inject a drawlist in drawdata at a given position and not attempt to manipulate ImDrawCmd order. - ImDrawList* draw_list = window->RootWindow->DrawList; + ImDrawList* draw_list = window->RootWindowDockTree->DrawList; + draw_list->ChannelsMerge(); if (draw_list->CmdBuffer.Size == 0) draw_list->AddDrawCmd(); draw_list->PushClipRect(viewport_rect.Min - ImVec2(1, 1), viewport_rect.Max + ImVec2(1, 1), false); // FIXME: Need to stricty ensure ImDrawCmd are not merged (ElemCount==6 checks below will verify that) @@ -5507,6 +5741,18 @@ static void ImGui::RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 draw_list->AddDrawCmd(); // We need to create a command as CmdBuffer.back().IdxOffset won't be correct if we append to same command. draw_list->PopClipRect(); } + + // Draw over sibling docking nodes in a same docking tree + if (window->RootWindow->DockIsActive) + { + ImDrawList* draw_list = FindFrontMostVisibleChildWindow(window->RootWindowDockTree)->DrawList; + draw_list->ChannelsMerge(); + if (draw_list->CmdBuffer.Size == 0) + draw_list->AddDrawCmd(); + draw_list->PushClipRect(viewport_rect.Min, viewport_rect.Max, false); + RenderRectFilledWithHole(draw_list, window->RootWindowDockTree->Rect(), window->RootWindow->Rect(), col, 0.0f);// window->RootWindowDockTree->WindowRounding); + draw_list->PopClipRect(); + } } ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* parent_window) @@ -5526,6 +5772,8 @@ ImGuiWindow* ImGui::FindBottomMostVisibleWindowWithinBeginStack(ImGuiWindow* par return bottom_most_visible_window; } +// Important: AddWindowToDrawData() has not been called yet, meaning DockNodeHost windows needs a DrawList->ChannelsMerge() before usage. +// We call ChannelsMerge() lazily here at it is faster that doing a full iteration of g.Windows[] prior to calling RenderDimmedBackgrounds(). static void ImGui::RenderDimmedBackgrounds() { ImGuiContext& g = *GImGui; @@ -5537,31 +5785,50 @@ static void ImGui::RenderDimmedBackgrounds() if (!dim_bg_for_modal && !dim_bg_for_window_list) return; + ImGuiViewport* viewports_already_dimmed[2] = { NULL, NULL }; if (dim_bg_for_modal) { // Draw dimming behind modal or a begin stack child, whichever comes first in draw order. ImGuiWindow* dim_behind_window = FindBottomMostVisibleWindowWithinBeginStack(modal_window); RenderDimmedBackgroundBehindWindow(dim_behind_window, GetColorU32(modal_window->DC.ModalDimBgColor, g.DimBgRatio)); + viewports_already_dimmed[0] = modal_window->Viewport; } else if (dim_bg_for_window_list) { // Draw dimming behind CTRL+Tab target window and behind CTRL+Tab UI window RenderDimmedBackgroundBehindWindow(g.NavWindowingTargetAnim, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); + if (g.NavWindowingListWindow != NULL && g.NavWindowingListWindow->Viewport && g.NavWindowingListWindow->Viewport != g.NavWindowingTargetAnim->Viewport) + RenderDimmedBackgroundBehindWindow(g.NavWindowingListWindow, GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio)); + viewports_already_dimmed[0] = g.NavWindowingTargetAnim->Viewport; + viewports_already_dimmed[1] = g.NavWindowingListWindow ? g.NavWindowingListWindow->Viewport : NULL; // Draw border around CTRL+Tab target window ImGuiWindow* window = g.NavWindowingTargetAnim; - ImGuiViewport* viewport = GetMainViewport(); + ImGuiViewport* viewport = window->Viewport; float distance = g.FontSize; ImRect bb = window->Rect(); bb.Expand(distance); if (bb.GetWidth() >= viewport->Size.x && bb.GetHeight() >= viewport->Size.y) bb.Expand(-distance - 1.0f); // If a window fits the entire viewport, adjust its highlight inward + window->DrawList->ChannelsMerge(); if (window->DrawList->CmdBuffer.Size == 0) window->DrawList->AddDrawCmd(); window->DrawList->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size); window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), window->WindowRounding, 0, 3.0f); window->DrawList->PopClipRect(); } + + // Draw dimming background on _other_ viewports than the ones our windows are in + for (ImGuiViewportP* viewport : g.Viewports) + { + if (viewport == viewports_already_dimmed[0] || viewport == viewports_already_dimmed[1]) + continue; + if (modal_window && viewport->Window && IsWindowAbove(viewport->Window, modal_window)) + continue; + ImDrawList* draw_list = GetForegroundDrawList(viewport); + const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); + draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col); + } } // This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal. @@ -5587,8 +5854,10 @@ void ImGui::EndFrame() ImGuiPlatformImeData* ime_data = &g.PlatformImeData; if (g.PlatformIO.Platform_SetImeDataFn != NULL && memcmp(ime_data, &g.PlatformImeDataPrev, sizeof(ImGuiPlatformImeData)) != 0) { + ImGuiViewport* viewport = FindViewportByID(g.PlatformImeViewport); IMGUI_DEBUG_LOG_IO("[io] Calling Platform_SetImeDataFn(): WantVisible: %d, InputPos (%.2f,%.2f)\n", ime_data->WantVisible, ime_data->InputPos.x, ime_data->InputPos.y); - ImGuiViewport* viewport = GetMainViewport(); + if (viewport == NULL) + viewport = GetMainViewport(); g.PlatformIO.Platform_SetImeDataFn(&g, viewport, ime_data); } @@ -5601,6 +5870,11 @@ void ImGui::EndFrame() // Update navigation: CTRL+Tab, wrap-around requests NavEndFrame(); + // Update docking + DockContextEndFrame(&g); + + SetCurrentViewport(NULL, NULL); + // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted) if (g.DragDropActive) { @@ -5629,6 +5903,9 @@ void ImGui::EndFrame() // Initiate moving window + handle left-click and right-click focus UpdateMouseMovingWindowEndFrame(); + // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some) + UpdateViewportsEndFrame(); + // Sort the window list so that all child windows are after their parent // We cannot do that on FocusWindow() because children may not exist yet g.WindowsTempSortBuffer.resize(0); @@ -5687,7 +5964,7 @@ void ImGui::Render() // Add ImDrawList to render ImGuiWindow* windows_to_render_top_most[2]; - windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindow : NULL; + windows_to_render_top_most[0] = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget->RootWindowDockTree : NULL; windows_to_render_top_most[1] = (g.NavWindowingTarget ? g.NavWindowingListWindow : NULL); for (ImGuiWindow* window : g.Windows) { @@ -5766,8 +6043,15 @@ void ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_vi ImGuiWindow* hovered_window = NULL; ImGuiWindow* hovered_window_under_moving_window = NULL; - if (find_first_and_in_any_viewport == false && g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs)) - hovered_window = g.MovingWindow; + // Special handling for the window being moved: Ignore the mouse viewport check (because it may reset/lose its viewport during the undocking frame) + ImGuiViewportP* backup_moving_window_viewport = NULL; + if (find_first_and_in_any_viewport == false && g.MovingWindow) + { + backup_moving_window_viewport = g.MovingWindow->Viewport; + g.MovingWindow->Viewport = g.MouseViewport; + if (!(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs)) + hovered_window = g.MovingWindow; + } ImVec2 padding_regular = g.Style.TouchExtraPadding; ImVec2 padding_for_resize = g.IO.ConfigWindowsResizeFromEdges ? g.WindowsHoverPadding : padding_regular; @@ -5779,6 +6063,9 @@ void ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_vi continue; if (window->Flags & ImGuiWindowFlags_NoMouseInputs) continue; + IM_ASSERT(window->Viewport); + if (window->Viewport != g.MouseViewport) + continue; // Using the clipped AABB, a child window will typically be clipped by its parent (not always) ImVec2 hit_padding = (window->Flags & (ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) ? padding_regular : padding_for_resize; @@ -5805,7 +6092,7 @@ void ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_vi if (hovered_window == NULL) hovered_window = window; IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer. - if (hovered_window_under_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow)) + if (hovered_window_under_moving_window == NULL && (!g.MovingWindow || window->RootWindowDockTree != g.MovingWindow->RootWindowDockTree)) hovered_window_under_moving_window = window; if (hovered_window && hovered_window_under_moving_window) break; @@ -5815,6 +6102,8 @@ void ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_vi *out_hovered_window = hovered_window; if (out_hovered_window_under_moving_window != NULL) *out_hovered_window_under_moving_window = hovered_window_under_moving_window; + if (find_first_and_in_any_viewport == false && g.MovingWindow) + g.MovingWindow->Viewport = backup_moving_window_viewport; } bool ImGui::IsItemActive() @@ -5852,7 +6141,16 @@ bool ImGui::IsItemDeactivatedAfterEdit() bool ImGui::IsItemFocused() { ImGuiContext& g = *GImGui; - return g.NavId == g.LastItemData.ID && g.NavId != 0; + if (g.NavId != g.LastItemData.ID || g.NavId == 0) + return false; + + // Special handling for the dummy item after Begin() which represent the title bar or tab. + // When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case. + ImGuiWindow* window = g.CurrentWindow; + if (g.LastItemData.ID == window->ID && window->WriteAccessed) + return false; + + return true; } // Important: this can be useful but it is NOT equivalent to the behavior of e.g.Button()! @@ -6012,7 +6310,7 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, I child_flags &= ~ImGuiChildFlags_ResizeY; // Set window flags - window_flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar; + window_flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking; window_flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag if (child_flags & (ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize)) window_flags |= ImGuiWindowFlags_AlwaysAutoResize; @@ -6152,7 +6450,14 @@ void ImGui::EndChild() } if (g.HoveredWindow == child_window) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; + child_window->DC.ChildItemStatusFlags = g.LastItemData.StatusFlags; + //SetLastItemDataForChildWindowItem(child_window, child_window->Rect()); // Not needed, effectively done by ItemAdd() + } + else + { + SetLastItemDataForChildWindowItem(child_window, child_window->Rect()); } + g.WithinEndChildID = backup_within_end_child_id; g.LogLinePosY = -FLT_MAX; // To enforce a carriage return } @@ -6162,6 +6467,7 @@ static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, b window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags); window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags); window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags); + window->SetWindowDockAllowFlags = enabled ? (window->SetWindowDockAllowFlags | flags) : (window->SetWindowDockAllowFlags & ~flags); } ImGuiWindow* ImGui::FindWindowByID(ImGuiID id) @@ -6178,10 +6484,19 @@ ImGuiWindow* ImGui::FindWindowByName(const char* name) static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings) { - window->Pos = ImTrunc(ImVec2(settings->Pos.x, settings->Pos.y)); + const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + window->ViewportPos = main_viewport->Pos; + if (settings->ViewportId) + { + window->ViewportId = settings->ViewportId; + window->ViewportPos = ImVec2(settings->ViewportPos.x, settings->ViewportPos.y); + } + window->Pos = ImTrunc(ImVec2(settings->Pos.x + window->ViewportPos.x, settings->Pos.y + window->ViewportPos.y)); if (settings->Size.x > 0 && settings->Size.y > 0) window->Size = window->SizeFull = ImTrunc(ImVec2(settings->Size.x, settings->Size.y)); window->Collapsed = settings->Collapsed; + window->DockId = settings->DockId; + window->DockOrder = settings->DockOrder; } static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings) @@ -6191,7 +6506,8 @@ static void InitOrLoadWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* s const ImGuiViewport* main_viewport = ImGui::GetMainViewport(); window->Pos = main_viewport->Pos + ImVec2(60, 60); window->Size = window->SizeFull = ImVec2(0, 0); - window->SetWindowPosAllowFlags = window->SetWindowSizeAllowFlags = window->SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; + window->ViewportPos = main_viewport->Pos; + window->SetWindowPosAllowFlags = window->SetWindowSizeAllowFlags = window->SetWindowCollapsedAllowFlags = window->SetWindowDockAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; if (settings != NULL) { @@ -6239,6 +6555,16 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) return window; } +static ImGuiWindow* GetWindowForTitleDisplay(ImGuiWindow* window) +{ + return window->DockNodeAsHost ? window->DockNodeAsHost->VisibleWindow : window; +} + +static ImGuiWindow* GetWindowForTitleAndMenuHeight(ImGuiWindow* window) +{ + return (window->DockNodeAsHost && window->DockNodeAsHost->VisibleWindow) ? window->DockNodeAsHost->VisibleWindow : window; +} + static inline ImVec2 CalcWindowMinSize(ImGuiWindow* window) { // We give windows non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups) @@ -6258,7 +6584,7 @@ static inline ImVec2 CalcWindowMinSize(ImGuiWindow* window) } // Reduce artifacts with very small windows - ImGuiWindow* window_for_height = window; + ImGuiWindow* window_for_height = GetWindowForTitleAndMenuHeight(window); size_min.y = ImMax(size_min.y, window_for_height->TitleBarHeight + window_for_height->MenuBarHeight + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); return size_min; } @@ -6329,8 +6655,19 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont { // Maximum window size is determined by the viewport size or monitor size ImVec2 size_min = CalcWindowMinSize(window); - ImVec2 size_max = ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_Popup)) ? ImVec2(FLT_MAX, FLT_MAX) : ImGui::GetMainViewport()->WorkSize - style.DisplaySafeAreaPadding * 2.0f; - ImVec2 size_auto_fit = ImClamp(size_desired, size_min, size_max); + ImVec2 size_max = ImVec2(FLT_MAX, FLT_MAX); + + // Child windows are layed within their parent (unless they are also popups/menus) and thus have no restriction + if ((window->Flags & ImGuiWindowFlags_ChildWindow) == 0 || (window->Flags & ImGuiWindowFlags_Popup) != 0) + { + if (!window->ViewportOwned) + size_max = ImGui::GetMainViewport()->WorkSize - style.DisplaySafeAreaPadding * 2.0f; + const int monitor_idx = window->ViewportAllowPlatformMonitorExtend; + if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size) + size_max = g.PlatformIO.Monitors[monitor_idx].WorkSize - style.DisplaySafeAreaPadding * 2.0f; + } + + ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, size_max)); // FIXME: CalcWindowAutoFitSize() doesn't take into account that only one axis may be auto-fit when calculating scrollbars, // we may need to compute/store three variants of size_auto_fit, for x/y/xy. @@ -6367,7 +6704,7 @@ static ImGuiCol GetWindowBgColorIdx(ImGuiWindow* window) { if (window->Flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) return ImGuiCol_PopupBg; - if (window->Flags & ImGuiWindowFlags_ChildWindow) + if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !window->DockIsActive) return ImGuiCol_ChildBg; return ImGuiCol_WindowBg; } @@ -6433,7 +6770,7 @@ static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_ ImGuiID ImGui::GetWindowResizeCornerID(ImGuiWindow* window, int n) { IM_ASSERT(n >= 0 && n < 4); - ImGuiID id = window->ID; + ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID; id = ImHashStr("#RESIZE", 0, id); id = ImHashData(&n, sizeof(int), id); return id; @@ -6444,7 +6781,7 @@ ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir) { IM_ASSERT(dir >= 0 && dir < 4); int n = (int)dir + 4; - ImGuiID id = window->ID; + ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID; id = ImHashStr("#RESIZE", 0, id); id = ImHashData(&n, sizeof(int), id); return id; @@ -6475,6 +6812,16 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si ImVec2 pos_target(FLT_MAX, FLT_MAX); ImVec2 size_target(FLT_MAX, FLT_MAX); + // Clip mouse interaction rectangles within the viewport rectangle (in practice the narrowing is going to happen most of the time). + // - Not narrowing would mostly benefit the situation where OS windows _without_ decoration have a threshold for hovering when outside their limits. + // This is however not the case with current backends under Win32, but a custom borderless window implementation would benefit from it. + // - When decoration are enabled we typically benefit from that distance, but then our resize elements would be conflicting with OS resize elements, so we also narrow. + // - Note that we are unable to tell if the platform setup allows hovering with a distance threshold (on Win32, decorated window have such threshold). + // We only clip interaction so we overwrite window->ClipRect, cannot call PushClipRect() yet as DrawList is not yet setup. + const bool clip_with_viewport_rect = !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) || (g.IO.MouseHoveredViewport != window->ViewportId) || !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration); + if (clip_with_viewport_rect) + window->ClipRect = window->Viewport->GetMainRect(); + // Resize grips and borders are on layer 1 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; @@ -6560,7 +6907,7 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si // Switch to relative resizing mode when border geometry moved (e.g. resizing a child altering parent scroll), in order to avoid resizing feedback loop. // Currently only using relative mode on resizable child windows, as the problem to solve is more likely noticeable for them, but could apply for all windows eventually. // FIXME: May want to generalize this idiom at lower-level, so more widgets can use it! - const bool just_scrolled_manually_while_resizing = (g.WheelingWindow != NULL && g.WheelingWindowScrolledFrame == g.FrameCount && IsWindowChildOf(window, g.WheelingWindow, false)); + const bool just_scrolled_manually_while_resizing = (g.WheelingWindow != NULL && g.WheelingWindowScrolledFrame == g.FrameCount && IsWindowChildOf(window, g.WheelingWindow, false, true)); if (g.ActiveIdIsJustActivated || just_scrolled_manually_while_resizing) { g.WindowResizeBorderExpectedRect = border_rect; @@ -6618,7 +6965,7 @@ static int ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& si // Navigation resize (keyboard/gamepad) // FIXME: This cannot be moved to NavUpdateWindowing() because CalcWindowSizeAfterConstraint() need to callback into user. // Not even sure the callback works here. - if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window) + if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindowDockTree == window) { ImVec2 nav_resize_dir; if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift) @@ -6669,7 +7016,9 @@ static inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_ { ImGuiContext& g = *GImGui; ImVec2 size_for_clamping = window->Size; - if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) + if (g.IO.ConfigWindowsMoveFromTitleBarOnly && window->DockNodeAsHost) + size_for_clamping.y = ImGui::GetFrameHeight(); // Not using window->TitleBarHeight() as DockNodeAsHost will report 0.0f here. + else if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) size_for_clamping.y = window->TitleBarHeight; window->Pos = ImClamp(window->Pos, visibility_rect.Min - size_for_clamping, visibility_rect.Max); } @@ -6704,10 +7053,10 @@ static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) const ImU32 border_col_resizing = GetColorU32((window->ResizeBorderHeld != -1) ? ImGuiCol_SeparatorActive : ImGuiCol_SeparatorHovered); RenderWindowOuterSingleBorder(window, border_n, border_col_resizing, ImMax(2.0f, window->WindowBorderSize)); // Thicker than usual } - if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) + if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive) { float y = window->Pos.y + window->TitleBarHeight - 1; - window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), border_col, g.Style.FrameBorderSize); + window->DrawList->AddLine(ImVec2(window->Pos.x + border_size * 0.5f, y), ImVec2(window->Pos.x + window->Size.x - border_size * 0.5f, y), border_col, g.Style.FrameBorderSize); } } @@ -6734,6 +7083,8 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar const float backup_border_size = style.FrameBorderSize; g.Style.FrameBorderSize = window->WindowBorderSize; ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && g.NavCursorVisible) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed); + if (window->ViewportOwned) + title_bar_col |= IM_COL32_A_MASK; // No alpha (we don't support is_docking_transparent_payload here because simpler and less meaningful, but could with a bit of code shuffle/reuse) RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding); g.Style.FrameBorderSize = backup_border_size; } @@ -6742,23 +7093,58 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar // Window background if (!(flags & ImGuiWindowFlags_NoBackground)) { + bool is_docking_transparent_payload = false; + if (g.DragDropActive && (g.FrameCount - g.DragDropAcceptFrameCount) <= 1 && g.IO.ConfigDockingTransparentPayload) + if (g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && *(ImGuiWindow**)g.DragDropPayload.Data == window) + is_docking_transparent_payload = true; + ImU32 bg_col = GetColorU32(GetWindowBgColorIdx(window)); - bool override_alpha = false; - float alpha = 1.0f; - if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha) + if (window->ViewportOwned) + { + bg_col |= IM_COL32_A_MASK; // No alpha + if (is_docking_transparent_payload) + window->Viewport->Alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA; + } + else { - alpha = g.NextWindowData.BgAlphaVal; - override_alpha = true; + // Adjust alpha. For docking + bool override_alpha = false; + float alpha = 1.0f; + if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha) + { + alpha = g.NextWindowData.BgAlphaVal; + override_alpha = true; + } + if (is_docking_transparent_payload) + { + alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA; // FIXME-DOCK: Should that be an override? + override_alpha = true; + } + if (override_alpha) + bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT); } - if (override_alpha) - bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT); - window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom); + + // Render, for docked windows and host windows we ensure bg goes before decorations + if (window->DockIsActive) + window->DockNode->LastBgColor = bg_col; + ImDrawList* bg_draw_list = window->DockIsActive ? window->DockNode->HostWindow->DrawList : window->DrawList; + if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost)) + bg_draw_list->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG); + bg_draw_list->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? 0 : ImDrawFlags_RoundCornersBottom); + if (window->DockIsActive || (flags & ImGuiWindowFlags_DockNodeHost)) + bg_draw_list->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); } + if (window->DockIsActive) + window->DockNode->IsBgDrawnThisFrame = true; // Title bar - if (!(flags & ImGuiWindowFlags_NoTitleBar)) + // (when docked, DockNode are drawing their own title bar. Individual windows however do NOT set the _NoTitleBar flag, + // in order for their pos/size to be matching their undocking state.) + if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive) { ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); + if (window->ViewportOwned) + title_bar_col |= IM_COL32_A_MASK; // No alpha window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawFlags_RoundCornersTop); } @@ -6767,9 +7153,30 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar { ImRect menu_bar_rect = window->MenuBarRect(); menu_bar_rect.ClipWith(window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them. - window->DrawList->AddRectFilled(menu_bar_rect.Min + ImVec2(window_border_size, 0), menu_bar_rect.Max - ImVec2(window_border_size, 0), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop); + window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawFlags_RoundCornersTop); if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y) - window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); + window->DrawList->AddLine(menu_bar_rect.GetBL() + ImVec2(window_border_size * 0.5f, 0.0f), menu_bar_rect.GetBR() - ImVec2(window_border_size * 0.5f, 0.0f), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); + } + + // Docking: Unhide tab bar (small triangle in the corner), drag from small triangle to quickly undock + ImGuiDockNode* node = window->DockNode; + if (window->DockIsActive && node->IsHiddenTabBar() && !node->IsNoTabBar()) + { + float unhide_sz_draw = ImTrunc(g.FontSize * 0.70f); + float unhide_sz_hit = ImTrunc(g.FontSize * 0.55f); + ImVec2 p = node->Pos; + ImRect r(p, p + ImVec2(unhide_sz_hit, unhide_sz_hit)); + ImGuiID unhide_id = window->GetID("#UNHIDE"); + KeepAliveID(unhide_id); + bool hovered, held; + if (ButtonBehavior(r, unhide_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren)) + node->WantHiddenTabBarToggle = true; + else if (held && IsMouseDragging(0)) + StartMouseMovingWindowOrNode(window, node, true); // Undock from tab-bar triangle = same as window/collapse menu button + + // FIXME-DOCK: Ideally we'd use ImGuiCol_TitleBgActive/ImGuiCol_TitleBg here, but neither is guaranteed to be visible enough at this sort of size.. + ImU32 col = GetColorU32(((held && hovered) || (node->IsFocused && !hovered)) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + window->DrawList->AddTriangleFilled(p, p + ImVec2(unhide_sz_draw, 0.0f), p + ImVec2(0.0f, unhide_sz_draw), col); } // Scrollbars @@ -6788,20 +7195,22 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar continue; const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPosN); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, window_border_size))); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, window_border_size) : ImVec2(window_border_size, resize_grip_draw_size))); - window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12); + const float border_inner = IM_ROUND(window_border_size * 0.5f); + window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(border_inner, resize_grip_draw_size) : ImVec2(resize_grip_draw_size, border_inner))); + window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(resize_grip_draw_size, border_inner) : ImVec2(border_inner, resize_grip_draw_size))); + window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + border_inner), corner.y + grip.InnerDir.y * (window_rounding + border_inner)), window_rounding, grip.AngleMin12, grip.AngleMax12); window->DrawList->PathFillConvex(col); } } - // Borders - if (handle_borders_and_resize_grips) + // Borders (for dock node host they will be rendered over after the tab bar) + if (handle_borders_and_resize_grips && !window->DockNodeAsHost) RenderWindowOuterBorders(window); } window->DC.NavLayerCurrent = ImGuiNavLayer_Main; } +// When inside a dock node, this is handled in DockNodeCalcTabBarLayout() instead. // Render title text, collapse button, close button void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open) { @@ -6843,7 +7252,7 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl // Collapse button (submitting first so it gets priority when choosing a navigation init fallback) if (has_collapse_button) - if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos)) + if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos, NULL)) window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function // Close button @@ -6894,12 +7303,16 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) { window->ParentWindow = parent_window; - window->RootWindow = window->RootWindowPopupTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; + window->RootWindow = window->RootWindowPopupTree = window->RootWindowDockTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) - window->RootWindow = parent_window->RootWindow; + { + window->RootWindowDockTree = parent_window->RootWindowDockTree; + if (!window->DockIsActive && !(parent_window->Flags & ImGuiWindowFlags_DockNodeHost)) + window->RootWindow = parent_window->RootWindow; + } if (parent_window && (flags & ImGuiWindowFlags_Popup)) window->RootWindowPopupTree = parent_window->RootWindowPopupTree; - if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) + if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) // FIXME: simply use _NoTitleBar ? window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight; while (window->RootWindowForNav->ChildFlags & ImGuiChildFlags_NavFlattened) { @@ -6978,22 +7391,24 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); window->IsFallbackWindow = (g.CurrentWindowStack.Size == 0 && g.WithinFrameScopeWithImplicitWindow); - // Update the Appearing flag - bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on + // Update the Appearing flag (note: the BeginDocked() path may also set this to true later) + bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on if (flags & ImGuiWindowFlags_Popup) { ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed window_just_activated_by_user |= (window != popup_ref.Window); } - window->Appearing = window_just_activated_by_user; - if (window->Appearing) - SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); // Update Flags, LastFrameActive, BeginOrderXXX fields + const bool window_was_appearing = window->Appearing; if (first_begin_of_the_frame) { UpdateWindowInFocusOrderList(window, window_just_created, flags); + window->Appearing = window_just_activated_by_user; + if (window->Appearing) + SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); + window->FlagsPreviousFrame = window->Flags; window->Flags = (ImGuiWindowFlags)flags; window->ChildFlags = (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : 0; window->LastFrameActive = current_frame; @@ -7006,8 +7421,42 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) flags = window->Flags; } + // Docking + // (NB: during the frame dock nodes are created, it is possible that (window->DockIsActive == false) even though (window->DockNode->Windows.Size > 1) + IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); // Cannot be both + if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasDock) + SetWindowDock(window, g.NextWindowData.DockId, g.NextWindowData.DockCond); + if (first_begin_of_the_frame) + { + bool has_dock_node = (window->DockId != 0 || window->DockNode != NULL); + bool new_auto_dock_node = !has_dock_node && GetWindowAlwaysWantOwnTabBar(window); + bool dock_node_was_visible = window->DockNodeIsVisible; + bool dock_tab_was_visible = window->DockTabIsVisible; + if (has_dock_node || new_auto_dock_node) + { + BeginDocked(window, p_open); + flags = window->Flags; + if (window->DockIsActive) + { + IM_ASSERT(window->DockNode != NULL); + g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; // Docking currently override constraints + } + + // Amend the Appearing flag + if (window->DockTabIsVisible && !dock_tab_was_visible && dock_node_was_visible && !window->Appearing && !window_was_appearing) + { + window->Appearing = true; + SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); + } + } + else + { + window->DockIsActive = window->DockNodeIsVisible = window->DockTabIsVisible = false; + } + } + // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack - ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window; + ImGuiWindow* parent_window_in_stack = (window->DockIsActive && window->DockNode->HostWindow) ? window->DockNode->HostWindow : g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back().Window; ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); @@ -7033,9 +7482,23 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) UpdateWindowParentAndRootLinks(window, flags, parent_window); window->ParentWindowInBeginStack = parent_window_in_stack; + // Focus route // There's little point to expose a flag to set this: because the interesting cases won't be using parent_window_in_stack, - // e.g. linking a tool window in a standalone viewport to a document window, regardless of their Begin() stack parenting. (#6798) - window->ParentWindowForFocusRoute = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window_in_stack : NULL; + // Use for e.g. linking a tool window in a standalone viewport to a document window, regardless of their Begin() stack parenting. (#6798) + window->ParentWindowForFocusRoute = (window->RootWindow != window) ? parent_window_in_stack : NULL; + if (window->ParentWindowForFocusRoute == NULL && window->DockNode != NULL) + if (window->DockNode->MergedFlags & ImGuiDockNodeFlags_DockedWindowsInFocusRoute) + window->ParentWindowForFocusRoute = window->DockNode->HostWindow; + + // Override with SetNextWindowClass() field or direct call to SetWindowParentWindowForFocusRoute() + if (window->WindowClass.FocusRouteParentWindowId != 0) + { + window->ParentWindowForFocusRoute = FindWindowByID(window->WindowClass.FocusRouteParentWindowId); + IM_ASSERT(window->ParentWindowForFocusRoute != 0); // Invalid value for FocusRouteParentWindowId. + } + + // Inherit SetWindowFontScale() from parent until we fix this system... + window->FontWindowScaleParents = parent_window ? parent_window->FontWindowScaleParents * parent_window->FontWindowScale : 1.0f; } // Add to focus scope stack @@ -7099,6 +7562,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal; else if (first_begin_of_the_frame) window->ContentSizeExplicit = ImVec2(0.0f, 0.0f); + if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasWindowClass) + window->WindowClass = g.NextWindowData.WindowClass; if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed) SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond); if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus) @@ -7128,6 +7593,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->IDStack.resize(1); window->DrawList->_ResetForNewFrame(); window->DC.CurrentTableIdx = -1; + if (flags & ImGuiWindowFlags_DockNodeHost) + { + window->DrawList->ChannelsSplit(2); + window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); // Render decorations on channel 1 as we will render the backgrounds manually later + } // Restore buffer capacity when woken from a compacted state, to avoid if (window->MemoryCompacted) @@ -7136,7 +7606,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged). // The title bar always display the 'name' parameter, so we only update the string storage if it needs to be visible to the end-user elsewhere. bool window_title_visible_elsewhere = false; - if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB + if ((window->Viewport && window->Viewport->Window == window) || (window->DockIsActive)) + window_title_visible_elsewhere = true; + else if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB window_title_visible_elsewhere = true; if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0) { @@ -7149,6 +7621,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update contents size from last frame for auto-fitting (or use explicit size) CalcWindowContentSizes(window, &window->ContentSize, &window->ContentSizeIdeal); + + // FIXME: These flags are decremented before they are used. This means that in order to have these fields produce their intended behaviors + // for one frame we must set them to at least 2, which is counter-intuitive. HiddenFramesCannotSkipItems is a more complicated case because + // it has a single usage before this code block and may be set below before it is finally checked. if (window->HiddenFramesCanSkipItems > 0) window->HiddenFramesCanSkipItems--; if (window->HiddenFramesCannotSkipItems > 0) @@ -7176,20 +7652,23 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // SELECT VIEWPORT - // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style) + // We need to do this before using any style/font sizes, as viewport with a different DPI may affect font sizes. - ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport(); - SetWindowViewport(window, viewport); + WindowSelectViewport(window); + SetCurrentViewport(window, window->Viewport); + window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f; SetCurrentWindow(window); + flags = window->Flags; // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies) + // We read Style data after the call to UpdateSelectWindowViewport() which might be swapping the style. - if (flags & ImGuiWindowFlags_ChildWindow) + if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow)) window->WindowBorderSize = style.ChildBorderSize; else window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; window->WindowPadding = style.WindowPadding; - if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !(window->ChildFlags & ImGuiChildFlags_AlwaysUseWindowPadding) && window->WindowBorderSize == 0.0f) + if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !(window->ChildFlags & ImGuiChildFlags_AlwaysUseWindowPadding) && window->WindowBorderSize == 0.0f) window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f); // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size. @@ -7197,6 +7676,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y; window->TitleBarHeight = (flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : g.FontSize + g.Style.FramePadding.y * 2.0f; window->MenuBarHeight = (flags & ImGuiWindowFlags_MenuBar) ? window->DC.MenuBarOffset.y + g.FontSize + g.Style.FramePadding.y * 2.0f : 0.0f; + window->FontRefSize = g.FontSize; // Lock this to discourage calling window->CalcFontSize() outside of current window. // Depending on condition we use previous or current window size to compare against contents size to decide if a scrollbar should be visible. // Those flags will be altered further down in the function depending on more conditions. @@ -7209,7 +7689,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Collapse window by double-clicking on title bar // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing - if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse)) + if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse) && !window->DockIsActive) { // We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), // so verify that we don't have items over the title bar. @@ -7310,23 +7790,66 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) window->Pos = FindBestWindowPosForPopup(window); + // Late create viewport if we don't fit within our current host viewport. + if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_IsMinimized)) + if (!window->Viewport->GetMainRect().Contains(window->Rect())) + { + // This is based on the assumption that the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) + //ImGuiViewport* old_viewport = window->Viewport; + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing); + + // FIXME-DPI + //IM_ASSERT(old_viewport->DpiScale == window->Viewport->DpiScale); // FIXME-DPI: Something went wrong + SetCurrentViewport(window, window->Viewport); + window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f; + SetCurrentWindow(window); + } + + if (window->ViewportOwned) + WindowSyncOwnedViewport(window, parent_window_in_stack); + // Calculate the range of allowed position for that window (to be movable and visible past safe area padding) // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect. - ImRect viewport_rect(viewport->GetMainRect()); - ImRect viewport_work_rect(viewport->GetWorkRect()); + ImRect viewport_rect(window->Viewport->GetMainRect()); + ImRect viewport_work_rect(window->Viewport->GetWorkRect()); ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding); // Clamp position/size so window stays visible within its viewport or monitor // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. + // FIXME: Similar to code in GetWindowAllowedExtentRect() if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow)) - if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f) + { + if (!window->ViewportOwned && viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f) + { + ClampWindowPos(window, visibility_rect); + } + else if (window->ViewportOwned && g.PlatformIO.Monitors.Size > 0) + { + if (g.MovingWindow != NULL && window->RootWindowDockTree == g.MovingWindow->RootWindowDockTree) + { + // While moving windows we allow them to straddle monitors (#7299, #3071) + visibility_rect = g.PlatformMonitorsFullWorkRect; + } + else + { + // When not moving ensure visible in its monitor + // Lost windows (e.g. a monitor disconnected) will naturally moved to the fallback/dummy monitor aka the main viewport. + const ImGuiPlatformMonitor* monitor = GetViewportPlatformMonitor(window->Viewport); + visibility_rect = ImRect(monitor->WorkPos, monitor->WorkPos + monitor->WorkSize); + } + visibility_rect.Expand(-visibility_padding); ClampWindowPos(window, visibility_rect); + } + } window->Pos = ImTrunc(window->Pos); // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) // Large values tend to lead to variety of artifacts and are not recommended. - window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; + if (window->ViewportOwned || window->DockIsActive) + window->WindowRounding = 0.0f; + else + window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts. //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar)) @@ -7338,7 +7861,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { if (flags & ImGuiWindowFlags_Popup) want_focus = true; - else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0) + else if ((window->DockIsActive || (flags & ImGuiWindowFlags_ChildWindow) == 0) && !(flags & ImGuiWindowFlags_Tooltip)) want_focus = true; } @@ -7357,12 +7880,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } #endif + // Decide if we are going to handle borders and resize grips + const bool handle_borders_and_resize_grips = (window->DockNodeAsHost || !window->DockIsActive); + // Handle manual resize: Resize Grips, Borders, Gamepad int border_hovered = -1, border_held = -1; ImU32 resize_grip_col[4] = {}; const int resize_grip_count = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup)) ? 0 : g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it. const float resize_grip_draw_size = IM_TRUNC(ImMax(g.FontSize * 1.10f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); - if (!window->Collapsed) + if (handle_borders_and_resize_grips && !window->Collapsed) if (int auto_fit_mask = UpdateWindowManualResize(window, size_auto_fit, &border_hovered, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect)) { if (auto_fit_mask & (1 << ImGuiAxis_X)) @@ -7373,6 +7899,20 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->ResizeBorderHovered = (signed char)border_hovered; window->ResizeBorderHeld = (signed char)border_held; + // Synchronize window --> viewport again and one last time (clamping and manual resize may have affected either) + if (window->ViewportOwned) + { + if (!window->Viewport->PlatformRequestMove) + window->Viewport->Pos = window->Pos; + if (!window->Viewport->PlatformRequestResize) + window->Viewport->Size = window->Size; + window->Viewport->UpdateWorkRect(); + viewport_rect = window->Viewport->GetMainRect(); + } + + // Save last known viewport position within the window itself (so it can be saved in .ini file and restored) + window->ViewportPos = window->Viewport->Pos; + // SCROLLBAR VISIBILITY // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size). @@ -7411,6 +7951,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const ImRect outer_rect = window->Rect(); const ImRect title_bar_rect = window->TitleBarRect(); window->OuterRectClipped = outer_rect; + if (window->DockIsActive) + window->OuterRectClipped.Min.y += window->TitleBarHeight; window->OuterRectClipped.ClipWith(host_rect); // Inner rectangle @@ -7473,6 +8015,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Child windows can render their decoration (bg color, border, scrollbars, etc.) within their parent to save a draw call (since 1.71) // When using overlapping child windows, this will break the assumption that child z-order is mapped to submission order. // FIXME: User code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected (github #4493) + const bool is_undocked_or_docked_visible = !window->DockIsActive || window->DockTabIsVisible; + if (is_undocked_or_docked_visible) { bool render_decorations_in_parent = false; if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) @@ -7490,8 +8034,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Handle title bar, scrollbar, resize grips and resize borders const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow; - const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight); - const bool handle_borders_and_resize_grips = true; // This exists to facilitate merge with 'docking' branch. + const bool title_bar_is_highlight = want_focus || (window_to_highlight && (window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight || (window->DockNode && window->DockNode == window_to_highlight->DockNode))); RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size); if (render_decorations_in_parent) @@ -7574,6 +8117,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->AutoFitFramesY > 0) window->AutoFitFramesY--; + // Clear SetNextWindowXXX data (can aim to move this higher in the function) + g.NextWindowData.ClearFlags(); + // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there) // We ImGuiFocusRequestFlags_UnlessBelowModal to: // - Avoid focusing a window that is created outside of a modal. This will prevent active modal from being closed. @@ -7583,6 +8129,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (want_focus && window == g.NavWindow) NavInitWindow(window, false); // <-- this is in the way for us to be able to defer and sort reappearing FocusWindow() calls + // Close requested by platform window (apply to all windows in this viewport) + if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport()) + { + IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Window '%s' closed by PlatformRequestClose\n", window->Name); + *p_open = false; + g.NavWindowingToggleLayer = false; // Assume user mapped PlatformRequestClose on ALT-F4 so we disable ALT for menu toggle. False positive not an issue. // FIXME-NAV: Try removing. + } + // Pressing CTRL+C copy window content into the clipboard // [EXPERIMENTAL] Breaks on nested Begin/End pairs. We need to work that out and add better logging scope. // [EXPERIMENTAL] Text outputs has many issues. @@ -7591,8 +8145,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) LogToClipboard(0); // Title bar - if (!(flags & ImGuiWindowFlags_NoTitleBar)) + if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive) RenderWindowTitleBarContents(window, ImRect(title_bar_rect.Min.x + window->WindowBorderSize, title_bar_rect.Min.y, title_bar_rect.Max.x - window->WindowBorderSize, title_bar_rect.Max.y), name, p_open); + else if (!(flags & ImGuiWindowFlags_NoTitleBar) && window->DockIsActive) + LogText("%.*s\n", (int)(FindRenderedTextEnd(window->Name) - window->Name), window->Name); // Clear hit test shape every frame window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0; @@ -7600,8 +8156,24 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (flags & ImGuiWindowFlags_Tooltip) g.TooltipPreviousWindow = window; + if (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) + { + // Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source. + // We need to do this _before_ we overwrite window->DC.LastItemId below because BeginDockableDragDropSource() also overwrites it. + if (g.MovingWindow == window && (window->RootWindowDockTree->Flags & ImGuiWindowFlags_NoDocking) == 0) + BeginDockableDragDropSource(window); + + // Docking: Any dockable window can act as a target. For dock node hosts we call BeginDockableDragDropTarget() in DockNodeUpdate() instead. + if (g.DragDropActive && !(flags & ImGuiWindowFlags_NoDocking)) + if (g.MovingWindow == NULL || g.MovingWindow->RootWindowDockTree != window) + if ((window == window->RootWindowDockTree) && !(window->Flags & ImGuiWindowFlags_DockNodeHost)) + BeginDockableDragDropTarget(window); + } + // We fill last item data based on Title Bar/Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin(). // This is useful to allow creating context menus on title bar only, etc. + window->DC.WindowItemStatusFlags = ImGuiItemStatusFlags_None; + window->DC.WindowItemStatusFlags |= IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0; SetLastItemDataForWindow(window, title_bar_rect); // [DEBUG] @@ -7627,26 +8199,39 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) SetWindowActiveForSkipRefresh(window); // Append + SetCurrentViewport(window, window->Viewport); SetCurrentWindow(window); + g.NextWindowData.ClearFlags(); SetLastItemDataForWindow(window, window->TitleBarRect()); } - if (!window->SkipRefresh) + if (!(flags & ImGuiWindowFlags_DockNodeHost) && !window->SkipRefresh) PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); // Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused) window->WriteAccessed = false; window->BeginCount++; - g.NextWindowData.ClearFlags(); // Update visibility if (first_begin_of_the_frame && !window->SkipRefresh) { + // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesCannotSkipItems. + // This will have the important effect of actually returning true in Begin() and not setting SkipItems, allowing an earlier submission of the window contents. + // This is analogous to regular windows being hidden from one frame. + // It is especially important as e.g. nested TabBars would otherwise generate flicker in the form of one empty frame, or focus requests won't be processed. + if (window->DockIsActive && !window->DockTabIsVisible) + { + if (window->LastFrameJustFocused == g.FrameCount) + window->HiddenFramesCannotSkipItems = 1; + else + window->HiddenFramesCanSkipItems = 1; + } + if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_ChildMenu)) { // Child window can be out of sight and have "negative" clip windows. // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). - IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); + IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0 || window->DockIsActive); const bool nav_request = (window->ChildFlags & ImGuiChildFlags_NavFlattened) && (g.NavAnyRequest && g.NavWindow && g.NavWindow->RootWindowForNav == window->RootWindowForNav); if (!g.LogEnabled && !nav_request) if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y) @@ -7685,6 +8270,16 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesCannotSkipItems <= 0) skip_items = true; window->SkipItems = skip_items; + + // Restore NavLayersActiveMaskNext to previous value when not visible, so a CTRL+Tab back can use a safe value. + if (window->SkipItems) + window->DC.NavLayersActiveMaskNext = window->DC.NavLayersActiveMask; + + // Sanity check: there are two spots which can set Appearing = true + // - when 'window_just_activated_by_user' is set -> HiddenFramesCannotSkipItems is set -> SkipItems always false + // - in BeginDocked() path when DockNodeIsVisible == DockTabIsVisible == true -> hidden _should_ be all zero // FIXME: Not formally proven, hence the assert. + if (window->SkipItems && !window->Appearing) + IM_ASSERT(window->Appearing == false); // Please report on GitHub if this triggers: https://github.com/ocornut/imgui/issues/4177 } else if (first_begin_of_the_frame) { @@ -7707,12 +8302,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) return !window->SkipItems; } -static void ImGui::SetLastItemDataForWindow(ImGuiWindow* window, const ImRect& rect) -{ - ImGuiContext& g = *GImGui; - SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(rect.Min, rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, rect); -} - void ImGui::End() { ImGuiContext& g = *GImGui; @@ -7727,14 +8316,14 @@ void ImGui::End() ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back(); // Error checking: verify that user doesn't directly call End() on a child window. - if (window->Flags & ImGuiWindowFlags_ChildWindow) + if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !(window->Flags & ImGuiWindowFlags_DockNodeHost) && !window->DockIsActive) IM_ASSERT_USER_ERROR(g.WithinEndChildID == window->ID, "Must call EndChild() and not End()!"); // Close anything that is open if (window->DC.CurrentColumns) EndColumns(); - if (!window->SkipRefresh) - PopClipRect(); // Inner window clip rectangle + if (!(window->Flags & ImGuiWindowFlags_DockNodeHost) && !window->SkipRefresh) // Pop inner window clip rectangle + PopClipRect(); PopFocusScope(); if (window_stack_data.DisabledOverrideReenable && window->RootWindow == window) EndDisabledOverrideReenable(); @@ -7752,6 +8341,11 @@ void ImGui::End() if (window->DC.IsSetPos) ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); + // Docking: report contents sizes to parent to allow for auto-resize + if (window->DockNode && window->DockTabIsVisible) + if (ImGuiWindow* host_window = window->DockNode->HostWindow) // FIXME-DOCK + host_window->DC.CursorMaxPos = window->DC.CursorMaxPos + window->WindowPadding - host_window->WindowPadding; + // Pop from window stack g.LastItemData = window_stack_data.ParentLastItemDataBackup; if (window->Flags & ImGuiWindowFlags_ChildMenu) @@ -7765,6 +8359,8 @@ void ImGui::End() g.CurrentWindowStack.pop_back(); SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window); + if (g.CurrentWindow) + SetCurrentViewport(g.CurrentWindow, g.CurrentWindow->Viewport); } // Important: this alone doesn't alter current ImDrawList state. This is called by PushFont/PopFont only. @@ -7925,7 +8521,7 @@ void ImGui::PopTextWrapPos() window->DC.TextWrapPosStack.pop_back(); } -static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy) +static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy, bool dock_hierarchy) { ImGuiWindow* last_window = NULL; while (last_window != window) @@ -7934,13 +8530,15 @@ static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierar window = window->RootWindow; if (popup_hierarchy) window = window->RootWindowPopupTree; - } + if (dock_hierarchy) + window = window->RootWindowDockTree; + } return window; } -bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy) +bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy) { - ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy); + ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy, dock_hierarchy); if (window_root == potential_parent) return true; while (window != NULL) @@ -8005,12 +8603,13 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) { IM_ASSERT(cur_window); // Not inside a Begin()/End() const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0; + const bool dock_hierarchy = (flags & ImGuiHoveredFlags_DockHierarchy) != 0; if (flags & ImGuiHoveredFlags_RootWindow) - cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy); + cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy); bool result; if (flags & ImGuiHoveredFlags_ChildWindows) - result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy); + result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy); else result = (ref_window == cur_window); if (!result) @@ -8036,6 +8635,18 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) return true; } +ImGuiID ImGui::GetWindowDockID() +{ + ImGuiContext& g = *GImGui; + return g.CurrentWindow->DockId; +} + +bool ImGui::IsWindowDocked() +{ + ImGuiContext& g = *GImGui; + return g.CurrentWindow->DockIsActive; +} + float ImGui::GetWindowWidth() { ImGuiWindow* window = GImGui->CurrentWindow; @@ -8072,6 +8683,7 @@ void ImGui::SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond) if (offset.x == 0.0f && offset.y == 0.0f) return; MarkIniSettingsDirty(window); + // FIXME: share code with TranslateWindow(), need to confirm whether the 3 rect modified by TranslateWindow() are desirable here. window->DC.CursorPos += offset; // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor window->DC.CursorMaxPos += offset; // And more importantly we need to offset CursorMaxPos/CursorStartPos this so ContentSize calculation doesn't get affected. window->DC.IdealMaxPos += offset; @@ -8191,6 +8803,7 @@ void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pi g.NextWindowData.PosVal = pos; g.NextWindowData.PosPivotVal = pivot; g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always; + g.NextWindowData.PosUndock = true; } void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond) @@ -8247,6 +8860,29 @@ void ImGui::SetNextWindowBgAlpha(float alpha) g.NextWindowData.BgAlphaVal = alpha; } +void ImGui::SetNextWindowViewport(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasViewport; + g.NextWindowData.ViewportId = id; +} + +void ImGui::SetNextWindowDockID(ImGuiID id, ImGuiCond cond) +{ + ImGuiContext& g = *GImGui; + g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasDock; + g.NextWindowData.DockCond = cond ? cond : ImGuiCond_Always; + g.NextWindowData.DockId = id; +} + +void ImGui::SetNextWindowClass(const ImGuiWindowClass* window_class) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT((window_class->ViewportFlagsOverrideSet & window_class->ViewportFlagsOverrideClear) == 0); // Cannot set both set and clear for the same bit + g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasWindowClass; + g.NextWindowData.WindowClass = *window_class; +} + // This is experimental and meant to be a toy for exploring a future/wider range of features. void ImGui::SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags flags) { @@ -8261,6 +8897,19 @@ ImDrawList* ImGui::GetWindowDrawList() return window->DrawList; } +float ImGui::GetWindowDpiScale() +{ + ImGuiContext& g = *GImGui; + return g.CurrentDpiScale; +} + +ImGuiViewport* ImGui::GetWindowViewport() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.CurrentViewport != NULL && g.CurrentViewport == g.CurrentWindow->Viewport); + return g.CurrentViewport; +} + ImFont* ImGui::GetFont() { return GImGui->Font; @@ -9204,6 +9853,17 @@ bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id) return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id) } +// Use if you absolutely need to distinguish single-click from double-click by introducing a delay. +// Generally use with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount == 1' test. +// This is a very rarely used UI idiom, but some apps use this: e.g. MS Explorer single click on an icon to rename. +bool ImGui::IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + const float time_since_release = (float)(g.Time - g.IO.MouseReleasedTime[button]); + return !IsMouseDown(button) && (time_since_release - g.IO.DeltaTime < delay) && (time_since_release >= delay); +} + bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; @@ -9240,6 +9900,8 @@ bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool c // Hit testing, expanded for touch input if (!rect_clipped.ContainsWithPad(g.IO.MousePos, g.Style.TouchExtraPadding)) return false; + if (!g.MouseViewport->GetMainRect().Overlaps(rect_clipped)) + return false; return true; } @@ -9477,6 +10139,8 @@ static void ImGui::UpdateMouseInputs() io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f; io.MouseClickedCount[i] = 0; // Will be filled below io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f; + if (io.MouseReleased[i]) + io.MouseReleasedTime[i] = g.Time; io.MouseDownDurationPrev[i] = io.MouseDownDuration[i]; io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f; if (io.MouseClicked[i]) @@ -9495,13 +10159,16 @@ static void ImGui::UpdateMouseInputs() io.MouseClickedTime[i] = g.Time; io.MouseClickedPos[i] = io.MousePos; io.MouseClickedCount[i] = io.MouseClickedLastCount[i]; + io.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); io.MouseDragMaxDistanceSqr[i] = 0.0f; } else if (io.MouseDown[i]) { // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold - float delta_sqr_click_pos = IsMousePosValid(&io.MousePos) ? ImLengthSqr(io.MousePos - io.MouseClickedPos[i]) : 0.0f; - io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], delta_sqr_click_pos); + ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); + io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos)); + io.MouseDragMaxDistanceAbs[i].x = ImMax(io.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x); + io.MouseDragMaxDistanceAbs[i].y = ImMax(io.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y); } // We provide io.MouseDoubleClicked[] as a legacy service @@ -9645,7 +10312,7 @@ void ImGui::UpdateMouseWheel() { LockWheelingWindow(window, wheel.x); float max_step = window->InnerRect.GetWidth() * 0.67f; - float scroll_step = ImTrunc(ImMin(2 * window->CalcFontSize(), max_step)); + float scroll_step = ImTrunc(ImMin(2 * window->FontRefSize, max_step)); SetScrollX(window, window->Scroll.x - wheel.x * scroll_step); g.WheelingWindowScrolledFrame = g.FrameCount; } @@ -9653,7 +10320,7 @@ void ImGui::UpdateMouseWheel() { LockWheelingWindow(window, wheel.y); float max_step = window->InnerRect.GetHeight() * 0.67f; - float scroll_step = ImTrunc(ImMin(5 * window->CalcFontSize(), max_step)); + float scroll_step = ImTrunc(ImMin(5 * window->FontRefSize, max_step)); SetScrollY(window, window->Scroll.y - wheel.y * scroll_step); g.WheelingWindowScrolledFrame = g.FrameCount; } @@ -9691,6 +10358,7 @@ static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e) if (e->Type == ImGuiInputEventType_MousePos) { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (-FLT_MAX, -FLT_MAX)\n", prefix); else IMGUI_DEBUG_LOG_IO("[io] %s: MousePos (%.1f, %.1f) (%s)\n", prefix, e->MousePos.PosX, e->MousePos.PosY, GetMouseSourceName(e->MousePos.MouseSource)); return; } if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("[io] %s: MouseButton %d %s (%s)\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up", GetMouseSourceName(e->MouseButton.MouseSource)); return; } if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("[io] %s: MouseWheel (%.3f, %.3f) (%s)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY, GetMouseSourceName(e->MouseWheel.MouseSource)); return; } + if (e->Type == ImGuiInputEventType_MouseViewport){IMGUI_DEBUG_LOG_IO("[io] %s: MouseViewport (0x%08X)\n", prefix, e->MouseViewport.HoveredViewportID); return; } if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG_IO("[io] %s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; } if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_LOG_IO("[io] %s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; } if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG_IO("[io] %s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; } @@ -9754,6 +10422,10 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) io.MouseSource = e->MouseWheel.MouseSource; mouse_wheeled = true; } + else if (e->Type == ImGuiInputEventType_MouseViewport) + { + io.MouseHoveredViewport = e->MouseViewport.HoveredViewportID; + } else if (e->Type == ImGuiInputEventType_Key) { // Trickling Rule: Stop processing queued events if we got multiple action on the same button @@ -10160,10 +10832,49 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() if (g.IO.SetClipboardTextFn != NULL && (g.PlatformIO.Platform_SetClipboardTextFn == NULL || g.PlatformIO.Platform_SetClipboardTextFn == Platform_SetClipboardTextFn_DefaultImpl)) g.PlatformIO.Platform_SetClipboardTextFn = [](ImGuiContext* ctx, const char* text) { return ctx->IO.SetClipboardTextFn(ctx->IO.ClipboardUserData, text); }; #endif -} -static void ImGui::ErrorCheckEndFrameSanityChecks() -{ + // Perform simple check: error if Docking or Viewport are enabled _exactly_ on frame 1 (instead of frame 0 or later), which is a common error leading to loss of .ini data. + if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_DockingEnable) == 0) + IM_ASSERT(0 && "Please set DockingEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!"); + if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable) == 0) + IM_ASSERT(0 && "Please set ViewportsEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!"); + + // Perform simple checks: multi-viewport and platform windows support + if (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + if ((g.IO.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasViewports)) + { + IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() in main loop after EndFrame()? Check examples/ applications for reference."); + IM_ASSERT(g.PlatformIO.Platform_CreateWindow != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Platform_GetWindowPos != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Platform_SetWindowPos != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Platform_GetWindowSize != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Platform_SetWindowSize != NULL && "Platform init didn't install handlers?"); + IM_ASSERT(g.PlatformIO.Monitors.Size > 0 && "Platform init didn't setup Monitors list?"); + IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && "Platform init didn't setup main viewport."); + if (g.IO.ConfigDockingTransparentPayload && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) + IM_ASSERT(g.PlatformIO.Platform_SetWindowAlpha != NULL && "Platform_SetWindowAlpha handler is required to use io.ConfigDockingTransparent!"); + } + else + { + // Disable feature, our backends do not support it + g.IO.ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable; + } + + // Perform simple checks on platform monitor data + compute a total bounding box for quick early outs + for (ImGuiPlatformMonitor& mon : g.PlatformIO.Monitors) + { + IM_UNUSED(mon); + IM_ASSERT(mon.MainSize.x > 0.0f && mon.MainSize.y > 0.0f && "Monitor main bounds not setup properly."); + IM_ASSERT(ImRect(mon.MainPos, mon.MainPos + mon.MainSize).Contains(ImRect(mon.WorkPos, mon.WorkPos + mon.WorkSize)) && "Monitor work bounds not setup properly. If you don't have work area information, just copy MainPos/MainSize into them."); + IM_ASSERT(mon.DpiScale > 0.0f && mon.DpiScale < 99.0f && "Monitor DpiScale is invalid."); // Typical correct values would be between 1.0f and 4.0f + } + } +} + +static void ImGui::ErrorCheckEndFrameSanityChecks() +{ // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame() // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame(). // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will @@ -11268,7 +11979,8 @@ bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags ext SetWindowHiddenAndSkipItemsForCurrentFrame(g.TooltipPreviousWindow); ImFormatString(window_name, IM_ARRAYSIZE(window_name), window_name_template, ++g.TooltipOverrideCount); } - ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize; + + ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDocking; Begin(window_name, NULL, flags | extra_window_flags); // 2023-03-09: Added bool return value to the API, but currently always returning true. // If this ever returns false we need to update BeginDragDropSource() accordingly. @@ -11341,8 +12053,8 @@ bool ImGui::IsPopupOpen(ImGuiID id, ImGuiPopupFlags popup_flags) if (popup_flags & ImGuiPopupFlags_AnyPopupLevel) { // Return true if the popup is open anywhere in the popup stack - for (int n = 0; n < g.OpenPopupStack.Size; n++) - if (g.OpenPopupStack[n].PopupId == id) + for (ImGuiPopupData& popup_data : g.OpenPopupStack) + if (popup_data.PopupId == id) return true; return false; } @@ -11518,12 +12230,13 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to // Window -> Popup1 -> Window2(Ref) // - Clicking/focusing Popup1 will close Popup2 and Popup3: // Window -> Popup1(Ref) -> Popup2 -> Popup3 - // - Each popups may contain child windows, which is why we compare ->RootWindow! + // - Each popups may contain child windows, which is why we compare ->RootWindowDockTree! // Window -> Popup1 -> Popup1_Child -> Popup2 -> Popup2_Child // We step through every popup from bottom to top to validate their position relative to reference window. bool ref_window_is_descendent_of_popup = false; for (int n = popup_count_to_keep; n < g.OpenPopupStack.Size; n++) if (ImGuiWindow* popup_window = g.OpenPopupStack[n].Window) + //if (popup_window->RootWindowDockTree == ref_window->RootWindowDockTree) // FIXME-MERGE if (IsWindowWithinBeginStackOf(ref_window, popup_window)) { ref_window_is_descendent_of_popup = true; @@ -11627,7 +12340,7 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_window_flags) else ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame - bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup); + bool is_open = Begin(name, NULL, extra_window_flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoDocking); if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) EndPopup(); @@ -11671,11 +12384,11 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0) { - const ImGuiViewport* viewport = GetMainViewport(); + const ImGuiViewport* viewport = window->WasActive ? window->Viewport : GetMainViewport(); // FIXME-VIEWPORT: What may be our reference viewport? SetNextWindowPos(viewport->GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); } - flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse; + flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking; const bool is_open = Begin(name, p_open, flags); if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) { @@ -11862,8 +12575,19 @@ ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& s ImRect ImGui::GetPopupAllowedExtentRect(ImGuiWindow* window) { ImGuiContext& g = *GImGui; - IM_UNUSED(window); - ImRect r_screen = ((ImGuiViewportP*)(void*)GetMainViewport())->GetMainRect(); + ImRect r_screen; + if (window->ViewportAllowPlatformMonitorExtend >= 0) + { + // Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here) + const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportAllowPlatformMonitorExtend]; + r_screen.Min = monitor.WorkPos; + r_screen.Max = monitor.WorkPos + monitor.WorkSize; + } + else + { + // Use the full viewport area (not work area) for popups + r_screen = window->Viewport->GetMainRect(); + } ImVec2 padding = g.Style.DisplaySafeAreaPadding; r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f)); return r_screen; @@ -11878,8 +12602,7 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) { // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds. // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. - IM_ASSERT(g.CurrentWindow == window); - ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2].Window; + ImGuiWindow* parent_window = window->ParentWindow; float horizontal_overlap = g.Style.ItemInnerSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x). ImRect r_avoid; if (parent_window->DC.MenuBarAppending) @@ -11979,11 +12702,12 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) IM_ASSERT(cur_window); // Not inside a Begin()/End() const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0; + const bool dock_hierarchy = (flags & ImGuiFocusedFlags_DockHierarchy) != 0; if (flags & ImGuiHoveredFlags_RootWindow) - cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy); + cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy); if (flags & ImGuiHoveredFlags_ChildWindows) - return IsWindowChildOf(ref_window, cur_window, popup_hierarchy); + return IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy); else return (ref_window == cur_window); } @@ -12047,7 +12771,7 @@ void ImGui::BringWindowToDisplayFront(ImGuiWindow* window) { ImGuiContext& g = *GImGui; ImGuiWindow* current_front_window = g.Windows.back(); - if (current_front_window == window || current_front_window->RootWindow == window) // Cheap early out (could be better) + if (current_front_window == window || current_front_window->RootWindowDockTree == window) // Cheap early out (could be better) return; for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the top-most window if (g.Windows[i] == window) @@ -12140,31 +12864,39 @@ void ImGui::FocusWindow(ImGuiWindow* window, ImGuiFocusRequestFlags flags) } // Move the root window to the top of the pile - IM_ASSERT(window == NULL || window->RootWindow != NULL); - ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; // NB: In docking branch this is window->RootWindowDockStop - ImGuiWindow* display_front_window = window ? window->RootWindow : NULL; + IM_ASSERT(window == NULL || window->RootWindowDockTree != NULL); + ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; + ImGuiWindow* display_front_window = window ? window->RootWindowDockTree : NULL; + ImGuiDockNode* dock_node = window ? window->DockNode : NULL; + bool active_id_window_is_dock_node_host = (g.ActiveIdWindow && dock_node && dock_node->HostWindow == g.ActiveIdWindow); // Steal active widgets. Some of the cases it triggers includes: // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run. // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId) + // - Using dock host items (tab, collapse button) can trigger this before we redirect the ActiveIdWindow toward the child window. if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window) - if (!g.ActiveIdNoClearOnFocusLoss) + if (!g.ActiveIdNoClearOnFocusLoss && !active_id_window_is_dock_node_host) ClearActiveID(); // Passing NULL allow to disable keyboard focus if (!window) return; + window->LastFrameJustFocused = g.FrameCount; + + // Select in dock node + // For #2304 we avoid applying focus immediately before the tabbar is visible. + //if (dock_node && dock_node->TabBar) + // dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->TabId; // Bring to front BringWindowToFocusFront(focus_front_window); - if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0) + if (((window->Flags | focus_front_window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0) BringWindowToDisplayFront(display_front_window); } void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWindow* ignore_window, ImGuiViewport* filter_viewport, ImGuiFocusRequestFlags flags) { ImGuiContext& g = *GImGui; - IM_UNUSED(filter_viewport); // Unused in master branch. int start_idx = g.WindowsFocusOrder.Size - 1; if (under_this_window != NULL) { @@ -12183,8 +12915,15 @@ void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWind ImGuiWindow* window = g.WindowsFocusOrder[i]; if (window == ignore_window || !window->WasActive) continue; + if (filter_viewport != NULL && window->Viewport != filter_viewport) + continue; if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) { + // FIXME-DOCK: When ImGuiFocusRequestFlags_RestoreFocusedChild is set... + // This is failing (lagging by one frame) for docked windows. + // If A and B are docked into window and B disappear, at the NewFrame() call site window->NavLastChildNavWindow will still point to B. + // We might leverage the tab order implicitly stored in window->DockNodeAsHost->TabBar (essentially the 'most_recently_selected_tab' code in tab bar will do that but on next update) + // to tell which is the "previous" window. Or we may leverage 'LastFrameFocused/LastFrameJustFocused' and have this function handle child window itself? FocusWindow(window, flags); return; } @@ -12717,6 +13456,9 @@ static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window) { if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive) return window->NavLastChildNavWindow; + if (window->DockNodeAsHost && window->DockNodeAsHost->TabBar) + if (ImGuiTabItem* tab = TabBarFindMostRecentlySelectedTabForActiveWindow(window->DockNodeAsHost->TabBar)) + return tab->Window; return window; } @@ -12754,6 +13496,7 @@ static inline void ImGui::NavUpdateAnyRequestFlag() // This needs to be called before we submit any widget (aka in or before Begin) void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) { + // FIXME: ChildWindow test here is wrong for docking ImGuiContext& g = *GImGui; IM_ASSERT(window == g.NavWindow); @@ -12829,7 +13572,7 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() ref_rect.Translate(window->Scroll - next_scroll); } ImVec2 pos = ImVec2(ref_rect.Min.x + ImMin(g.Style.FramePadding.x * 4, ref_rect.GetWidth()), ref_rect.Max.y - ImMin(g.Style.FramePadding.y, ref_rect.GetHeight())); - ImGuiViewport* viewport = GetMainViewport(); + ImGuiViewport* viewport = window->Viewport; return ImTrunc(ImClamp(pos, viewport->Pos, viewport->Pos + viewport->Size)); // ImTrunc() is important because non-integer mouse position application in backend might be lossy and result in undesirable non-zero delta. } } @@ -12983,7 +13726,7 @@ static void ImGui::NavUpdate() { // *Fallback* manual-scroll with Nav directional keys when window has no navigable item ImGuiWindow* window = g.NavWindow; - const float scroll_speed = IM_ROUND(window->CalcFontSize() * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. + const float scroll_speed = IM_ROUND(window->FontRefSize * 100 * io.DeltaTime); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. const ImGuiDir move_dir = g.NavMoveDir; if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavWindowHasScrollY && move_dir != ImGuiDir_None) { @@ -13173,8 +13916,8 @@ void ImGui::NavUpdateCreateMoveRequest() if ((clamp_x || clamp_y) && !inner_rect_rel.Contains(window->NavRectRel[g.NavLayer])) { IMGUI_DEBUG_LOG_NAV("[nav] NavMoveRequest: clamp NavRectRel for gamepad move\n"); - float pad_x = ImMin(inner_rect_rel.GetWidth(), window->CalcFontSize() * 0.5f); - float pad_y = ImMin(inner_rect_rel.GetHeight(), window->CalcFontSize() * 0.5f); // Terrible approximation for the intent of starting navigation from first fully visible item + float pad_x = ImMin(inner_rect_rel.GetWidth(), window->FontRefSize * 0.5f); + float pad_y = ImMin(inner_rect_rel.GetHeight(), window->FontRefSize * 0.5f); // Terrible approximation for the intent of starting navigation from first fully visible item inner_rect_rel.Min.x = clamp_x ? (inner_rect_rel.Min.x + pad_x) : -FLT_MAX; inner_rect_rel.Max.x = clamp_x ? (inner_rect_rel.Max.x - pad_x) : +FLT_MAX; inner_rect_rel.Min.y = clamp_y ? (inner_rect_rel.Min.y + pad_y) : -FLT_MAX; @@ -13432,7 +14175,7 @@ static float ImGui::NavUpdatePageUpPageDown() else { ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; - const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); + const float page_offset_y = ImMax(0.0f, window->InnerRect.GetHeight() - window->FontRefSize * 1.0f + nav_rect_rel.GetHeight()); float nav_scoring_rect_offset_y = 0.0f; if (IsKeyPressed(ImGuiKey_PageUp, true)) { @@ -13610,7 +14353,7 @@ static void ImGui::NavUpdateWindowing() // Start CTRL+Tab or Square+L/R window selection // (g.ConfigNavWindowingKeyNext/g.ConfigNavWindowingKeyPrev defaults are ImGuiMod_Ctrl|ImGuiKey_Tab and ImGuiMod_Ctrl|ImGuiMod_Shift|ImGuiKey_Tab) - const ImGuiID owner_id = ImHashStr("###NavUpdateWindowing"); + const ImGuiID owner_id = ImHashStr("##NavUpdateWindowing"); const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways, owner_id); @@ -13727,7 +14470,7 @@ static void ImGui::NavUpdateWindowing() ImVec2 accum_floored = ImTrunc(g.NavWindowingAccumDeltaPos); if (accum_floored.x != 0.0f || accum_floored.y != 0.0f) { - ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow; + ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindowDockTree; SetWindowPos(moving_window, moving_window->Pos + accum_floored, ImGuiCond_Always); g.NavWindowingAccumDeltaPos -= accum_floored; } @@ -13737,6 +14480,9 @@ static void ImGui::NavUpdateWindowing() // Apply final focus if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)) { + // FIXME: Many actions here could be part of a higher-level/reused function. Why aren't they in FocusWindow() + // Investigate for each of them: ClearActiveID(), NavRestoreHighlightAfterMove(), NavRestoreLastChildNavWindow(), ClosePopupsOverWindow(), NavInitWindow() + ImGuiViewport* previous_viewport = g.NavWindow ? g.NavWindow->Viewport : NULL; ClearActiveID(); SetNavCursorVisibleAfterMove(); ClosePopupsOverWindow(apply_focus_window, false); @@ -13754,6 +14500,10 @@ static void ImGui::NavUpdateWindowing() // won't be valid. if (apply_focus_window->DC.NavLayersActiveMaskNext == (1 << ImGuiNavLayer_Menu)) g.NavLayer = ImGuiNavLayer_Menu; + + // Request OS level focus + if (apply_focus_window->Viewport != previous_viewport && g.PlatformIO.Platform_SetWindowFocus) + g.PlatformIO.Platform_SetWindowFocus(apply_focus_window->Viewport); } if (apply_focus_window) g.NavWindowingTarget = NULL; @@ -13782,7 +14532,8 @@ static void ImGui::NavUpdateWindowing() if (new_nav_layer != g.NavLayer) { // Reinitialize navigation when entering menu bar with the Alt key (FIXME: could be a properly of the layer?) - if (new_nav_layer == ImGuiNavLayer_Menu) + const bool preserve_layer_1_nav_id = (new_nav_window->DockNodeAsHost != NULL); + if (new_nav_layer == ImGuiNavLayer_Menu && !preserve_layer_1_nav_id) g.NavWindow->NavLastIds[new_nav_layer] = 0; NavRestoreLayer(new_nav_layer); SetNavCursorVisibleAfterMove(); @@ -13797,6 +14548,8 @@ static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window) return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingPopup); if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0) return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingMainMenuBar); + if (window->DockNodeAsHost) + return "(Dock node)"; // Not normally shown to user. return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingUntitled); } @@ -13810,12 +14563,12 @@ void ImGui::NavUpdateWindowingOverlay() return; if (g.NavWindowingListWindow == NULL) - g.NavWindowingListWindow = FindWindowByName("###NavWindowingList"); - const ImGuiViewport* viewport = GetMainViewport(); + g.NavWindowingListWindow = FindWindowByName("##NavWindowingOverlay"); + const ImGuiViewport* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ GetMainViewport(); SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); - Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); + Begin("##NavWindowingOverlay", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); if (g.ContextName[0] != 0) SeparatorText(g.ContextName); for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--) @@ -14061,7 +14814,7 @@ bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id) ImGuiWindow* window = g.CurrentWindow; ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow; - if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow) + if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree) return false; IM_ASSERT(id != 0); if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId)) @@ -14091,7 +14844,7 @@ bool ImGui::BeginDragDropTarget() if (!(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect)) return false; ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow; - if (hovered_window == NULL || window->RootWindow != hovered_window->RootWindow || window->SkipItems) + if (hovered_window == NULL || window->RootWindowDockTree != hovered_window->RootWindowDockTree || window->SkipItems) return false; const ImRect& display_rect = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? g.LastItemData.DisplayRect : g.LastItemData.Rect; @@ -14697,11 +15450,14 @@ ImGuiWindowSettings* ImGui::FindWindowSettingsByWindow(ImGuiWindow* window) void ImGui::ClearWindowSettings(const char* name) { //IMGUI_DEBUG_LOG("ClearWindowSettings('%s')\n", name); + ImGuiContext& g = *GImGui; ImGuiWindow* window = FindWindowByName(name); if (window != NULL) { window->Flags |= ImGuiWindowFlags_NoSavedSettings; InitOrLoadWindowSettings(window, NULL); + if (window->DockId != 0) + DockContextProcessUndockWindow(&g, window, true); } if (ImGuiWindowSettings* settings = window ? FindWindowSettingsByWindow(window) : FindWindowSettingsByID(ImHashStr(name))) settings->WantDelete = true; @@ -14733,10 +15489,16 @@ static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry; int x, y; int i; - if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); } - else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); } - else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } - else if (sscanf(line, "IsChild=%d", &i) == 1) { settings->IsChild = (i != 0); } + ImU32 u1; + if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); } + else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); } + else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1) { settings->ViewportId = u1; } + else if (sscanf(line, "ViewportPos=%i,%i", &x, &y) == 2){ settings->ViewportPos = ImVec2ih((short)x, (short)y); } + else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); } + else if (sscanf(line, "IsChild=%d", &i) == 1) { settings->IsChild = (i != 0); } + else if (sscanf(line, "DockId=0x%X,%d", &u1, &i) == 2) { settings->DockId = u1; settings->DockOrder = (short)i; } + else if (sscanf(line, "DockId=0x%X", &u1) == 1) { settings->DockId = u1; settings->DockOrder = -1; } + else if (sscanf(line, "ClassId=0x%X", &u1) == 1) { settings->ClassId = u1; } } // Apply to existing windows (if any) @@ -14769,10 +15531,16 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings); } IM_ASSERT(settings->ID == window->ID); - settings->Pos = ImVec2ih(window->Pos); + settings->Pos = ImVec2ih(window->Pos - window->ViewportPos); settings->Size = ImVec2ih(window->SizeFull); - settings->IsChild = (window->Flags & ImGuiWindowFlags_ChildWindow) != 0; + settings->ViewportId = window->ViewportId; + settings->ViewportPos = ImVec2ih(window->ViewportPos); + IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId); + settings->DockId = window->DockId; + settings->ClassId = window->WindowClass.ClassId; + settings->DockOrder = window->DockOrder; settings->Collapsed = window->Collapsed; + settings->IsChild = (window->RootWindow != window); // Cannot rely on ImGuiWindowFlags_ChildWindow here as docked windows have this set. settings->WantDelete = false; } @@ -14791,10 +15559,26 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl } else { - buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y); - buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y); - if (settings->Collapsed) - buf->appendf("Collapsed=1\n"); + if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID) + { + buf->appendf("ViewportPos=%d,%d\n", settings->ViewportPos.x, settings->ViewportPos.y); + buf->appendf("ViewportId=0x%08X\n", settings->ViewportId); + } + if (settings->Pos.x != 0 || settings->Pos.y != 0 || settings->ViewportId == ImGui::IMGUI_VIEWPORT_DEFAULT_ID) + buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y); + if (settings->Size.x != 0 || settings->Size.y != 0) + buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y); + buf->appendf("Collapsed=%d\n", settings->Collapsed); + if (settings->DockId != 0) + { + //buf->appendf("TabId=0x%08X\n", ImHashStr("#TAB", 4, settings->ID)); // window->TabId: this is not read back but writing it makes "debugging" the .ini data easier. + if (settings->DockOrder == -1) + buf->appendf("DockId=0x%08X\n", settings->DockId); + else + buf->appendf("DockId=0x%08X,%d\n", settings->DockId, settings->DockOrder); + if (settings->ClassId != 0) + buf->appendf("ClassId=0x%08X\n", settings->ClassId); + } } buf->append("\n"); } @@ -14815,10 +15599,28 @@ void ImGui::LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count) // [SECTION] VIEWPORTS, PLATFORM WINDOWS //----------------------------------------------------------------------------- // - GetMainViewport() +// - FindViewportByID() +// - FindViewportByPlatformHandle() +// - SetCurrentViewport() [Internal] // - SetWindowViewport() [Internal] +// - GetWindowAlwaysWantOwnViewport() [Internal] +// - UpdateTryMergeWindowIntoHostViewport() [Internal] +// - UpdateTryMergeWindowIntoHostViewports() [Internal] +// - TranslateWindowsInViewport() [Internal] // - ScaleWindowsInViewport() [Internal] +// - FindHoveredViewportFromPlatformWindowStack() [Internal] // - UpdateViewportsNewFrame() [Internal] -// (this section is more complete in the 'docking' branch) +// - UpdateViewportsEndFrame() [Internal] +// - AddUpdateViewport() [Internal] +// - WindowSelectViewport() [Internal] +// - WindowSyncOwnedViewport() [Internal] +// - UpdatePlatformWindows() +// - RenderPlatformWindowsDefault() +// - FindPlatformMonitorForPos() [Internal] +// - FindPlatformMonitorForRect() [Internal] +// - UpdateViewportPlatformMonitor() [Internal] +// - DestroyPlatformWindow() [Internal] +// - DestroyPlatformWindows() //----------------------------------------------------------------------------- ImGuiViewport* ImGui::GetMainViewport() @@ -14827,156 +15629,5033 @@ ImGuiViewport* ImGui::GetMainViewport() return g.Viewports[0]; } +// FIXME: This leaks access to viewports not listed in PlatformIO.Viewports[]. Problematic? (#4236) +ImGuiViewport* ImGui::FindViewportByID(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + for (ImGuiViewportP* viewport : g.Viewports) + if (viewport->ID == id) + return viewport; + return NULL; +} + +ImGuiViewport* ImGui::FindViewportByPlatformHandle(void* platform_handle) +{ + ImGuiContext& g = *GImGui; + for (ImGuiViewportP* viewport : g.Viewports) + if (viewport->PlatformHandle == platform_handle) + return viewport; + return NULL; +} + +void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* viewport) +{ + ImGuiContext& g = *GImGui; + (void)current_window; + + if (viewport) + viewport->LastFrameActive = g.FrameCount; + if (g.CurrentViewport == viewport) + return; + g.CurrentDpiScale = viewport ? viewport->DpiScale : 1.0f; + g.CurrentViewport = viewport; + IM_ASSERT(g.CurrentDpiScale > 0.0f && g.CurrentDpiScale < 99.0f); // Typical correct values would be between 1.0f and 4.0f + //IMGUI_DEBUG_LOG_VIEWPORT("[viewport] SetCurrentViewport changed '%s' 0x%08X\n", current_window ? current_window->Name : NULL, viewport ? viewport->ID : 0); + + // Notify platform layer of viewport changes + // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI + if (g.CurrentViewport && g.PlatformIO.Platform_OnChangedViewport) + g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport); +} + void ImGui::SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) { + // Abandon viewport + if (window->ViewportOwned && window->Viewport->Window == window) + window->Viewport->Size = ImVec2(0.0f, 0.0f); + window->Viewport = viewport; + window->ViewportId = viewport->ID; + window->ViewportOwned = (viewport->Window == window); } -static void ScaleWindow(ImGuiWindow* window, float scale) +static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) { - ImVec2 origin = window->Viewport->Pos; - window->Pos = ImFloor((window->Pos - origin) * scale + origin); - window->Size = ImTrunc(window->Size * scale); - window->SizeFull = ImTrunc(window->SizeFull * scale); - window->ContentSize = ImTrunc(window->ContentSize * scale); + // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protrude and create their own. + ImGuiContext& g = *GImGui; + if (g.IO.ConfigViewportsNoAutoMerge || (window->WindowClass.ViewportFlagsOverrideSet & ImGuiViewportFlags_NoAutoMerge)) + if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) + if (!window->DockIsActive) + if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip)) == 0) + if ((window->Flags & ImGuiWindowFlags_Popup) == 0 || (window->Flags & ImGuiWindowFlags_Modal) != 0) + return true; + return false; +} + +static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport) +{ + ImGuiContext& g = *GImGui; + if (window->Viewport == viewport) + return false; + if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) == 0) + return false; + if ((viewport->Flags & ImGuiViewportFlags_IsMinimized) != 0) + return false; + if (!viewport->GetMainRect().Contains(window->Rect())) + return false; + if (GetWindowAlwaysWantOwnViewport(window)) + return false; + + // FIXME: Can't use g.WindowsFocusOrder[] for root windows only as we care about Z order. If we maintained a DisplayOrder along with FocusOrder we could.. + for (ImGuiWindow* window_behind : g.Windows) + { + if (window_behind == window) + break; + if (window_behind->WasActive && window_behind->ViewportOwned && !(window_behind->Flags & ImGuiWindowFlags_ChildWindow)) + if (window_behind->Viewport->GetMainRect().Overlaps(window->Rect())) + return false; + } + + // Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child) + ImGuiViewportP* old_viewport = window->Viewport; + if (window->ViewportOwned) + for (int n = 0; n < g.Windows.Size; n++) + if (g.Windows[n]->Viewport == old_viewport) + SetWindowViewport(g.Windows[n], viewport); + SetWindowViewport(window, viewport); + BringWindowToDisplayFront(window); + + return true; +} + +// FIXME: handle 0 to N host viewports +static bool ImGui::UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + return UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); +} + +// Translate Dear ImGui windows when a Host Viewport has been moved +// (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!) +void ImGui::TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos, const ImVec2& old_size, const ImVec2& new_size) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(viewport->Window == NULL && (viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows)); + + // 1) We test if ImGuiConfigFlags_ViewportsEnable was just toggled, which allows us to conveniently + // translate imgui windows from OS-window-local to absolute coordinates or vice-versa. + // 2) If it's not going to fit into the new size, keep it at same absolute position. + // One problem with this is that most Win32 applications doesn't update their render while dragging, + // and so the window will appear to teleport when releasing the mouse. + const bool translate_all_windows = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable); + ImRect test_still_fit_rect(old_pos, old_pos + viewport->Size); + ImVec2 delta_pos = new_pos - old_pos; + for (ImGuiWindow* window : g.Windows) // FIXME-OPT + if (translate_all_windows || (window->Viewport == viewport && (old_size == new_size || test_still_fit_rect.Contains(window->Rect())))) + TranslateWindow(window, delta_pos); } // Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!) void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) { ImGuiContext& g = *GImGui; - for (ImGuiWindow* window : g.Windows) - if (window->Viewport == viewport) - ScaleWindow(window, scale); + if (viewport->Window) + { + ScaleWindow(viewport->Window, scale); + } + else + { + for (ImGuiWindow* window : g.Windows) + if (window->Viewport == viewport) + ScaleWindow(window, scale); + } +} + +// If the backend doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves. +// A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. +// B) It requires Platform_GetWindowFocus to be implemented by backend. +ImGuiViewportP* ImGui::FindHoveredViewportFromPlatformWindowStack(const ImVec2& mouse_platform_pos) +{ + ImGuiContext& g = *GImGui; + ImGuiViewportP* best_candidate = NULL; + for (ImGuiViewportP* viewport : g.Viewports) + if (!(viewport->Flags & (ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_IsMinimized)) && viewport->GetMainRect().Contains(mouse_platform_pos)) + if (best_candidate == NULL || best_candidate->LastFocusedStampCount < viewport->LastFocusedStampCount) + best_candidate = viewport; + return best_candidate; } // Update viewports and monitor infos +// Note that this is running even if 'ImGuiConfigFlags_ViewportsEnable' is not set, in order to clear unused viewports (if any) and update monitor info. static void ImGui::UpdateViewportsNewFrame() { ImGuiContext& g = *GImGui; - IM_ASSERT(g.Viewports.Size == 1); + IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size); + + // Update Minimized status (we need it first in order to decide if we'll apply Pos/Size of the main viewport) + // Update Focused status + const bool viewports_enabled = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != 0; + if (viewports_enabled) + { + ImGuiViewportP* focused_viewport = NULL; + for (ImGuiViewportP* viewport : g.Viewports) + { + const bool platform_funcs_available = viewport->PlatformWindowCreated; + if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available) + { + bool is_minimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); + if (is_minimized) + viewport->Flags |= ImGuiViewportFlags_IsMinimized; + else + viewport->Flags &= ~ImGuiViewportFlags_IsMinimized; + } + + // Update our implicit z-order knowledge of platform windows, which is used when the backend cannot provide io.MouseHoveredViewport. + // When setting Platform_GetWindowFocus, it is expected that the platform backend can handle calls without crashing if it doesn't have data stored. + if (g.PlatformIO.Platform_GetWindowFocus && platform_funcs_available) + { + bool is_focused = g.PlatformIO.Platform_GetWindowFocus(viewport); + if (is_focused) + viewport->Flags |= ImGuiViewportFlags_IsFocused; + else + viewport->Flags &= ~ImGuiViewportFlags_IsFocused; + if (is_focused) + focused_viewport = viewport; + } + } + + // Focused viewport has changed? + if (focused_viewport && g.PlatformLastFocusedViewportId != focused_viewport->ID) + { + IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Focused viewport changed %08X -> %08X, attempting to apply our focus.\n", g.PlatformLastFocusedViewportId, focused_viewport->ID); + const ImGuiViewport* prev_focused_viewport = FindViewportByID(g.PlatformLastFocusedViewportId); + const bool prev_focused_has_been_destroyed = (prev_focused_viewport == NULL) || (prev_focused_viewport->PlatformWindowCreated == false); + + // Store a tag so we can infer z-order easily from all our windows + // We compare PlatformLastFocusedViewportId so newly created viewports with _NoFocusOnAppearing flag + // will keep the front most stamp instead of losing it back to their parent viewport. + if (focused_viewport->LastFocusedStampCount != g.ViewportFocusedStampCount) + focused_viewport->LastFocusedStampCount = ++g.ViewportFocusedStampCount; + g.PlatformLastFocusedViewportId = focused_viewport->ID; + + // Focus associated dear imgui window + // - if focus didn't happen with a click within imgui boundaries, e.g. Clicking platform title bar. (#6299) + // - if focus didn't happen because we destroyed another window (#6462) + // FIXME: perhaps 'FocusTopMostWindowUnderOne()' can handle the 'focused_window->Window != NULL' case as well. + const bool apply_imgui_focus_on_focused_viewport = !IsAnyMouseDown() && !prev_focused_has_been_destroyed; + if (apply_imgui_focus_on_focused_viewport) + { + focused_viewport->LastFocusedHadNavWindow |= (g.NavWindow != NULL) && (g.NavWindow->Viewport == focused_viewport); // Update so a window changing viewport won't lose focus. + ImGuiFocusRequestFlags focus_request_flags = ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild; + if (focused_viewport->Window != NULL) + FocusWindow(focused_viewport->Window, focus_request_flags); + else if (focused_viewport->LastFocusedHadNavWindow) + FocusTopMostWindowUnderOne(NULL, NULL, focused_viewport, focus_request_flags); // Focus top most in viewport + else + FocusWindow(NULL, focus_request_flags); // No window had focus last time viewport was focused + } + } + if (focused_viewport) + focused_viewport->LastFocusedHadNavWindow = (g.NavWindow != NULL) && (g.NavWindow->Viewport == focused_viewport); + } - // Update main viewport with current platform position. + // Create/update main viewport with current platform position. // FIXME-VIEWPORT: Size is driven by backend/user code for backward-compatibility but we should aim to make this more consistent. ImGuiViewportP* main_viewport = g.Viewports[0]; - main_viewport->Flags = ImGuiViewportFlags_IsPlatformWindow | ImGuiViewportFlags_OwnedByApp; - main_viewport->Pos = ImVec2(0.0f, 0.0f); - main_viewport->Size = g.IO.DisplaySize; + IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); + IM_ASSERT(main_viewport->Window == NULL); + ImVec2 main_viewport_pos = viewports_enabled ? g.PlatformIO.Platform_GetWindowPos(main_viewport) : ImVec2(0.0f, 0.0f); + ImVec2 main_viewport_size = g.IO.DisplaySize; + if (viewports_enabled && (main_viewport->Flags & ImGuiViewportFlags_IsMinimized)) + { + main_viewport_pos = main_viewport->Pos; // Preserve last pos/size when minimized (FIXME: We don't do the same for Size outside of the viewport path) + main_viewport_size = main_viewport->Size; + } + AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_pos, main_viewport_size, ImGuiViewportFlags_OwnedByApp | ImGuiViewportFlags_CanHostOtherWindows); - for (ImGuiViewportP* viewport : g.Viewports) + g.CurrentDpiScale = 0.0f; + g.CurrentViewport = NULL; + g.MouseViewport = NULL; + for (int n = 0; n < g.Viewports.Size; n++) { - // Lock down space taken by menu bars and status bars + ImGuiViewportP* viewport = g.Viewports[n]; + viewport->Idx = n; + + // Erase unused viewports + if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2) + { + DestroyViewport(viewport); + n--; + continue; + } + + const bool platform_funcs_available = viewport->PlatformWindowCreated; + if (viewports_enabled) + { + // Update Position and Size (from Platform Window to ImGui) if requested. + // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. + if (!(viewport->Flags & ImGuiViewportFlags_IsMinimized) && platform_funcs_available) + { + // Viewport->WorkPos and WorkSize will be updated below + if (viewport->PlatformRequestMove) + viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport); + if (viewport->PlatformRequestResize) + viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport); + } + } + + // Update/copy monitor info + UpdateViewportPlatformMonitor(viewport); + + // Lock down space taken by menu bars and status bars + query initial insets from backend // Setup initial value for functions like BeginMainMenuBar(), DockSpaceOverViewport() etc. viewport->WorkInsetMin = viewport->BuildWorkInsetMin; viewport->WorkInsetMax = viewport->BuildWorkInsetMax; viewport->BuildWorkInsetMin = viewport->BuildWorkInsetMax = ImVec2(0.0f, 0.0f); + if (g.PlatformIO.Platform_GetWindowWorkAreaInsets != NULL && platform_funcs_available) + { + ImVec4 insets = g.PlatformIO.Platform_GetWindowWorkAreaInsets(viewport); + IM_ASSERT(insets.x >= 0.0f && insets.y >= 0.0f && insets.z >= 0.0f && insets.w >= 0.0f); + viewport->BuildWorkInsetMin = ImVec2(insets.x, insets.y); + viewport->BuildWorkInsetMax = ImVec2(insets.z, insets.w); + } viewport->UpdateWorkRect(); - } -} -//----------------------------------------------------------------------------- -// [SECTION] DOCKING -//----------------------------------------------------------------------------- + // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back. + viewport->Alpha = 1.0f; + + // Translate Dear ImGui windows when a Host Viewport has been moved + // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!) + const ImVec2 viewport_delta_pos = viewport->Pos - viewport->LastPos; + if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta_pos.x != 0.0f || viewport_delta_pos.y != 0.0f)) + TranslateWindowsInViewport(viewport, viewport->LastPos, viewport->Pos, viewport->LastSize, viewport->Size); + + // Update DPI scale + float new_dpi_scale; + if (g.PlatformIO.Platform_GetWindowDpiScale && platform_funcs_available) + new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport); + else if (viewport->PlatformMonitor != -1) + new_dpi_scale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale; + else + new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f; + IM_ASSERT(new_dpi_scale > 0.0f && new_dpi_scale < 99.0f); // Typical correct values would be between 1.0f and 4.0f + if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale) + { + float scale_factor = new_dpi_scale / viewport->DpiScale; + if (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) + ScaleWindowsInViewport(viewport, scale_factor); + //if (viewport == GetMainViewport()) + // g.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor); + + // Scale our window moving pivot so that the window will rescale roughly around the mouse position. + // FIXME-VIEWPORT: This currently creates a resizing feedback loop when a window is straddling a DPI transition border. + // (Minor: since our sizes do not perfectly linearly scale, deferring the click offset scale until we know the actual window scale ratio may get us slightly more precise mouse positioning.) + //if (g.MovingWindow != NULL && g.MovingWindow->Viewport == viewport) + // g.ActiveIdClickOffset = ImTrunc(g.ActiveIdClickOffset * scale_factor); + } + viewport->DpiScale = new_dpi_scale; + } -// (this section is filled in the 'docking' branch) + // Update fallback monitor + g.PlatformMonitorsFullWorkRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); + if (g.PlatformIO.Monitors.Size == 0) + { + ImGuiPlatformMonitor* monitor = &g.FallbackMonitor; + monitor->MainPos = main_viewport->Pos; + monitor->MainSize = main_viewport->Size; + monitor->WorkPos = main_viewport->WorkPos; + monitor->WorkSize = main_viewport->WorkSize; + monitor->DpiScale = main_viewport->DpiScale; + g.PlatformMonitorsFullWorkRect.Add(monitor->WorkPos); + g.PlatformMonitorsFullWorkRect.Add(monitor->WorkPos + monitor->WorkSize); + } + else + { + g.FallbackMonitor = g.PlatformIO.Monitors[0]; + } + for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors) + { + g.PlatformMonitorsFullWorkRect.Add(monitor.WorkPos); + g.PlatformMonitorsFullWorkRect.Add(monitor.WorkPos + monitor.WorkSize); + } + if (!viewports_enabled) + { + g.MouseViewport = main_viewport; + return; + } -//----------------------------------------------------------------------------- -// [SECTION] PLATFORM DEPENDENT HELPERS -//----------------------------------------------------------------------------- -// - Default clipboard handlers -// - Default shell function handlers -// - Default IME handlers -//----------------------------------------------------------------------------- + // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport. + // Note that 'viewport_hovered' should skip over any viewport that has the ImGuiViewportFlags_NoInputs flags set. + ImGuiViewportP* viewport_hovered = NULL; + if (g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) + { + viewport_hovered = g.IO.MouseHoveredViewport ? (ImGuiViewportP*)FindViewportByID(g.IO.MouseHoveredViewport) : NULL; + if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) + viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); // Backend failed to handle _NoInputs viewport: revert to our fallback. + } + else + { + // If the backend doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search: + // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. + // B) won't take account of how the backend apply parent<>child relationship to secondary viewports, which affects their Z order. + // C) uses LastFrameAsRefViewport as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO) + viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); + } + if (viewport_hovered != NULL) + g.MouseLastHoveredViewport = viewport_hovered; + else if (g.MouseLastHoveredViewport == NULL) + g.MouseLastHoveredViewport = g.Viewports[0]; + + // Update mouse reference viewport + // (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode) + // (MovingViewport->Viewport will be NULL in the rare situation where the window disappared while moving, set UpdateMouseMovingWindowNewFrame() for details) + if (g.MovingWindow && g.MovingWindow->Viewport) + g.MouseViewport = g.MovingWindow->Viewport; + else + g.MouseViewport = g.MouseLastHoveredViewport; -#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) + // When dragging something, always refer to the last hovered viewport. + // - when releasing a moving window we will revert to aiming behind (at viewport_hovered) + // - when we are between viewports, our dragged preview will tend to show in the last viewport _even_ if we don't have tooltips in their viewports (when lacking monitor info) + // - consider the case of holding on a menu item to browse child menus: even thou a mouse button is held, there's no active id because menu items only react on mouse release. + // FIXME-VIEWPORT: This is essentially broken, when ImGuiBackendFlags_HasMouseHoveredViewport is set we want to trust when viewport_hovered==NULL and use that. + const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive; + if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL) + viewport_hovered = g.MouseLastHoveredViewport; + if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !IsAnyMouseDown()) + if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) + g.MouseViewport = viewport_hovered; -#ifdef _MSC_VER -#pragma comment(lib, "user32") -#pragma comment(lib, "kernel32") -#endif + IM_ASSERT(g.MouseViewport != NULL); +} -// Win32 clipboard implementation -// We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown() -static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx) +// Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some) +static void ImGui::UpdateViewportsEndFrame() { - ImGuiContext& g = *ctx; - g.ClipboardHandlerData.clear(); - if (!::OpenClipboard(NULL)) - return NULL; - HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT); - if (wbuf_handle == NULL) - { - ::CloseClipboard(); - return NULL; - } - if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle)) + ImGuiContext& g = *GImGui; + g.PlatformIO.Viewports.resize(0); + for (int i = 0; i < g.Viewports.Size; i++) { - int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL); - g.ClipboardHandlerData.resize(buf_len); - ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL); + ImGuiViewportP* viewport = g.Viewports[i]; + viewport->LastPos = viewport->Pos; + viewport->LastSize = viewport->Size; + if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f) + if (i > 0) // Always include main viewport in the list + continue; + if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)) + continue; + if (i > 0) + IM_ASSERT(viewport->Window != NULL); + g.PlatformIO.Viewports.push_back(viewport); } - ::GlobalUnlock(wbuf_handle); - ::CloseClipboard(); - return g.ClipboardHandlerData.Data; + g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called } -static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text) +// FIXME: We should ideally refactor the system to call this every frame (we currently don't) +ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags) { - if (!::OpenClipboard(NULL)) - return; - const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0); - HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR)); - if (wbuf_handle == NULL) + ImGuiContext& g = *GImGui; + IM_ASSERT(id != 0); + + flags |= ImGuiViewportFlags_IsPlatformWindow; + if (window != NULL) { - ::CloseClipboard(); - return; + if (g.MovingWindow && g.MovingWindow->RootWindowDockTree == window) + flags |= ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_NoFocusOnAppearing; + if ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs)) + flags |= ImGuiViewportFlags_NoInputs; + if (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing) + flags |= ImGuiViewportFlags_NoFocusOnAppearing; } - WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle); - ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length); - ::GlobalUnlock(wbuf_handle); - ::EmptyClipboard(); - if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL) - ::GlobalFree(wbuf_handle); - ::CloseClipboard(); -} -#elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS) + ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id); + if (viewport) + { + // Always update for main viewport as we are already pulling correct platform pos/size (see #4900) + if (!viewport->PlatformRequestMove || viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID) + viewport->Pos = pos; + if (!viewport->PlatformRequestResize || viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID) + viewport->Size = size; + viewport->Flags = flags | (viewport->Flags & (ImGuiViewportFlags_IsMinimized | ImGuiViewportFlags_IsFocused)); // Preserve existing flags + } + else + { + // New viewport + viewport = IM_NEW(ImGuiViewportP)(); + viewport->ID = id; + viewport->Idx = g.Viewports.Size; + viewport->Pos = viewport->LastPos = pos; + viewport->Size = viewport->LastSize = size; + viewport->Flags = flags; + UpdateViewportPlatformMonitor(viewport); + g.Viewports.push_back(viewport); + g.ViewportCreatedCount++; + IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Add Viewport %08X '%s'\n", id, window ? window->Name : ""); + + // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport. + // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame + g.DrawListSharedData.ClipRectFullscreen.x = ImMin(g.DrawListSharedData.ClipRectFullscreen.x, viewport->Pos.x); + g.DrawListSharedData.ClipRectFullscreen.y = ImMin(g.DrawListSharedData.ClipRectFullscreen.y, viewport->Pos.y); + g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x); + g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y); + + // Store initial DpiScale before the OS platform window creation, based on expected monitor data. + // This is so we can select an appropriate font size on the first frame of our window lifetime + if (viewport->PlatformMonitor != -1) + viewport->DpiScale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale; + } + + viewport->Window = window; + viewport->LastFrameActive = g.FrameCount; + viewport->UpdateWorkRect(); + IM_ASSERT(window == NULL || viewport->ID == window->ID); -#include // Use old API to avoid need for separate .mm file -static PasteboardRef main_clipboard = 0; + if (window != NULL) + window->ViewportOwned = true; -// OSX clipboard implementation -// If you enable this you will need to add '-framework ApplicationServices' to your linker command-line! -static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text) + return viewport; +} + +static void ImGui::DestroyViewport(ImGuiViewportP* viewport) { - if (!main_clipboard) - PasteboardCreate(kPasteboardClipboard, &main_clipboard); - PasteboardClear(main_clipboard); - CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text)); - if (cf_data) + // Clear references to this viewport in windows (window->ViewportId becomes the master data) + ImGuiContext& g = *GImGui; + for (ImGuiWindow* window : g.Windows) { - PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0); - CFRelease(cf_data); + if (window->Viewport != viewport) + continue; + window->Viewport = NULL; + window->ViewportOwned = false; } + if (viewport == g.MouseLastHoveredViewport) + g.MouseLastHoveredViewport = NULL; + + // Destroy + IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Delete Viewport %08X '%s'\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); + DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here. + IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); + IM_ASSERT(g.Viewports[viewport->Idx] == viewport); + g.Viewports.erase(g.Viewports.Data + viewport->Idx); + IM_DELETE(viewport); } -static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx) +// FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten. +static void ImGui::WindowSelectViewport(ImGuiWindow* window) { - ImGuiContext& g = *ctx; - if (!main_clipboard) - PasteboardCreate(kPasteboardClipboard, &main_clipboard); - PasteboardSynchronize(main_clipboard); + ImGuiContext& g = *GImGui; + ImGuiWindowFlags flags = window->Flags; + window->ViewportAllowPlatformMonitorExtend = -1; - ItemCount item_count = 0; - PasteboardGetItemCount(main_clipboard, &item_count); - for (ItemCount i = 0; i < item_count; i++) + // Restore main viewport if multi-viewport is not supported by the backend + ImGuiViewportP* main_viewport = (ImGuiViewportP*)(void*)GetMainViewport(); + if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) { - PasteboardItemID item_id = 0; - PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id); - CFArrayRef flavor_type_array = 0; - PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array); - for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++) + SetWindowViewport(window, main_viewport); + return; + } + window->ViewportOwned = false; + + // Appearing popups reset their viewport so they can inherit again + if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && window->Appearing) + { + window->Viewport = NULL; + window->ViewportId = 0; + } + + if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport) == 0) + { + // By default inherit from parent window + if (window->Viewport == NULL && window->ParentWindow && (!window->ParentWindow->IsFallbackWindow || window->ParentWindow->WasActive)) + window->Viewport = window->ParentWindow->Viewport; + + // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file + if (window->Viewport == NULL && window->ViewportId != 0) + { + window->Viewport = (ImGuiViewportP*)FindViewportByID(window->ViewportId); + if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX) + window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_None); + } + } + + bool lock_viewport = false; + if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport) + { + // Code explicitly request a viewport + window->Viewport = (ImGuiViewportP*)FindViewportByID(g.NextWindowData.ViewportId); + window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet. + if (window->Viewport && (window->Flags & ImGuiWindowFlags_DockNodeHost) != 0 && window->Viewport->Window != NULL) + { + window->Viewport->Window = window; + window->Viewport->ID = window->ViewportId = window->ID; // Overwrite ID (always owned by node) + } + lock_viewport = true; + } + else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu)) + { + // Always inherit viewport from parent window + if (window->DockNode && window->DockNode->HostWindow) + IM_ASSERT(window->DockNode->HostWindow->Viewport == window->ParentWindow->Viewport); + window->Viewport = window->ParentWindow->Viewport; + } + else if (window->DockNode && window->DockNode->HostWindow) + { + // This covers the "always inherit viewport from parent window" case for when a window reattach to a node that was just created mid-frame + window->Viewport = window->DockNode->HostWindow->Viewport; + } + else if (flags & ImGuiWindowFlags_Tooltip) + { + window->Viewport = g.MouseViewport; + } + else if (GetWindowAlwaysWantOwnViewport(window)) + { + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); + } + else if (g.MovingWindow && g.MovingWindow->RootWindowDockTree == window && IsMousePosValid()) + { + if (window->Viewport != NULL && window->Viewport->Window == window) + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); + } + else + { + // Merge into host viewport? + // We cannot test window->ViewportOwned as it set lower in the function. + // Testing (g.ActiveId == 0 || g.ActiveIdAllowOverlap) to avoid merging during a short-term widget interaction. Main intent was to avoid during resize (see #4212) + bool try_to_merge_into_host_viewport = (window->Viewport && window == window->Viewport->Window && (g.ActiveId == 0 || g.ActiveIdAllowOverlap)); + if (try_to_merge_into_host_viewport) + UpdateTryMergeWindowIntoHostViewports(window); + } + + // Fallback: merge in default viewport if z-order matches, otherwise create a new viewport + if (window->Viewport == NULL) + if (!UpdateTryMergeWindowIntoHostViewport(window, main_viewport)) + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); + + // Mark window as allowed to protrude outside of its viewport and into the current monitor + if (!lock_viewport) + { + if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) + { + // We need to take account of the possibility that mouse may become invalid. + // Popups/Tooltip always set ViewportAllowPlatformMonitorExtend so GetWindowAllowedExtentRect() will return full monitor bounds. + ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.BeginPopupStack.back().OpenMousePos; + bool use_mouse_ref = (!g.NavCursorVisible || !g.NavHighlightItemUnderNav || !g.NavWindow); + bool mouse_valid = IsMousePosValid(&mouse_ref); + if ((window->Appearing || (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_ChildMenu))) && (!use_mouse_ref || mouse_valid)) + window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos()); + else + window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; + } + else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow) && window->DockNode == NULL) + { + // When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code. + const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true; + if ((window->Flags & ImGuiWindowFlags_DockNodeHost) && window->Viewport->LastFrameActive < g.FrameCount && will_be_visible) + { + // Steal/transfer ownership + IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Window '%s' steal Viewport %08X from Window '%s'\n", window->Name, window->Viewport->ID, window->Viewport->Window->Name); + window->Viewport->Window = window; + window->Viewport->ID = window->ID; + window->Viewport->LastNameHash = 0; + } + else if (!UpdateTryMergeWindowIntoHostViewports(window)) // Merge? + { + // New viewport + window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing); + } + } + else if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0) + { + // Regular (non-child, non-popup) windows by default are also allowed to protrude + // Child windows are kept contained within their parent. + window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; + } + } + + // Update flags + window->ViewportOwned = (window == window->Viewport->Window); + window->ViewportId = window->Viewport->ID; + + // If the OS window has a title bar, hide our imgui title bar + //if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) + // window->Flags |= ImGuiWindowFlags_NoTitleBar; +} + +void ImGui::WindowSyncOwnedViewport(ImGuiWindow* window, ImGuiWindow* parent_window_in_stack) +{ + ImGuiContext& g = *GImGui; + + bool viewport_rect_changed = false; + + // Synchronize window --> viewport in most situations + // Synchronize viewport -> window in case the platform window has been moved or resized from the OS/WM + if (window->Viewport->PlatformRequestMove) + { + window->Pos = window->Viewport->Pos; + MarkIniSettingsDirty(window); + } + else if (memcmp(&window->Viewport->Pos, &window->Pos, sizeof(window->Pos)) != 0) + { + viewport_rect_changed = true; + window->Viewport->Pos = window->Pos; + } + + if (window->Viewport->PlatformRequestResize) + { + window->Size = window->SizeFull = window->Viewport->Size; + MarkIniSettingsDirty(window); + } + else if (memcmp(&window->Viewport->Size, &window->Size, sizeof(window->Size)) != 0) + { + viewport_rect_changed = true; + window->Viewport->Size = window->Size; + } + window->Viewport->UpdateWorkRect(); + + // The viewport may have changed monitor since the global update in UpdateViewportsNewFrame() + // Either a SetNextWindowPos() call in the current frame or a SetWindowPos() call in the previous frame may have this effect. + if (viewport_rect_changed) + UpdateViewportPlatformMonitor(window->Viewport); + + // Update common viewport flags + const ImGuiViewportFlags viewport_flags_to_clear = ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoRendererClear; + ImGuiViewportFlags viewport_flags = window->Viewport->Flags & ~viewport_flags_to_clear; + ImGuiWindowFlags window_flags = window->Flags; + const bool is_modal = (window_flags & ImGuiWindowFlags_Modal) != 0; + const bool is_short_lived_floating_window = (window_flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0; + if (window_flags & ImGuiWindowFlags_Tooltip) + viewport_flags |= ImGuiViewportFlags_TopMost; + if ((g.IO.ConfigViewportsNoTaskBarIcon || is_short_lived_floating_window) && !is_modal) + viewport_flags |= ImGuiViewportFlags_NoTaskBarIcon; + if (g.IO.ConfigViewportsNoDecoration || is_short_lived_floating_window) + viewport_flags |= ImGuiViewportFlags_NoDecoration; + + // Not correct to set modal as topmost because: + // - Because other popups can be stacked above a modal (e.g. combo box in a modal) + // - ImGuiViewportFlags_TopMost is currently handled different in backends: in Win32 it is "appear top most" whereas in GLFW and SDL it is "stay topmost" + //if (flags & ImGuiWindowFlags_Modal) + // viewport_flags |= ImGuiViewportFlags_TopMost; + + // For popups and menus that may be protruding out of their parent viewport, we enable _NoFocusOnClick so that clicking on them + // won't steal the OS focus away from their parent window (which may be reflected in OS the title bar decoration). + // Setting _NoFocusOnClick would technically prevent us from bringing back to front in case they are being covered by an OS window from a different app, + // but it shouldn't be much of a problem considering those are already popups that are closed when clicking elsewhere. + if (is_short_lived_floating_window && !is_modal) + viewport_flags |= ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoFocusOnClick; + + // We can overwrite viewport flags using ImGuiWindowClass (advanced users) + if (window->WindowClass.ViewportFlagsOverrideSet) + viewport_flags |= window->WindowClass.ViewportFlagsOverrideSet; + if (window->WindowClass.ViewportFlagsOverrideClear) + viewport_flags &= ~window->WindowClass.ViewportFlagsOverrideClear; + + // We can also tell the backend that clearing the platform window won't be necessary, + // as our window background is filling the viewport and we have disabled BgAlpha. + // FIXME: Work on support for per-viewport transparency (#2766) + if (!(window_flags & ImGuiWindowFlags_NoBackground)) + viewport_flags |= ImGuiViewportFlags_NoRendererClear; + + window->Viewport->Flags = viewport_flags; + + // Update parent viewport ID + // (the !IsFallbackWindow test mimic the one done in WindowSelectViewport()) + if (window->WindowClass.ParentViewportId != (ImGuiID)-1) + window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId; + else if ((window_flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && parent_window_in_stack && (!parent_window_in_stack->IsFallbackWindow || parent_window_in_stack->WasActive)) + window->Viewport->ParentViewportId = parent_window_in_stack->Viewport->ID; + else + window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoDefaultParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID; +} + +// Called by user at the end of the main loop, after EndFrame() +// This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO api. +void ImGui::UpdatePlatformWindows() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?"); + IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount); + g.FrameCountPlatformEnded = g.FrameCount; + if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) + return; + + // Create/resize/destroy platform windows to match each active viewport. + // Skip the main viewport (index 0), which is always fully handled by the application! + for (int i = 1; i < g.Viewports.Size; i++) + { + ImGuiViewportP* viewport = g.Viewports[i]; + + // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window + // (the implicit/fallback Debug##Default window will be registering its viewport then be disabled, causing a dummy DestroyPlatformWindow to be made each frame) + bool destroy_platform_window = false; + destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1); + destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)); + if (destroy_platform_window) + { + DestroyPlatformWindow(viewport); + continue; + } + + // New windows that appears directly in a new viewport won't always have a size on their first frame + if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0 || viewport->Size.y <= 0) + continue; + + // Create window + const bool is_new_platform_window = (viewport->PlatformWindowCreated == false); + if (is_new_platform_window) + { + IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Create Platform Window %08X '%s'\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); + g.PlatformIO.Platform_CreateWindow(viewport); + if (g.PlatformIO.Renderer_CreateWindow != NULL) + g.PlatformIO.Renderer_CreateWindow(viewport); + g.PlatformWindowsCreatedCount++; + viewport->LastNameHash = 0; + viewport->LastPlatformPos = viewport->LastPlatformSize = ImVec2(FLT_MAX, FLT_MAX); // By clearing those we'll enforce a call to Platform_SetWindowPos/Size below, before Platform_ShowWindow (FIXME: Is that necessary?) + viewport->LastRendererSize = viewport->Size; // We don't need to call Renderer_SetWindowSize() as it is expected Renderer_CreateWindow() already did it. + viewport->PlatformWindowCreated = true; + } + + // Apply Position and Size (from ImGui to Platform/Renderer backends) + if ((viewport->LastPlatformPos.x != viewport->Pos.x || viewport->LastPlatformPos.y != viewport->Pos.y) && !viewport->PlatformRequestMove) + g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos); + if ((viewport->LastPlatformSize.x != viewport->Size.x || viewport->LastPlatformSize.y != viewport->Size.y) && !viewport->PlatformRequestResize) + g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size); + if ((viewport->LastRendererSize.x != viewport->Size.x || viewport->LastRendererSize.y != viewport->Size.y) && g.PlatformIO.Renderer_SetWindowSize) + g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size); + viewport->LastPlatformPos = viewport->Pos; + viewport->LastPlatformSize = viewport->LastRendererSize = viewport->Size; + + // Update title bar (if it changed) + if (ImGuiWindow* window_for_title = GetWindowForTitleDisplay(viewport->Window)) + { + const char* title_begin = window_for_title->Name; + char* title_end = (char*)(intptr_t)FindRenderedTextEnd(title_begin); + const ImGuiID title_hash = ImHashStr(title_begin, title_end - title_begin); + if (viewport->LastNameHash != title_hash) + { + char title_end_backup_c = *title_end; + *title_end = 0; // Cut existing buffer short instead of doing an alloc/free, no small gain. + g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin); + *title_end = title_end_backup_c; + viewport->LastNameHash = title_hash; + } + } + + // Update alpha (if it changed) + if (viewport->LastAlpha != viewport->Alpha && g.PlatformIO.Platform_SetWindowAlpha) + g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha); + viewport->LastAlpha = viewport->Alpha; + + // Optional, general purpose call to allow the backend to perform general book-keeping even if things haven't changed. + if (g.PlatformIO.Platform_UpdateWindow) + g.PlatformIO.Platform_UpdateWindow(viewport); + + if (is_new_platform_window) + { + // On startup ensure new platform window don't steal focus (give it a few frames, as nested contents may lead to viewport being created a few frames late) + if (g.FrameCount < 3) + viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing; + + // Show window + g.PlatformIO.Platform_ShowWindow(viewport); + + // Even without focus, we assume the window becomes front-most. + // This is useful for our platform z-order heuristic when io.MouseHoveredViewport is not available. + if (viewport->LastFocusedStampCount != g.ViewportFocusedStampCount) + viewport->LastFocusedStampCount = ++g.ViewportFocusedStampCount; + } + + // Clear request flags + viewport->ClearRequestFlags(); + } +} + +// This is a default/basic function for performing the rendering/swap of multiple Platform Windows. +// Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves. +// The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself: +// +// ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); +// for (int i = 1; i < platform_io.Viewports.Size; i++) +// if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0) +// MyRenderFunction(platform_io.Viewports[i], my_args); +// for (int i = 1; i < platform_io.Viewports.Size; i++) +// if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0) +// MySwapBufferFunction(platform_io.Viewports[i], my_args); +// +void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg) +{ + // Skip the main viewport (index 0), which is always fully handled by the application! + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + for (int i = 1; i < platform_io.Viewports.Size; i++) + { + ImGuiViewport* viewport = platform_io.Viewports[i]; + if (viewport->Flags & ImGuiViewportFlags_IsMinimized) + continue; + if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg); + if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg); + } + for (int i = 1; i < platform_io.Viewports.Size; i++) + { + ImGuiViewport* viewport = platform_io.Viewports[i]; + if (viewport->Flags & ImGuiViewportFlags_IsMinimized) + continue; + if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg); + if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg); + } +} + +static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos) +{ + ImGuiContext& g = *GImGui; + for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) + { + const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; + if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(pos)) + return monitor_n; + } + return -1; +} + +// Search for the monitor with the largest intersection area with the given rectangle +// We generally try to avoid searching loops but the monitor count should be very small here +// FIXME-OPT: We could test the last monitor used for that viewport first, and early +static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) +{ + ImGuiContext& g = *GImGui; + + const int monitor_count = g.PlatformIO.Monitors.Size; + if (monitor_count <= 1) + return monitor_count - 1; + + // Use a minimum threshold of 1.0f so a zero-sized rect won't false positive, and will still find the correct monitor given its position. + // This is necessary for tooltips which always resize down to zero at first. + const float surface_threshold = ImMax(rect.GetWidth() * rect.GetHeight() * 0.5f, 1.0f); + int best_monitor_n = -1; + float best_monitor_surface = 0.001f; + + for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++) + { + const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; + const ImRect monitor_rect = ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize); + if (monitor_rect.Contains(rect)) + return monitor_n; + ImRect overlapping_rect = rect; + overlapping_rect.ClipWithFull(monitor_rect); + float overlapping_surface = overlapping_rect.GetWidth() * overlapping_rect.GetHeight(); + if (overlapping_surface < best_monitor_surface) + continue; + best_monitor_surface = overlapping_surface; + best_monitor_n = monitor_n; + } + return best_monitor_n; +} + +// Update monitor from viewport rectangle (we'll use this info to clamp windows and save windows lost in a removed monitor) +static void ImGui::UpdateViewportPlatformMonitor(ImGuiViewportP* viewport) +{ + viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetMainRect()); +} + +// Return value is always != NULL, but don't hold on it across frames. +const ImGuiPlatformMonitor* ImGui::GetViewportPlatformMonitor(ImGuiViewport* viewport_p) +{ + ImGuiContext& g = *GImGui; + ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)viewport_p; + int monitor_idx = viewport->PlatformMonitor; + if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size) + return &g.PlatformIO.Monitors[monitor_idx]; + return &g.FallbackMonitor; +} + +void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) +{ + ImGuiContext& g = *GImGui; + if (viewport->PlatformWindowCreated) + { + IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Destroy Platform Window %08X '%s'\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); + if (g.PlatformIO.Renderer_DestroyWindow) + g.PlatformIO.Renderer_DestroyWindow(viewport); + if (g.PlatformIO.Platform_DestroyWindow) + g.PlatformIO.Platform_DestroyWindow(viewport); + IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL); + + // Don't clear PlatformWindowCreated for the main viewport, as we initially set that up to true in Initialize() + // The righter way may be to leave it to the backend to set this flag all-together, and made the flag public. + if (viewport->ID != IMGUI_VIEWPORT_DEFAULT_ID) + viewport->PlatformWindowCreated = false; + } + else + { + IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); + } + viewport->RendererUserData = viewport->PlatformUserData = viewport->PlatformHandle = NULL; + viewport->ClearRequestFlags(); +} + +void ImGui::DestroyPlatformWindows() +{ + // We call the destroy window on every viewport (including the main viewport, index 0) to give a chance to the backend + // to clear any data they may have stored in e.g. PlatformUserData, RendererUserData. + // It is convenient for the platform backend code to store something in the main viewport, in order for e.g. the mouse handling + // code to operator a consistent manner. + // It is expected that the backend can handle calls to Renderer_DestroyWindow/Platform_DestroyWindow without + // crashing if it doesn't have data stored. + ImGuiContext& g = *GImGui; + for (ImGuiViewportP* viewport : g.Viewports) + DestroyPlatformWindow(viewport); +} + + +//----------------------------------------------------------------------------- +// [SECTION] DOCKING +//----------------------------------------------------------------------------- +// Docking: Internal Types +// Docking: Forward Declarations +// Docking: ImGuiDockContext +// Docking: ImGuiDockContext Docking/Undocking functions +// Docking: ImGuiDockNode +// Docking: ImGuiDockNode Tree manipulation functions +// Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport) +// Docking: Builder Functions +// Docking: Begin/End Support Functions (called from Begin/End) +// Docking: Settings +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Typical Docking call flow: (root level is generally public API): +//----------------------------------------------------------------------------- +// - NewFrame() new dear imgui frame +// | DockContextNewFrameUpdateUndocking() - process queued undocking requests +// | - DockContextProcessUndockWindow() - process one window undocking request +// | - DockContextProcessUndockNode() - process one whole node undocking request +// | DockContextNewFrameUpdateUndocking() - process queue docking requests, create floating dock nodes +// | - update g.HoveredDockNode - [debug] update node hovered by mouse +// | - DockContextProcessDock() - process one docking request +// | - DockNodeUpdate() +// | - DockNodeUpdateForRootNode() +// | - DockNodeUpdateFlagsAndCollapse() +// | - DockNodeFindInfo() +// | - destroy unused node or tab bar +// | - create dock node host window +// | - Begin() etc. +// | - DockNodeStartMouseMovingWindow() +// | - DockNodeTreeUpdatePosSize() +// | - DockNodeTreeUpdateSplitter() +// | - draw node background +// | - DockNodeUpdateTabBar() - create/update tab bar for a docking node +// | - DockNodeAddTabBar() +// | - DockNodeWindowMenuUpdate() +// | - DockNodeCalcTabBarLayout() +// | - BeginTabBarEx() +// | - TabItemEx() calls +// | - EndTabBar() +// | - BeginDockableDragDropTarget() +// | - DockNodeUpdate() - recurse into child nodes... +//----------------------------------------------------------------------------- +// - DockSpace() user submit a dockspace into a window +// | Begin(Child) - create a child window +// | DockNodeUpdate() - call main dock node update function +// | End(Child) +// | ItemSize() +//----------------------------------------------------------------------------- +// - Begin() +// | BeginDocked() +// | BeginDockableDragDropSource() +// | BeginDockableDragDropTarget() +// | - DockNodePreviewDockRender() +//----------------------------------------------------------------------------- +// - EndFrame() +// | DockContextEndFrame() +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Docking: Internal Types +//----------------------------------------------------------------------------- +// - ImGuiDockRequestType +// - ImGuiDockRequest +// - ImGuiDockPreviewData +// - ImGuiDockNodeSettings +// - ImGuiDockContext +//----------------------------------------------------------------------------- + +enum ImGuiDockRequestType +{ + ImGuiDockRequestType_None = 0, + ImGuiDockRequestType_Dock, + ImGuiDockRequestType_Undock, + ImGuiDockRequestType_Split // Split is the same as Dock but without a DockPayload +}; + +struct ImGuiDockRequest +{ + ImGuiDockRequestType Type; + ImGuiWindow* DockTargetWindow; // Destination/Target Window to dock into (may be a loose window or a DockNode, might be NULL in which case DockTargetNode cannot be NULL) + ImGuiDockNode* DockTargetNode; // Destination/Target Node to dock into + ImGuiWindow* DockPayload; // Source/Payload window to dock (may be a loose window or a DockNode), [Optional] + ImGuiDir DockSplitDir; + float DockSplitRatio; + bool DockSplitOuter; + ImGuiWindow* UndockTargetWindow; + ImGuiDockNode* UndockTargetNode; + + ImGuiDockRequest() + { + Type = ImGuiDockRequestType_None; + DockTargetWindow = DockPayload = UndockTargetWindow = NULL; + DockTargetNode = UndockTargetNode = NULL; + DockSplitDir = ImGuiDir_None; + DockSplitRatio = 0.5f; + DockSplitOuter = false; + } +}; + +struct ImGuiDockPreviewData +{ + ImGuiDockNode FutureNode; + bool IsDropAllowed; + bool IsCenterAvailable; + bool IsSidesAvailable; // Hold your breath, grammar freaks.. + bool IsSplitDirExplicit; // Set when hovered the drop rect (vs. implicit SplitDir==None when hovered the window) + ImGuiDockNode* SplitNode; + ImGuiDir SplitDir; + float SplitRatio; + ImRect DropRectsDraw[ImGuiDir_COUNT + 1]; // May be slightly different from hit-testing drop rects used in DockNodeCalcDropRects() + + ImGuiDockPreviewData() : FutureNode(0) { IsDropAllowed = IsCenterAvailable = IsSidesAvailable = IsSplitDirExplicit = false; SplitNode = NULL; SplitDir = ImGuiDir_None; SplitRatio = 0.f; for (int n = 0; n < IM_ARRAYSIZE(DropRectsDraw); n++) DropRectsDraw[n] = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); } +}; + +// Persistent Settings data, stored contiguously in SettingsNodes (sizeof() ~32 bytes) +struct ImGuiDockNodeSettings +{ + ImGuiID ID; + ImGuiID ParentNodeId; + ImGuiID ParentWindowId; + ImGuiID SelectedTabId; + signed char SplitAxis; + char Depth; + ImGuiDockNodeFlags Flags; // NB: We save individual flags one by one in ascii format (ImGuiDockNodeFlags_SavedFlagsMask_) + ImVec2ih Pos; + ImVec2ih Size; + ImVec2ih SizeRef; + ImGuiDockNodeSettings() { memset(this, 0, sizeof(*this)); SplitAxis = ImGuiAxis_None; } +}; + +//----------------------------------------------------------------------------- +// Docking: Forward Declarations +//----------------------------------------------------------------------------- + +namespace ImGui +{ + // ImGuiDockContext + static ImGuiDockNode* DockContextAddNode(ImGuiContext* ctx, ImGuiID id); + static void DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node); + static void DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node); + static void DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req); + static void DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx); + static ImGuiDockNode* DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window); + static void DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count); + static void DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id); // Use root_id==0 to add all + + // ImGuiDockNode + static void DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar); + static void DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node); + static void DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node); + static ImGuiWindow* DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id); + static void DockNodeApplyPosSizeToWindows(ImGuiDockNode* node); + static void DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id); + static void DockNodeHideHostWindow(ImGuiDockNode* node); + static void DockNodeUpdate(ImGuiDockNode* node); + static void DockNodeUpdateForRootNode(ImGuiDockNode* node); + static void DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node); + static void DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node); + static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window); + static void DockNodeAddTabBar(ImGuiDockNode* node); + static void DockNodeRemoveTabBar(ImGuiDockNode* node); + static void DockNodeWindowMenuUpdate(ImGuiDockNode* node, ImGuiTabBar* tab_bar); + static void DockNodeUpdateVisibleFlag(ImGuiDockNode* node); + static void DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window); + static bool DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window); + static void DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking); + static void DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data); + static void DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos, ImVec2* out_close_button_pos); + static void DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired); + static bool DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking, ImVec2* test_mouse_pos); + static const char* DockNodeGetHostWindowTitle(ImGuiDockNode* node, char* buf, int buf_size) { ImFormatString(buf, buf_size, "##DockNode_%02X", node->ID); return buf; } + static int DockNodeGetTabOrder(ImGuiWindow* window); + + // ImGuiDockNode tree manipulations + static void DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_first_child, float split_ratio, ImGuiDockNode* new_node); + static void DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child); + static void DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, ImGuiDockNode* only_write_to_single_node = NULL); + static void DockNodeTreeUpdateSplitter(ImGuiDockNode* node); + static ImGuiDockNode* DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos); + static ImGuiDockNode* DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node); + + // Settings + static void DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id); + static void DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count); + static ImGuiDockNodeSettings* DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID node_id); + static void DockSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*); + static void DockSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*); + static void* DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); + static void DockSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); + static void DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf); +} + +//----------------------------------------------------------------------------- +// Docking: ImGuiDockContext +//----------------------------------------------------------------------------- +// The lifetime model is different from the one of regular windows: we always create a ImGuiDockNode for each ImGuiDockNodeSettings, +// or we always hold the entire docking node tree. Nodes are frequently hidden, e.g. if the window(s) or child nodes they host are not active. +// At boot time only, we run a simple GC to remove nodes that have no references. +// Because dock node settings (which are small, contiguous structures) are always mirrored by their corresponding dock nodes (more complete structures), +// we can also very easily recreate the nodes from scratch given the settings data (this is what DockContextRebuild() does). +// This is convenient as docking reconfiguration can be implemented by mostly poking at the simpler settings data. +//----------------------------------------------------------------------------- +// - DockContextInitialize() +// - DockContextShutdown() +// - DockContextClearNodes() +// - DockContextRebuildNodes() +// - DockContextNewFrameUpdateUndocking() +// - DockContextNewFrameUpdateDocking() +// - DockContextEndFrame() +// - DockContextFindNodeByID() +// - DockContextBindNodeToWindow() +// - DockContextGenNodeID() +// - DockContextAddNode() +// - DockContextRemoveNode() +// - ImGuiDockContextPruneNodeData +// - DockContextPruneUnusedSettingsNodes() +// - DockContextBuildNodesFromSettings() +// - DockContextBuildAddWindowsToNodes() +//----------------------------------------------------------------------------- + +void ImGui::DockContextInitialize(ImGuiContext* ctx) +{ + ImGuiContext& g = *ctx; + + // Add .ini handle for persistent docking data + ImGuiSettingsHandler ini_handler; + ini_handler.TypeName = "Docking"; + ini_handler.TypeHash = ImHashStr("Docking"); + ini_handler.ClearAllFn = DockSettingsHandler_ClearAll; + ini_handler.ReadInitFn = DockSettingsHandler_ClearAll; // Also clear on read + ini_handler.ReadOpenFn = DockSettingsHandler_ReadOpen; + ini_handler.ReadLineFn = DockSettingsHandler_ReadLine; + ini_handler.ApplyAllFn = DockSettingsHandler_ApplyAll; + ini_handler.WriteAllFn = DockSettingsHandler_WriteAll; + g.SettingsHandlers.push_back(ini_handler); + + g.DockNodeWindowMenuHandler = &DockNodeWindowMenuHandler_Default; +} + +void ImGui::DockContextShutdown(ImGuiContext* ctx) +{ + ImGuiDockContext* dc = &ctx->DockContext; + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) + IM_DELETE(node); +} + +void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs) +{ + IM_UNUSED(ctx); + IM_ASSERT(ctx == GImGui); + DockBuilderRemoveNodeDockedWindows(root_id, clear_settings_refs); + DockBuilderRemoveNodeChildNodes(root_id); +} + +// [DEBUG] This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch +// (Different from DockSettingsHandler_ClearAll() + DockSettingsHandler_ApplyAll() because this reuses current settings!) +void ImGui::DockContextRebuildNodes(ImGuiContext* ctx) +{ + ImGuiContext& g = *ctx; + ImGuiDockContext* dc = &ctx->DockContext; + IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextRebuildNodes\n"); + SaveIniSettingsToMemory(); + ImGuiID root_id = 0; // Rebuild all + DockContextClearNodes(ctx, root_id, false); + DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size); + DockContextBuildAddWindowsToNodes(ctx, root_id); +} + +// Docking context update function, called by NewFrame() +void ImGui::DockContextNewFrameUpdateUndocking(ImGuiContext* ctx) +{ + ImGuiContext& g = *ctx; + ImGuiDockContext* dc = &ctx->DockContext; + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) + { + if (dc->Nodes.Data.Size > 0 || dc->Requests.Size > 0) + DockContextClearNodes(ctx, 0, true); + return; + } + + // Setting NoSplit at runtime merges all nodes + if (g.IO.ConfigDockingNoSplit) + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) + if (node->IsRootNode() && node->IsSplitNode()) + { + DockBuilderRemoveNodeChildNodes(node->ID); + //dc->WantFullRebuild = true; + } + + // Process full rebuild +#if 0 + if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C))) + dc->WantFullRebuild = true; +#endif + if (dc->WantFullRebuild) + { + DockContextRebuildNodes(ctx); + dc->WantFullRebuild = false; + } + + // Process Undocking requests (we need to process them _before_ the UpdateMouseMovingWindowNewFrame call in NewFrame) + for (ImGuiDockRequest& req : dc->Requests) + { + if (req.Type == ImGuiDockRequestType_Undock && req.UndockTargetWindow) + DockContextProcessUndockWindow(ctx, req.UndockTargetWindow); + else if (req.Type == ImGuiDockRequestType_Undock && req.UndockTargetNode) + DockContextProcessUndockNode(ctx, req.UndockTargetNode); + } +} + +// Docking context update function, called by NewFrame() +void ImGui::DockContextNewFrameUpdateDocking(ImGuiContext* ctx) +{ + ImGuiContext& g = *ctx; + ImGuiDockContext* dc = &ctx->DockContext; + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) + return; + + // [DEBUG] Store hovered dock node. + // We could in theory use DockNodeTreeFindVisibleNodeByPos() on the root host dock node, but using ->DockNode is a good shortcut. + // Note this is mostly a debug thing and isn't actually used for docking target, because docking involve more detailed filtering. + g.DebugHoveredDockNode = NULL; + if (ImGuiWindow* hovered_window = g.HoveredWindowUnderMovingWindow) + { + if (hovered_window->DockNodeAsHost) + g.DebugHoveredDockNode = DockNodeTreeFindVisibleNodeByPos(hovered_window->DockNodeAsHost, g.IO.MousePos); + else if (hovered_window->RootWindow->DockNode) + g.DebugHoveredDockNode = hovered_window->RootWindow->DockNode; + } + + // Process Docking requests + for (ImGuiDockRequest& req : dc->Requests) + if (req.Type == ImGuiDockRequestType_Dock) + DockContextProcessDock(ctx, &req); + dc->Requests.resize(0); + + // Create windows for each automatic docking nodes + // We can have NULL pointers when we delete nodes, but because ID are recycled this should amortize nicely (and our node count will never be very high) + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) + if (node->IsFloatingNode()) + DockNodeUpdate(node); +} + +void ImGui::DockContextEndFrame(ImGuiContext* ctx) +{ + // Draw backgrounds of node missing their window + ImGuiContext& g = *ctx; + ImGuiDockContext* dc = &g.DockContext; + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) + if (node->LastFrameActive == g.FrameCount && node->IsVisible && node->HostWindow && node->IsLeafNode() && !node->IsBgDrawnThisFrame) + { + ImRect bg_rect(node->Pos + ImVec2(0.0f, GetFrameHeight()), node->Pos + node->Size); + ImDrawFlags bg_rounding_flags = CalcRoundingFlagsForRectInRect(bg_rect, node->HostWindow->Rect(), g.Style.DockingSeparatorSize); + node->HostWindow->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG); + node->HostWindow->DrawList->AddRectFilled(bg_rect.Min, bg_rect.Max, node->LastBgColor, node->HostWindow->WindowRounding, bg_rounding_flags); + } +} + +ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id) +{ + return (ImGuiDockNode*)ctx->DockContext.Nodes.GetVoidPtr(id); +} + +ImGuiID ImGui::DockContextGenNodeID(ImGuiContext* ctx) +{ + // Generate an ID for new node (the exact ID value doesn't matter as long as it is not already used) + // FIXME-OPT FIXME-DOCK: This is suboptimal, even if the node count is small enough not to be a worry.0 + // We should poke in ctx->Nodes to find a suitable ID faster. Even more so trivial that ctx->Nodes lookup is already sorted. + ImGuiID id = 0x0001; + while (DockContextFindNodeByID(ctx, id) != NULL) + id++; + return id; +} + +static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id) +{ + // Generate an ID for the new node (the exact ID value doesn't matter as long as it is not already used) and add the first window. + ImGuiContext& g = *ctx; + if (id == 0) + id = DockContextGenNodeID(ctx); + else + IM_ASSERT(DockContextFindNodeByID(ctx, id) == NULL); + + // We don't set node->LastFrameAlive on construction. Nodes are always created at all time to reflect .ini settings! + IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextAddNode 0x%08X\n", id); + ImGuiDockNode* node = IM_NEW(ImGuiDockNode)(id); + ctx->DockContext.Nodes.SetVoidPtr(node->ID, node); + return node; +} + +static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node) +{ + ImGuiContext& g = *ctx; + ImGuiDockContext* dc = &ctx->DockContext; + + IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextRemoveNode 0x%08X\n", node->ID); + IM_ASSERT(DockContextFindNodeByID(ctx, node->ID) == node); + IM_ASSERT(node->ChildNodes[0] == NULL && node->ChildNodes[1] == NULL); + IM_ASSERT(node->Windows.Size == 0); + + if (node->HostWindow) + node->HostWindow->DockNodeAsHost = NULL; + + ImGuiDockNode* parent_node = node->ParentNode; + const bool merge = (merge_sibling_into_parent_node && parent_node != NULL); + if (merge) + { + IM_ASSERT(parent_node->ChildNodes[0] == node || parent_node->ChildNodes[1] == node); + ImGuiDockNode* sibling_node = (parent_node->ChildNodes[0] == node ? parent_node->ChildNodes[1] : parent_node->ChildNodes[0]); + DockNodeTreeMerge(&g, parent_node, sibling_node); + } + else + { + for (int n = 0; parent_node && n < IM_ARRAYSIZE(parent_node->ChildNodes); n++) + if (parent_node->ChildNodes[n] == node) + node->ParentNode->ChildNodes[n] = NULL; + dc->Nodes.SetVoidPtr(node->ID, NULL); + IM_DELETE(node); + } +} + +static int IMGUI_CDECL DockNodeComparerDepthMostFirst(const void* lhs, const void* rhs) +{ + const ImGuiDockNode* a = *(const ImGuiDockNode* const*)lhs; + const ImGuiDockNode* b = *(const ImGuiDockNode* const*)rhs; + return ImGui::DockNodeGetDepth(b) - ImGui::DockNodeGetDepth(a); +} + +// Pre C++0x doesn't allow us to use a function-local type (without linkage) as template parameter, so we moved this here. +struct ImGuiDockContextPruneNodeData +{ + int CountWindows, CountChildWindows, CountChildNodes; + ImGuiID RootId; + ImGuiDockContextPruneNodeData() { CountWindows = CountChildWindows = CountChildNodes = 0; RootId = 0; } +}; + +// Garbage collect unused nodes (run once at init time) +static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) +{ + ImGuiContext& g = *ctx; + ImGuiDockContext* dc = &ctx->DockContext; + IM_ASSERT(g.Windows.Size == 0); + + ImPool pool; + pool.Reserve(dc->NodesSettings.Size); + + // Count child nodes and compute RootID + for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++) + { + ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n]; + ImGuiDockContextPruneNodeData* parent_data = settings->ParentNodeId ? pool.GetByKey(settings->ParentNodeId) : 0; + pool.GetOrAddByKey(settings->ID)->RootId = parent_data ? parent_data->RootId : settings->ID; + if (settings->ParentNodeId) + pool.GetOrAddByKey(settings->ParentNodeId)->CountChildNodes++; + } + + // Count reference to dock ids from dockspaces + // We track the 'auto-DockNode <- manual-Window <- manual-DockSpace' in order to avoid 'auto-DockNode' being ditched by DockContextPruneUnusedSettingsNodes() + for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++) + { + ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n]; + if (settings->ParentWindowId != 0) + if (ImGuiWindowSettings* window_settings = FindWindowSettingsByID(settings->ParentWindowId)) + if (window_settings->DockId) + if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(window_settings->DockId)) + data->CountChildNodes++; + } + + // Count reference to dock ids from window settings + // We guard against the possibility of an invalid .ini file (RootID may point to a missing node) + for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) + if (ImGuiID dock_id = settings->DockId) + if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(dock_id)) + { + data->CountWindows++; + if (ImGuiDockContextPruneNodeData* data_root = (data->RootId == dock_id) ? data : pool.GetByKey(data->RootId)) + data_root->CountChildWindows++; + } + + // Prune + for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++) + { + ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n]; + ImGuiDockContextPruneNodeData* data = pool.GetByKey(settings->ID); + if (data->CountWindows > 1) + continue; + ImGuiDockContextPruneNodeData* data_root = (data->RootId == settings->ID) ? data : pool.GetByKey(data->RootId); + + bool remove = false; + remove |= (data->CountWindows == 1 && settings->ParentNodeId == 0 && data->CountChildNodes == 0 && !(settings->Flags & ImGuiDockNodeFlags_CentralNode)); // Floating root node with only 1 window + remove |= (data->CountWindows == 0 && settings->ParentNodeId == 0 && data->CountChildNodes == 0); // Leaf nodes with 0 window + remove |= (data_root->CountChildWindows == 0); + if (remove) + { + IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextPruneUnusedSettingsNodes: Prune 0x%08X\n", settings->ID); + DockSettingsRemoveNodeReferences(&settings->ID, 1); + settings->ID = 0; + } + } +} + +static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count) +{ + // Build nodes + for (int node_n = 0; node_n < node_settings_count; node_n++) + { + ImGuiDockNodeSettings* settings = &node_settings_array[node_n]; + if (settings->ID == 0) + continue; + ImGuiDockNode* node = DockContextAddNode(ctx, settings->ID); + node->ParentNode = settings->ParentNodeId ? DockContextFindNodeByID(ctx, settings->ParentNodeId) : NULL; + node->Pos = ImVec2(settings->Pos.x, settings->Pos.y); + node->Size = ImVec2(settings->Size.x, settings->Size.y); + node->SizeRef = ImVec2(settings->SizeRef.x, settings->SizeRef.y); + node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_DockNode; + if (node->ParentNode && node->ParentNode->ChildNodes[0] == NULL) + node->ParentNode->ChildNodes[0] = node; + else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL) + node->ParentNode->ChildNodes[1] = node; + node->SelectedTabId = settings->SelectedTabId; + node->SplitAxis = (ImGuiAxis)settings->SplitAxis; + node->SetLocalFlags(settings->Flags & ImGuiDockNodeFlags_SavedFlagsMask_); + + // Bind host window immediately if it already exist (in case of a rebuild) + // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set. + char host_window_title[20]; + ImGuiDockNode* root_node = DockNodeGetRootNode(node); + node->HostWindow = FindWindowByName(DockNodeGetHostWindowTitle(root_node, host_window_title, IM_ARRAYSIZE(host_window_title))); + } +} + +void ImGui::DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id) +{ + // Rebind all windows to nodes (they can also lazily rebind but we'll have a visible glitch during the first frame) + ImGuiContext& g = *ctx; + for (ImGuiWindow* window : g.Windows) + { + if (window->DockId == 0 || window->LastFrameActive < g.FrameCount - 1) + continue; + if (window->DockNode != NULL) + continue; + + ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId); + IM_ASSERT(node != NULL); // This should have been called after DockContextBuildNodesFromSettings() + if (root_id == 0 || DockNodeGetRootNode(node)->ID == root_id) + DockNodeAddWindow(node, window, true); + } +} + +//----------------------------------------------------------------------------- +// Docking: ImGuiDockContext Docking/Undocking functions +//----------------------------------------------------------------------------- +// - DockContextQueueDock() +// - DockContextQueueUndockWindow() +// - DockContextQueueUndockNode() +// - DockContextQueueNotifyRemovedNode() +// - DockContextProcessDock() +// - DockContextProcessUndockWindow() +// - DockContextProcessUndockNode() +// - DockContextCalcDropPosForDocking() +//----------------------------------------------------------------------------- + +void ImGui::DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer) +{ + IM_ASSERT(target != payload); + ImGuiDockRequest req; + req.Type = ImGuiDockRequestType_Dock; + req.DockTargetWindow = target; + req.DockTargetNode = target_node; + req.DockPayload = payload; + req.DockSplitDir = split_dir; + req.DockSplitRatio = split_ratio; + req.DockSplitOuter = split_outer; + ctx->DockContext.Requests.push_back(req); +} + +void ImGui::DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window) +{ + ImGuiDockRequest req; + req.Type = ImGuiDockRequestType_Undock; + req.UndockTargetWindow = window; + ctx->DockContext.Requests.push_back(req); +} + +void ImGui::DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) +{ + ImGuiDockRequest req; + req.Type = ImGuiDockRequestType_Undock; + req.UndockTargetNode = node; + ctx->DockContext.Requests.push_back(req); +} + +void ImGui::DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node) +{ + ImGuiDockContext* dc = &ctx->DockContext; + for (ImGuiDockRequest& req : dc->Requests) + if (req.DockTargetNode == node) + req.Type = ImGuiDockRequestType_None; +} + +void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) +{ + IM_ASSERT((req->Type == ImGuiDockRequestType_Dock && req->DockPayload != NULL) || (req->Type == ImGuiDockRequestType_Split && req->DockPayload == NULL)); + IM_ASSERT(req->DockTargetWindow != NULL || req->DockTargetNode != NULL); + + ImGuiContext& g = *ctx; + IM_UNUSED(g); + + ImGuiWindow* payload_window = req->DockPayload; // Optional + ImGuiWindow* target_window = req->DockTargetWindow; + ImGuiDockNode* node = req->DockTargetNode; + if (payload_window) + IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessDock node 0x%08X target '%s' dock window '%s', split_dir %d\n", node ? node->ID : 0, target_window ? target_window->Name : "NULL", payload_window->Name, req->DockSplitDir); + else + IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessDock node 0x%08X, split_dir %d\n", node ? node->ID : 0, req->DockSplitDir); + + // Decide which Tab will be selected at the end of the operation + ImGuiID next_selected_id = 0; + ImGuiDockNode* payload_node = NULL; + if (payload_window) + { + payload_node = payload_window->DockNodeAsHost; + payload_window->DockNodeAsHost = NULL; // Important to clear this as the node will have its life as a child which might be merged/deleted later. + if (payload_node && payload_node->IsLeafNode()) + next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId; + if (payload_node == NULL) + next_selected_id = payload_window->TabId; + } + + // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well + // When processing an interactive split, usually LastFrameAlive will be < g.FrameCount. But DockBuilder operations can make it ==. + if (node) + IM_ASSERT(node->LastFrameAlive <= g.FrameCount); + if (node && target_window && node == target_window->DockNodeAsHost) + IM_ASSERT(node->Windows.Size > 0 || node->IsSplitNode() || node->IsCentralNode()); + + // Create new node and add existing window to it + if (node == NULL) + { + node = DockContextAddNode(ctx, 0); + node->Pos = target_window->Pos; + node->Size = target_window->Size; + if (target_window->DockNodeAsHost == NULL) + { + DockNodeAddWindow(node, target_window, true); + node->TabBar->Tabs[0].Flags &= ~ImGuiTabItemFlags_Unsorted; + target_window->DockIsActive = true; + } + } + + ImGuiDir split_dir = req->DockSplitDir; + if (split_dir != ImGuiDir_None) + { + // Split into two, one side will be our payload node unless we are dropping a loose window + const ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; + const int split_inheritor_child_idx = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0; // Current contents will be moved to the opposite side + const float split_ratio = req->DockSplitRatio; + DockNodeTreeSplit(ctx, node, split_axis, split_inheritor_child_idx, split_ratio, payload_node); // payload_node may be NULL here! + ImGuiDockNode* new_node = node->ChildNodes[split_inheritor_child_idx ^ 1]; + new_node->HostWindow = node->HostWindow; + node = new_node; + } + node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_HiddenTabBar); + + if (node != payload_node) + { + // Create tab bar before we call DockNodeMoveWindows (which would attempt to move the old tab-bar, which would lead us to payload tabs wrongly appearing before target tabs!) + if (node->Windows.Size > 0 && node->TabBar == NULL) + { + DockNodeAddTabBar(node); + for (int n = 0; n < node->Windows.Size; n++) + TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]); + } + + if (payload_node != NULL) + { + // Transfer full payload node (with 1+ child windows or child nodes) + if (payload_node->IsSplitNode()) + { + if (node->Windows.Size > 0) + { + // We can dock a split payload into a node that already has windows _only_ if our payload is a node tree with a single visible node. + // In this situation, we move the windows of the target node into the currently visible node of the payload. + // This allows us to preserve some of the underlying dock tree settings nicely. + IM_ASSERT(payload_node->OnlyNodeWithWindows != NULL); // The docking should have been blocked by DockNodePreviewDockSetup() early on and never submitted. + ImGuiDockNode* visible_node = payload_node->OnlyNodeWithWindows; + if (visible_node->TabBar) + IM_ASSERT(visible_node->TabBar->Tabs.Size > 0); + DockNodeMoveWindows(node, visible_node); + DockNodeMoveWindows(visible_node, node); + DockSettingsRenameNodeReferences(node->ID, visible_node->ID); + } + if (node->IsCentralNode()) + { + // Central node property needs to be moved to a leaf node, pick the last focused one. + // FIXME-DOCK: If we had to transfer other flags here, what would the policy be? + ImGuiDockNode* last_focused_node = DockContextFindNodeByID(ctx, payload_node->LastFocusedNodeId); + IM_ASSERT(last_focused_node != NULL); + ImGuiDockNode* last_focused_root_node = DockNodeGetRootNode(last_focused_node); + IM_ASSERT(last_focused_root_node == DockNodeGetRootNode(payload_node)); + last_focused_node->SetLocalFlags(last_focused_node->LocalFlags | ImGuiDockNodeFlags_CentralNode); + node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_CentralNode); + last_focused_root_node->CentralNode = last_focused_node; + } + + IM_ASSERT(node->Windows.Size == 0); + DockNodeMoveChildNodes(node, payload_node); + } + else + { + const ImGuiID payload_dock_id = payload_node->ID; + DockNodeMoveWindows(node, payload_node); + DockSettingsRenameNodeReferences(payload_dock_id, node->ID); + } + DockContextRemoveNode(ctx, payload_node, true); + } + else if (payload_window) + { + // Transfer single window + const ImGuiID payload_dock_id = payload_window->DockId; + node->VisibleWindow = payload_window; + DockNodeAddWindow(node, payload_window, true); + if (payload_dock_id != 0) + DockSettingsRenameNodeReferences(payload_dock_id, node->ID); + } + } + else + { + // When docking a floating single window node we want to reevaluate auto-hiding of the tab bar + node->WantHiddenTabBarUpdate = true; + } + + // Update selection immediately + if (ImGuiTabBar* tab_bar = node->TabBar) + tab_bar->NextSelectedTabId = next_selected_id; + MarkIniSettingsDirty(); +} + +// Problem: +// Undocking a large (~full screen) window would leave it so large that the bottom right sizing corner would more +// than likely be off the screen and the window would be hard to resize to fit on screen. This can be particularly problematic +// with 'ConfigWindowsMoveFromTitleBarOnly=true' and/or with 'ConfigWindowsResizeFromEdges=false' as well (the later can be +// due to missing ImGuiBackendFlags_HasMouseCursors backend flag). +// Solution: +// When undocking a window we currently force its maximum size to 90% of the host viewport or monitor. +// Reevaluate this when we implement preserving docked/undocked size ("docking_wip/undocked_size" branch). +static ImVec2 FixLargeWindowsWhenUndocking(const ImVec2& size, ImGuiViewport* ref_viewport) +{ + if (ref_viewport == NULL) + return size; + + ImGuiContext& g = *GImGui; + ImVec2 max_size = ImTrunc(ref_viewport->WorkSize * 0.90f); + if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) + { + const ImGuiPlatformMonitor* monitor = ImGui::GetViewportPlatformMonitor(ref_viewport); + max_size = ImTrunc(monitor->WorkSize * 0.90f); + } + return ImMin(size, max_size); +} + +void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref) +{ + ImGuiContext& g = *ctx; + IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessUndockWindow window '%s', clear_persistent_docking_ref = %d\n", window->Name, clear_persistent_docking_ref); + if (window->DockNode) + DockNodeRemoveWindow(window->DockNode, window, clear_persistent_docking_ref ? 0 : window->DockId); + else + window->DockId = 0; + window->Collapsed = false; + window->DockIsActive = false; + window->DockNodeIsVisible = window->DockTabIsVisible = false; + window->Size = window->SizeFull = FixLargeWindowsWhenUndocking(window->SizeFull, window->Viewport); + + MarkIniSettingsDirty(); +} + +void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) +{ + ImGuiContext& g = *ctx; + IMGUI_DEBUG_LOG_DOCKING("[docking] DockContextProcessUndockNode node %08X\n", node->ID); + IM_ASSERT(node->IsLeafNode()); + IM_ASSERT(node->Windows.Size >= 1); + + if (node->IsRootNode() || node->IsCentralNode()) + { + // In the case of a root node or central node, the node will have to stay in place. Create a new node to receive the payload. + ImGuiDockNode* new_node = DockContextAddNode(ctx, 0); + new_node->Pos = node->Pos; + new_node->Size = node->Size; + new_node->SizeRef = node->SizeRef; + DockNodeMoveWindows(new_node, node); + DockSettingsRenameNodeReferences(node->ID, new_node->ID); + node = new_node; + } + else + { + // Otherwise extract our node and merge our sibling back into the parent node. + IM_ASSERT(node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); + int index_in_parent = (node->ParentNode->ChildNodes[0] == node) ? 0 : 1; + node->ParentNode->ChildNodes[index_in_parent] = NULL; + DockNodeTreeMerge(ctx, node->ParentNode, node->ParentNode->ChildNodes[index_in_parent ^ 1]); + node->ParentNode->AuthorityForViewport = ImGuiDataAuthority_Window; // The node that stays in place keeps the viewport, so our newly dragged out node will create a new viewport + node->ParentNode = NULL; + } + for (ImGuiWindow* window : node->Windows) + { + window->Flags &= ~ImGuiWindowFlags_ChildWindow; + if (window->ParentWindow) + window->ParentWindow->DC.ChildWindows.find_erase(window); + UpdateWindowParentAndRootLinks(window, window->Flags, NULL); + } + node->AuthorityForPos = node->AuthorityForSize = ImGuiDataAuthority_DockNode; + node->Size = FixLargeWindowsWhenUndocking(node->Size, node->Windows[0]->Viewport); + node->WantMouseMove = true; + MarkIniSettingsDirty(); +} + +// This is mostly used for automation. +bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos) +{ + if (target != NULL && target_node == NULL) + target_node = target->DockNode; + + // In DockNodePreviewDockSetup() for a root central node instead of showing both "inner" and "outer" drop rects + // (which would be functionally identical) we only show the outer one. Reflect this here. + if (target_node && target_node->ParentNode == NULL && target_node->IsCentralNode() && split_dir != ImGuiDir_None) + split_outer = true; + ImGuiDockPreviewData split_data; + DockNodePreviewDockSetup(target, target_node, payload_window, payload_node, &split_data, false, split_outer); + if (split_data.DropRectsDraw[split_dir+1].IsInverted()) + return false; + *out_pos = split_data.DropRectsDraw[split_dir+1].GetCenter(); + return true; +} + +//----------------------------------------------------------------------------- +// Docking: ImGuiDockNode +//----------------------------------------------------------------------------- +// - DockNodeGetTabOrder() +// - DockNodeAddWindow() +// - DockNodeRemoveWindow() +// - DockNodeMoveChildNodes() +// - DockNodeMoveWindows() +// - DockNodeApplyPosSizeToWindows() +// - DockNodeHideHostWindow() +// - ImGuiDockNodeFindInfoResults +// - DockNodeFindInfo() +// - DockNodeFindWindowByID() +// - DockNodeUpdateFlagsAndCollapse() +// - DockNodeUpdateHasCentralNodeFlag() +// - DockNodeUpdateVisibleFlag() +// - DockNodeStartMouseMovingWindow() +// - DockNodeUpdate() +// - DockNodeUpdateWindowMenu() +// - DockNodeBeginAmendTabBar() +// - DockNodeEndAmendTabBar() +// - DockNodeUpdateTabBar() +// - DockNodeAddTabBar() +// - DockNodeRemoveTabBar() +// - DockNodeIsDropAllowedOne() +// - DockNodeIsDropAllowed() +// - DockNodeCalcTabBarLayout() +// - DockNodeCalcSplitRects() +// - DockNodeCalcDropRectsAndTestMousePos() +// - DockNodePreviewDockSetup() +// - DockNodePreviewDockRender() +//----------------------------------------------------------------------------- + +ImGuiDockNode::ImGuiDockNode(ImGuiID id) +{ + ID = id; + SharedFlags = LocalFlags = LocalFlagsInWindows = MergedFlags = ImGuiDockNodeFlags_None; + ParentNode = ChildNodes[0] = ChildNodes[1] = NULL; + TabBar = NULL; + SplitAxis = ImGuiAxis_None; + + State = ImGuiDockNodeState_Unknown; + LastBgColor = IM_COL32_WHITE; + HostWindow = VisibleWindow = NULL; + CentralNode = OnlyNodeWithWindows = NULL; + CountNodeWithWindows = 0; + LastFrameAlive = LastFrameActive = LastFrameFocused = -1; + LastFocusedNodeId = 0; + SelectedTabId = 0; + WantCloseTabId = 0; + RefViewportId = 0; + AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode; + AuthorityForViewport = ImGuiDataAuthority_Auto; + IsVisible = true; + IsFocused = HasCloseButton = HasWindowMenuButton = HasCentralNodeChild = false; + IsBgDrawnThisFrame = false; + WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false; +} + +ImGuiDockNode::~ImGuiDockNode() +{ + IM_DELETE(TabBar); + TabBar = NULL; + ChildNodes[0] = ChildNodes[1] = NULL; +} + +int ImGui::DockNodeGetTabOrder(ImGuiWindow* window) +{ + ImGuiTabBar* tab_bar = window->DockNode->TabBar; + if (tab_bar == NULL) + return -1; + ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, window->TabId); + return tab ? TabBarGetTabOrder(tab_bar, tab) : -1; +} + +static void DockNodeHideWindowDuringHostWindowCreation(ImGuiWindow* window) +{ + window->Hidden = true; + window->HiddenFramesCanSkipItems = window->Active ? 1 : 2; +} + +static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar) +{ + ImGuiContext& g = *GImGui; (void)g; + if (window->DockNode) + { + // Can overwrite an existing window->DockNode (e.g. pointing to a disabled DockSpace node) + IM_ASSERT(window->DockNode->ID != node->ID); + DockNodeRemoveWindow(window->DockNode, window, 0); + } + IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); + IMGUI_DEBUG_LOG_DOCKING("[docking] DockNodeAddWindow node 0x%08X window '%s'\n", node->ID, window->Name); + + // If more than 2 windows appeared on the same frame leading to the creation of a new hosting window, + // we'll hide windows until the host window is ready. Hide the 1st window after its been output (so it is not visible for one frame). + // We will call DockNodeHideWindowDuringHostWindowCreation() on ourselves in Begin() + if (node->HostWindow == NULL && node->Windows.Size == 1 && node->Windows[0]->WasActive == false) + DockNodeHideWindowDuringHostWindowCreation(node->Windows[0]); + + node->Windows.push_back(window); + node->WantHiddenTabBarUpdate = true; + window->DockNode = node; + window->DockId = node->ID; + window->DockIsActive = (node->Windows.Size > 1); + window->DockTabWantClose = false; + + // When reactivating a node with one or two loose window, the window pos/size/viewport are authoritative over the node storage. + // In particular it is important we init the viewport from the first window so we don't create two viewports and drop one. + if (node->HostWindow == NULL && node->IsFloatingNode()) + { + if (node->AuthorityForPos == ImGuiDataAuthority_Auto) + node->AuthorityForPos = ImGuiDataAuthority_Window; + if (node->AuthorityForSize == ImGuiDataAuthority_Auto) + node->AuthorityForSize = ImGuiDataAuthority_Window; + if (node->AuthorityForViewport == ImGuiDataAuthority_Auto) + node->AuthorityForViewport = ImGuiDataAuthority_Window; + } + + // Add to tab bar if requested + if (add_to_tab_bar) + { + if (node->TabBar == NULL) + { + DockNodeAddTabBar(node); + node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabId; + + // Add existing windows + for (int n = 0; n < node->Windows.Size - 1; n++) + TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]); + } + TabBarAddTab(node->TabBar, ImGuiTabItemFlags_Unsorted, window); + } + + DockNodeUpdateVisibleFlag(node); + + // Update this without waiting for the next time we Begin() in the window, so our host window will have the proper title bar color on its first frame. + if (node->HostWindow) + UpdateWindowParentAndRootLinks(window, window->Flags | ImGuiWindowFlags_ChildWindow, node->HostWindow); +} + +static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(window->DockNode == node); + //IM_ASSERT(window->RootWindowDockTree == node->HostWindow); + //IM_ASSERT(window->LastFrameActive < g.FrameCount); // We may call this from Begin() + IM_ASSERT(save_dock_id == 0 || save_dock_id == node->ID); + IMGUI_DEBUG_LOG_DOCKING("[docking] DockNodeRemoveWindow node 0x%08X window '%s'\n", node->ID, window->Name); + + window->DockNode = NULL; + window->DockIsActive = window->DockTabWantClose = false; + window->DockId = save_dock_id; + window->Flags &= ~ImGuiWindowFlags_ChildWindow; + if (window->ParentWindow) + window->ParentWindow->DC.ChildWindows.find_erase(window); + UpdateWindowParentAndRootLinks(window, window->Flags, NULL); // Update immediately + + if (node->HostWindow && node->HostWindow->ViewportOwned) + { + // When undocking from a user interaction this will always run in NewFrame() and have not much effect. + // But mid-frame, if we clear viewport we need to mark window as hidden as well. + window->Viewport = NULL; + window->ViewportId = 0; + window->ViewportOwned = false; + window->Hidden = true; + } + + // Remove window + bool erased = false; + for (int n = 0; n < node->Windows.Size; n++) + if (node->Windows[n] == window) + { + node->Windows.erase(node->Windows.Data + n); + erased = true; + break; + } + if (!erased) + IM_ASSERT(erased); + if (node->VisibleWindow == window) + node->VisibleWindow = NULL; + + // Remove tab and possibly tab bar + node->WantHiddenTabBarUpdate = true; + if (node->TabBar) + { + TabBarRemoveTab(node->TabBar, window->TabId); + const int tab_count_threshold_for_tab_bar = node->IsCentralNode() ? 1 : 2; + if (node->Windows.Size < tab_count_threshold_for_tab_bar) + DockNodeRemoveTabBar(node); + } + + if (node->Windows.Size == 0 && !node->IsCentralNode() && !node->IsDockSpace() && window->DockId != node->ID) + { + // Automatic dock node delete themselves if they are not holding at least one tab + DockContextRemoveNode(&g, node, true); + return; + } + + if (node->Windows.Size == 1 && !node->IsCentralNode() && node->HostWindow) + { + ImGuiWindow* remaining_window = node->Windows[0]; + // Note: we used to transport viewport ownership here. + remaining_window->Collapsed = node->HostWindow->Collapsed; + } + + // Update visibility immediately is required so the DockNodeUpdateRemoveInactiveChilds() processing can reflect changes up the tree + DockNodeUpdateVisibleFlag(node); +} + +static void ImGui::DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node) +{ + IM_ASSERT(dst_node->Windows.Size == 0); + dst_node->ChildNodes[0] = src_node->ChildNodes[0]; + dst_node->ChildNodes[1] = src_node->ChildNodes[1]; + if (dst_node->ChildNodes[0]) + dst_node->ChildNodes[0]->ParentNode = dst_node; + if (dst_node->ChildNodes[1]) + dst_node->ChildNodes[1]->ParentNode = dst_node; + dst_node->SplitAxis = src_node->SplitAxis; + dst_node->SizeRef = src_node->SizeRef; + src_node->ChildNodes[0] = src_node->ChildNodes[1] = NULL; +} + +static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node) +{ + // Insert tabs in the same orders as currently ordered (node->Windows isn't ordered) + IM_ASSERT(src_node && dst_node && dst_node != src_node); + ImGuiTabBar* src_tab_bar = src_node->TabBar; + if (src_tab_bar != NULL) + IM_ASSERT(src_node->Windows.Size <= src_node->TabBar->Tabs.Size); + + // If the dst_node is empty we can just move the entire tab bar (to preserve selection, scrolling, etc.) + bool move_tab_bar = (src_tab_bar != NULL) && (dst_node->TabBar == NULL); + if (move_tab_bar) + { + dst_node->TabBar = src_node->TabBar; + src_node->TabBar = NULL; + } + + // Tab order is not important here, it is preserved by sorting in DockNodeUpdateTabBar(). + for (ImGuiWindow* window : src_node->Windows) + { + window->DockNode = NULL; + window->DockIsActive = false; + DockNodeAddWindow(dst_node, window, !move_tab_bar); + } + src_node->Windows.clear(); + + if (!move_tab_bar && src_node->TabBar) + { + if (dst_node->TabBar) + dst_node->TabBar->SelectedTabId = src_node->TabBar->SelectedTabId; + DockNodeRemoveTabBar(src_node); + } +} + +static void ImGui::DockNodeApplyPosSizeToWindows(ImGuiDockNode* node) +{ + for (ImGuiWindow* window : node->Windows) + { + SetWindowPos(window, node->Pos, ImGuiCond_Always); // We don't assign directly to Pos because it can break the calculation of SizeContents on next frame + SetWindowSize(window, node->Size, ImGuiCond_Always); + } +} + +static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node) +{ + if (node->HostWindow) + { + if (node->HostWindow->DockNodeAsHost == node) + node->HostWindow->DockNodeAsHost = NULL; + node->HostWindow = NULL; + } + + if (node->Windows.Size == 1) + { + node->VisibleWindow = node->Windows[0]; + node->Windows[0]->DockIsActive = false; + } + + if (node->TabBar) + DockNodeRemoveTabBar(node); +} + +// Search function called once by root node in DockNodeUpdate() +struct ImGuiDockNodeTreeInfo +{ + ImGuiDockNode* CentralNode; + ImGuiDockNode* FirstNodeWithWindows; + int CountNodesWithWindows; + //ImGuiWindowClass WindowClassForMerges; + + ImGuiDockNodeTreeInfo() { memset(this, 0, sizeof(*this)); } +}; + +static void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeTreeInfo* info) +{ + if (node->Windows.Size > 0) + { + if (info->FirstNodeWithWindows == NULL) + info->FirstNodeWithWindows = node; + info->CountNodesWithWindows++; + } + if (node->IsCentralNode()) + { + IM_ASSERT(info->CentralNode == NULL); // Should be only one + IM_ASSERT(node->IsLeafNode() && "If you get this assert: please submit .ini file + repro of actions leading to this."); + info->CentralNode = node; + } + if (info->CountNodesWithWindows > 1 && info->CentralNode != NULL) + return; + if (node->ChildNodes[0]) + DockNodeFindInfo(node->ChildNodes[0], info); + if (node->ChildNodes[1]) + DockNodeFindInfo(node->ChildNodes[1], info); +} + +static ImGuiWindow* ImGui::DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id) +{ + IM_ASSERT(id != 0); + for (ImGuiWindow* window : node->Windows) + if (window->ID == id) + return window; + return NULL; +} + +// - Remove inactive windows/nodes. +// - Update visibility flag. +static void ImGui::DockNodeUpdateFlagsAndCollapse(ImGuiDockNode* node) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); + + // Inherit most flags + if (node->ParentNode) + node->SharedFlags = node->ParentNode->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_; + + // Recurse into children + // There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'. + // If 'node->ChildNode[0]' delete itself, then 'node->ChildNode[1]->Windows' will be moved into 'node' + // If 'node->ChildNode[1]' delete itself, then 'node->ChildNode[0]->Windows' will be moved into 'node' and the "remove inactive windows" loop will have run twice on those windows (harmless) + node->HasCentralNodeChild = false; + if (node->ChildNodes[0]) + DockNodeUpdateFlagsAndCollapse(node->ChildNodes[0]); + if (node->ChildNodes[1]) + DockNodeUpdateFlagsAndCollapse(node->ChildNodes[1]); + + // Remove inactive windows, collapse nodes + // Merge node flags overrides stored in windows + node->LocalFlagsInWindows = ImGuiDockNodeFlags_None; + for (int window_n = 0; window_n < node->Windows.Size; window_n++) + { + ImGuiWindow* window = node->Windows[window_n]; + IM_ASSERT(window->DockNode == node); + + bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount); + bool remove = false; + remove |= node_was_active && (window->LastFrameActive + 1 < g.FrameCount); + remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabId == window->TabId) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument); // Submit all _expected_ closure from last frame + remove |= (window->DockTabWantClose); + if (remove) + { + window->DockTabWantClose = false; + if (node->Windows.Size == 1 && !node->IsCentralNode()) + { + DockNodeHideHostWindow(node); + node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow; + DockNodeRemoveWindow(node, window, node->ID); // Will delete the node so it'll be invalid on return + return; + } + DockNodeRemoveWindow(node, window, node->ID); + window_n--; + continue; + } + + // FIXME-DOCKING: Missing policies for conflict resolution, hence the "Experimental" tag on this. + //node->LocalFlagsInWindow &= ~window->WindowClass.DockNodeFlagsOverrideClear; + node->LocalFlagsInWindows |= window->WindowClass.DockNodeFlagsOverrideSet; + } + node->UpdateMergedFlags(); + + // Auto-hide tab bar option + ImGuiDockNodeFlags node_flags = node->MergedFlags; + if (node->WantHiddenTabBarUpdate && node->Windows.Size == 1 && (node_flags & ImGuiDockNodeFlags_AutoHideTabBar) && !node->IsHiddenTabBar()) + node->WantHiddenTabBarToggle = true; + node->WantHiddenTabBarUpdate = false; + + // Cancel toggling if we know our tab bar is enforced to be hidden at all times + if (node->WantHiddenTabBarToggle && node->VisibleWindow && (node->VisibleWindow->WindowClass.DockNodeFlagsOverrideSet & ImGuiDockNodeFlags_HiddenTabBar)) + node->WantHiddenTabBarToggle = false; + + // Apply toggles at a single point of the frame (here!) + if (node->Windows.Size > 1) + node->SetLocalFlags(node->LocalFlags & ~ImGuiDockNodeFlags_HiddenTabBar); + else if (node->WantHiddenTabBarToggle) + node->SetLocalFlags(node->LocalFlags ^ ImGuiDockNodeFlags_HiddenTabBar); + node->WantHiddenTabBarToggle = false; + + DockNodeUpdateVisibleFlag(node); +} + +// This is rarely called as DockNodeUpdateForRootNode() generally does it most frames. +static void ImGui::DockNodeUpdateHasCentralNodeChild(ImGuiDockNode* node) +{ + node->HasCentralNodeChild = false; + if (node->ChildNodes[0]) + DockNodeUpdateHasCentralNodeChild(node->ChildNodes[0]); + if (node->ChildNodes[1]) + DockNodeUpdateHasCentralNodeChild(node->ChildNodes[1]); + if (node->IsRootNode()) + { + ImGuiDockNode* mark_node = node->CentralNode; + while (mark_node) + { + mark_node->HasCentralNodeChild = true; + mark_node = mark_node->ParentNode; + } + } +} + +static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node) +{ + // Update visibility flag + bool is_visible = (node->ParentNode == NULL) ? node->IsDockSpace() : node->IsCentralNode(); + is_visible |= (node->Windows.Size > 0); + is_visible |= (node->ChildNodes[0] && node->ChildNodes[0]->IsVisible); + is_visible |= (node->ChildNodes[1] && node->ChildNodes[1]->IsVisible); + node->IsVisible = is_visible; +} + +static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(node->WantMouseMove == true); + StartMouseMovingWindow(window); + g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - node->Pos; + g.MovingWindow = window; // If we are docked into a non moveable root window, StartMouseMovingWindow() won't set g.MovingWindow. Override that decision. + node->WantMouseMove = false; +} + +// Update CentralNode, OnlyNodeWithWindows, LastFocusedNodeID. Copy window class. +static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node) +{ + DockNodeUpdateFlagsAndCollapse(node); + + // - Setup central node pointers + // - Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!) + // Cannot merge this with DockNodeUpdateFlagsAndCollapse() because FirstNodeWithWindows is found after window removal and child collapsing + ImGuiDockNodeTreeInfo info; + DockNodeFindInfo(node, &info); + node->CentralNode = info.CentralNode; + node->OnlyNodeWithWindows = (info.CountNodesWithWindows == 1) ? info.FirstNodeWithWindows : NULL; + node->CountNodeWithWindows = info.CountNodesWithWindows; + if (node->LastFocusedNodeId == 0 && info.FirstNodeWithWindows != NULL) + node->LastFocusedNodeId = info.FirstNodeWithWindows->ID; + + // Copy the window class from of our first window so it can be used for proper dock filtering. + // When node has mixed windows, prioritize the class with the most constraint (DockingAllowUnclassed = false) as the reference to copy. + // FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec. + if (ImGuiDockNode* first_node_with_windows = info.FirstNodeWithWindows) + { + node->WindowClass = first_node_with_windows->Windows[0]->WindowClass; + for (int n = 1; n < first_node_with_windows->Windows.Size; n++) + if (first_node_with_windows->Windows[n]->WindowClass.DockingAllowUnclassed == false) + { + node->WindowClass = first_node_with_windows->Windows[n]->WindowClass; + break; + } + } + + ImGuiDockNode* mark_node = node->CentralNode; + while (mark_node) + { + mark_node->HasCentralNodeChild = true; + mark_node = mark_node->ParentNode; + } +} + +static void DockNodeSetupHostWindow(ImGuiDockNode* node, ImGuiWindow* host_window) +{ + // Remove ourselves from any previous different host window + // This can happen if a user mistakenly does (see #4295 for details): + // - N+0: DockBuilderAddNode(id, 0) // missing ImGuiDockNodeFlags_DockSpace + // - N+1: NewFrame() // will create floating host window for that node + // - N+1: DockSpace(id) // requalify node as dockspace, moving host window + if (node->HostWindow && node->HostWindow != host_window && node->HostWindow->DockNodeAsHost == node) + node->HostWindow->DockNodeAsHost = NULL; + + host_window->DockNodeAsHost = node; + node->HostWindow = host_window; +} + +static void ImGui::DockNodeUpdate(ImGuiDockNode* node) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(node->LastFrameActive != g.FrameCount); + node->LastFrameAlive = g.FrameCount; + node->IsBgDrawnThisFrame = false; + + node->CentralNode = node->OnlyNodeWithWindows = NULL; + if (node->IsRootNode()) + DockNodeUpdateForRootNode(node); + + // Remove tab bar if not needed + if (node->TabBar && node->IsNoTabBar()) + DockNodeRemoveTabBar(node); + + // Early out for hidden root dock nodes (when all DockId references are in inactive windows, or there is only 1 floating window holding on the DockId) + bool want_to_hide_host_window = false; + if (node->IsFloatingNode()) + { + if (node->Windows.Size <= 1 && node->IsLeafNode()) + if (!g.IO.ConfigDockingAlwaysTabBar && (node->Windows.Size == 0 || !node->Windows[0]->WindowClass.DockingAlwaysTabBar)) + want_to_hide_host_window = true; + if (node->CountNodeWithWindows == 0) + want_to_hide_host_window = true; + } + if (want_to_hide_host_window) + { + if (node->Windows.Size == 1) + { + // Floating window pos/size is authoritative + ImGuiWindow* single_window = node->Windows[0]; + node->Pos = single_window->Pos; + node->Size = single_window->SizeFull; + node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window; + + // Transfer focus immediately so when we revert to a regular window it is immediately selected + if (node->HostWindow && g.NavWindow == node->HostWindow) + FocusWindow(single_window); + if (node->HostWindow) + { + IMGUI_DEBUG_LOG_VIEWPORT("[viewport] Node %08X transfer Viewport %08X->%08X to Window '%s'\n", node->ID, node->HostWindow->Viewport->ID, single_window->ID, single_window->Name); + single_window->Viewport = node->HostWindow->Viewport; + single_window->ViewportId = node->HostWindow->ViewportId; + if (node->HostWindow->ViewportOwned) + { + single_window->Viewport->ID = single_window->ID; + single_window->Viewport->Window = single_window; + single_window->ViewportOwned = true; + } + } + node->RefViewportId = single_window->ViewportId; + } + + DockNodeHideHostWindow(node); + node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow; + node->WantCloseAll = false; + node->WantCloseTabId = 0; + node->HasCloseButton = node->HasWindowMenuButton = false; + node->LastFrameActive = g.FrameCount; + + if (node->WantMouseMove && node->Windows.Size == 1) + DockNodeStartMouseMovingWindow(node, node->Windows[0]); + return; + } + + // In some circumstance we will defer creating the host window (so everything will be kept hidden), + // while the expected visible window is resizing itself. + // This is important for first-time (no ini settings restored) single window when io.ConfigDockingAlwaysTabBar is enabled, + // otherwise the node ends up using the minimum window size. Effectively those windows will take an extra frame to show up: + // N+0: Begin(): window created (with no known size), node is created + // N+1: DockNodeUpdate(): node skip creating host window / Begin(): window size applied, not visible + // N+2: DockNodeUpdate(): node can create host window / Begin(): window becomes visible + // We could remove this frame if we could reliably calculate the expected window size during node update, before the Begin() code. + // It would require a generalization of CalcWindowExpectedSize(), probably extracting code away from Begin(). + // In reality it isn't very important as user quickly ends up with size data in .ini file. + if (node->IsVisible && node->HostWindow == NULL && node->IsFloatingNode() && node->IsLeafNode()) + { + IM_ASSERT(node->Windows.Size > 0); + ImGuiWindow* ref_window = NULL; + if (node->SelectedTabId != 0) // Note that we prune single-window-node settings on .ini loading, so this is generally 0 for them! + ref_window = DockNodeFindWindowByID(node, node->SelectedTabId); + if (ref_window == NULL) + ref_window = node->Windows[0]; + if (ref_window->AutoFitFramesX > 0 || ref_window->AutoFitFramesY > 0) + { + node->State = ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing; + return; + } + } + + const ImGuiDockNodeFlags node_flags = node->MergedFlags; + + // Decide if the node will have a close button and a window menu button + node->HasWindowMenuButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0; + node->HasCloseButton = false; + for (ImGuiWindow* window : node->Windows) + { + // FIXME-DOCK: Setting DockIsActive here means that for single active window in a leaf node, DockIsActive will be cleared until the next Begin() call. + node->HasCloseButton |= window->HasCloseButton; + window->DockIsActive = (node->Windows.Size > 1); + } + if (node_flags & ImGuiDockNodeFlags_NoCloseButton) + node->HasCloseButton = false; + + // Bind or create host window + ImGuiWindow* host_window = NULL; + bool beginned_into_host_window = false; + if (node->IsDockSpace()) + { + // [Explicit root dockspace node] + IM_ASSERT(node->HostWindow); + host_window = node->HostWindow; + } + else + { + // [Automatic root or child nodes] + if (node->IsRootNode() && node->IsVisible) + { + ImGuiWindow* ref_window = (node->Windows.Size > 0) ? node->Windows[0] : NULL; + + // Sync Pos + if (node->AuthorityForPos == ImGuiDataAuthority_Window && ref_window) + SetNextWindowPos(ref_window->Pos); + else if (node->AuthorityForPos == ImGuiDataAuthority_DockNode) + SetNextWindowPos(node->Pos); + + // Sync Size + if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window) + SetNextWindowSize(ref_window->SizeFull); + else if (node->AuthorityForSize == ImGuiDataAuthority_DockNode) + SetNextWindowSize(node->Size); + + // Sync Collapsed + if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window) + SetNextWindowCollapsed(ref_window->Collapsed); + + // Sync Viewport + if (node->AuthorityForViewport == ImGuiDataAuthority_Window && ref_window) + SetNextWindowViewport(ref_window->ViewportId); + else if (node->AuthorityForViewport == ImGuiDataAuthority_Window && node->RefViewportId != 0) + SetNextWindowViewport(node->RefViewportId); + + SetNextWindowClass(&node->WindowClass); + + // Begin into the host window + char window_label[20]; + DockNodeGetHostWindowTitle(node, window_label, IM_ARRAYSIZE(window_label)); + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_DockNodeHost; + window_flags |= ImGuiWindowFlags_NoFocusOnAppearing; + window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoCollapse; + window_flags |= ImGuiWindowFlags_NoTitleBar; + + SetNextWindowBgAlpha(0.0f); // Don't set ImGuiWindowFlags_NoBackground because it disables borders + PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); + Begin(window_label, NULL, window_flags); + PopStyleVar(); + beginned_into_host_window = true; + + host_window = g.CurrentWindow; + DockNodeSetupHostWindow(node, host_window); + host_window->DC.CursorPos = host_window->Pos; + node->Pos = host_window->Pos; + node->Size = host_window->Size; + + // We set ImGuiWindowFlags_NoFocusOnAppearing because we don't want the host window to take full focus (e.g. steal NavWindow) + // But we still it bring it to the front of display. There's no way to choose this precise behavior via window flags. + // One simple case to ponder if: window A has a toggle to create windows B/C/D. Dock B/C/D together, clear the toggle and enable it again. + // When reappearing B/C/D will request focus and be moved to the top of the display pile, but they are not linked to the dock host window + // during the frame they appear. The dock host window would keep its old display order, and the sorting in EndFrame would move B/C/D back + // after the dock host window, losing their top-most status. + if (node->HostWindow->Appearing) + BringWindowToDisplayFront(node->HostWindow); + + node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto; + } + else if (node->ParentNode) + { + node->HostWindow = host_window = node->ParentNode->HostWindow; + node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto; + } + if (node->WantMouseMove && node->HostWindow) + DockNodeStartMouseMovingWindow(node, node->HostWindow); + } + node->RefViewportId = 0; // Clear when we have a host window + + // Update focused node (the one whose title bar is highlight) within a node tree + if (node->IsSplitNode()) + IM_ASSERT(node->TabBar == NULL); + if (node->IsRootNode()) + if (ImGuiWindow* p_window = g.NavWindow ? g.NavWindow->RootWindow : NULL) + while (p_window != NULL && p_window->DockNode != NULL) + { + ImGuiDockNode* p_node = DockNodeGetRootNode(p_window->DockNode); + if (p_node == node) + { + node->LastFocusedNodeId = p_window->DockNode->ID; // Note: not using root node ID! + break; + } + p_window = p_node->HostWindow ? p_node->HostWindow->RootWindow : NULL; + } + + // Register a hit-test hole in the window unless we are currently dragging a window that is compatible with our dockspace + ImGuiDockNode* central_node = node->CentralNode; + const bool central_node_hole = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0 && central_node != NULL && central_node->IsEmpty(); + bool central_node_hole_register_hit_test_hole = central_node_hole; + if (central_node_hole) + if (const ImGuiPayload* payload = ImGui::GetDragDropPayload()) + if (payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && DockNodeIsDropAllowed(host_window, *(ImGuiWindow**)payload->Data)) + central_node_hole_register_hit_test_hole = false; + if (central_node_hole_register_hit_test_hole) + { + // We add a little padding to match the "resize from edges" behavior and allow grabbing the splitter easily. + // (But we only add it if there's something else on the other side of the hole, otherwise for e.g. fullscreen + // covering passthru node we'd have a gap on the edge not covered by the hole) + IM_ASSERT(node->IsDockSpace()); // We cannot pass this flag without the DockSpace() api. Testing this because we also setup the hole in host_window->ParentNode + ImGuiDockNode* root_node = DockNodeGetRootNode(central_node); + ImRect root_rect(root_node->Pos, root_node->Pos + root_node->Size); + ImRect hole_rect(central_node->Pos, central_node->Pos + central_node->Size); + if (hole_rect.Min.x > root_rect.Min.x) { hole_rect.Min.x += WINDOWS_HOVER_PADDING; } + if (hole_rect.Max.x < root_rect.Max.x) { hole_rect.Max.x -= WINDOWS_HOVER_PADDING; } + if (hole_rect.Min.y > root_rect.Min.y) { hole_rect.Min.y += WINDOWS_HOVER_PADDING; } + if (hole_rect.Max.y < root_rect.Max.y) { hole_rect.Max.y -= WINDOWS_HOVER_PADDING; } + //GetForegroundDrawList()->AddRect(hole_rect.Min, hole_rect.Max, IM_COL32(255, 0, 0, 255)); + if (central_node_hole && !hole_rect.IsInverted()) + { + SetWindowHitTestHole(host_window, hole_rect.Min, hole_rect.Max - hole_rect.Min); + if (host_window->ParentWindow) + SetWindowHitTestHole(host_window->ParentWindow, hole_rect.Min, hole_rect.Max - hole_rect.Min); + } + } + + // Update position/size, process and draw resizing splitters + if (node->IsRootNode() && host_window) + { + DockNodeTreeUpdatePosSize(node, host_window->Pos, host_window->Size); + PushStyleColor(ImGuiCol_Separator, g.Style.Colors[ImGuiCol_Border]); + PushStyleColor(ImGuiCol_SeparatorActive, g.Style.Colors[ImGuiCol_ResizeGripActive]); + PushStyleColor(ImGuiCol_SeparatorHovered, g.Style.Colors[ImGuiCol_ResizeGripHovered]); + DockNodeTreeUpdateSplitter(node); + PopStyleColor(3); + } + + // Draw empty node background (currently can only be the Central Node) + if (host_window && node->IsEmpty() && node->IsVisible) + { + host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG); + node->LastBgColor = (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) ? 0 : GetColorU32(ImGuiCol_DockingEmptyBg); + if (node->LastBgColor != 0) + host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, node->LastBgColor); + node->IsBgDrawnThisFrame = true; + } + + // Draw whole dockspace background if ImGuiDockNodeFlags_PassthruCentralNode if set. + // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size + // _after_ processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order! + const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0; + if (render_dockspace_bg && node->IsVisible) + { + host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_BG); + if (central_node_hole) + RenderRectFilledWithHole(host_window->DrawList, node->Rect(), central_node->Rect(), GetColorU32(ImGuiCol_WindowBg), 0.0f); + else + host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_WindowBg), 0.0f); + } + + // Draw and populate Tab Bar + if (host_window) + host_window->DrawList->ChannelsSetCurrent(DOCKING_HOST_DRAW_CHANNEL_FG); + if (host_window && node->Windows.Size > 0) + { + DockNodeUpdateTabBar(node, host_window); + } + else + { + node->WantCloseAll = false; + node->WantCloseTabId = 0; + node->IsFocused = false; + } + if (node->TabBar && node->TabBar->SelectedTabId) + node->SelectedTabId = node->TabBar->SelectedTabId; + else if (node->Windows.Size > 0) + node->SelectedTabId = node->Windows[0]->TabId; + + // Draw payload drop target + if (host_window && node->IsVisible) + if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindowDockTree != host_window)) + BeginDockableDragDropTarget(host_window); + + // We update this after DockNodeUpdateTabBar() + node->LastFrameActive = g.FrameCount; + + // Recurse into children + // FIXME-DOCK FIXME-OPT: Should not need to recurse into children + if (host_window) + { + if (node->ChildNodes[0]) + DockNodeUpdate(node->ChildNodes[0]); + if (node->ChildNodes[1]) + DockNodeUpdate(node->ChildNodes[1]); + + // Render outer borders last (after the tab bar) + if (node->IsRootNode()) + RenderWindowOuterBorders(host_window); + } + + // End host window + if (beginned_into_host_window) //-V1020 + End(); +} + +// Compare TabItem nodes given the last known DockOrder (will persist in .ini file as hint), used to sort tabs when multiple tabs are added on the same frame. +static int IMGUI_CDECL TabItemComparerByDockOrder(const void* lhs, const void* rhs) +{ + ImGuiWindow* a = ((const ImGuiTabItem*)lhs)->Window; + ImGuiWindow* b = ((const ImGuiTabItem*)rhs)->Window; + if (int d = ((a->DockOrder == -1) ? INT_MAX : a->DockOrder) - ((b->DockOrder == -1) ? INT_MAX : b->DockOrder)) + return d; + return (a->BeginOrderWithinContext - b->BeginOrderWithinContext); +} + +// Default handler for g.DockNodeWindowMenuHandler(): display the list of windows for a given dock-node. +// This is exceptionally stored in a function pointer to also user applications to tweak this menu (undocumented) +// Custom overrides may want to decorate, group, sort entries. +// Please note those are internal structures: if you copy this expect occasional breakage. +// (if you don't need to modify the "Tabs.Size == 1" behavior/path it is recommend you call this function in your handler) +void ImGui::DockNodeWindowMenuHandler_Default(ImGuiContext* ctx, ImGuiDockNode* node, ImGuiTabBar* tab_bar) +{ + IM_UNUSED(ctx); + if (tab_bar->Tabs.Size == 1) + { + // "Hide tab bar" option. Being one of our rare user-facing string we pull it from a table. + if (MenuItem(LocalizeGetMsg(ImGuiLocKey_DockingHideTabBar), NULL, node->IsHiddenTabBar())) + node->WantHiddenTabBarToggle = true; + } + else + { + // Display a selectable list of windows in this docking node + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + if (tab->Flags & ImGuiTabItemFlags_Button) + continue; + if (Selectable(TabBarGetTabName(tab_bar, tab), tab->ID == tab_bar->SelectedTabId)) + TabBarQueueFocus(tab_bar, tab); + SameLine(); + Text(" "); + } + } +} + +static void ImGui::DockNodeWindowMenuUpdate(ImGuiDockNode* node, ImGuiTabBar* tab_bar) +{ + // Try to position the menu so it is more likely to stays within the same viewport + ImGuiContext& g = *GImGui; + if (g.Style.WindowMenuButtonPosition == ImGuiDir_Left) + SetNextWindowPos(ImVec2(node->Pos.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(0.0f, 0.0f)); + else + SetNextWindowPos(ImVec2(node->Pos.x + node->Size.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(1.0f, 0.0f)); + if (BeginPopup("#WindowMenu")) + { + node->IsFocused = true; + g.DockNodeWindowMenuHandler(&g, node, tab_bar); + EndPopup(); + } +} + +// User helper to append/amend into a dock node tab bar. Most commonly used to add e.g. a "+" button. +bool ImGui::DockNodeBeginAmendTabBar(ImGuiDockNode* node) +{ + if (node->TabBar == NULL || node->HostWindow == NULL) + return false; + if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly) + return false; + if (node->TabBar->ID == 0) + return false; + Begin(node->HostWindow->Name); + PushOverrideID(node->ID); + bool ret = BeginTabBarEx(node->TabBar, node->TabBar->BarRect, node->TabBar->Flags); + IM_UNUSED(ret); + IM_ASSERT(ret); + return true; +} + +void ImGui::DockNodeEndAmendTabBar() +{ + EndTabBar(); + PopID(); + End(); +} + +static bool IsDockNodeTitleBarHighlighted(ImGuiDockNode* node, ImGuiDockNode* root_node) +{ + // CTRL+Tab highlight (only highlighting leaf node, not whole hierarchy) + ImGuiContext& g = *GImGui; + if (g.NavWindowingTarget) + return (g.NavWindowingTarget->DockNode == node); + + // FIXME-DOCKING: May want alternative to treat central node void differently? e.g. if (g.NavWindow == host_window) + if (g.NavWindow && root_node->LastFocusedNodeId == node->ID) + { + // FIXME: This could all be backed in RootWindowForTitleBarHighlight? Probably need to reorganize for both dock nodes + other RootWindowForTitleBarHighlight users (not-node) + ImGuiWindow* parent_window = g.NavWindow->RootWindow; + while (parent_window->Flags & ImGuiWindowFlags_ChildMenu) + parent_window = parent_window->ParentWindow->RootWindow; + ImGuiDockNode* start_parent_node = parent_window->DockNodeAsHost ? parent_window->DockNodeAsHost : parent_window->DockNode; + for (ImGuiDockNode* parent_node = start_parent_node; parent_node != NULL; parent_node = parent_node->HostWindow ? parent_node->HostWindow->RootWindow->DockNode : NULL) + if ((parent_node = ImGui::DockNodeGetRootNode(parent_node)) == root_node) + return true; + } + return false; +} + +// Submit the tab bar corresponding to a dock node and various housekeeping details. +static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window) +{ + ImGuiContext& g = *GImGui; + ImGuiStyle& style = g.Style; + + const bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount); + const bool closed_all = node->WantCloseAll && node_was_active; + const ImGuiID closed_one = node->WantCloseTabId && node_was_active; + node->WantCloseAll = false; + node->WantCloseTabId = 0; + + // Decide if we should use a focused title bar color + bool is_focused = false; + ImGuiDockNode* root_node = DockNodeGetRootNode(node); + if (IsDockNodeTitleBarHighlighted(node, root_node)) + is_focused = true; + + // Hidden tab bar will show a triangle on the upper-left (in Begin) + if (node->IsHiddenTabBar() || node->IsNoTabBar()) + { + node->VisibleWindow = (node->Windows.Size > 0) ? node->Windows[0] : NULL; + node->IsFocused = is_focused; + if (is_focused) + node->LastFrameFocused = g.FrameCount; + if (node->VisibleWindow) + { + // Notify root of visible window (used to display title in OS task bar) + if (is_focused || root_node->VisibleWindow == NULL) + root_node->VisibleWindow = node->VisibleWindow; + if (node->TabBar) + node->TabBar->VisibleTabId = node->VisibleWindow->TabId; + } + return; + } + + // Move ourselves to the Menu layer (so we can be accessed by tapping Alt) + undo SkipItems flag in order to draw over the title bar even if the window is collapsed + bool backup_skip_item = host_window->SkipItems; + if (!node->IsDockSpace()) + { + host_window->SkipItems = false; + host_window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; + } + + // Use PushOverrideID() instead of PushID() to use the node id _without_ the host window ID. + // This is to facilitate computing those ID from the outside, and will affect more or less only the ID of the collapse button, popup and tabs, + // as docked windows themselves will override the stack with their own root ID. + PushOverrideID(node->ID); + ImGuiTabBar* tab_bar = node->TabBar; + bool tab_bar_is_recreated = (tab_bar == NULL); // Tab bar are automatically destroyed when a node gets hidden + if (tab_bar == NULL) + { + DockNodeAddTabBar(node); + tab_bar = node->TabBar; + } + + ImGuiID focus_tab_id = 0; + node->IsFocused = is_focused; + + const ImGuiDockNodeFlags node_flags = node->MergedFlags; + const bool has_window_menu_button = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0 && (style.WindowMenuButtonPosition != ImGuiDir_None); + + // In a dock node, the Collapse Button turns into the Window Menu button. + // FIXME-DOCK FIXME-OPT: Could we recycle popups id across multiple dock nodes? + if (has_window_menu_button && IsPopupOpen("#WindowMenu")) + { + ImGuiID next_selected_tab_id = tab_bar->NextSelectedTabId; + DockNodeWindowMenuUpdate(node, tab_bar); + if (tab_bar->NextSelectedTabId != 0 && tab_bar->NextSelectedTabId != next_selected_tab_id) + focus_tab_id = tab_bar->NextSelectedTabId; + is_focused |= node->IsFocused; + } + + // Layout + ImRect title_bar_rect, tab_bar_rect; + ImVec2 window_menu_button_pos; + ImVec2 close_button_pos; + DockNodeCalcTabBarLayout(node, &title_bar_rect, &tab_bar_rect, &window_menu_button_pos, &close_button_pos); + + // Submit new tabs, they will be added as Unsorted and sorted below based on relative DockOrder value. + const int tabs_count_old = tab_bar->Tabs.Size; + for (int window_n = 0; window_n < node->Windows.Size; window_n++) + { + ImGuiWindow* window = node->Windows[window_n]; + if (TabBarFindTabByID(tab_bar, window->TabId) == NULL) + TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window); + } + + // Title bar + if (is_focused) + node->LastFrameFocused = g.FrameCount; + ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); + ImDrawFlags rounding_flags = CalcRoundingFlagsForRectInRect(title_bar_rect, host_window->Rect(), g.Style.DockingSeparatorSize); + host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, rounding_flags); + + // Docking/Collapse button + if (has_window_menu_button) + { + if (CollapseButton(host_window->GetID("#COLLAPSE"), window_menu_button_pos, node)) // == DockNodeGetWindowMenuButtonId(node) + OpenPopup("#WindowMenu"); + if (IsItemActive()) + focus_tab_id = tab_bar->SelectedTabId; + if (IsItemHovered(ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_DelayNormal) && g.HoveredIdTimer > 0.5f) + SetTooltip("%s", LocalizeGetMsg(ImGuiLocKey_DockingDragToUndockOrMoveNode)); + } + + // If multiple tabs are appearing on the same frame, sort them based on their persistent DockOrder value + int tabs_unsorted_start = tab_bar->Tabs.Size; + for (int tab_n = tab_bar->Tabs.Size - 1; tab_n >= 0 && (tab_bar->Tabs[tab_n].Flags & ImGuiTabItemFlags_Unsorted); tab_n--) + { + // FIXME-DOCK: Consider only clearing the flag after the tab has been alive for a few consecutive frames, allowing late comers to not break sorting? + tab_bar->Tabs[tab_n].Flags &= ~ImGuiTabItemFlags_Unsorted; + tabs_unsorted_start = tab_n; + } + if (tab_bar->Tabs.Size > tabs_unsorted_start) + { + IMGUI_DEBUG_LOG_DOCKING("[docking] In node 0x%08X: %d new appearing tabs:%s\n", node->ID, tab_bar->Tabs.Size - tabs_unsorted_start, (tab_bar->Tabs.Size > tabs_unsorted_start + 1) ? " (will sort)" : ""); + for (int tab_n = tabs_unsorted_start; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + IM_UNUSED(tab); + IMGUI_DEBUG_LOG_DOCKING("[docking] - Tab 0x%08X '%s' Order %d\n", tab->ID, TabBarGetTabName(tab_bar, tab), tab->Window ? tab->Window->DockOrder : -1); + } + IMGUI_DEBUG_LOG_DOCKING("[docking] SelectedTabId = 0x%08X, NavWindow->TabId = 0x%08X\n", node->SelectedTabId, g.NavWindow ? g.NavWindow->TabId : -1); + if (tab_bar->Tabs.Size > tabs_unsorted_start + 1) + ImQsort(tab_bar->Tabs.Data + tabs_unsorted_start, tab_bar->Tabs.Size - tabs_unsorted_start, sizeof(ImGuiTabItem), TabItemComparerByDockOrder); + } + + // Apply NavWindow focus back to the tab bar + if (g.NavWindow && g.NavWindow->RootWindow->DockNode == node) + tab_bar->SelectedTabId = g.NavWindow->RootWindow->TabId; + + // Selected newly added tabs, or persistent tab ID if the tab bar was just recreated + if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabId) != NULL) + tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabId; + else if (tab_bar->Tabs.Size > tabs_count_old) + tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->TabId; + + // Begin tab bar + ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs; // | ImGuiTabBarFlags_NoTabListScrollingButtons); + tab_bar_flags |= ImGuiTabBarFlags_SaveSettings | ImGuiTabBarFlags_DockNode;// | ImGuiTabBarFlags_FittingPolicyScroll; + tab_bar_flags |= ImGuiTabBarFlags_DrawSelectedOverline; + if (!host_window->Collapsed && is_focused) + tab_bar_flags |= ImGuiTabBarFlags_IsFocused; + tab_bar->ID = GetID("#TabBar"); + tab_bar->SeparatorMinX = node->Pos.x + host_window->WindowBorderSize; // Separator cover the whole node width + tab_bar->SeparatorMaxX = node->Pos.x + node->Size.x - host_window->WindowBorderSize; + BeginTabBarEx(tab_bar, tab_bar_rect, tab_bar_flags); + //host_window->DrawList->AddRect(tab_bar_rect.Min, tab_bar_rect.Max, IM_COL32(255,0,255,255)); + + // Backup style colors + ImVec4 backup_style_cols[ImGuiWindowDockStyleCol_COUNT]; + for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++) + backup_style_cols[color_n] = g.Style.Colors[GWindowDockStyleColors[color_n]]; + + // Submit actual tabs + node->VisibleWindow = NULL; + for (int window_n = 0; window_n < node->Windows.Size; window_n++) + { + ImGuiWindow* window = node->Windows[window_n]; + if ((closed_all || closed_one == window->TabId) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument)) + continue; + if (window->LastFrameActive + 1 >= g.FrameCount || !node_was_active) + { + ImGuiTabItemFlags tab_item_flags = 0; + tab_item_flags |= window->WindowClass.TabItemFlagsOverrideSet; + if (window->Flags & ImGuiWindowFlags_UnsavedDocument) + tab_item_flags |= ImGuiTabItemFlags_UnsavedDocument; + if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) + tab_item_flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; + + // Apply stored style overrides for the window + for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++) + g.Style.Colors[GWindowDockStyleColors[color_n]] = ColorConvertU32ToFloat4(window->DockStyle.Colors[color_n]); + + // Note that TabItemEx() calls TabBarCalcTabID() so our tab item ID will ignore the current ID stack (rightly so) + bool tab_open = true; + TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window); + if (!tab_open) + node->WantCloseTabId = window->TabId; + if (tab_bar->VisibleTabId == window->TabId) + node->VisibleWindow = window; + + // Store last item data so it can be queried with IsItemXXX functions after the user Begin() call + window->DC.DockTabItemStatusFlags = g.LastItemData.StatusFlags; + window->DC.DockTabItemRect = g.LastItemData.Rect; + + // Update navigation ID on menu layer + if (g.NavWindow && g.NavWindow->RootWindow == window && (window->DC.NavLayersActiveMask & (1 << ImGuiNavLayer_Menu)) == 0) + host_window->NavLastIds[1] = window->TabId; + } + } + + // Restore style colors + for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++) + g.Style.Colors[GWindowDockStyleColors[color_n]] = backup_style_cols[color_n]; + + // Notify root of visible window (used to display title in OS task bar) + if (node->VisibleWindow) + if (is_focused || root_node->VisibleWindow == NULL) + root_node->VisibleWindow = node->VisibleWindow; + + // Close button (after VisibleWindow was updated) + // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->TabId may be != from tab_bar->SelectedTabId + const bool close_button_is_enabled = node->HasCloseButton && node->VisibleWindow && node->VisibleWindow->HasCloseButton; + const bool close_button_is_visible = node->HasCloseButton; + //const bool close_button_is_visible = close_button_is_enabled; // Most people would expect this behavior of not even showing the button (leaving a hole since we can't claim that space as other windows in the tba bar have one) + if (close_button_is_visible) + { + if (!close_button_is_enabled) + { + PushItemFlag(ImGuiItemFlags_Disabled, true); + PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_Text] * ImVec4(1.0f,1.0f,1.0f,0.4f)); + } + if (CloseButton(host_window->GetID("#CLOSE"), close_button_pos)) + { + node->WantCloseAll = true; + for (int n = 0; n < tab_bar->Tabs.Size; n++) + TabBarCloseTab(tab_bar, &tab_bar->Tabs[n]); + } + //if (IsItemActive()) + // focus_tab_id = tab_bar->SelectedTabId; + if (!close_button_is_enabled) + { + PopStyleColor(); + PopItemFlag(); + } + } + + // When clicking on the title bar outside of tabs, we still focus the selected tab for that node + // FIXME: TabItems submitted earlier use AllowItemOverlap so we manually perform a more specific test for now (hovered || held) in order to not cover them. + ImGuiID title_bar_id = host_window->GetID("#TITLEBAR"); + if (g.HoveredId == 0 || g.HoveredId == title_bar_id || g.ActiveId == title_bar_id) + { + // AllowOverlap mode required for appending into dock node tab bar, + // otherwise dragging window will steal HoveredId and amended tabs cannot get them. + bool held; + KeepAliveID(title_bar_id); + ButtonBehavior(title_bar_rect, title_bar_id, NULL, &held, ImGuiButtonFlags_AllowOverlap); + if (g.HoveredId == title_bar_id) + { + g.LastItemData.ID = title_bar_id; + } + if (held) + { + if (IsMouseClicked(0)) + focus_tab_id = tab_bar->SelectedTabId; + + // Forward moving request to selected window + if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId)) + StartMouseMovingWindowOrNode(tab->Window ? tab->Window : node->HostWindow, node, false); // Undock from tab bar empty space + } + } + + // Forward focus from host node to selected window + //if (is_focused && g.NavWindow == host_window && !g.NavWindowingTarget) + // focus_tab_id = tab_bar->SelectedTabId; + + // When clicked on a tab we requested focus to the docked child + // This overrides the value set by "forward focus from host node to selected window". + if (tab_bar->NextSelectedTabId) + focus_tab_id = tab_bar->NextSelectedTabId; + + // Apply navigation focus + if (focus_tab_id != 0) + if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id)) + if (tab->Window) + { + FocusWindow(tab->Window); + NavInitWindow(tab->Window, false); + } + + EndTabBar(); + PopID(); + + // Restore SkipItems flag + if (!node->IsDockSpace()) + { + host_window->DC.NavLayerCurrent = ImGuiNavLayer_Main; + host_window->SkipItems = backup_skip_item; + } +} + +static void ImGui::DockNodeAddTabBar(ImGuiDockNode* node) +{ + IM_ASSERT(node->TabBar == NULL); + node->TabBar = IM_NEW(ImGuiTabBar); +} + +static void ImGui::DockNodeRemoveTabBar(ImGuiDockNode* node) +{ + if (node->TabBar == NULL) + return; + IM_DELETE(node->TabBar); + node->TabBar = NULL; +} + +static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_window) +{ + if (host_window->DockNodeAsHost && host_window->DockNodeAsHost->IsDockSpace() && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext) + return false; + + ImGuiWindowClass* host_class = host_window->DockNodeAsHost ? &host_window->DockNodeAsHost->WindowClass : &host_window->WindowClass; + ImGuiWindowClass* payload_class = &payload->WindowClass; + if (host_class->ClassId != payload_class->ClassId) + { + bool pass = false; + if (host_class->ClassId != 0 && host_class->DockingAllowUnclassed && payload_class->ClassId == 0) + pass = true; + if (payload_class->ClassId != 0 && payload_class->DockingAllowUnclassed && host_class->ClassId == 0) + pass = true; + if (!pass) + return false; + } + + // Prevent docking any window created above a popup + // Technically we should support it (e.g. in the case of a long-lived modal window that had fancy docking features), + // by e.g. adding a 'if (!ImGui::IsWindowWithinBeginStackOf(host_window, popup_window))' test. + // But it would requires more work on our end because the dock host windows is technically created in NewFrame() + // and our ->ParentXXX and ->RootXXX pointers inside windows are currently mislading or lacking. + ImGuiContext& g = *GImGui; + for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--) + if (ImGuiWindow* popup_window = g.OpenPopupStack[i].Window) + if (ImGui::IsWindowWithinBeginStackOf(payload, popup_window)) // Payload is created from within a popup begin stack. + return false; + + return true; +} + +static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* root_payload) +{ + if (root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode()) // FIXME-DOCK: Missing filtering + return true; + + const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows.Size : 1; + for (int payload_n = 0; payload_n < payload_count; payload_n++) + { + ImGuiWindow* payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows[payload_n] : root_payload; + if (DockNodeIsDropAllowedOne(payload, host_window)) + return true; + } + return false; +} + +// window menu button == collapse button when not in a dock node. +// FIXME: This is similar to RenderWindowTitleBarContents(), may want to share code. +static void ImGui::DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos, ImVec2* out_close_button_pos) +{ + ImGuiContext& g = *GImGui; + ImGuiStyle& style = g.Style; + + ImRect r = ImRect(node->Pos.x, node->Pos.y, node->Pos.x + node->Size.x, node->Pos.y + g.FontSize + g.Style.FramePadding.y * 2.0f); + if (out_title_rect) { *out_title_rect = r; } + + r.Min.x += style.WindowBorderSize; + r.Max.x -= style.WindowBorderSize; + + float button_sz = g.FontSize; + r.Min.x += style.FramePadding.x; + r.Max.x -= style.FramePadding.x; + ImVec2 window_menu_button_pos = ImVec2(r.Min.x, r.Min.y + style.FramePadding.y); + if (node->HasCloseButton) + { + if (out_close_button_pos) *out_close_button_pos = ImVec2(r.Max.x - button_sz, r.Min.y + style.FramePadding.y); + r.Max.x -= button_sz + style.ItemInnerSpacing.x; + } + if (node->HasWindowMenuButton && style.WindowMenuButtonPosition == ImGuiDir_Left) + { + r.Min.x += button_sz + style.ItemInnerSpacing.x; + } + else if (node->HasWindowMenuButton && style.WindowMenuButtonPosition == ImGuiDir_Right) + { + window_menu_button_pos = ImVec2(r.Max.x - button_sz, r.Min.y + style.FramePadding.y); + r.Max.x -= button_sz + style.ItemInnerSpacing.x; + } + if (out_tab_bar_rect) { *out_tab_bar_rect = r; } + if (out_window_menu_button_pos) { *out_window_menu_button_pos = window_menu_button_pos; } +} + +void ImGui::DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired) +{ + ImGuiContext& g = *GImGui; + const float dock_spacing = g.Style.ItemInnerSpacing.x; + const ImGuiAxis axis = (dir == ImGuiDir_Left || dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; + pos_new[axis ^ 1] = pos_old[axis ^ 1]; + size_new[axis ^ 1] = size_old[axis ^ 1]; + + // Distribute size on given axis (with a desired size or equally) + const float w_avail = size_old[axis] - dock_spacing; + if (size_new_desired[axis] > 0.0f && size_new_desired[axis] <= w_avail * 0.5f) + { + size_new[axis] = size_new_desired[axis]; + size_old[axis] = IM_TRUNC(w_avail - size_new[axis]); + } + else + { + size_new[axis] = IM_TRUNC(w_avail * 0.5f); + size_old[axis] = IM_TRUNC(w_avail - size_new[axis]); + } + + // Position each node + if (dir == ImGuiDir_Right || dir == ImGuiDir_Down) + { + pos_new[axis] = pos_old[axis] + size_old[axis] + dock_spacing; + } + else if (dir == ImGuiDir_Left || dir == ImGuiDir_Up) + { + pos_new[axis] = pos_old[axis]; + pos_old[axis] = pos_new[axis] + size_new[axis] + dock_spacing; + } +} + +// Retrieve the drop rectangles for a given direction or for the center + perform hit testing. +bool ImGui::DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_r, bool outer_docking, ImVec2* test_mouse_pos) +{ + ImGuiContext& g = *GImGui; + + const float parent_smaller_axis = ImMin(parent.GetWidth(), parent.GetHeight()); + const float hs_for_central_nodes = ImMin(g.FontSize * 1.5f, ImMax(g.FontSize * 0.5f, parent_smaller_axis / 8.0f)); + float hs_w; // Half-size, longer axis + float hs_h; // Half-size, smaller axis + ImVec2 off; // Distance from edge or center + if (outer_docking) + { + //hs_w = ImTrunc(ImClamp(parent_smaller_axis - hs_for_central_nodes * 4.0f, g.FontSize * 0.5f, g.FontSize * 8.0f)); + //hs_h = ImTrunc(hs_w * 0.15f); + //off = ImVec2(ImTrunc(parent.GetWidth() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h), ImTrunc(parent.GetHeight() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h)); + hs_w = ImTrunc(hs_for_central_nodes * 1.50f); + hs_h = ImTrunc(hs_for_central_nodes * 0.80f); + off = ImTrunc(ImVec2(parent.GetWidth() * 0.5f - hs_h, parent.GetHeight() * 0.5f - hs_h)); + } + else + { + hs_w = ImTrunc(hs_for_central_nodes); + hs_h = ImTrunc(hs_for_central_nodes * 0.90f); + off = ImTrunc(ImVec2(hs_w * 2.40f, hs_w * 2.40f)); + } + + ImVec2 c = ImTrunc(parent.GetCenter()); + if (dir == ImGuiDir_None) { out_r = ImRect(c.x - hs_w, c.y - hs_w, c.x + hs_w, c.y + hs_w); } + else if (dir == ImGuiDir_Up) { out_r = ImRect(c.x - hs_w, c.y - off.y - hs_h, c.x + hs_w, c.y - off.y + hs_h); } + else if (dir == ImGuiDir_Down) { out_r = ImRect(c.x - hs_w, c.y + off.y - hs_h, c.x + hs_w, c.y + off.y + hs_h); } + else if (dir == ImGuiDir_Left) { out_r = ImRect(c.x - off.x - hs_h, c.y - hs_w, c.x - off.x + hs_h, c.y + hs_w); } + else if (dir == ImGuiDir_Right) { out_r = ImRect(c.x + off.x - hs_h, c.y - hs_w, c.x + off.x + hs_h, c.y + hs_w); } + + if (test_mouse_pos == NULL) + return false; + + ImRect hit_r = out_r; + if (!outer_docking) + { + // Custom hit testing for the 5-way selection, designed to reduce flickering when moving diagonally between sides + hit_r.Expand(ImTrunc(hs_w * 0.30f)); + ImVec2 mouse_delta = (*test_mouse_pos - c); + float mouse_delta_len2 = ImLengthSqr(mouse_delta); + float r_threshold_center = hs_w * 1.4f; + float r_threshold_sides = hs_w * (1.4f + 1.2f); + if (mouse_delta_len2 < r_threshold_center * r_threshold_center) + return (dir == ImGuiDir_None); + if (mouse_delta_len2 < r_threshold_sides * r_threshold_sides) + return (dir == ImGetDirQuadrantFromDelta(mouse_delta.x, mouse_delta.y)); + } + return hit_r.Contains(*test_mouse_pos); +} + +// host_node may be NULL if the window doesn't have a DockNode already. +// FIXME-DOCK: This is misnamed since it's also doing the filtering. +static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking) +{ + ImGuiContext& g = *GImGui; + + // There is an edge case when docking into a dockspace which only has inactive nodes. + // In this case DockNodeTreeFindNodeByPos() will have selected a leaf node which is inactive. + // Because the inactive leaf node doesn't have proper pos/size yet, we'll use the root node as reference. + if (payload_node == NULL) + payload_node = payload_window->DockNodeAsHost; + ImGuiDockNode* ref_node_for_rect = (host_node && !host_node->IsVisible) ? DockNodeGetRootNode(host_node) : host_node; + if (ref_node_for_rect) + IM_ASSERT(ref_node_for_rect->IsVisible == true); + + // Filter, figure out where we are allowed to dock + ImGuiDockNodeFlags src_node_flags = payload_node ? payload_node->MergedFlags : payload_window->WindowClass.DockNodeFlagsOverrideSet; + ImGuiDockNodeFlags dst_node_flags = host_node ? host_node->MergedFlags : host_window->WindowClass.DockNodeFlagsOverrideSet; + data->IsCenterAvailable = true; + if (is_outer_docking) + data->IsCenterAvailable = false; + else if (dst_node_flags & ImGuiDockNodeFlags_NoDockingOverMe) + data->IsCenterAvailable = false; + else if (host_node && (dst_node_flags & ImGuiDockNodeFlags_NoDockingOverCentralNode) && host_node->IsCentralNode()) + data->IsCenterAvailable = false; + else if ((!host_node || !host_node->IsEmpty()) && payload_node && payload_node->IsSplitNode() && (payload_node->OnlyNodeWithWindows == NULL)) // Is _visibly_ split? + data->IsCenterAvailable = false; + else if ((src_node_flags & ImGuiDockNodeFlags_NoDockingOverOther) && (!host_node || !host_node->IsEmpty())) + data->IsCenterAvailable = false; + else if ((src_node_flags & ImGuiDockNodeFlags_NoDockingOverEmpty) && host_node && host_node->IsEmpty()) + data->IsCenterAvailable = false; + + data->IsSidesAvailable = true; + if ((dst_node_flags & ImGuiDockNodeFlags_NoDockingSplit) || g.IO.ConfigDockingNoSplit) + data->IsSidesAvailable = false; + else if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode()) + data->IsSidesAvailable = false; + else if (src_node_flags & ImGuiDockNodeFlags_NoDockingSplitOther) + data->IsSidesAvailable = false; + + // Build a tentative future node (reuse same structure because it is practical. Shape will be readjusted when previewing a split) + data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (payload_window->HasCloseButton); + data->FutureNode.HasWindowMenuButton = host_node ? true : ((host_window->Flags & ImGuiWindowFlags_NoCollapse) == 0); + data->FutureNode.Pos = ref_node_for_rect ? ref_node_for_rect->Pos : host_window->Pos; + data->FutureNode.Size = ref_node_for_rect ? ref_node_for_rect->Size : host_window->Size; + + // Calculate drop shapes geometry for allowed splitting directions + IM_ASSERT(ImGuiDir_None == -1); + data->SplitNode = host_node; + data->SplitDir = ImGuiDir_None; + data->IsSplitDirExplicit = false; + if (!host_window->Collapsed) + for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++) + { + if (dir == ImGuiDir_None && !data->IsCenterAvailable) + continue; + if (dir != ImGuiDir_None && !data->IsSidesAvailable) + continue; + if (DockNodeCalcDropRectsAndTestMousePos(data->FutureNode.Rect(), (ImGuiDir)dir, data->DropRectsDraw[dir+1], is_outer_docking, &g.IO.MousePos)) + { + data->SplitDir = (ImGuiDir)dir; + data->IsSplitDirExplicit = true; + } + } + + // When docking without holding Shift, we only allow and preview docking when hovering over a drop rect or over the title bar + data->IsDropAllowed = (data->SplitDir != ImGuiDir_None) || (data->IsCenterAvailable); + if (!is_explicit_target && !data->IsSplitDirExplicit && !g.IO.ConfigDockingWithShift) + data->IsDropAllowed = false; + + // Calculate split area + data->SplitRatio = 0.0f; + if (data->SplitDir != ImGuiDir_None) + { + ImGuiDir split_dir = data->SplitDir; + ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; + ImVec2 pos_new, pos_old = data->FutureNode.Pos; + ImVec2 size_new, size_old = data->FutureNode.Size; + DockNodeCalcSplitRects(pos_old, size_old, pos_new, size_new, split_dir, payload_window->Size); + + // Calculate split ratio so we can pass it down the docking request + float split_ratio = ImSaturate(size_new[split_axis] / data->FutureNode.Size[split_axis]); + data->FutureNode.Pos = pos_new; + data->FutureNode.Size = size_new; + data->SplitRatio = (split_dir == ImGuiDir_Right || split_dir == ImGuiDir_Down) ? (1.0f - split_ratio) : (split_ratio); + } +} + +static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, const ImGuiDockPreviewData* data) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.CurrentWindow == host_window); // Because we rely on font size to calculate tab sizes + + // With this option, we only display the preview on the target viewport, and the payload viewport is made transparent. + // To compensate for the single layer obstructed by the payload, we'll increase the alpha of the preview nodes. + const bool is_transparent_payload = g.IO.ConfigDockingTransparentPayload; + + // In case the two windows involved are on different viewports, we will draw the overlay on each of them. + int overlay_draw_lists_count = 0; + ImDrawList* overlay_draw_lists[2]; + overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(host_window->Viewport); + if (host_window->Viewport != root_payload->Viewport && !is_transparent_payload) + overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(root_payload->Viewport); + + // Draw main preview rectangle + const ImU32 overlay_col_main = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.60f : 0.40f); + const ImU32 overlay_col_drop = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.90f : 0.70f); + const ImU32 overlay_col_drop_hovered = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 1.20f : 1.00f); + const ImU32 overlay_col_lines = GetColorU32(ImGuiCol_NavWindowingHighlight, is_transparent_payload ? 0.80f : 0.60f); + + // Display area preview + const bool can_preview_tabs = (root_payload->DockNodeAsHost == NULL || root_payload->DockNodeAsHost->Windows.Size > 0); + if (data->IsDropAllowed) + { + ImRect overlay_rect = data->FutureNode.Rect(); + if (data->SplitDir == ImGuiDir_None && can_preview_tabs) + overlay_rect.Min.y += GetFrameHeight(); + if (data->SplitDir != ImGuiDir_None || data->IsCenterAvailable) + for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++) + overlay_draw_lists[overlay_n]->AddRectFilled(overlay_rect.Min, overlay_rect.Max, overlay_col_main, host_window->WindowRounding, CalcRoundingFlagsForRectInRect(overlay_rect, host_window->Rect(), g.Style.DockingSeparatorSize)); + } + + // Display tab shape/label preview unless we are splitting node (it generally makes the situation harder to read) + if (data->IsDropAllowed && can_preview_tabs && data->SplitDir == ImGuiDir_None && data->IsCenterAvailable) + { + // Compute target tab bar geometry so we can locate our preview tabs + ImRect tab_bar_rect; + DockNodeCalcTabBarLayout(&data->FutureNode, NULL, &tab_bar_rect, NULL, NULL); + ImVec2 tab_pos = tab_bar_rect.Min; + if (host_node && host_node->TabBar) + { + if (!host_node->IsHiddenTabBar() && !host_node->IsNoTabBar()) + tab_pos.x += host_node->TabBar->WidthAllTabs + g.Style.ItemInnerSpacing.x; // We don't use OffsetNewTab because when using non-persistent-order tab bar it is incremented with each Tab submission. + else + tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_node->Windows[0]).x; + } + else if (!(host_window->Flags & ImGuiWindowFlags_DockNodeHost)) + { + tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_window).x; // Account for slight offset which will be added when changing from title bar to tab bar + } + + // Draw tab shape/label preview (payload may be a loose window or a host window carrying multiple tabbed windows) + if (root_payload->DockNodeAsHost) + IM_ASSERT(root_payload->DockNodeAsHost->Windows.Size <= root_payload->DockNodeAsHost->TabBar->Tabs.Size); + ImGuiTabBar* tab_bar_with_payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar : NULL; + const int payload_count = tab_bar_with_payload ? tab_bar_with_payload->Tabs.Size : 1; + for (int payload_n = 0; payload_n < payload_count; payload_n++) + { + // DockNode's TabBar may have non-window Tabs manually appended by user + ImGuiWindow* payload_window = tab_bar_with_payload ? tab_bar_with_payload->Tabs[payload_n].Window : root_payload; + if (tab_bar_with_payload && payload_window == NULL) + continue; + if (!DockNodeIsDropAllowedOne(payload_window, host_window)) + continue; + + // Calculate the tab bounding box for each payload window + ImVec2 tab_size = TabItemCalcSize(payload_window); + ImRect tab_bb(tab_pos.x, tab_pos.y, tab_pos.x + tab_size.x, tab_pos.y + tab_size.y); + tab_pos.x += tab_size.x + g.Style.ItemInnerSpacing.x; + const ImU32 overlay_col_text = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_Text]); + const ImU32 overlay_col_tabs = GetColorU32(payload_window->DockStyle.Colors[ImGuiWindowDockStyleCol_TabSelected]); + PushStyleColor(ImGuiCol_Text, overlay_col_text); + for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++) + { + ImGuiTabItemFlags tab_flags = (payload_window->Flags & ImGuiWindowFlags_UnsavedDocument) ? ImGuiTabItemFlags_UnsavedDocument : 0; + if (!tab_bar_rect.Contains(tab_bb)) + overlay_draw_lists[overlay_n]->PushClipRect(tab_bar_rect.Min, tab_bar_rect.Max); + TabItemBackground(overlay_draw_lists[overlay_n], tab_bb, tab_flags, overlay_col_tabs); + TabItemLabelAndCloseButton(overlay_draw_lists[overlay_n], tab_bb, tab_flags, g.Style.FramePadding, payload_window->Name, 0, 0, false, NULL, NULL); + if (!tab_bar_rect.Contains(tab_bb)) + overlay_draw_lists[overlay_n]->PopClipRect(); + } + PopStyleColor(); + } + } + + // Display drop boxes + const float overlay_rounding = ImMax(3.0f, g.Style.FrameRounding); + for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++) + { + if (!data->DropRectsDraw[dir + 1].IsInverted()) + { + ImRect draw_r = data->DropRectsDraw[dir + 1]; + ImRect draw_r_in = draw_r; + draw_r_in.Expand(-2.0f); + ImU32 overlay_col = (data->SplitDir == (ImGuiDir)dir && data->IsSplitDirExplicit) ? overlay_col_drop_hovered : overlay_col_drop; + for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++) + { + ImVec2 center = ImFloor(draw_r_in.GetCenter()); + overlay_draw_lists[overlay_n]->AddRectFilled(draw_r.Min, draw_r.Max, overlay_col, overlay_rounding); + overlay_draw_lists[overlay_n]->AddRect(draw_r_in.Min, draw_r_in.Max, overlay_col_lines, overlay_rounding); + if (dir == ImGuiDir_Left || dir == ImGuiDir_Right) + overlay_draw_lists[overlay_n]->AddLine(ImVec2(center.x, draw_r_in.Min.y), ImVec2(center.x, draw_r_in.Max.y), overlay_col_lines); + if (dir == ImGuiDir_Up || dir == ImGuiDir_Down) + overlay_draw_lists[overlay_n]->AddLine(ImVec2(draw_r_in.Min.x, center.y), ImVec2(draw_r_in.Max.x, center.y), overlay_col_lines); + } + } + + // Stop after ImGuiDir_None + if ((host_node && (host_node->MergedFlags & ImGuiDockNodeFlags_NoDockingSplit)) || g.IO.ConfigDockingNoSplit) + return; + } +} + +//----------------------------------------------------------------------------- +// Docking: ImGuiDockNode Tree manipulation functions +//----------------------------------------------------------------------------- +// - DockNodeTreeSplit() +// - DockNodeTreeMerge() +// - DockNodeTreeUpdatePosSize() +// - DockNodeTreeUpdateSplitterFindTouchingNode() +// - DockNodeTreeUpdateSplitter() +// - DockNodeTreeFindFallbackLeafNode() +// - DockNodeTreeFindNodeByPos() +//----------------------------------------------------------------------------- + +void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_inheritor_child_idx, float split_ratio, ImGuiDockNode* new_node) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(split_axis != ImGuiAxis_None); + + ImGuiDockNode* child_0 = (new_node && split_inheritor_child_idx != 0) ? new_node : DockContextAddNode(ctx, 0); + child_0->ParentNode = parent_node; + + ImGuiDockNode* child_1 = (new_node && split_inheritor_child_idx != 1) ? new_node : DockContextAddNode(ctx, 0); + child_1->ParentNode = parent_node; + + ImGuiDockNode* child_inheritor = (split_inheritor_child_idx == 0) ? child_0 : child_1; + DockNodeMoveChildNodes(child_inheritor, parent_node); + parent_node->ChildNodes[0] = child_0; + parent_node->ChildNodes[1] = child_1; + parent_node->ChildNodes[split_inheritor_child_idx]->VisibleWindow = parent_node->VisibleWindow; + parent_node->SplitAxis = split_axis; + parent_node->VisibleWindow = NULL; + parent_node->AuthorityForPos = parent_node->AuthorityForSize = ImGuiDataAuthority_DockNode; + + float size_avail = (parent_node->Size[split_axis] - g.Style.DockingSeparatorSize); + size_avail = ImMax(size_avail, g.Style.WindowMinSize[split_axis] * 2.0f); + IM_ASSERT(size_avail > 0.0f); // If you created a node manually with DockBuilderAddNode(), you need to also call DockBuilderSetNodeSize() before splitting. + child_0->SizeRef = child_1->SizeRef = parent_node->Size; + child_0->SizeRef[split_axis] = ImTrunc(size_avail * split_ratio); + child_1->SizeRef[split_axis] = ImTrunc(size_avail - child_0->SizeRef[split_axis]); + + DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node); + DockSettingsRenameNodeReferences(parent_node->ID, parent_node->ChildNodes[split_inheritor_child_idx]->ID); + DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(parent_node)); + DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size); + + // Flags transfer (e.g. this is where we transfer the ImGuiDockNodeFlags_CentralNode property) + child_0->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_; + child_1->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_; + child_inheritor->LocalFlags = parent_node->LocalFlags & ImGuiDockNodeFlags_LocalFlagsTransferMask_; + parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; + child_0->UpdateMergedFlags(); + child_1->UpdateMergedFlags(); + parent_node->UpdateMergedFlags(); + if (child_inheritor->IsCentralNode()) + DockNodeGetRootNode(parent_node)->CentralNode = child_inheritor; +} + +void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child) +{ + // When called from DockContextProcessUndockNode() it is possible that one of the child is NULL. + ImGuiContext& g = *GImGui; + ImGuiDockNode* child_0 = parent_node->ChildNodes[0]; + ImGuiDockNode* child_1 = parent_node->ChildNodes[1]; + IM_ASSERT(child_0 || child_1); + IM_ASSERT(merge_lead_child == child_0 || merge_lead_child == child_1); + if ((child_0 && child_0->Windows.Size > 0) || (child_1 && child_1->Windows.Size > 0)) + { + IM_ASSERT(parent_node->TabBar == NULL); + IM_ASSERT(parent_node->Windows.Size == 0); + } + IMGUI_DEBUG_LOG_DOCKING("[docking] DockNodeTreeMerge: 0x%08X + 0x%08X back into parent 0x%08X\n", child_0 ? child_0->ID : 0, child_1 ? child_1->ID : 0, parent_node->ID); + + ImVec2 backup_last_explicit_size = parent_node->SizeRef; + DockNodeMoveChildNodes(parent_node, merge_lead_child); + if (child_0) + { + DockNodeMoveWindows(parent_node, child_0); // Generally only 1 of the 2 child node will have windows + DockSettingsRenameNodeReferences(child_0->ID, parent_node->ID); + } + if (child_1) + { + DockNodeMoveWindows(parent_node, child_1); + DockSettingsRenameNodeReferences(child_1->ID, parent_node->ID); + } + DockNodeApplyPosSizeToWindows(parent_node); + parent_node->AuthorityForPos = parent_node->AuthorityForSize = parent_node->AuthorityForViewport = ImGuiDataAuthority_Auto; + parent_node->VisibleWindow = merge_lead_child->VisibleWindow; + parent_node->SizeRef = backup_last_explicit_size; + + // Flags transfer + parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; // Preserve Dockspace flag + parent_node->LocalFlags |= (child_0 ? child_0->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_; + parent_node->LocalFlags |= (child_1 ? child_1->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_; + parent_node->LocalFlagsInWindows = (child_0 ? child_0->LocalFlagsInWindows : 0) | (child_1 ? child_1->LocalFlagsInWindows : 0); // FIXME: Would be more consistent to update from actual windows + parent_node->UpdateMergedFlags(); + + if (child_0) + { + ctx->DockContext.Nodes.SetVoidPtr(child_0->ID, NULL); + IM_DELETE(child_0); + } + if (child_1) + { + ctx->DockContext.Nodes.SetVoidPtr(child_1->ID, NULL); + IM_DELETE(child_1); + } +} + +// Update Pos/Size for a node hierarchy (don't affect child Windows yet) +// (Depth-first, Pre-Order) +void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, ImGuiDockNode* only_write_to_single_node) +{ + // During the regular dock node update we write to all nodes. + // 'only_write_to_single_node' is only set when turning a node visible mid-frame and we need its size right-away. + ImGuiContext& g = *GImGui; + const bool write_to_node = only_write_to_single_node == NULL || only_write_to_single_node == node; + if (write_to_node) + { + node->Pos = pos; + node->Size = size; + } + + if (node->IsLeafNode()) + return; + + ImGuiDockNode* child_0 = node->ChildNodes[0]; + ImGuiDockNode* child_1 = node->ChildNodes[1]; + ImVec2 child_0_pos = pos, child_1_pos = pos; + ImVec2 child_0_size = size, child_1_size = size; + + const bool child_0_is_toward_single_node = (only_write_to_single_node != NULL && DockNodeIsInHierarchyOf(only_write_to_single_node, child_0)); + const bool child_1_is_toward_single_node = (only_write_to_single_node != NULL && DockNodeIsInHierarchyOf(only_write_to_single_node, child_1)); + const bool child_0_is_or_will_be_visible = child_0->IsVisible || child_0_is_toward_single_node; + const bool child_1_is_or_will_be_visible = child_1->IsVisible || child_1_is_toward_single_node; + + if (child_0_is_or_will_be_visible && child_1_is_or_will_be_visible) + { + const float spacing = g.Style.DockingSeparatorSize; + const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis; + const float size_avail = ImMax(size[axis] - spacing, 0.0f); + + // Size allocation policy + // 1) The first 0..WindowMinSize[axis]*2 are allocated evenly to both windows. + const float size_min_each = ImTrunc(ImMin(size_avail, g.Style.WindowMinSize[axis] * 2.0f) * 0.5f); + + // FIXME: Blocks 2) and 3) are essentially doing nearly the same thing. + // Difference are: write-back to SizeRef; application of a minimum size; rounding before ImTrunc() + // Clarify and rework differences between Size & SizeRef and purpose of WantLockSizeOnce + + // 2) Process locked absolute size (during a splitter resize we preserve the child of nodes not touching the splitter edge) + if (child_0->WantLockSizeOnce && !child_1->WantLockSizeOnce) + { + child_0_size[axis] = child_0->SizeRef[axis] = ImMin(size_avail - 1.0f, child_0->Size[axis]); + child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]); + IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f); + } + else if (child_1->WantLockSizeOnce && !child_0->WantLockSizeOnce) + { + child_1_size[axis] = child_1->SizeRef[axis] = ImMin(size_avail - 1.0f, child_1->Size[axis]); + child_0_size[axis] = child_0->SizeRef[axis] = (size_avail - child_1_size[axis]); + IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f); + } + else if (child_0->WantLockSizeOnce && child_1->WantLockSizeOnce) + { + // FIXME-DOCK: We cannot honor the requested size, so apply ratio. + // Currently this path will only be taken if code programmatically sets WantLockSizeOnce + float split_ratio = child_0_size[axis] / (child_0_size[axis] + child_1_size[axis]); + child_0_size[axis] = child_0->SizeRef[axis] = ImTrunc(size_avail * split_ratio); + child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]); + IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f); + } + + // 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node + else if (child_0->SizeRef[axis] != 0.0f && child_1->HasCentralNodeChild) + { + child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]); + child_1_size[axis] = (size_avail - child_0_size[axis]); + } + else if (child_1->SizeRef[axis] != 0.0f && child_0->HasCentralNodeChild) + { + child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]); + child_0_size[axis] = (size_avail - child_1_size[axis]); + } + else + { + // 4) Otherwise distribute according to the relative ratio of each SizeRef value + float split_ratio = child_0->SizeRef[axis] / (child_0->SizeRef[axis] + child_1->SizeRef[axis]); + child_0_size[axis] = ImMax(size_min_each, ImTrunc(size_avail * split_ratio + 0.5f)); + child_1_size[axis] = (size_avail - child_0_size[axis]); + } + + child_1_pos[axis] += spacing + child_0_size[axis]; + } + + if (only_write_to_single_node == NULL) + child_0->WantLockSizeOnce = child_1->WantLockSizeOnce = false; + + const bool child_0_recurse = only_write_to_single_node ? child_0_is_toward_single_node : child_0->IsVisible; + const bool child_1_recurse = only_write_to_single_node ? child_1_is_toward_single_node : child_1->IsVisible; + if (child_0_recurse) + DockNodeTreeUpdatePosSize(child_0, child_0_pos, child_0_size); + if (child_1_recurse) + DockNodeTreeUpdatePosSize(child_1, child_1_pos, child_1_size); +} + +static void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGuiAxis axis, int side, ImVector* touching_nodes) +{ + if (node->IsLeafNode()) + { + touching_nodes->push_back(node); + return; + } + if (node->ChildNodes[0]->IsVisible) + if (node->SplitAxis != axis || side == 0 || !node->ChildNodes[1]->IsVisible) + DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[0], axis, side, touching_nodes); + if (node->ChildNodes[1]->IsVisible) + if (node->SplitAxis != axis || side == 1 || !node->ChildNodes[0]->IsVisible) + DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[1], axis, side, touching_nodes); +} + +// (Depth-First, Pre-Order) +void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) +{ + if (node->IsLeafNode()) + return; + + ImGuiContext& g = *GImGui; + + ImGuiDockNode* child_0 = node->ChildNodes[0]; + ImGuiDockNode* child_1 = node->ChildNodes[1]; + if (child_0->IsVisible && child_1->IsVisible) + { + // Bounding box of the splitter cover the space between both nodes (w = Spacing, h = Size[xy^1] for when splitting horizontally) + const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis; + IM_ASSERT(axis != ImGuiAxis_None); + ImRect bb; + bb.Min = child_0->Pos; + bb.Max = child_1->Pos; + bb.Min[axis] += child_0->Size[axis]; + bb.Max[axis ^ 1] += child_1->Size[axis ^ 1]; + //if (g.IO.KeyCtrl) GetForegroundDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255)); + + const ImGuiDockNodeFlags merged_flags = child_0->MergedFlags | child_1->MergedFlags; // Merged flags for BOTH childs + const ImGuiDockNodeFlags no_resize_axis_flag = (axis == ImGuiAxis_X) ? ImGuiDockNodeFlags_NoResizeX : ImGuiDockNodeFlags_NoResizeY; + if ((merged_flags & ImGuiDockNodeFlags_NoResize) || (merged_flags & no_resize_axis_flag)) + { + ImGuiWindow* window = g.CurrentWindow; + window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator), g.Style.FrameRounding); + } + else + { + //bb.Min[axis] += 1; // Display a little inward so highlight doesn't connect with nearby tabs on the neighbor node. + //bb.Max[axis] -= 1; + PushID(node->ID); + + // Find resizing limits by gathering list of nodes that are touching the splitter line. + ImVector touching_nodes[2]; + float min_size = g.Style.WindowMinSize[axis]; + float resize_limits[2]; + resize_limits[0] = node->ChildNodes[0]->Pos[axis] + min_size; + resize_limits[1] = node->ChildNodes[1]->Pos[axis] + node->ChildNodes[1]->Size[axis] - min_size; + + ImGuiID splitter_id = GetID("##Splitter"); + if (g.ActiveId == splitter_id) // Only process when splitter is active + { + DockNodeTreeUpdateSplitterFindTouchingNode(child_0, axis, 1, &touching_nodes[0]); + DockNodeTreeUpdateSplitterFindTouchingNode(child_1, axis, 0, &touching_nodes[1]); + for (int touching_node_n = 0; touching_node_n < touching_nodes[0].Size; touching_node_n++) + resize_limits[0] = ImMax(resize_limits[0], touching_nodes[0][touching_node_n]->Rect().Min[axis] + min_size); + for (int touching_node_n = 0; touching_node_n < touching_nodes[1].Size; touching_node_n++) + resize_limits[1] = ImMin(resize_limits[1], touching_nodes[1][touching_node_n]->Rect().Max[axis] - min_size); + + // [DEBUG] Render touching nodes & limits + /* + ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport()); + for (int n = 0; n < 2; n++) + { + for (int touching_node_n = 0; touching_node_n < touching_nodes[n].Size; touching_node_n++) + draw_list->AddRect(touching_nodes[n][touching_node_n]->Pos, touching_nodes[n][touching_node_n]->Pos + touching_nodes[n][touching_node_n]->Size, IM_COL32(0, 255, 0, 255)); + if (axis == ImGuiAxis_X) + draw_list->AddLine(ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y), ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y + node->ChildNodes[n]->Size.y), IM_COL32(255, 0, 255, 255), 3.0f); + else + draw_list->AddLine(ImVec2(node->ChildNodes[n]->Pos.x, resize_limits[n]), ImVec2(node->ChildNodes[n]->Pos.x + node->ChildNodes[n]->Size.x, resize_limits[n]), IM_COL32(255, 0, 255, 255), 3.0f); + } + */ + } + + // Use a short delay before highlighting the splitter (and changing the mouse cursor) in order for regular mouse movement to not highlight many splitters + float cur_size_0 = child_0->Size[axis]; + float cur_size_1 = child_1->Size[axis]; + float min_size_0 = resize_limits[0] - child_0->Pos[axis]; + float min_size_1 = child_1->Pos[axis] + child_1->Size[axis] - resize_limits[1]; + ImU32 bg_col = GetColorU32(ImGuiCol_WindowBg); + if (SplitterBehavior(bb, GetID("##Splitter"), axis, &cur_size_0, &cur_size_1, min_size_0, min_size_1, WINDOWS_HOVER_PADDING, WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER, bg_col)) + { + if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0) + { + child_0->Size[axis] = child_0->SizeRef[axis] = cur_size_0; + child_1->Pos[axis] -= cur_size_1 - child_1->Size[axis]; + child_1->Size[axis] = child_1->SizeRef[axis] = cur_size_1; + + // Lock the size of every node that is a sibling of the node we are touching + // This might be less desirable if we can merge sibling of a same axis into the same parental level. + for (int side_n = 0; side_n < 2; side_n++) + for (int touching_node_n = 0; touching_node_n < touching_nodes[side_n].Size; touching_node_n++) + { + ImGuiDockNode* touching_node = touching_nodes[side_n][touching_node_n]; + //ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport()); + //draw_list->AddRect(touching_node->Pos, touching_node->Pos + touching_node->Size, IM_COL32(255, 128, 0, 255)); + while (touching_node->ParentNode != node) + { + if (touching_node->ParentNode->SplitAxis == axis) + { + // Mark other node so its size will be preserved during the upcoming call to DockNodeTreeUpdatePosSize(). + ImGuiDockNode* node_to_preserve = touching_node->ParentNode->ChildNodes[side_n]; + node_to_preserve->WantLockSizeOnce = true; + //draw_list->AddRect(touching_node->Pos, touching_node->Rect().Max, IM_COL32(255, 0, 0, 255)); + //draw_list->AddRectFilled(node_to_preserve->Pos, node_to_preserve->Rect().Max, IM_COL32(0, 255, 0, 100)); + } + touching_node = touching_node->ParentNode; + } + } + + DockNodeTreeUpdatePosSize(child_0, child_0->Pos, child_0->Size); + DockNodeTreeUpdatePosSize(child_1, child_1->Pos, child_1->Size); + MarkIniSettingsDirty(); + } + } + PopID(); + } + } + + if (child_0->IsVisible) + DockNodeTreeUpdateSplitter(child_0); + if (child_1->IsVisible) + DockNodeTreeUpdateSplitter(child_1); +} + +ImGuiDockNode* ImGui::DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node) +{ + if (node->IsLeafNode()) + return node; + if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[0])) + return leaf_node; + if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[1])) + return leaf_node; + return NULL; +} + +ImGuiDockNode* ImGui::DockNodeTreeFindVisibleNodeByPos(ImGuiDockNode* node, ImVec2 pos) +{ + if (!node->IsVisible) + return NULL; + + const float dock_spacing = 0.0f;// g.Style.ItemInnerSpacing.x; // FIXME: Relation to DOCKING_SPLITTER_SIZE? + ImRect r(node->Pos, node->Pos + node->Size); + r.Expand(dock_spacing * 0.5f); + bool inside = r.Contains(pos); + if (!inside) + return NULL; + + if (node->IsLeafNode()) + return node; + if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[0], pos)) + return hovered_node; + if (ImGuiDockNode* hovered_node = DockNodeTreeFindVisibleNodeByPos(node->ChildNodes[1], pos)) + return hovered_node; + + // This means we are hovering over the splitter/spacing of a parent node + return node; +} + +//----------------------------------------------------------------------------- +// Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport) +//----------------------------------------------------------------------------- +// - SetWindowDock() [Internal] +// - DockSpace() +// - DockSpaceOverViewport() +//----------------------------------------------------------------------------- + +// [Internal] Called via SetNextWindowDockID() +void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) +{ + // Test condition (NB: bit 0 is always true) and clear flags for next time + if (cond && (window->SetWindowDockAllowFlags & cond) == 0) + return; + window->SetWindowDockAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); + + if (window->DockId == dock_id) + return; + + // If the user attempt to set a dock id that is a split node, we'll dig within to find a suitable docking spot + ImGuiContext& g = *GImGui; + if (ImGuiDockNode* new_node = DockContextFindNodeByID(&g, dock_id)) + if (new_node->IsSplitNode()) + { + // Policy: Find central node or latest focused node. We first move back to our root node. + new_node = DockNodeGetRootNode(new_node); + if (new_node->CentralNode) + { + IM_ASSERT(new_node->CentralNode->IsCentralNode()); + dock_id = new_node->CentralNode->ID; + } + else + { + dock_id = new_node->LastFocusedNodeId; + } + } + + if (window->DockId == dock_id) + return; + + if (window->DockNode) + DockNodeRemoveWindow(window->DockNode, window, 0); + window->DockId = dock_id; +} + +// Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a CentralNode by default. +// The Central Node is always displayed even when empty and shrink/extend according to the requested size of its neighbors. +// DockSpace() needs to be submitted _before_ any window they can host. If you use a dockspace, submit it early in your app. +// When ImGuiDockNodeFlags_KeepAliveOnly is set, nothing is submitted in the current window (function may be called from any location). +ImGuiID ImGui::DockSpace(ImGuiID dockspace_id, const ImVec2& size_arg, ImGuiDockNodeFlags flags, const ImGuiWindowClass* window_class) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindowRead(); + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) + return 0; + + // Early out if parent window is hidden/collapsed + // This is faster but also DockNodeUpdateTabBar() relies on TabBarLayout() running (which won't if SkipItems=true) to set NextSelectedTabId = 0). See #2960. + // If for whichever reason this is causing problem we would need to ensure that DockNodeUpdateTabBar() ends up clearing NextSelectedTabId even if SkipItems=true. + if (window->SkipItems) + flags |= ImGuiDockNodeFlags_KeepAliveOnly; + if ((flags & ImGuiDockNodeFlags_KeepAliveOnly) == 0) + window = GetCurrentWindow(); // call to set window->WriteAccessed = true; + + IM_ASSERT((flags & ImGuiDockNodeFlags_DockSpace) == 0); // Flag is automatically set by DockSpace() as LocalFlags, not SharedFlags! + IM_ASSERT((flags & ImGuiDockNodeFlags_CentralNode) == 0); // Flag is automatically set by DockSpace() as LocalFlags, not SharedFlags! (#8145) + + IM_ASSERT(dockspace_id != 0); + ImGuiDockNode* node = DockContextFindNodeByID(&g, dockspace_id); + if (node == NULL) + { + IMGUI_DEBUG_LOG_DOCKING("[docking] DockSpace: dockspace node 0x%08X created\n", dockspace_id); + node = DockContextAddNode(&g, dockspace_id); + node->SetLocalFlags(ImGuiDockNodeFlags_CentralNode); + } + if (window_class && window_class->ClassId != node->WindowClass.ClassId) + IMGUI_DEBUG_LOG_DOCKING("[docking] DockSpace: dockspace node 0x%08X: setup WindowClass 0x%08X -> 0x%08X\n", dockspace_id, node->WindowClass.ClassId, window_class->ClassId); + node->SharedFlags = flags; + node->WindowClass = window_class ? *window_class : ImGuiWindowClass(); + + // When a DockSpace transitioned form implicit to explicit this may be called a second time + // It is possible that the node has already been claimed by a docked window which appeared before the DockSpace() node, so we overwrite IsDockSpace again. + if (node->LastFrameActive == g.FrameCount && !(flags & ImGuiDockNodeFlags_KeepAliveOnly)) + { + IM_ASSERT(node->IsDockSpace() == false && "Cannot call DockSpace() twice a frame with the same ID"); + node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_DockSpace); + return dockspace_id; + } + node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_DockSpace); + + // Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible + if (flags & ImGuiDockNodeFlags_KeepAliveOnly) + { + node->LastFrameAlive = g.FrameCount; + return dockspace_id; + } + + const ImVec2 content_avail = GetContentRegionAvail(); + ImVec2 size = ImTrunc(size_arg); + if (size.x <= 0.0f) + size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues) + if (size.y <= 0.0f) + size.y = ImMax(content_avail.y + size.y, 4.0f); + IM_ASSERT(size.x > 0.0f && size.y > 0.0f); + + node->Pos = window->DC.CursorPos; + node->Size = node->SizeRef = size; + SetNextWindowPos(node->Pos); + SetNextWindowSize(node->Size); + g.NextWindowData.PosUndock = false; + + // FIXME-DOCK: Why do we need a child window to host a dockspace, could we host it in the existing window? + // FIXME-DOCK: What is the reason for not simply calling BeginChild()? (OK to have a reason but should be commented) + ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_DockNodeHost; + window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar; + window_flags |= ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; + window_flags |= ImGuiWindowFlags_NoBackground; + + char title[256]; + ImFormatString(title, IM_ARRAYSIZE(title), "%s/DockSpace_%08X", window->Name, dockspace_id); + + PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f); + Begin(title, NULL, window_flags); + PopStyleVar(); + + ImGuiWindow* host_window = g.CurrentWindow; + DockNodeSetupHostWindow(node, host_window); + host_window->ChildId = window->GetID(title); + node->OnlyNodeWithWindows = NULL; + + IM_ASSERT(node->IsRootNode()); + + // We need to handle the rare case were a central node is missing. + // This can happen if the node was first created manually with DockBuilderAddNode() but _without_ the ImGuiDockNodeFlags_Dockspace. + // Doing it correctly would set the _CentralNode flags, which would then propagate according to subsequent split. + // It would also be ambiguous to attempt to assign a central node while there are split nodes, so we wait until there's a single node remaining. + // The specific sub-property of _CentralNode we are interested in recovering here is the "Don't delete when empty" property, + // as it doesn't make sense for an empty dockspace to not have this property. + if (node->IsLeafNode() && !node->IsCentralNode()) + node->SetLocalFlags(node->LocalFlags | ImGuiDockNodeFlags_CentralNode); + + // Update the node + DockNodeUpdate(node); + + End(); + + ImRect bb(node->Pos, node->Pos + size); + ItemSize(size); + ItemAdd(bb, dockspace_id, NULL, ImGuiItemFlags_NoNav); // Not a nav point (could be, would need to draw the nav rect and replicate/refactor activation from BeginChild(), but seems like CTRL+Tab works better here?) + if ((g.LastItemData.StatusFlags & ImGuiItemStatusFlags_HoveredRect) && IsWindowChildOf(g.HoveredWindow, host_window, false, true)) // To fullfill IsItemHovered(), similar to EndChild() + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; + + return dockspace_id; +} + +// Tips: Use with ImGuiDockNodeFlags_PassthruCentralNode! +// The limitation with this call is that your window won't have a local menu bar, but you can also use BeginMainMenuBar(). +// Even though we could pass window flags, it would also require the user to be able to call BeginMenuBar() somehow meaning we can't Begin/End in a single function. +// If you really want a menu bar inside the same window as the one hosting the dockspace, you will need to copy this code somewhere and tweak it. +ImGuiID ImGui::DockSpaceOverViewport(ImGuiID dockspace_id, const ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags, const ImGuiWindowClass* window_class) +{ + if (viewport == NULL) + viewport = GetMainViewport(); + + // Submit a window filling the entire viewport + SetNextWindowPos(viewport->WorkPos); + SetNextWindowSize(viewport->WorkSize); + SetNextWindowViewport(viewport->ID); + + ImGuiWindowFlags host_window_flags = 0; + host_window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking; + host_window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; + if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) + host_window_flags |= ImGuiWindowFlags_NoBackground; + + // FIXME-OPT: When using ImGuiDockNodeFlags_KeepAliveOnly with DockSpaceOverViewport() we might be able to spare submitting the window, + // since DockSpace() with that flag doesn't need a window. We'd only need to compute the default ID accordingly. + if (dockspace_flags & ImGuiDockNodeFlags_KeepAliveOnly) + host_window_flags |= ImGuiWindowFlags_NoMouseInputs; + + char label[32]; + ImFormatString(label, IM_ARRAYSIZE(label), "WindowOverViewport_%08X", viewport->ID); + + PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + Begin(label, NULL, host_window_flags); + PopStyleVar(3); + + // Submit the dockspace + if (dockspace_id == 0) + dockspace_id = GetID("DockSpace"); + DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags, window_class); + + End(); + + return dockspace_id; +} + +//----------------------------------------------------------------------------- +// Docking: Builder Functions +//----------------------------------------------------------------------------- +// Very early end-user API to manipulate dock nodes. +// Only available in imgui_internal.h. Expect this API to change/break! +// It is expected that those functions are all called _before_ the dockspace node submission. +//----------------------------------------------------------------------------- +// - DockBuilderDockWindow() +// - DockBuilderGetNode() +// - DockBuilderSetNodePos() +// - DockBuilderSetNodeSize() +// - DockBuilderAddNode() +// - DockBuilderRemoveNode() +// - DockBuilderRemoveNodeChildNodes() +// - DockBuilderRemoveNodeDockedWindows() +// - DockBuilderSplitNode() +// - DockBuilderCopyNodeRec() +// - DockBuilderCopyNode() +// - DockBuilderCopyWindowSettings() +// - DockBuilderCopyDockSpace() +// - DockBuilderFinish() +//----------------------------------------------------------------------------- + +void ImGui::DockBuilderDockWindow(const char* window_name, ImGuiID node_id) +{ + // We don't preserve relative order of multiple docked windows (by clearing DockOrder back to -1) + ImGuiContext& g = *GImGui; IM_UNUSED(g); + IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderDockWindow '%s' to node 0x%08X\n", window_name, node_id); + ImGuiID window_id = ImHashStr(window_name); + if (ImGuiWindow* window = FindWindowByID(window_id)) + { + // Apply to created window + ImGuiID prev_node_id = window->DockId; + SetWindowDock(window, node_id, ImGuiCond_Always); + if (window->DockId != prev_node_id) + window->DockOrder = -1; + } + else + { + // Apply to settings + ImGuiWindowSettings* settings = FindWindowSettingsByID(window_id); + if (settings == NULL) + settings = CreateNewWindowSettings(window_name); + if (settings->DockId != node_id) + settings->DockOrder = -1; + settings->DockId = node_id; + } +} + +ImGuiDockNode* ImGui::DockBuilderGetNode(ImGuiID node_id) +{ + ImGuiContext& g = *GImGui; + return DockContextFindNodeByID(&g, node_id); +} + +void ImGui::DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos) +{ + ImGuiContext& g = *GImGui; + ImGuiDockNode* node = DockContextFindNodeByID(&g, node_id); + if (node == NULL) + return; + node->Pos = pos; + node->AuthorityForPos = ImGuiDataAuthority_DockNode; +} + +void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size) +{ + ImGuiContext& g = *GImGui; + ImGuiDockNode* node = DockContextFindNodeByID(&g, node_id); + if (node == NULL) + return; + IM_ASSERT(size.x > 0.0f && size.y > 0.0f); + node->Size = node->SizeRef = size; + node->AuthorityForSize = ImGuiDataAuthority_DockNode; +} + +// Make sure to use the ImGuiDockNodeFlags_DockSpace flag to create a dockspace node! Otherwise this will create a floating node! +// - Floating node: you can then call DockBuilderSetNodePos()/DockBuilderSetNodeSize() to position and size the floating node. +// - Dockspace node: calling DockBuilderSetNodePos() is unnecessary. +// - If you intend to split a node immediately after creation using DockBuilderSplitNode(), make sure to call DockBuilderSetNodeSize() beforehand! +// For various reason, the splitting code currently needs a base size otherwise space may not be allocated as precisely as you would expect. +// - Use (id == 0) to let the system allocate a node identifier. +// - Existing node with a same id will be removed. +ImGuiID ImGui::DockBuilderAddNode(ImGuiID node_id, ImGuiDockNodeFlags flags) +{ + ImGuiContext& g = *GImGui; IM_UNUSED(g); + IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderAddNode 0x%08X flags=%08X\n", node_id, flags); + + if (node_id != 0) + DockBuilderRemoveNode(node_id); + + ImGuiDockNode* node = NULL; + if (flags & ImGuiDockNodeFlags_DockSpace) + { + DockSpace(node_id, ImVec2(0, 0), (flags & ~ImGuiDockNodeFlags_DockSpace) | ImGuiDockNodeFlags_KeepAliveOnly); + node = DockContextFindNodeByID(&g, node_id); + } + else + { + node = DockContextAddNode(&g, node_id); + node->SetLocalFlags(flags); + } + node->LastFrameAlive = g.FrameCount; // Set this otherwise BeginDocked will undock during the same frame. + return node->ID; +} + +void ImGui::DockBuilderRemoveNode(ImGuiID node_id) +{ + ImGuiContext& g = *GImGui; IM_UNUSED(g); + IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderRemoveNode 0x%08X\n", node_id); + + ImGuiDockNode* node = DockContextFindNodeByID(&g, node_id); + if (node == NULL) + return; + DockBuilderRemoveNodeDockedWindows(node_id, true); + DockBuilderRemoveNodeChildNodes(node_id); + // Node may have moved or deleted if e.g. any merge happened + node = DockContextFindNodeByID(&g, node_id); + if (node == NULL) + return; + if (node->IsCentralNode() && node->ParentNode) + node->ParentNode->SetLocalFlags(node->ParentNode->LocalFlags | ImGuiDockNodeFlags_CentralNode); + DockContextRemoveNode(&g, node, true); +} + +// root_id = 0 to remove all, root_id != 0 to remove child of given node. +void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) +{ + ImGuiContext& g = *GImGui; + ImGuiDockContext* dc = &g.DockContext; + + ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(&g, root_id) : NULL; + if (root_id && root_node == NULL) + return; + bool has_central_node = false; + + ImGuiDataAuthority backup_root_node_authority_for_pos = root_node ? root_node->AuthorityForPos : ImGuiDataAuthority_Auto; + ImGuiDataAuthority backup_root_node_authority_for_size = root_node ? root_node->AuthorityForSize : ImGuiDataAuthority_Auto; + + // Process active windows + ImVector nodes_to_remove; + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) + { + bool want_removal = (root_id == 0) || (node->ID != root_id && DockNodeGetRootNode(node)->ID == root_id); + if (want_removal) + { + if (node->IsCentralNode()) + has_central_node = true; + if (root_id != 0) + DockContextQueueNotifyRemovedNode(&g, node); + if (root_node) + { + DockNodeMoveWindows(root_node, node); + DockSettingsRenameNodeReferences(node->ID, root_node->ID); + } + nodes_to_remove.push_back(node); + } + } + + // DockNodeMoveWindows->DockNodeAddWindow will normally set those when reaching two windows (which is only adequate during interactive merge) + // Make sure we don't lose our current pos/size. (FIXME-DOCK: Consider tidying up that code in DockNodeAddWindow instead) + if (root_node) + { + root_node->AuthorityForPos = backup_root_node_authority_for_pos; + root_node->AuthorityForSize = backup_root_node_authority_for_size; + } + + // Apply to settings + for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) + if (ImGuiID window_settings_dock_id = settings->DockId) + for (int n = 0; n < nodes_to_remove.Size; n++) + if (nodes_to_remove[n]->ID == window_settings_dock_id) + { + settings->DockId = root_id; + break; + } + + // Not really efficient, but easier to destroy a whole hierarchy considering DockContextRemoveNode is attempting to merge nodes + if (nodes_to_remove.Size > 1) + ImQsort(nodes_to_remove.Data, nodes_to_remove.Size, sizeof(ImGuiDockNode*), DockNodeComparerDepthMostFirst); + for (int n = 0; n < nodes_to_remove.Size; n++) + DockContextRemoveNode(&g, nodes_to_remove[n], false); + + if (root_id == 0) + { + dc->Nodes.Clear(); + dc->Requests.clear(); + } + else if (has_central_node) + { + root_node->CentralNode = root_node; + root_node->SetLocalFlags(root_node->LocalFlags | ImGuiDockNodeFlags_CentralNode); + } +} + +void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiID root_id, bool clear_settings_refs) +{ + // Clear references in settings + ImGuiContext& g = *GImGui; + if (clear_settings_refs) + { + for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) + { + bool want_removal = (root_id == 0) || (settings->DockId == root_id); + if (!want_removal && settings->DockId != 0) + if (ImGuiDockNode* node = DockContextFindNodeByID(&g, settings->DockId)) + if (DockNodeGetRootNode(node)->ID == root_id) + want_removal = true; + if (want_removal) + settings->DockId = 0; + } + } + + // Clear references in windows + for (int n = 0; n < g.Windows.Size; n++) + { + ImGuiWindow* window = g.Windows[n]; + bool want_removal = (root_id == 0) || (window->DockNode && DockNodeGetRootNode(window->DockNode)->ID == root_id) || (window->DockNodeAsHost && window->DockNodeAsHost->ID == root_id); + if (want_removal) + { + const ImGuiID backup_dock_id = window->DockId; + IM_UNUSED(backup_dock_id); + DockContextProcessUndockWindow(&g, window, clear_settings_refs); + if (!clear_settings_refs) + IM_ASSERT(window->DockId == backup_dock_id); + } + } +} + +// If 'out_id_at_dir' or 'out_id_at_opposite_dir' are non NULL, the function will write out the ID of the two new nodes created. +// Return value is ID of the node at the specified direction, so same as (*out_id_at_dir) if that pointer is set. +// FIXME-DOCK: We are not exposing nor using split_outer. +ImGuiID ImGui::DockBuilderSplitNode(ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_at_opposite_dir) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(split_dir != ImGuiDir_None); + IMGUI_DEBUG_LOG_DOCKING("[docking] DockBuilderSplitNode: node 0x%08X, split_dir %d\n", id, split_dir); + + ImGuiDockNode* node = DockContextFindNodeByID(&g, id); + if (node == NULL) + { + IM_ASSERT(node != NULL); + return 0; + } + + IM_ASSERT(!node->IsSplitNode()); // Assert if already Split + + ImGuiDockRequest req; + req.Type = ImGuiDockRequestType_Split; + req.DockTargetWindow = NULL; + req.DockTargetNode = node; + req.DockPayload = NULL; + req.DockSplitDir = split_dir; + req.DockSplitRatio = ImSaturate((split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? size_ratio_for_node_at_dir : 1.0f - size_ratio_for_node_at_dir); + req.DockSplitOuter = false; + DockContextProcessDock(&g, &req); + + ImGuiID id_at_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 0 : 1]->ID; + ImGuiID id_at_opposite_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0]->ID; + if (out_id_at_dir) + *out_id_at_dir = id_at_dir; + if (out_id_at_opposite_dir) + *out_id_at_opposite_dir = id_at_opposite_dir; + return id_at_dir; +} + +static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiDockNode* src_node, ImGuiID dst_node_id_if_known, ImVector* out_node_remap_pairs) +{ + ImGuiContext& g = *GImGui; + ImGuiDockNode* dst_node = ImGui::DockContextAddNode(&g, dst_node_id_if_known); + dst_node->SharedFlags = src_node->SharedFlags; + dst_node->LocalFlags = src_node->LocalFlags; + dst_node->LocalFlagsInWindows = ImGuiDockNodeFlags_None; + dst_node->Pos = src_node->Pos; + dst_node->Size = src_node->Size; + dst_node->SizeRef = src_node->SizeRef; + dst_node->SplitAxis = src_node->SplitAxis; + dst_node->UpdateMergedFlags(); + + out_node_remap_pairs->push_back(src_node->ID); + out_node_remap_pairs->push_back(dst_node->ID); + + for (int child_n = 0; child_n < IM_ARRAYSIZE(src_node->ChildNodes); child_n++) + if (src_node->ChildNodes[child_n]) + { + dst_node->ChildNodes[child_n] = DockBuilderCopyNodeRec(src_node->ChildNodes[child_n], 0, out_node_remap_pairs); + dst_node->ChildNodes[child_n]->ParentNode = dst_node; + } + + IMGUI_DEBUG_LOG_DOCKING("[docking] Fork node %08X -> %08X (%d childs)\n", src_node->ID, dst_node->ID, dst_node->IsSplitNode() ? 2 : 0); + return dst_node; +} + +void ImGui::DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(src_node_id != 0); + IM_ASSERT(dst_node_id != 0); + IM_ASSERT(out_node_remap_pairs != NULL); + + DockBuilderRemoveNode(dst_node_id); + + ImGuiDockNode* src_node = DockContextFindNodeByID(&g, src_node_id); + IM_ASSERT(src_node != NULL); + + out_node_remap_pairs->clear(); + DockBuilderCopyNodeRec(src_node, dst_node_id, out_node_remap_pairs); + + IM_ASSERT((out_node_remap_pairs->Size % 2) == 0); +} + +void ImGui::DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name) +{ + ImGuiWindow* src_window = FindWindowByName(src_name); + if (src_window == NULL) + return; + if (ImGuiWindow* dst_window = FindWindowByName(dst_name)) + { + dst_window->Pos = src_window->Pos; + dst_window->Size = src_window->Size; + dst_window->SizeFull = src_window->SizeFull; + dst_window->Collapsed = src_window->Collapsed; + } + else + { + ImGuiWindowSettings* dst_settings = FindWindowSettingsByID(ImHashStr(dst_name)); + if (!dst_settings) + dst_settings = CreateNewWindowSettings(dst_name); + ImVec2ih window_pos_2ih = ImVec2ih(src_window->Pos); + if (src_window->ViewportId != 0 && src_window->ViewportId != IMGUI_VIEWPORT_DEFAULT_ID) + { + dst_settings->ViewportPos = window_pos_2ih; + dst_settings->ViewportId = src_window->ViewportId; + dst_settings->Pos = ImVec2ih(0, 0); + } + else + { + dst_settings->Pos = window_pos_2ih; + } + dst_settings->Size = ImVec2ih(src_window->SizeFull); + dst_settings->Collapsed = src_window->Collapsed; + } +} + +// FIXME: Will probably want to change this signature, in particular how the window remapping pairs are passed. +void ImGui::DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(src_dockspace_id != 0); + IM_ASSERT(dst_dockspace_id != 0); + IM_ASSERT(in_window_remap_pairs != NULL); + IM_ASSERT((in_window_remap_pairs->Size % 2) == 0); + + // Duplicate entire dock + // FIXME: When overwriting dst_dockspace_id, windows that aren't part of our dockspace window class but that are docked in a same node will be split apart, + // whereas we could attempt to at least keep them together in a new, same floating node. + ImVector node_remap_pairs; + DockBuilderCopyNode(src_dockspace_id, dst_dockspace_id, &node_remap_pairs); + + // Attempt to transition all the upcoming windows associated to dst_dockspace_id into the newly created hierarchy of dock nodes + // (The windows associated to src_dockspace_id are staying in place) + ImVector src_windows; + for (int remap_window_n = 0; remap_window_n < in_window_remap_pairs->Size; remap_window_n += 2) + { + const char* src_window_name = (*in_window_remap_pairs)[remap_window_n]; + const char* dst_window_name = (*in_window_remap_pairs)[remap_window_n + 1]; + ImGuiID src_window_id = ImHashStr(src_window_name); + src_windows.push_back(src_window_id); + + // Search in the remapping tables + ImGuiID src_dock_id = 0; + if (ImGuiWindow* src_window = FindWindowByID(src_window_id)) + src_dock_id = src_window->DockId; + else if (ImGuiWindowSettings* src_window_settings = FindWindowSettingsByID(src_window_id)) + src_dock_id = src_window_settings->DockId; + ImGuiID dst_dock_id = 0; + for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2) + if (node_remap_pairs[dock_remap_n] == src_dock_id) + { + dst_dock_id = node_remap_pairs[dock_remap_n + 1]; + //node_remap_pairs[dock_remap_n] = node_remap_pairs[dock_remap_n + 1] = 0; // Clear + break; + } + + if (dst_dock_id != 0) + { + // Docked windows gets redocked into the new node hierarchy. + IMGUI_DEBUG_LOG_DOCKING("[docking] Remap live window '%s' 0x%08X -> '%s' 0x%08X\n", src_window_name, src_dock_id, dst_window_name, dst_dock_id); + DockBuilderDockWindow(dst_window_name, dst_dock_id); + } + else + { + // Floating windows gets their settings transferred (regardless of whether the new window already exist or not) + // When this is leading to a Copy and not a Move, we would get two overlapping floating windows. Could we possibly dock them together? + IMGUI_DEBUG_LOG_DOCKING("[docking] Remap window settings '%s' -> '%s'\n", src_window_name, dst_window_name); + DockBuilderCopyWindowSettings(src_window_name, dst_window_name); + } + } + + // Anything else in the source nodes of 'node_remap_pairs' are windows that are not included in the remapping list. + // Find those windows and move to them to the cloned dock node. This may be optional? + // Dock those are a second step as undocking would invalidate source dock nodes. + struct DockRemainingWindowTask { ImGuiWindow* Window; ImGuiID DockId; DockRemainingWindowTask(ImGuiWindow* window, ImGuiID dock_id) { Window = window; DockId = dock_id; } }; + ImVector dock_remaining_windows; + for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2) + if (ImGuiID src_dock_id = node_remap_pairs[dock_remap_n]) + { + ImGuiID dst_dock_id = node_remap_pairs[dock_remap_n + 1]; + ImGuiDockNode* node = DockBuilderGetNode(src_dock_id); + for (int window_n = 0; window_n < node->Windows.Size; window_n++) + { + ImGuiWindow* window = node->Windows[window_n]; + if (src_windows.contains(window->ID)) + continue; + + // Docked windows gets redocked into the new node hierarchy. + IMGUI_DEBUG_LOG_DOCKING("[docking] Remap window '%s' %08X -> %08X\n", window->Name, src_dock_id, dst_dock_id); + dock_remaining_windows.push_back(DockRemainingWindowTask(window, dst_dock_id)); + } + } + for (const DockRemainingWindowTask& task : dock_remaining_windows) + DockBuilderDockWindow(task.Window->Name, task.DockId); +} + +// FIXME-DOCK: This is awkward because in series of split user is likely to loose access to its root node. +void ImGui::DockBuilderFinish(ImGuiID root_id) +{ + ImGuiContext& g = *GImGui; + //DockContextRebuild(&g); + DockContextBuildAddWindowsToNodes(&g, root_id); +} + +//----------------------------------------------------------------------------- +// Docking: Begin/End Support Functions (called from Begin/End) +//----------------------------------------------------------------------------- +// - GetWindowAlwaysWantOwnTabBar() +// - DockContextBindNodeToWindow() +// - BeginDocked() +// - BeginDockableDragDropSource() +// - BeginDockableDragDropTarget() +//----------------------------------------------------------------------------- + +bool ImGui::GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + if (g.IO.ConfigDockingAlwaysTabBar || window->WindowClass.DockingAlwaysTabBar) + if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)) == 0) + if (!window->IsFallbackWindow) // We don't support AlwaysTabBar on the fallback/implicit window to avoid unused dock-node overhead/noise + return true; + return false; +} + +static ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window) +{ + ImGuiContext& g = *ctx; + ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId); + IM_ASSERT(window->DockNode == NULL); + + // We should not be docking into a split node (SetWindowDock should avoid this) + if (node && node->IsSplitNode()) + { + DockContextProcessUndockWindow(ctx, window); + return NULL; + } + + // Create node + if (node == NULL) + { + node = DockContextAddNode(ctx, window->DockId); + node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window; + node->LastFrameAlive = g.FrameCount; + } + + // If the node just turned visible and is part of a hierarchy, it doesn't have a Size assigned by DockNodeTreeUpdatePosSize() yet, + // so we're forcing a Pos/Size update from the first ancestor that is already visible (often it will be the root node). + // If we don't do this, the window will be assigned a zero-size on its first frame, which won't ideally warm up the layout. + // This is a little wonky because we don't normally update the Pos/Size of visible node mid-frame. + if (!node->IsVisible) + { + ImGuiDockNode* ancestor_node = node; + while (!ancestor_node->IsVisible && ancestor_node->ParentNode) + ancestor_node = ancestor_node->ParentNode; + IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f); + DockNodeUpdateHasCentralNodeChild(DockNodeGetRootNode(ancestor_node)); + DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, node); + } + + // Add window to node + bool node_was_visible = node->IsVisible; + DockNodeAddWindow(node, window, true); + node->IsVisible = node_was_visible; // Don't mark visible right away (so DockContextEndFrame() doesn't render it, maybe other side effects? will see) + IM_ASSERT(node == window->DockNode); + return node; +} + +static void StoreDockStyleForWindow(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + for (int color_n = 0; color_n < ImGuiWindowDockStyleCol_COUNT; color_n++) + window->DockStyle.Colors[color_n] = ImGui::ColorConvertFloat4ToU32(g.Style.Colors[GWindowDockStyleColors[color_n]]); +} + +void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) +{ + ImGuiContext& g = *GImGui; + + // Clear fields ahead so most early-out paths don't have to do it + window->DockIsActive = window->DockNodeIsVisible = window->DockTabIsVisible = false; + + const bool auto_dock_node = GetWindowAlwaysWantOwnTabBar(window); + if (auto_dock_node) + { + if (window->DockId == 0) + { + IM_ASSERT(window->DockNode == NULL); + window->DockId = DockContextGenNodeID(&g); + } + } + else + { + // Calling SetNextWindowPos() undock windows by default (by setting PosUndock) + bool want_undock = false; + want_undock |= (window->Flags & ImGuiWindowFlags_NoDocking) != 0; + want_undock |= (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) && (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) && g.NextWindowData.PosUndock; + if (want_undock) + { + DockContextProcessUndockWindow(&g, window); + return; + } + } + + // Bind to our dock node + ImGuiDockNode* node = window->DockNode; + if (node != NULL) + IM_ASSERT(window->DockId == node->ID); + if (window->DockId != 0 && node == NULL) + { + node = DockContextBindNodeToWindow(&g, window); + if (node == NULL) + return; + } + +#if 0 + // Undock if the ImGuiDockNodeFlags_NoDockingInCentralNode got set + if (node->IsCentralNode && (node->Flags & ImGuiDockNodeFlags_NoDockingInCentralNode)) + { + DockContextProcessUndockWindow(ctx, window); + return; + } +#endif + + // Undock if our dockspace node disappeared + // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags_KeepAliveOnly. + if (node->LastFrameAlive < g.FrameCount) + { + // If the window has been orphaned, transition the docknode to an implicit node processed in DockContextNewFrameUpdateDocking() + ImGuiDockNode* root_node = DockNodeGetRootNode(node); + if (root_node->LastFrameAlive < g.FrameCount) + DockContextProcessUndockWindow(&g, window); + else + window->DockIsActive = true; + return; + } + + // Store style overrides + StoreDockStyleForWindow(window); + + // Fast path return. It is common for windows to hold on a persistent DockId but be the only visible window, + // and never create neither a host window neither a tab bar. + // FIXME-DOCK: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test) + if (node->HostWindow == NULL) + { + if (node->State == ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing) + window->DockIsActive = true; + if (node->Windows.Size > 1 && window->Appearing) // Only hide appearing window + DockNodeHideWindowDuringHostWindowCreation(window); + return; + } + + // We can have zero-sized nodes (e.g. children of a small-size dockspace) + IM_ASSERT(node->HostWindow); + IM_ASSERT(node->IsLeafNode()); + IM_ASSERT(node->Size.x >= 0.0f && node->Size.y >= 0.0f); + node->State = ImGuiDockNodeState_HostWindowVisible; + + // Undock if we are submitted earlier than the host window + if (!(node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly) && window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext) + { + DockContextProcessUndockWindow(&g, window); + return; + } + + // Position/Size window + SetNextWindowPos(node->Pos); + SetNextWindowSize(node->Size); + g.NextWindowData.PosUndock = false; // Cancel implicit undocking of SetNextWindowPos() + window->DockIsActive = true; + window->DockNodeIsVisible = true; + window->DockTabIsVisible = false; + if (node->MergedFlags & ImGuiDockNodeFlags_KeepAliveOnly) + return; + + // When the window is selected we mark it as visible. + if (node->VisibleWindow == window) + window->DockTabIsVisible = true; + + // Update window flag + IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0); + window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoResize; + window->ChildFlags |= ImGuiChildFlags_AlwaysUseWindowPadding; + if (node->IsHiddenTabBar() || node->IsNoTabBar()) + window->Flags |= ImGuiWindowFlags_NoTitleBar; + else + window->Flags &= ~ImGuiWindowFlags_NoTitleBar; // Clear the NoTitleBar flag in case the user set it: confusingly enough we need a title bar height so we are correctly offset, but it won't be displayed! + + // Save new dock order only if the window has been visible once already + // This allows multiple windows to be created in the same frame and have their respective dock orders preserved. + if (node->TabBar && window->WasActive) + window->DockOrder = (short)DockNodeGetTabOrder(window); + + if ((node->WantCloseAll || node->WantCloseTabId == window->TabId) && p_open != NULL) + *p_open = false; + + // Update ChildId to allow returning from Child to Parent with Escape + ImGuiWindow* parent_window = window->DockNode->HostWindow; + window->ChildId = parent_window->GetID(window->Name); +} + +void ImGui::BeginDockableDragDropSource(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.ActiveId == window->MoveId); + IM_ASSERT(g.MovingWindow == window); + IM_ASSERT(g.CurrentWindow == window); + + // 0: Hold SHIFT to disable docking, 1: Hold SHIFT to enable docking. + if (g.IO.ConfigDockingWithShift != g.IO.KeyShift) + { + // When ConfigDockingWithShift is set, display a tooltip to increase UI affordance. + // We cannot set for HoveredWindowUnderMovingWindow != NULL here, as it is only valid/useful when drag and drop is already active + // (because of the 'is_mouse_dragging_with_an_expected_destination' logic in UpdateViewportsNewFrame() function) + IM_ASSERT(g.NextWindowData.Flags == 0); + if (g.IO.ConfigDockingWithShift && g.MouseStationaryTimer >= 1.0f && g.ActiveId >= 1.0f) + SetTooltip("%s", LocalizeGetMsg(ImGuiLocKey_DockingHoldShiftToDock)); + return; + } + + g.LastItemData.ID = window->MoveId; + window = window->RootWindowDockTree; + IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); + bool is_drag_docking = (g.IO.ConfigDockingWithShift) || ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset); // FIXME-DOCKING: Need to make this stateful and explicit + ImGuiDragDropFlags drag_drop_flags = ImGuiDragDropFlags_SourceNoPreviewTooltip | ImGuiDragDropFlags_SourceNoHoldToOpenOthers | ImGuiDragDropFlags_PayloadAutoExpire | ImGuiDragDropFlags_PayloadNoCrossContext | ImGuiDragDropFlags_PayloadNoCrossProcess; + if (is_drag_docking && BeginDragDropSource(drag_drop_flags)) + { + SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, &window, sizeof(window)); + EndDragDropSource(); + StoreDockStyleForWindow(window); // Store style overrides while dragging (even when not docked) because docking preview may need it. + } +} + +void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + + //IM_ASSERT(window->RootWindowDockTree == window); // May also be a DockSpace + IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); + if (!g.DragDropActive) + return; + //GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); + if (!BeginDragDropTargetCustom(window->Rect(), window->ID)) + return; + + // Peek into the payload before calling AcceptDragDropPayload() so we can handle overlapping dock nodes with filtering + // (this is a little unusual pattern, normally most code would call AcceptDragDropPayload directly) + const ImGuiPayload* payload = &g.DragDropPayload; + if (!payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) || !DockNodeIsDropAllowed(window, *(ImGuiWindow**)payload->Data)) + { + EndDragDropTarget(); + return; + } + + ImGuiWindow* payload_window = *(ImGuiWindow**)payload->Data; + if (AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect)) + { + // Select target node + // (Important: we cannot use g.HoveredDockNode here! Because each of our target node have filters based on payload, each candidate drop target will do its own evaluation) + bool dock_into_floating_window = false; + ImGuiDockNode* node = NULL; + if (window->DockNodeAsHost) + { + // Cannot assume that node will != NULL even though we passed the rectangle test: it depends on padding/spacing handled by DockNodeTreeFindVisibleNodeByPos(). + node = DockNodeTreeFindVisibleNodeByPos(window->DockNodeAsHost, g.IO.MousePos); + + // There is an edge case when docking into a dockspace which only has _inactive_ nodes (because none of the windows are active) + // In this case we need to fallback into any leaf mode, possibly the central node. + // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first. + if (node && node->IsDockSpace() && node->IsRootNode()) + node = (node->CentralNode && node->IsLeafNode()) ? node->CentralNode : DockNodeTreeFindFallbackLeafNode(node); + } + else + { + if (window->DockNode) + node = window->DockNode; + else + dock_into_floating_window = true; // Dock into a regular window + } + + const ImRect explicit_target_rect = (node && node->TabBar && !node->IsHiddenTabBar() && !node->IsNoTabBar()) ? node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight())); + const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max); + + // Preview docking request and find out split direction/ratio + //const bool do_preview = true; // Ignore testing for payload->IsPreview() which removes one frame of delay, but breaks overlapping drop targets within the same window. + const bool do_preview = payload->IsPreview() || payload->IsDelivery(); + if (do_preview && (node != NULL || dock_into_floating_window)) + { + // If we have a non-leaf node it means we are hovering the border of a parent node, in which case only outer markers will appear. + ImGuiDockPreviewData split_inner; + ImGuiDockPreviewData split_outer; + ImGuiDockPreviewData* split_data = &split_inner; + if (node && (node->ParentNode || node->IsCentralNode() || !node->IsLeafNode())) + if (ImGuiDockNode* root_node = DockNodeGetRootNode(node)) + { + DockNodePreviewDockSetup(window, root_node, payload_window, NULL, &split_outer, is_explicit_target, true); + if (split_outer.IsSplitDirExplicit) + split_data = &split_outer; + } + if (!node || node->IsLeafNode()) + DockNodePreviewDockSetup(window, node, payload_window, NULL, &split_inner, is_explicit_target, false); + if (split_data == &split_outer) + split_inner.IsDropAllowed = false; + + // Draw inner then outer, so that previewed tab (in inner data) will be behind the outer drop boxes + DockNodePreviewDockRender(window, node, payload_window, &split_inner); + DockNodePreviewDockRender(window, node, payload_window, &split_outer); + + // Queue docking request + if (split_data->IsDropAllowed && payload->IsDelivery()) + DockContextQueueDock(&g, window, split_data->SplitNode, payload_window, split_data->SplitDir, split_data->SplitRatio, split_data == &split_outer); + } + } + EndDragDropTarget(); +} + +//----------------------------------------------------------------------------- +// Docking: Settings +//----------------------------------------------------------------------------- +// - DockSettingsRenameNodeReferences() +// - DockSettingsRemoveNodeReferences() +// - DockSettingsFindNodeSettings() +// - DockSettingsHandler_ApplyAll() +// - DockSettingsHandler_ReadOpen() +// - DockSettingsHandler_ReadLine() +// - DockSettingsHandler_DockNodeToSettings() +// - DockSettingsHandler_WriteAll() +//----------------------------------------------------------------------------- + +static void ImGui::DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id) +{ + ImGuiContext& g = *GImGui; + IMGUI_DEBUG_LOG_DOCKING("[docking] DockSettingsRenameNodeReferences: from 0x%08X -> to 0x%08X\n", old_node_id, new_node_id); + for (int window_n = 0; window_n < g.Windows.Size; window_n++) + { + ImGuiWindow* window = g.Windows[window_n]; + if (window->DockId == old_node_id && window->DockNode == NULL) + window->DockId = new_node_id; + } + //// FIXME-OPT: We could remove this loop by storing the index in the map + for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) + if (settings->DockId == old_node_id) + settings->DockId = new_node_id; +} + +// Remove references stored in ImGuiWindowSettings to the given ImGuiDockNodeSettings +static void ImGui::DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count) +{ + ImGuiContext& g = *GImGui; + int found = 0; + //// FIXME-OPT: We could remove this loop by storing the index in the map + for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) + for (int node_n = 0; node_n < node_ids_count; node_n++) + if (settings->DockId == node_ids[node_n]) + { + settings->DockId = 0; + settings->DockOrder = -1; + if (++found < node_ids_count) + break; + return; + } +} + +static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID id) +{ + // FIXME-OPT + ImGuiDockContext* dc = &ctx->DockContext; + for (int n = 0; n < dc->NodesSettings.Size; n++) + if (dc->NodesSettings[n].ID == id) + return &dc->NodesSettings[n]; + return NULL; +} + +// Clear settings data +static void ImGui::DockSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) +{ + ImGuiDockContext* dc = &ctx->DockContext; + dc->NodesSettings.clear(); + DockContextClearNodes(ctx, 0, true); +} + +// Recreate nodes based on settings data +static void ImGui::DockSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*) +{ + // Prune settings at boot time only + ImGuiDockContext* dc = &ctx->DockContext; + if (ctx->Windows.Size == 0) + DockContextPruneUnusedSettingsNodes(ctx); + DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size); + DockContextBuildAddWindowsToNodes(ctx, 0); +} + +static void* ImGui::DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) +{ + if (strcmp(name, "Data") != 0) + return NULL; + return (void*)1; +} + +static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler*, void*, const char* line) +{ + char c = 0; + int x = 0, y = 0; + int r = 0; + + // Parsing, e.g. + // " DockNode ID=0x00000001 Pos=383,193 Size=201,322 Split=Y,0.506 " + // " DockNode ID=0x00000002 Parent=0x00000001 " + // Important: this code expect currently fields in a fixed order. + ImGuiDockNodeSettings node; + line = ImStrSkipBlank(line); + if (strncmp(line, "DockNode", 8) == 0) { line = ImStrSkipBlank(line + strlen("DockNode")); } + else if (strncmp(line, "DockSpace", 9) == 0) { line = ImStrSkipBlank(line + strlen("DockSpace")); node.Flags |= ImGuiDockNodeFlags_DockSpace; } + else return; + if (sscanf(line, "ID=0x%08X%n", &node.ID, &r) == 1) { line += r; } else return; + if (sscanf(line, " Parent=0x%08X%n", &node.ParentNodeId, &r) == 1) { line += r; if (node.ParentNodeId == 0) return; } + if (sscanf(line, " Window=0x%08X%n", &node.ParentWindowId, &r) ==1) { line += r; if (node.ParentWindowId == 0) return; } + if (node.ParentNodeId == 0) + { + if (sscanf(line, " Pos=%i,%i%n", &x, &y, &r) == 2) { line += r; node.Pos = ImVec2ih((short)x, (short)y); } else return; + if (sscanf(line, " Size=%i,%i%n", &x, &y, &r) == 2) { line += r; node.Size = ImVec2ih((short)x, (short)y); } else return; + } + else + { + if (sscanf(line, " SizeRef=%i,%i%n", &x, &y, &r) == 2) { line += r; node.SizeRef = ImVec2ih((short)x, (short)y); } + } + if (sscanf(line, " Split=%c%n", &c, &r) == 1) { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; } + if (sscanf(line, " NoResize=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoResize; } + if (sscanf(line, " CentralNode=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_CentralNode; } + if (sscanf(line, " NoTabBar=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoTabBar; } + if (sscanf(line, " HiddenTabBar=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_HiddenTabBar; } + if (sscanf(line, " NoWindowMenuButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoWindowMenuButton; } + if (sscanf(line, " NoCloseButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoCloseButton; } + if (sscanf(line, " Selected=0x%08X%n", &node.SelectedTabId,&r) == 1) { line += r; } + if (node.ParentNodeId != 0) + if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentNodeId)) + node.Depth = parent_settings->Depth + 1; + ctx->DockContext.NodesSettings.push_back(node); +} + +static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDockNode* node, int depth) +{ + ImGuiDockNodeSettings node_settings; + IM_ASSERT(depth < (1 << (sizeof(node_settings.Depth) << 3))); + node_settings.ID = node->ID; + node_settings.ParentNodeId = node->ParentNode ? node->ParentNode->ID : 0; + node_settings.ParentWindowId = (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) ? node->HostWindow->ParentWindow->ID : 0; + node_settings.SelectedTabId = node->SelectedTabId; + node_settings.SplitAxis = (signed char)(node->IsSplitNode() ? node->SplitAxis : ImGuiAxis_None); + node_settings.Depth = (char)depth; + node_settings.Flags = (node->LocalFlags & ImGuiDockNodeFlags_SavedFlagsMask_); + node_settings.Pos = ImVec2ih(node->Pos); + node_settings.Size = ImVec2ih(node->Size); + node_settings.SizeRef = ImVec2ih(node->SizeRef); + dc->NodesSettings.push_back(node_settings); + if (node->ChildNodes[0]) + DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[0], depth + 1); + if (node->ChildNodes[1]) + DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[1], depth + 1); +} + +static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) +{ + ImGuiContext& g = *ctx; + ImGuiDockContext* dc = &ctx->DockContext; + if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) + return; + + // Gather settings data + // (unlike our windows settings, because nodes are always built we can do a full rewrite of the SettingsNode buffer) + dc->NodesSettings.resize(0); + dc->NodesSettings.reserve(dc->Nodes.Data.Size); + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) + if (node->IsRootNode()) + DockSettingsHandler_DockNodeToSettings(dc, node, 0); + + int max_depth = 0; + for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++) + max_depth = ImMax((int)dc->NodesSettings[node_n].Depth, max_depth); + + // Write to text buffer + buf->appendf("[%s][Data]\n", handler->TypeName); + for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++) + { + const int line_start_pos = buf->size(); (void)line_start_pos; + const ImGuiDockNodeSettings* node_settings = &dc->NodesSettings[node_n]; + buf->appendf("%*s%s%*s", node_settings->Depth * 2, "", (node_settings->Flags & ImGuiDockNodeFlags_DockSpace) ? "DockSpace" : "DockNode ", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file + buf->appendf(" ID=0x%08X", node_settings->ID); + if (node_settings->ParentNodeId) + { + buf->appendf(" Parent=0x%08X SizeRef=%d,%d", node_settings->ParentNodeId, node_settings->SizeRef.x, node_settings->SizeRef.y); + } + else + { + if (node_settings->ParentWindowId) + buf->appendf(" Window=0x%08X", node_settings->ParentWindowId); + buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y); + } + if (node_settings->SplitAxis != ImGuiAxis_None) + buf->appendf(" Split=%c", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y'); + if (node_settings->Flags & ImGuiDockNodeFlags_NoResize) + buf->appendf(" NoResize=1"); + if (node_settings->Flags & ImGuiDockNodeFlags_CentralNode) + buf->appendf(" CentralNode=1"); + if (node_settings->Flags & ImGuiDockNodeFlags_NoTabBar) + buf->appendf(" NoTabBar=1"); + if (node_settings->Flags & ImGuiDockNodeFlags_HiddenTabBar) + buf->appendf(" HiddenTabBar=1"); + if (node_settings->Flags & ImGuiDockNodeFlags_NoWindowMenuButton) + buf->appendf(" NoWindowMenuButton=1"); + if (node_settings->Flags & ImGuiDockNodeFlags_NoCloseButton) + buf->appendf(" NoCloseButton=1"); + if (node_settings->SelectedTabId) + buf->appendf(" Selected=0x%08X", node_settings->SelectedTabId); + + // [DEBUG] Include comments in the .ini file to ease debugging (this makes saving slower!) + if (g.IO.ConfigDebugIniSettings) + if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID)) + { + buf->appendf("%*s", ImMax(2, (line_start_pos + 92) - buf->size()), ""); // Align everything + if (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) + buf->appendf(" ; in '%s'", node->HostWindow->ParentWindow->Name); + // Iterate settings so we can give info about windows that didn't exist during the session. + int contains_window = 0; + for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) + if (settings->DockId == node_settings->ID) + { + if (contains_window++ == 0) + buf->appendf(" ; contains "); + buf->appendf("'%s' ", settings->GetName()); + } + } + + buf->appendf("\n"); + } + buf->appendf("\n"); +} + + +//----------------------------------------------------------------------------- +// [SECTION] PLATFORM DEPENDENT HELPERS +//----------------------------------------------------------------------------- +// - Default clipboard handlers +// - Default shell function handlers +// - Default IME handlers +//----------------------------------------------------------------------------- + +#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) + +#ifdef _MSC_VER +#pragma comment(lib, "user32") +#pragma comment(lib, "kernel32") +#endif + +// Win32 clipboard implementation +// We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown() +static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx) +{ + ImGuiContext& g = *ctx; + g.ClipboardHandlerData.clear(); + if (!::OpenClipboard(NULL)) + return NULL; + HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT); + if (wbuf_handle == NULL) + { + ::CloseClipboard(); + return NULL; + } + if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle)) + { + int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL); + g.ClipboardHandlerData.resize(buf_len); + ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL); + } + ::GlobalUnlock(wbuf_handle); + ::CloseClipboard(); + return g.ClipboardHandlerData.Data; +} + +static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text) +{ + if (!::OpenClipboard(NULL)) + return; + const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0); + HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR)); + if (wbuf_handle == NULL) + { + ::CloseClipboard(); + return; + } + WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle); + ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length); + ::GlobalUnlock(wbuf_handle); + ::EmptyClipboard(); + if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL) + ::GlobalFree(wbuf_handle); + ::CloseClipboard(); +} + +#elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS) + +#include // Use old API to avoid need for separate .mm file +static PasteboardRef main_clipboard = 0; + +// OSX clipboard implementation +// If you enable this you will need to add '-framework ApplicationServices' to your linker command-line! +static void Platform_SetClipboardTextFn_DefaultImpl(ImGuiContext*, const char* text) +{ + if (!main_clipboard) + PasteboardCreate(kPasteboardClipboard, &main_clipboard); + PasteboardClear(main_clipboard); + CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text)); + if (cf_data) + { + PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0); + CFRelease(cf_data); + } +} + +static const char* Platform_GetClipboardTextFn_DefaultImpl(ImGuiContext* ctx) +{ + ImGuiContext& g = *ctx; + if (!main_clipboard) + PasteboardCreate(kPasteboardClipboard, &main_clipboard); + PasteboardSynchronize(main_clipboard); + + ItemCount item_count = 0; + PasteboardGetItemCount(main_clipboard, &item_count); + for (ItemCount i = 0; i < item_count; i++) + { + PasteboardItemID item_id = 0; + PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id); + CFArrayRef flavor_type_array = 0; + PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array); + for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++) { CFDataRef cf_data; if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr) @@ -15088,14 +20767,14 @@ static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport* view if (HIMC himc = ::ImmGetContext(hwnd)) { COMPOSITIONFORM composition_form = {}; - composition_form.ptCurrentPos.x = (LONG)data->InputPos.x; - composition_form.ptCurrentPos.y = (LONG)data->InputPos.y; + composition_form.ptCurrentPos.x = (LONG)(data->InputPos.x - viewport->Pos.x); + composition_form.ptCurrentPos.y = (LONG)(data->InputPos.y - viewport->Pos.y); composition_form.dwStyle = CFS_FORCE_POSITION; ::ImmSetCompositionWindow(himc, &composition_form); CANDIDATEFORM candidate_form = {}; candidate_form.dwStyle = CFS_CANDIDATEPOS; - candidate_form.ptCurrentPos.x = (LONG)data->InputPos.x; - candidate_form.ptCurrentPos.y = (LONG)data->InputPos.y; + candidate_form.ptCurrentPos.x = (LONG)(data->InputPos.x - viewport->Pos.x); + candidate_form.ptCurrentPos.y = (LONG)(data->InputPos.y - viewport->Pos.y); ::ImmSetCandidateWindow(himc, &candidate_form); ::ImmReleaseContext(hwnd, himc); } @@ -15117,6 +20796,7 @@ static void Platform_SetImeDataFn_DefaultImpl(ImGuiContext*, ImGuiViewport*, ImG // - ShowFontAtlas() [Internal] // - ShowMetricsWindow() // - DebugNodeColumns() [Internal] +// - DebugNodeDockNode() [Internal] // - DebugNodeDrawList() [Internal] // - DebugNodeDrawCmdShowMeshAndBoundingBox() [Internal] // - DebugNodeFont() [Internal] @@ -15139,12 +20819,14 @@ void ImGui::DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* ImVec2 scale = bb.GetSize() / viewport->Size; ImVec2 off = bb.Min - viewport->Pos * scale; - float alpha_mul = 1.0f; + float alpha_mul = (viewport->Flags & ImGuiViewportFlags_IsMinimized) ? 0.30f : 1.00f; window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f)); for (ImGuiWindow* thumb_window : g.Windows) { if (!thumb_window->WasActive || (thumb_window->Flags & ImGuiWindowFlags_ChildWindow)) continue; + if (thumb_window->Viewport != viewport) + continue; ImRect thumb_r = thumb_window->Rect(); ImRect title_r = thumb_window->TitleBarRect(); @@ -15168,10 +20850,19 @@ static void RenderViewportsThumbnails() ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + // Draw monitor and calculate their boundaries float SCALE = 1.0f / 8.0f; - ImRect bb_full(g.Viewports[0]->Pos, g.Viewports[0]->Pos + g.Viewports[0]->Size); + ImRect bb_full(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); + for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors) + bb_full.Add(ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize)); ImVec2 p = window->DC.CursorPos; ImVec2 off = p - bb_full.Min * SCALE; + for (ImGuiPlatformMonitor& monitor : g.PlatformIO.Monitors) + { + ImRect monitor_draw_bb(off + (monitor.MainPos) * SCALE, off + (monitor.MainPos + monitor.MainSize) * SCALE); + window->DrawList->AddRect(monitor_draw_bb.Min, monitor_draw_bb.Max, (g.DebugMetricsConfig.HighlightMonitorIdx == g.PlatformIO.Monitors.index_from_ptr(&monitor)) ? IM_COL32(255, 255, 0, 255) : ImGui::GetColorU32(ImGuiCol_Border), 4.0f); + window->DrawList->AddRectFilled(monitor_draw_bb.Min, monitor_draw_bb.Max, ImGui::GetColorU32(ImGuiCol_Border, 0.10f), 4.0f); + } // Draw viewports for (ImGuiViewportP* viewport : g.Viewports) @@ -15182,6 +20873,13 @@ static void RenderViewportsThumbnails() ImGui::Dummy(bb_full.GetSize() * SCALE); } +static int IMGUI_CDECL ViewportComparerByLastFocusedStampCount(const void* lhs, const void* rhs) +{ + const ImGuiViewportP* a = *(const ImGuiViewportP* const*)lhs; + const ImGuiViewportP* b = *(const ImGuiViewportP* const*)rhs; + return b->LastFocusedStampCount - a->LastFocusedStampCount; +} + // Draw an arbitrary US keyboard layout to visualize translated keys void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list) { @@ -15560,14 +21258,38 @@ void ImGui::ShowMetricsWindow(bool* p_open) Checkbox("Show ImDrawCmd mesh when hovering", &cfg->ShowDrawCmdMesh); Checkbox("Show ImDrawCmd bounding boxes when hovering", &cfg->ShowDrawCmdBoundingBoxes); for (ImGuiViewportP* viewport : g.Viewports) + { + bool viewport_has_drawlist = false; for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists) + { + if (!viewport_has_drawlist) + Text("Active DrawLists in Viewport #%d, ID: 0x%08X", viewport->Idx, viewport->ID); + viewport_has_drawlist = true; DebugNodeDrawList(NULL, viewport, draw_list, "DrawList"); + } + } TreePop(); } // Viewports if (TreeNode("Viewports", "Viewports (%d)", g.Viewports.Size)) { + cfg->HighlightMonitorIdx = -1; + bool open = TreeNode("Monitors", "Monitors (%d)", g.PlatformIO.Monitors.Size); + SameLine(); + MetricsHelpMarker("Dear ImGui uses monitor data:\n- to query DPI settings on a per monitor basis\n- to position popup/tooltips so they don't straddle monitors."); + if (open) + { + for (int i = 0; i < g.PlatformIO.Monitors.Size; i++) + { + DebugNodePlatformMonitor(&g.PlatformIO.Monitors[i], "Monitor", i); + if (IsItemHovered()) + cfg->HighlightMonitorIdx = i; + } + DebugNodePlatformMonitor(&g.FallbackMonitor, "Fallback", 0); + TreePop(); + } + SetNextItemOpen(true, ImGuiCond_Once); if (TreeNode("Windows Minimap")) { @@ -15576,6 +21298,26 @@ void ImGui::ShowMetricsWindow(bool* p_open) } cfg->HighlightViewportID = 0; + BulletText("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport ? g.MouseViewport->ID : 0, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0); + if (TreeNode("Inferred Z order (front-to-back)")) + { + static ImVector viewports; + viewports.resize(g.Viewports.Size); + memcpy(viewports.Data, g.Viewports.Data, g.Viewports.size_in_bytes()); + if (viewports.Size > 1) + ImQsort(viewports.Data, viewports.Size, sizeof(ImGuiViewport*), ViewportComparerByLastFocusedStampCount); + for (ImGuiViewportP* viewport : viewports) + { + BulletText("Viewport #%d, ID: 0x%08X, LastFocused = %08d, PlatformFocused = %s, Window: \"%s\"", + viewport->Idx, viewport->ID, viewport->LastFocusedStampCount, + (g.PlatformIO.Platform_GetWindowFocus && viewport->PlatformWindowCreated) ? (g.PlatformIO.Platform_GetWindowFocus(viewport) ? "1" : "0") : "N/A", + viewport->Window ? viewport->Window->Name : "N/A"); + if (IsItemHovered()) + cfg->HighlightViewportID = viewport->ID; + } + TreePop(); + } + for (ImGuiViewportP* viewport : g.Viewports) DebugNodeViewport(viewport); TreePop(); @@ -15654,6 +21396,17 @@ void ImGui::ShowMetricsWindow(bool* p_open) #ifdef IMGUI_HAS_DOCK if (TreeNode("Docking")) { + static bool root_nodes_only = true; + ImGuiDockContext* dc = &g.DockContext; + Checkbox("List root nodes", &root_nodes_only); + Checkbox("Ctrl shows window dock info", &cfg->ShowDockingNodes); + if (SmallButton("Clear nodes")) { DockContextClearNodes(&g, 0, true); } + SameLine(); + if (SmallButton("Rebuild all")) { dc->WantFullRebuild = true; } + for (int n = 0; n < dc->Nodes.Data.Size; n++) + if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) + if (!root_nodes_only || node->IsRootNode()) + DebugNodeDockNode(node, "Node"); TreePop(); } #endif // #ifdef IMGUI_HAS_DOCK @@ -15697,6 +21450,29 @@ void ImGui::ShowMetricsWindow(bool* p_open) } #ifdef IMGUI_HAS_DOCK + if (TreeNode("SettingsDocking", "Settings packed data: Docking")) + { + ImGuiDockContext* dc = &g.DockContext; + Text("In SettingsWindows:"); + for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) + if (settings->DockId != 0) + BulletText("Window '%s' -> DockId %08X DockOrder=%d", settings->GetName(), settings->DockId, settings->DockOrder); + Text("In SettingsNodes:"); + for (int n = 0; n < dc->NodesSettings.Size; n++) + { + ImGuiDockNodeSettings* settings = &dc->NodesSettings[n]; + const char* selected_tab_name = NULL; + if (settings->SelectedTabId) + { + if (ImGuiWindow* window = FindWindowByID(settings->SelectedTabId)) + selected_tab_name = window->Name; + else if (ImGuiWindowSettings* window_settings = FindWindowSettingsByID(settings->SelectedTabId)) + selected_tab_name = window_settings->GetName(); + } + BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentNodeId, settings->SelectedTabId, selected_tab_name ? selected_tab_name : settings->SelectedTabId ? "N/A" : ""); + } + TreePop(); + } #endif // #ifdef IMGUI_HAS_DOCK if (TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size())) @@ -15823,9 +21599,11 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("WINDOWING"); Indent(); Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); - Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindow->Name : "NULL"); + Text("HoveredWindow->Root: '%s'", g.HoveredWindow ? g.HoveredWindow->RootWindowDockTree->Name : "NULL"); Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL"); + Text("HoveredDockNode: 0x%08X", g.DebugHoveredDockNode ? g.DebugHoveredDockNode->ID : 0); Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL"); + Text("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport->ID, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0); Unindent(); Text("ITEMS"); @@ -15919,8 +21697,21 @@ void ImGui::ShowMetricsWindow(bool* p_open) #ifdef IMGUI_HAS_DOCK // Overlay: Display Docking info - if (show_docking_nodes && g.IO.KeyCtrl) - { + if (cfg->ShowDockingNodes && g.IO.KeyCtrl && g.DebugHoveredDockNode) + { + char buf[64] = ""; + char* p = buf; + ImGuiDockNode* node = g.DebugHoveredDockNode; + ImDrawList* overlay_draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList(GetMainViewport()); + p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode() ? " *CentralNode*" : ""); + p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "WindowClass: %08X\n", node->WindowClass.ClassId); + p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y); + p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "SizeRef: (%.0f, %.0f)\n", node->SizeRef.x, node->SizeRef.y); + int depth = DockNodeGetDepth(node); + overlay_draw_list->AddRect(node->Pos + ImVec2(3, 3) * (float)depth, node->Pos + node->Size - ImVec2(3, 3) * (float)depth, IM_COL32(200, 100, 100, 255)); + ImVec2 pos = node->Pos + ImVec2(3, 3) * (float)depth; + overlay_draw_list->AddRectFilled(pos - ImVec2(1, 1), pos + CalcTextSize(buf) + ImVec2(1, 1), IM_COL32(200, 100, 100, 255)); + overlay_draw_list->AddText(NULL, 0.0f, pos, IM_COL32(255, 255, 255, 255), buf); } #endif // #ifdef IMGUI_HAS_DOCK @@ -15996,11 +21787,98 @@ void ImGui::DebugNodeColumns(ImGuiOldColumns* columns) TreePop(); } +static void DebugNodeDockNodeFlags(ImGuiDockNodeFlags* p_flags, const char* label, bool enabled) +{ + using namespace ImGui; + PushID(label); + PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); + Text("%s:", label); + if (!enabled) + BeginDisabled(); + CheckboxFlags("NoResize", p_flags, ImGuiDockNodeFlags_NoResize); + CheckboxFlags("NoResizeX", p_flags, ImGuiDockNodeFlags_NoResizeX); + CheckboxFlags("NoResizeY",p_flags, ImGuiDockNodeFlags_NoResizeY); + CheckboxFlags("NoTabBar", p_flags, ImGuiDockNodeFlags_NoTabBar); + CheckboxFlags("HiddenTabBar", p_flags, ImGuiDockNodeFlags_HiddenTabBar); + CheckboxFlags("NoWindowMenuButton", p_flags, ImGuiDockNodeFlags_NoWindowMenuButton); + CheckboxFlags("NoCloseButton", p_flags, ImGuiDockNodeFlags_NoCloseButton); + CheckboxFlags("DockedWindowsInFocusRoute", p_flags, ImGuiDockNodeFlags_DockedWindowsInFocusRoute); + CheckboxFlags("NoDocking", p_flags, ImGuiDockNodeFlags_NoDocking); // Multiple flags + CheckboxFlags("NoDockingSplit", p_flags, ImGuiDockNodeFlags_NoDockingSplit); + CheckboxFlags("NoDockingSplitOther", p_flags, ImGuiDockNodeFlags_NoDockingSplitOther); + CheckboxFlags("NoDockingOver", p_flags, ImGuiDockNodeFlags_NoDockingOverMe); + CheckboxFlags("NoDockingOverOther", p_flags, ImGuiDockNodeFlags_NoDockingOverOther); + CheckboxFlags("NoDockingOverEmpty", p_flags, ImGuiDockNodeFlags_NoDockingOverEmpty); + CheckboxFlags("NoUndocking", p_flags, ImGuiDockNodeFlags_NoUndocking); + if (!enabled) + EndDisabled(); + PopStyleVar(); + PopID(); +} + +// [DEBUG] Display contents of ImDockNode +void ImGui::DebugNodeDockNode(ImGuiDockNode* node, const char* label) +{ + ImGuiContext& g = *GImGui; + const bool is_alive = (g.FrameCount - node->LastFrameAlive < 2); // Submitted with ImGuiDockNodeFlags_KeepAliveOnly + const bool is_active = (g.FrameCount - node->LastFrameActive < 2); // Submitted + if (!is_alive) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); } + bool open; + ImGuiTreeNodeFlags tree_node_flags = node->IsFocused ? ImGuiTreeNodeFlags_Selected : ImGuiTreeNodeFlags_None; + if (node->Windows.Size > 0) + open = TreeNodeEx((void*)(intptr_t)node->ID, tree_node_flags, "%s 0x%04X%s: %d windows (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", node->Windows.Size, node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); + else + open = TreeNodeEx((void*)(intptr_t)node->ID, tree_node_flags, "%s 0x%04X%s: %s (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", (node->SplitAxis == ImGuiAxis_X) ? "horizontal split" : (node->SplitAxis == ImGuiAxis_Y) ? "vertical split" : "empty", node->VisibleWindow ? node->VisibleWindow->Name : "NULL"); + if (!is_alive) { PopStyleColor(); } + if (is_active && IsItemHovered()) + if (ImGuiWindow* window = node->HostWindow ? node->HostWindow : node->VisibleWindow) + GetForegroundDrawList(window)->AddRect(node->Pos, node->Pos + node->Size, IM_COL32(255, 255, 0, 255)); + if (open) + { + IM_ASSERT(node->ChildNodes[0] == NULL || node->ChildNodes[0]->ParentNode == node); + IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node); + BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f) Ref (%.0f, %.0f)", + node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y); + DebugNodeWindow(node->HostWindow, "HostWindow"); + DebugNodeWindow(node->VisibleWindow, "VisibleWindow"); + BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabId, node->LastFocusedNodeId); + BulletText("Misc:%s%s%s%s%s%s%s", + node->IsDockSpace() ? " IsDockSpace" : "", + node->IsCentralNode() ? " IsCentralNode" : "", + is_alive ? " IsAlive" : "", is_active ? " IsActive" : "", node->IsFocused ? " IsFocused" : "", + node->WantLockSizeOnce ? " WantLockSizeOnce" : "", + node->HasCentralNodeChild ? " HasCentralNodeChild" : ""); + if (TreeNode("flags", "Flags Merged: 0x%04X, Local: 0x%04X, InWindows: 0x%04X, Shared: 0x%04X", node->MergedFlags, node->LocalFlags, node->LocalFlagsInWindows, node->SharedFlags)) + { + if (BeginTable("flags", 4)) + { + TableNextColumn(); DebugNodeDockNodeFlags(&node->MergedFlags, "MergedFlags", false); + TableNextColumn(); DebugNodeDockNodeFlags(&node->LocalFlags, "LocalFlags", true); + TableNextColumn(); DebugNodeDockNodeFlags(&node->LocalFlagsInWindows, "LocalFlagsInWindows", false); + TableNextColumn(); DebugNodeDockNodeFlags(&node->SharedFlags, "SharedFlags", true); + EndTable(); + } + TreePop(); + } + if (node->ParentNode) + DebugNodeDockNode(node->ParentNode, "ParentNode"); + if (node->ChildNodes[0]) + DebugNodeDockNode(node->ChildNodes[0], "Child[0]"); + if (node->ChildNodes[1]) + DebugNodeDockNode(node->ChildNodes[1], "Child[1]"); + if (node->TabBar) + DebugNodeTabBar(node->TabBar, "TabBar"); + DebugNodeWindowsList(&node->Windows, "Windows"); + + TreePop(); + } +} + // [DEBUG] Display contents of ImDrawList +// Note that both 'window' and 'viewport' may be NULL here. Viewport is generally null of destroyed popups which previously owned a viewport. void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label) { ImGuiContext& g = *GImGui; - IM_UNUSED(viewport); // Used in docking branch ImGuiMetricsConfig* cfg = &g.DebugMetricsConfig; int cmd_count = draw_list->CmdBuffer.Size; if (cmd_count > 0 && draw_list->CmdBuffer.back().ElemCount == 0 && draw_list->CmdBuffer.back().UserCallback == NULL) @@ -16015,7 +21893,7 @@ void ImGui::DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, con return; } - ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list + ImDrawList* fg_draw_list = viewport ? GetForegroundDrawList(viewport) : NULL; // Render additional visuals into the top-most draw list if (window && IsItemHovered() && fg_draw_list) fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); if (!node_open) @@ -16125,18 +22003,24 @@ void ImGui::DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, co // [DEBUG] Display details for a single font, called by ShowStyleEditor(). void ImGui::DebugNodeFont(ImFont* font) { - bool opened = TreeNode(font, "Font: \"%s\"\n%.2f px, %d glyphs, %d file(s)", + bool opened = TreeNode(font, "Font: \"%s\": %.2f px, %d glyphs, %d sources(s)", font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size, font->ConfigDataCount); - SameLine(); - if (SmallButton("Set as default")) - GetIO().FontDefault = font; - if (!opened) - return; // Display preview text + if (!opened) + Indent(); + Indent(); PushFont(font); Text("The quick brown fox jumps over the lazy dog"); PopFont(); + if (!opened) + { + Unindent(); + Unindent(); + return; + } + if (SmallButton("Set as default")) + GetIO().FontDefault = font; // Display details SetNextItemWidth(GetFontSize() * 8); @@ -16155,62 +22039,69 @@ void ImGui::DebugNodeFont(ImFont* font) Text("Texture Area: about %d px ~%dx%d px", font->MetricsTotalSurface, surface_sqrt, surface_sqrt); for (int config_i = 0; config_i < font->ConfigDataCount; config_i++) if (font->ConfigData) - if (const ImFontConfig* cfg = &font->ConfigData[config_i]) - BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d, Offset: (%.1f,%.1f)", - config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y); + { + const ImFontConfig* cfg = &font->ConfigData[config_i]; + int oversample_h, oversample_v; + ImFontAtlasBuildGetOversampleFactors(cfg, &oversample_h, &oversample_v); + BulletText("Input %d: \'%s\', Oversample: (%d=>%d,%d=>%d), PixelSnapH: %d, Offset: (%.1f,%.1f)", + config_i, cfg->Name, cfg->OversampleH, oversample_h, cfg->OversampleV, oversample_v, cfg->PixelSnapH, cfg->GlyphOffset.x, cfg->GlyphOffset.y); + } // Display all glyphs of the fonts in separate pages of 256 characters - if (TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) - { - ImDrawList* draw_list = GetWindowDrawList(); - const ImU32 glyph_col = GetColorU32(ImGuiCol_Text); - const float cell_size = font->FontSize * 1; - const float cell_spacing = GetStyle().ItemSpacing.y; - for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256) - { - // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k) - // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT - // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here) - if (!(base & 4095) && font->IsGlyphRangeUnused(base, base + 4095)) + { + if (TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) + { + ImDrawList* draw_list = GetWindowDrawList(); + const ImU32 glyph_col = GetColorU32(ImGuiCol_Text); + const float cell_size = font->FontSize * 1; + const float cell_spacing = GetStyle().ItemSpacing.y; + for (unsigned int base = 0; base <= IM_UNICODE_CODEPOINT_MAX; base += 256) { - base += 4096 - 256; - continue; - } - - int count = 0; - for (unsigned int n = 0; n < 256; n++) - if (font->FindGlyphNoFallback((ImWchar)(base + n))) - count++; - if (count <= 0) - continue; - if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph")) - continue; + // Skip ahead if a large bunch of glyphs are not present in the font (test in chunks of 4k) + // This is only a small optimization to reduce the number of iterations when IM_UNICODE_MAX_CODEPOINT + // is large // (if ImWchar==ImWchar32 we will do at least about 272 queries here) + if (!(base & 8191) && font->IsGlyphRangeUnused(base, base + 8191)) + { + base += 8192 - 256; + continue; + } - // Draw a 16x16 grid of glyphs - ImVec2 base_pos = GetCursorScreenPos(); - for (unsigned int n = 0; n < 256; n++) - { - // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions - // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string. - ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing)); - ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size); - const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n)); - draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50)); - if (!glyph) + int count = 0; + for (unsigned int n = 0; n < 256; n++) + if (font->FindGlyphNoFallback((ImWchar)(base + n))) + count++; + if (count <= 0) + continue; + if (!TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base + 255, count, count > 1 ? "glyphs" : "glyph")) continue; - font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n)); - if (IsMouseHoveringRect(cell_p1, cell_p2) && BeginTooltip()) + + // Draw a 16x16 grid of glyphs + ImVec2 base_pos = GetCursorScreenPos(); + for (unsigned int n = 0; n < 256; n++) { - DebugNodeFontGlyph(font, glyph); - EndTooltip(); + // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions + // available here and thus cannot easily generate a zero-terminated UTF-8 encoded string. + ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing)); + ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size); + const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base + n)); + draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255, 255, 255, 100) : IM_COL32(255, 255, 255, 50)); + if (!glyph) + continue; + font->RenderChar(draw_list, cell_size, cell_p1, glyph_col, (ImWchar)(base + n)); + if (IsMouseHoveringRect(cell_p1, cell_p2) && BeginTooltip()) + { + DebugNodeFontGlyph(font, glyph); + EndTooltip(); + } } + Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16)); + TreePop(); } - Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16)); TreePop(); } - TreePop(); } TreePop(); + Unindent(); } void ImGui::DebugNodeFontGlyph(ImFont*, const ImFontGlyph* glyph) @@ -16281,25 +22172,46 @@ void ImGui::DebugNodeViewport(ImGuiViewportP* viewport) { ImGuiContext& g = *GImGui; SetNextItemOpen(true, ImGuiCond_Once); - bool open = TreeNode("viewport0", "Viewport #%d", 0); + bool open = TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, Parent: 0x%08X, Window: \"%s\"", viewport->Idx, viewport->ID, viewport->ParentViewportId, viewport->Window ? viewport->Window->Name : "N/A"); if (IsItemHovered()) g.DebugMetricsConfig.HighlightViewportID = viewport->ID; if (open) { ImGuiWindowFlags flags = viewport->Flags; - BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Inset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f", + BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Inset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f\nMonitor: %d, DpiScale: %.0f%%", viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y, - viewport->WorkInsetMin.x, viewport->WorkInsetMin.y, viewport->WorkInsetMax.x, viewport->WorkInsetMax.y); - BulletText("Flags: 0x%04X =%s%s%s", viewport->Flags, - (flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "", + viewport->WorkInsetMin.x, viewport->WorkInsetMin.y, viewport->WorkInsetMax.x, viewport->WorkInsetMax.y, + viewport->PlatformMonitor, viewport->DpiScale * 100.0f); + if (viewport->Idx > 0) { SameLine(); if (SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200, 200); viewport->UpdateWorkRect(); if (viewport->Window) viewport->Window->Pos = viewport->Pos; } } + BulletText("Flags: 0x%04X =%s%s%s%s%s%s%s%s%s%s%s%s%s", viewport->Flags, + //(flags & ImGuiViewportFlags_IsPlatformWindow) ? " IsPlatformWindow" : "", // Omitting because it is the standard (flags & ImGuiViewportFlags_IsPlatformMonitor) ? " IsPlatformMonitor" : "", - (flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : ""); + (flags & ImGuiViewportFlags_IsMinimized) ? " IsMinimized" : "", + (flags & ImGuiViewportFlags_IsFocused) ? " IsFocused" : "", + (flags & ImGuiViewportFlags_OwnedByApp) ? " OwnedByApp" : "", + (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", + (flags & ImGuiViewportFlags_NoTaskBarIcon) ? " NoTaskBarIcon" : "", + (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "", + (flags & ImGuiViewportFlags_NoFocusOnClick) ? " NoFocusOnClick" : "", + (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "", + (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : "", + (flags & ImGuiViewportFlags_NoAutoMerge) ? " NoAutoMerge" : "", + (flags & ImGuiViewportFlags_TopMost) ? " TopMost" : "", + (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : ""); for (ImDrawList* draw_list : viewport->DrawDataP.CmdLists) DebugNodeDrawList(NULL, viewport, draw_list, "DrawList"); TreePop(); } } +void ImGui::DebugNodePlatformMonitor(ImGuiPlatformMonitor* monitor, const char* label, int idx) +{ + BulletText("%s %d: DPI %.0f%%\n MainMin (%.0f,%.0f), MainMax (%.0f,%.0f), MainSize (%.0f,%.0f)\n WorkMin (%.0f,%.0f), WorkMax (%.0f,%.0f), WorkSize (%.0f,%.0f)", + label, idx, monitor->DpiScale * 100.0f, + monitor->MainPos.x, monitor->MainPos.y, monitor->MainPos.x + monitor->MainSize.x, monitor->MainPos.y + monitor->MainSize.y, monitor->MainSize.x, monitor->MainSize.y, + monitor->WorkPos.x, monitor->WorkPos.y, monitor->WorkPos.x + monitor->WorkSize.x, monitor->WorkPos.y + monitor->WorkSize.y, monitor->WorkSize.x, monitor->WorkSize.y); +} + void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) { if (window == NULL) @@ -16338,6 +22250,7 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) (window->ChildFlags & ImGuiChildFlags_ResizeX) ? "ResizeX " : "", (window->ChildFlags & ImGuiChildFlags_ResizeY) ? "ResizeY " : "", (window->ChildFlags & ImGuiChildFlags_NavFlattened) ? "NavFlattened " : ""); + BulletText("WindowClassId: 0x%08X", window->WindowClass.ClassId); BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : ""); BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems); @@ -16354,7 +22267,15 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) for (int layer = 0; layer < ImGuiNavLayer_COUNT; layer++) BulletText("NavPreferredScoringPosRel[%d] = {%.1f,%.1f)", layer, (pr[layer].x == FLT_MAX ? -99999.0f : pr[layer].x), (pr[layer].y == FLT_MAX ? -99999.0f : pr[layer].y)); // Display as 99999.0f so it looks neater. BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); + + BulletText("Viewport: %d%s, ViewportId: 0x%08X, ViewportPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportOwned ? " (Owned)" : "", window->ViewportId, window->ViewportPos.x, window->ViewportPos.y); + BulletText("ViewportMonitor: %d", window->Viewport ? window->Viewport->PlatformMonitor : -1); + BulletText("DockId: 0x%04X, DockOrder: %d, Act: %d, Vis: %d", window->DockId, window->DockOrder, window->DockIsActive, window->DockTabIsVisible); + if (window->DockNode || window->DockNodeAsHost) + DebugNodeDockNode(window->DockNodeAsHost ? window->DockNodeAsHost : window->DockNode, window->DockNodeAsHost ? "DockNodeAsHost" : "DockNode"); + if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); } + if (window->RootWindowDockTree != window->RootWindow) { DebugNodeWindow(window->RootWindowDockTree, "RootWindowDockTree"); } if (window->ParentWindow != NULL) { DebugNodeWindow(window->ParentWindow, "ParentWindow"); } if (window->ParentWindowForFocusRoute != NULL) { DebugNodeWindow(window->ParentWindowForFocusRoute, "ParentWindowForFocusRoute"); } if (window->DC.ChildWindows.Size > 0) { DebugNodeWindowsList(&window->DC.ChildWindows, "ChildWindows"); } @@ -16495,12 +22416,14 @@ void ImGui::ShowDebugLogWindow(bool* p_open) ShowDebugLogFlag("Errors", ImGuiDebugLogFlags_EventError); ShowDebugLogFlag("ActiveId", ImGuiDebugLogFlags_EventActiveId); ShowDebugLogFlag("Clipper", ImGuiDebugLogFlags_EventClipper); + ShowDebugLogFlag("Docking", ImGuiDebugLogFlags_EventDocking); ShowDebugLogFlag("Focus", ImGuiDebugLogFlags_EventFocus); ShowDebugLogFlag("IO", ImGuiDebugLogFlags_EventIO); //ShowDebugLogFlag("Font", ImGuiDebugLogFlags_EventFont); ShowDebugLogFlag("Nav", ImGuiDebugLogFlags_EventNav); ShowDebugLogFlag("Popup", ImGuiDebugLogFlags_EventPopup); ShowDebugLogFlag("Selection", ImGuiDebugLogFlags_EventSelection); + ShowDebugLogFlag("Viewport", ImGuiDebugLogFlags_EventViewport); ShowDebugLogFlag("InputRouting", ImGuiDebugLogFlags_EventInputRouting); if (SmallButton("Clear")) diff --git a/neo/libs/imgui/imgui.h b/neo/libs/imgui/imgui.h index 0f7bdbbc6..8cf98a25b 100644 --- a/neo/libs/imgui/imgui.h +++ b/neo/libs/imgui/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.91.7 +// dear imgui, v1.91.8 WIP // (headers) // Help: @@ -28,9 +28,11 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.91.7" -#define IMGUI_VERSION_NUM 19170 +#define IMGUI_VERSION "1.91.8 WIP" +#define IMGUI_VERSION_NUM 19174 #define IMGUI_HAS_TABLE +#define IMGUI_HAS_VIEWPORT // Viewport WIP branch +#define IMGUI_HAS_DOCK // Docking WIP branch /* @@ -43,13 +45,13 @@ Index of this file: // [SECTION] Helpers: Debug log, Memory allocations macros, ImVector<> // [SECTION] ImGuiStyle // [SECTION] ImGuiIO -// [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiPayload) +// [SECTION] Misc data structures (ImGuiInputTextCallbackData, ImGuiSizeCallbackData, ImGuiWindowClass, ImGuiPayload) // [SECTION] Helpers (ImGuiOnceUponAFrame, ImGuiTextFilter, ImGuiTextBuffer, ImGuiStorage, ImGuiListClipper, Math Operators, ImColor) // [SECTION] Multi-Select API flags and structures (ImGuiMultiSelectFlags, ImGuiMultiSelectIO, ImGuiSelectionRequest, ImGuiSelectionBasicStorage, ImGuiSelectionExternalStorage) // [SECTION] Drawing API (ImDrawCallback, ImDrawCmd, ImDrawIdx, ImDrawVert, ImDrawChannel, ImDrawListSplitter, ImDrawFlags, ImDrawListFlags, ImDrawList, ImDrawData) // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontGlyphRangesBuilder, ImFontAtlasFlags, ImFontAtlas, ImFont) // [SECTION] Viewports (ImGuiViewportFlags, ImGuiViewport) -// [SECTION] ImGuiPlatformIO + other Platform Dependent Interfaces (ImGuiPlatformImeData) +// [SECTION] ImGuiPlatformIO + other Platform Dependent Interfaces (ImGuiPlatformMonitor, ImGuiPlatformImeData) // [SECTION] Obsolete functions and types */ @@ -158,7 +160,7 @@ typedef unsigned int ImU32; // 32-bit unsigned integer (often used to st typedef signed long long ImS64; // 64-bit signed integer typedef unsigned long long ImU64; // 64-bit unsigned integer -// Forward declarations +// Forward declarations: ImDrawList, ImFontAtlas layer struct ImDrawChannel; // Temporary storage to output draw commands out of order, used by ImDrawListSplitter and ImDrawList::ChannelsSplit() struct ImDrawCmd; // A single draw command within a parent ImDrawList (generally maps to 1 GPU draw call, unless it is a callback) struct ImDrawData; // All draw command lists required to render the frame + pos/size coordinates to use for the projection matrix. @@ -173,6 +175,8 @@ struct ImFontConfig; // Configuration data when adding a font or struct ImFontGlyph; // A single font glyph (code point + coordinates within in ImFontAtlas + offset) struct ImFontGlyphRangesBuilder; // Helper to build glyph ranges from text/string data struct ImColor; // Helper functions to create a color that can be converted to either u32 or float4 (*OBSOLETE* please avoid using) + +// Forward declarations: ImGui layer struct ImGuiContext; // Dear ImGui context (opaque structure, unless including imgui_internal.h) struct ImGuiIO; // Main configuration and I/O between your application and ImGui (also see: ImGuiPlatformIO) struct ImGuiInputTextCallbackData; // Shared state of InputText() when using custom ImGuiInputTextCallback (rare/advanced use) @@ -181,8 +185,9 @@ struct ImGuiListClipper; // Helper to manually clip large list of ite struct ImGuiMultiSelectIO; // Structure to interact with a BeginMultiSelect()/EndMultiSelect() block struct ImGuiOnceUponAFrame; // Helper for running a block of code not more than once a frame struct ImGuiPayload; // User data payload for drag and drop operations -struct ImGuiPlatformIO; // Interface between platform/renderer backends and ImGui (e.g. Clipboard, IME hooks). Extends ImGuiIO. In docking branch, this gets extended to support multi-viewports. +struct ImGuiPlatformIO; // Interface between platform/renderer backends and ImGui (e.g. Clipboard, IME, Multi-Viewport support). Extends ImGuiIO. struct ImGuiPlatformImeData; // Platform IME data for io.PlatformSetImeDataFn() function. +struct ImGuiPlatformMonitor; // Multi-viewport support: user-provided bounds for each connected monitor/display. Used when positioning popups and tooltips to avoid them straddling monitors struct ImGuiSelectionBasicStorage; // Optional helper to store multi-selection state + apply multi-selection requests. struct ImGuiSelectionExternalStorage;//Optional helper to apply multi-selection requests to existing randomly accessible storage. struct ImGuiSelectionRequest; // A selection request (stored in ImGuiMultiSelectIO) @@ -194,7 +199,8 @@ struct ImGuiTableSortSpecs; // Sorting specifications for a table (often struct ImGuiTableColumnSortSpecs; // Sorting specification for one column of a table struct ImGuiTextBuffer; // Helper to hold and append into a text buffer (~string builder) struct ImGuiTextFilter; // Helper to parse and apply text filters (e.g. "aaaaa[,bbbbb][,ccccc]") -struct ImGuiViewport; // A Platform Window (always only one in 'master' branch), in the future may represent Platform Monitor +struct ImGuiViewport; // A Platform Window (always 1 unless multi-viewport are enabled. One per platform window to output to). In the future may represent Platform Monitor +struct ImGuiWindowClass; // Window class (rare/advanced uses: provide hints to the platform backend via altered viewport flags and parent/child info) // Enumerations // - We don't use strongly typed enums much because they add constraints (can't extend in private code, can't store typed in bit fields, extra casting on iteration) @@ -228,6 +234,7 @@ typedef int ImGuiChildFlags; // -> enum ImGuiChildFlags_ // Flags: f typedef int ImGuiColorEditFlags; // -> enum ImGuiColorEditFlags_ // Flags: for ColorEdit4(), ColorPicker4() etc. typedef int ImGuiConfigFlags; // -> enum ImGuiConfigFlags_ // Flags: for io.ConfigFlags typedef int ImGuiComboFlags; // -> enum ImGuiComboFlags_ // Flags: for BeginCombo() +typedef int ImGuiDockNodeFlags; // -> enum ImGuiDockNodeFlags_ // Flags: for DockSpace() typedef int ImGuiDragDropFlags; // -> enum ImGuiDragDropFlags_ // Flags: for BeginDragDropSource(), AcceptDragDropPayload() typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: for IsWindowFocused() typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc. @@ -399,10 +406,12 @@ namespace ImGui IMGUI_API bool IsWindowFocused(ImGuiFocusedFlags flags=0); // is current window focused? or its root/child, depending on flags. see flags for options. IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered and hoverable (e.g. not blocked by a popup/modal)? See ImGuiHoveredFlags_ for options. IMPORTANT: If you are trying to check whether your mouse should be dispatched to Dear ImGui or to your underlying app, you should not use this function! Use the 'io.WantCaptureMouse' boolean for that! Refer to FAQ entry "How can I tell whether to dispatch mouse/keyboard to Dear ImGui or my application?" for details. IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the current window, to append your own drawing primitives + IMGUI_API float GetWindowDpiScale(); // get DPI scale currently associated to the current window's viewport. IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (IT IS UNLIKELY YOU EVER NEED TO USE THIS. Consider always using GetCursorScreenPos() and GetContentRegionAvail() instead) IMGUI_API ImVec2 GetWindowSize(); // get current window size (IT IS UNLIKELY YOU EVER NEED TO USE THIS. Consider always using GetCursorScreenPos() and GetContentRegionAvail() instead) IMGUI_API float GetWindowWidth(); // get current window width (IT IS UNLIKELY YOU EVER NEED TO USE THIS). Shortcut for GetWindowSize().x. IMGUI_API float GetWindowHeight(); // get current window height (IT IS UNLIKELY YOU EVER NEED TO USE THIS). Shortcut for GetWindowSize().y. + IMGUI_API ImGuiViewport*GetWindowViewport(); // get viewport currently associated to the current window. // Window manipulation // - Prefer using SetNextXXX functions (before Begin) rather that SetXXX functions (after Begin). @@ -414,6 +423,7 @@ namespace ImGui IMGUI_API void SetNextWindowFocus(); // set next window to be focused / top-most. call before Begin() IMGUI_API void SetNextWindowScroll(const ImVec2& scroll); // set next window scrolling value (use < 0.0f to not affect a given axis). IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily override the Alpha component of ImGuiCol_WindowBg/ChildBg/PopupBg. you may also use ImGuiWindowFlags_NoBackground. + IMGUI_API void SetNextWindowViewport(ImGuiID viewport_id); // set next window viewport IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0); // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects. IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0, 0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). @@ -867,6 +877,26 @@ namespace ImGui IMGUI_API bool TabItemButton(const char* label, ImGuiTabItemFlags flags = 0); // create a Tab behaving like a button. return true when clicked. cannot be selected in the tab bar. IMGUI_API void SetTabItemClosed(const char* tab_or_docked_window_label); // notify TabBar or Docking system of a closed tab/window ahead (useful to reduce visual flicker on reorderable tab bars). For tab-bar: call after BeginTabBar() and before Tab submissions. Otherwise call with a window name. + // Docking + // [BETA API] Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable. + // Note: You can use most Docking facilities without calling any API. You DO NOT need to call DockSpace() to use Docking! + // - Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking. + // - Drag from window menu button (upper-left button) to undock an entire node (all windows). + // - When io.ConfigDockingWithShift == true, you instead need to hold SHIFT to enable docking. + // About dockspaces: + // - Use DockSpaceOverViewport() to create a window covering the screen or a specific viewport + a dockspace inside it. + // This is often used with ImGuiDockNodeFlags_PassthruCentralNode to make it transparent. + // - Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details. + // - Important: Dockspaces need to be submitted _before_ any window they can host. Submit it early in your frame! + // - Important: Dockspaces need to be kept alive if hidden, otherwise windows docked into it will be undocked. + // e.g. if you have multiple tabs with a dockspace inside each tab: submit the non-visible dockspaces with ImGuiDockNodeFlags_KeepAliveOnly. + IMGUI_API ImGuiID DockSpace(ImGuiID dockspace_id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); + IMGUI_API ImGuiID DockSpaceOverViewport(ImGuiID dockspace_id = 0, const ImGuiViewport* viewport = NULL, ImGuiDockNodeFlags flags = 0, const ImGuiWindowClass* window_class = NULL); + IMGUI_API void SetNextWindowDockID(ImGuiID dock_id, ImGuiCond cond = 0); // set next window dock id + IMGUI_API void SetNextWindowClass(const ImGuiWindowClass* window_class); // set next window class (control docking compatibility + provide hints to platform backend via custom viewport flags and platform parent/child relationship) + IMGUI_API ImGuiID GetWindowDockID(); + IMGUI_API bool IsWindowDocked(); // is current window docked into another window? + // Logging/Capture // - All text output from the interface can be captured into tty/file/clipboard. By default, tree nodes are automatically opened during logging. IMGUI_API void LogToTTY(int auto_open_depth = -1); // start logging to tty (stdout) @@ -941,8 +971,8 @@ namespace ImGui IMGUI_API ImGuiViewport* GetMainViewport(); // return primary/default viewport. This can never be NULL. // Background/Foreground Draw Lists - IMGUI_API ImDrawList* GetBackgroundDrawList(); // this draw list will be the first rendered one. Useful to quickly draw shapes/text behind dear imgui contents. - IMGUI_API ImDrawList* GetForegroundDrawList(); // this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents. + IMGUI_API ImDrawList* GetBackgroundDrawList(ImGuiViewport* viewport = NULL); // get background draw list for the given viewport or viewport associated to the current window. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents. + IMGUI_API ImDrawList* GetForegroundDrawList(ImGuiViewport* viewport = NULL); // get foreground draw list for the given viewport or viewport associated to the current window. this draw list will be the top-most rendered one. Useful to quickly draw shapes/text over dear imgui contents. // Miscellaneous Utilities IMGUI_API bool IsRectVisible(const ImVec2& size); // test if rectangle (of given size, starting from cursor position) is visible / not clipped. @@ -1009,6 +1039,7 @@ namespace ImGui IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, bool repeat = false); // did mouse button clicked? (went from !Down to Down). Same as GetMouseClickedCount() == 1. IMGUI_API bool IsMouseReleased(ImGuiMouseButton button); // did mouse button released? (went from Down to !Down) IMGUI_API bool IsMouseDoubleClicked(ImGuiMouseButton button); // did mouse button double-clicked? Same as GetMouseClickedCount() == 2. (note that a double-click will also report IsMouseClicked() == true) + IMGUI_API bool IsMouseReleasedWithDelay(ImGuiMouseButton button, float delay); // delayed mouse release (use very sparingly!). Generally used with 'delay >= io.MouseDoubleClickTime' + combined with a 'io.MouseClickedLastCount==1' test. This is a very rarely used UI idiom, but some apps use this: e.g. MS Explorer single click on an icon to rename. IMGUI_API int GetMouseClickedCount(ImGuiMouseButton button); // return the number of successive mouse-clicks at the time where a click happen (otherwise 0). IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true);// is mouse hovering given bounding rect (in screen space). clipped by current clipping settings, but disregarding of other consideration of focus/window ordering/popup-block. IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // by convention we use (-FLT_MAX,-FLT_MAX) to denote that there is no mouse available @@ -1056,6 +1087,15 @@ namespace ImGui IMGUI_API void* MemAlloc(size_t size); IMGUI_API void MemFree(void* ptr); + // (Optional) Platform/OS interface for multi-viewport support + // Read comments around the ImGuiPlatformIO structure for more details. + // Note: You may use GetWindowViewport() to get the current viewport of the current window. + IMGUI_API void UpdatePlatformWindows(); // call in main loop. will call CreateWindow/ResizeWindow/etc. platform functions for each secondary viewport, and DestroyWindow for each inactive viewport. + IMGUI_API void RenderPlatformWindowsDefault(void* platform_render_arg = NULL, void* renderer_render_arg = NULL); // call in main loop. will call RenderWindow/SwapBuffers platform functions for each secondary viewport which doesn't have the ImGuiViewportFlags_Minimized flag set. May be reimplemented by user for custom rendering needs. + IMGUI_API void DestroyPlatformWindows(); // call DestroyWindow platform functions for all viewports. call from backend Shutdown() if you need to close platform windows before imgui shutdown. otherwise will be called by DestroyContext(). + IMGUI_API ImGuiViewport* FindViewportByID(ImGuiID id); // this is a helper for backends. + IMGUI_API ImGuiViewport* FindViewportByPlatformHandle(void* platform_handle); // this is a helper for backends. the type platform_handle is decided by the backend (e.g. HWND, MyWindow*, GLFWwindow* etc.) + } // namespace ImGui //----------------------------------------------------------------------------- @@ -1086,11 +1126,13 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_NoNavInputs = 1 << 16, // No keyboard/gamepad navigation within the window ImGuiWindowFlags_NoNavFocus = 1 << 17, // No focusing toward this window with keyboard/gamepad navigation (e.g. skipped by CTRL+TAB) ImGuiWindowFlags_UnsavedDocument = 1 << 18, // Display a dot next to the title. When used in a tab/docking context, tab is selected when clicking the X + closure is not assumed (will wait for user to stop submitting the tab). Otherwise closure is assumed when pressing the X, so if you keep submitting the tab may reappear at end of tab bar. + ImGuiWindowFlags_NoDocking = 1 << 19, // Disable docking of this window ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, ImGuiWindowFlags_NoDecoration = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse, ImGuiWindowFlags_NoInputs = ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, // [Internal] + ImGuiWindowFlags_DockNodeHost = 1 << 23, // Don't use! For internal use by Begin()/NewFrame() ImGuiWindowFlags_ChildWindow = 1 << 24, // Don't use! For internal use by BeginChild() ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip() ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup() @@ -1315,7 +1357,7 @@ enum ImGuiFocusedFlags_ ImGuiFocusedFlags_RootWindow = 1 << 1, // Test from root window (top most parent of the current hierarchy) ImGuiFocusedFlags_AnyWindow = 1 << 2, // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! ImGuiFocusedFlags_NoPopupHierarchy = 1 << 3, // Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) - //ImGuiFocusedFlags_DockHierarchy = 1 << 4, // Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) + ImGuiFocusedFlags_DockHierarchy = 1 << 4, // Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows, }; @@ -1329,7 +1371,7 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered ImGuiHoveredFlags_NoPopupHierarchy = 1 << 3, // IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) - //ImGuiHoveredFlags_DockHierarchy = 1 << 4, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) + ImGuiHoveredFlags_DockHierarchy = 1 << 4, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 5, // Return true even if a popup window is normally blocking access to this item/window //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 6, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 7, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. @@ -1359,6 +1401,27 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_NoSharedDelay = 1 << 17, // IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays) }; +// Flags for ImGui::DockSpace(), shared/inherited by child nodes. +// (Some flags can be applied to individual nodes directly) +// FIXME-DOCK: Also see ImGuiDockNodeFlagsPrivate_ which may involve using the WIP and internal DockBuilder api. +enum ImGuiDockNodeFlags_ +{ + ImGuiDockNodeFlags_None = 0, + ImGuiDockNodeFlags_KeepAliveOnly = 1 << 0, // // Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked. + //ImGuiDockNodeFlags_NoCentralNode = 1 << 1, // // Disable Central Node (the node which can stay empty) + ImGuiDockNodeFlags_NoDockingOverCentralNode = 1 << 2, // // Disable docking over the Central Node, which will be always kept empty. + ImGuiDockNodeFlags_PassthruCentralNode = 1 << 3, // // Enable passthru dockspace: 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. See demo for details. + ImGuiDockNodeFlags_NoDockingSplit = 1 << 4, // // Disable other windows/nodes from splitting this node. + ImGuiDockNodeFlags_NoResize = 1 << 5, // Saved // Disable resizing node using the splitter/separators. Useful with programmatically setup dockspaces. + ImGuiDockNodeFlags_AutoHideTabBar = 1 << 6, // // Tab bar will automatically hide when there is a single window in the dock node. + ImGuiDockNodeFlags_NoUndocking = 1 << 7, // // Disable undocking this node. + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGuiDockNodeFlags_NoSplit = ImGuiDockNodeFlags_NoDockingSplit, // Renamed in 1.90 + ImGuiDockNodeFlags_NoDockingInCentralNode = ImGuiDockNodeFlags_NoDockingOverCentralNode, // Renamed in 1.90 +#endif +}; + // Flags for ImGui::BeginDragDropSource(), ImGui::AcceptDragDropPayload() enum ImGuiDragDropFlags_ { @@ -1589,6 +1652,15 @@ enum ImGuiConfigFlags_ ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct backend to not alter mouse cursor shape and visibility. Use if the backend cursor changes are interfering with yours and you don't want to use SetMouseCursor() to change mouse cursor. You may want to honor requests from imgui by reading GetMouseCursor() yourself instead. ImGuiConfigFlags_NoKeyboard = 1 << 6, // Instruct dear imgui to disable keyboard inputs and interactions. This is done by ignoring keyboard events and clearing existing states. + // [BETA] Docking + ImGuiConfigFlags_DockingEnable = 1 << 7, // Docking enable flags. + + // [BETA] Viewports + // When using viewports it is recommended that your default value for ImGuiCol_WindowBg is opaque (Alpha=1.0) so transition to a viewport won't be noticeable. + ImGuiConfigFlags_ViewportsEnable = 1 << 10, // Viewport enable flags (require both ImGuiBackendFlags_PlatformHasViewports + ImGuiBackendFlags_RendererHasViewports set by the respective backends) + ImGuiConfigFlags_DpiEnableScaleViewports= 1 << 14, // [BETA: Don't use] FIXME-DPI: Reposition and resize imgui windows when the DpiScale of a viewport changed (mostly useful for the main viewport hosting other window). Note that resizing the main window itself is up to your application. + ImGuiConfigFlags_DpiEnableScaleFonts = 1 << 15, // [BETA: Don't use] FIXME-DPI: Request bitmap-scaled fonts to match DpiScale. This is a very low-quality workaround. The correct way to handle DPI is _currently_ to replace the atlas and/or fonts in the Platform_OnChangedViewport callback, but this is all early work in progress. + // User storage (to allow your backend/engine to communicate to code that may be shared between multiple projects. Those flags are NOT used by core Dear ImGui) ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. ImGuiConfigFlags_IsTouchScreen = 1 << 21, // Application is using a touch screen instead of a mouse. @@ -1607,6 +1679,11 @@ enum ImGuiBackendFlags_ ImGuiBackendFlags_HasMouseCursors = 1 << 1, // Backend Platform supports honoring GetMouseCursor() value to change the OS cursor shape. ImGuiBackendFlags_HasSetMousePos = 1 << 2, // Backend Platform supports io.WantSetMousePos requests to reposition the OS mouse position (only used if io.ConfigNavMoveSetMousePos is set). ImGuiBackendFlags_RendererHasVtxOffset = 1 << 3, // Backend Renderer supports ImDrawCmd::VtxOffset. This enables output of large meshes (64K+ vertices) while still using 16-bit indices. + + // [BETA] Viewports + ImGuiBackendFlags_PlatformHasViewports = 1 << 10, // Backend Platform supports multiple viewports. + ImGuiBackendFlags_HasMouseHoveredViewport=1 << 11, // Backend Platform supports calling io.AddMouseViewportEvent() with the viewport under the mouse. IF POSSIBLE, ignore viewports with the ImGuiViewportFlags_NoInputs flag (Win32 backend, GLFW 3.30+ backend can do this, SDL backend cannot). If this cannot be done, Dear ImGui needs to use a flawed heuristic to find the viewport under. + ImGuiBackendFlags_RendererHasViewports = 1 << 12, // Backend Renderer supports multiple viewports. }; // Enumeration for PushStyleColor() / PopStyleColor() @@ -1652,6 +1729,8 @@ enum ImGuiCol_ ImGuiCol_TabDimmed, // Tab background, when tab-bar is unfocused & tab is unselected ImGuiCol_TabDimmedSelected, // Tab background, when tab-bar is unfocused & tab is selected ImGuiCol_TabDimmedSelectedOverline,//..horizontal overline, when tab-bar is unfocused & tab is selected + ImGuiCol_DockingPreview, // Preview overlay color when about to docking something + ImGuiCol_DockingEmptyBg, // Background color for empty node (e.g. CentralNode with no window docked into it) ImGuiCol_PlotLines, ImGuiCol_PlotLinesHovered, ImGuiCol_PlotHistogram, @@ -1722,6 +1801,7 @@ enum ImGuiStyleVar_ ImGuiStyleVar_SeparatorTextBorderSize, // float SeparatorTextBorderSize ImGuiStyleVar_SeparatorTextAlign, // ImVec2 SeparatorTextAlign ImGuiStyleVar_SeparatorTextPadding, // ImVec2 SeparatorTextPadding + ImGuiStyleVar_DockingSeparatorSize, // float DockingSeparatorSize ImGuiStyleVar_COUNT }; @@ -1751,10 +1831,16 @@ enum ImGuiColorEditFlags_ ImGuiColorEditFlags_NoDragDrop = 1 << 9, // // ColorEdit: disable drag and drop target. ColorButton: disable drag and drop source. ImGuiColorEditFlags_NoBorder = 1 << 10, // // ColorButton: disable border (which is enforced by default) + // Alpha preview + // - Prior to 1.91.8 (2025/01/21): alpha was made opaque in the preview by default using old name ImGuiColorEditFlags_AlphaPreview. + // - We now display the preview as transparent by default. You can use ImGuiColorEditFlags_AlphaOpaque to use old behavior. + // - The new flags may be combined better and allow finer controls. + ImGuiColorEditFlags_AlphaOpaque = 1 << 11, // // ColorEdit, ColorPicker, ColorButton: disable alpha in the preview,. Contrary to _NoAlpha it may still be edited when calling ColorEdit4()/ColorPicker4(). For ColorButton() this does the same as _NoAlpha. + ImGuiColorEditFlags_AlphaNoBg = 1 << 12, // // ColorEdit, ColorPicker, ColorButton: disable rendering a checkerboard background behind transparent color. + ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 13, // // ColorEdit, ColorPicker, ColorButton: display half opaque / half transparent preview. + // User Options (right-click on widget to change some of them). ImGuiColorEditFlags_AlphaBar = 1 << 16, // // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker. - ImGuiColorEditFlags_AlphaPreview = 1 << 17, // // ColorEdit, ColorPicker, ColorButton: display preview as a transparent color over a checkerboard, instead of opaque. - ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 18, // // ColorEdit, ColorPicker, ColorButton: display half opaque / half checkerboard, instead of opaque. ImGuiColorEditFlags_HDR = 1 << 19, // // (WIP) ColorEdit: Currently only disable 0.0f..1.0f limits in RGBA edition (note: you probably want to use ImGuiColorEditFlags_Float flag as well). ImGuiColorEditFlags_DisplayRGB = 1 << 20, // [Display] // ColorEdit: override _display_ type among RGB/HSV/Hex. ColorPicker: select any combination using one or more of RGB/HSV/Hex. ImGuiColorEditFlags_DisplayHSV = 1 << 21, // [Display] // " @@ -1771,12 +1857,16 @@ enum ImGuiColorEditFlags_ ImGuiColorEditFlags_DefaultOptions_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_PickerHueBar, // [Internal] Masks + ImGuiColorEditFlags_AlphaMask_ = ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaOpaque | ImGuiColorEditFlags_AlphaNoBg | ImGuiColorEditFlags_AlphaPreviewHalf, ImGuiColorEditFlags_DisplayMask_ = ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex, ImGuiColorEditFlags_DataTypeMask_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float, ImGuiColorEditFlags_PickerMask_ = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar, ImGuiColorEditFlags_InputMask_ = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV, // Obsolete names +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + ImGuiColorEditFlags_AlphaPreview = 0, // [Removed in 1.91.8] This is the default now. Will display a checkerboard unless ImGuiColorEditFlags_AlphaNoBg is set. +#endif //ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex // [renamed in 1.69] }; @@ -2169,6 +2259,7 @@ struct ImGuiStyle ImVec2 SeparatorTextPadding; // Horizontal offset of text from each edge of the separator + spacing on other axis. Generally small values. .y is recommended to be == FramePadding.y. ImVec2 DisplayWindowPadding; // Apply to regular windows: amount which we enforce to keep visible when moving near edges of your screen. ImVec2 DisplaySafeAreaPadding; // Apply to every windows, menus, popups, tooltips: amount where we avoid displaying contents. Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured). + float DockingSeparatorSize; // Thickness of resizing border between docked windows float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). We apply per-monitor DPI scaling over this scale. May be removed later. bool AntiAliasedLines; // Enable anti-aliased lines/borders. Disable if you are really tight on CPU/GPU. Latched at the beginning of the frame (copied to ImDrawList). bool AntiAliasedLinesUseTex; // Enable anti-aliased lines/borders using textures where possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). Latched at the beginning of the frame (copied to ImDrawList). @@ -2242,6 +2333,18 @@ struct ImGuiIO bool ConfigNavCursorVisibleAuto; // = true // Using directional navigation key makes the cursor visible. Mouse click hides the cursor. bool ConfigNavCursorVisibleAlways; // = false // Navigation cursor is always visible. + // Docking options (when ImGuiConfigFlags_DockingEnable is set) + bool ConfigDockingNoSplit; // = false // Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars. + bool ConfigDockingWithShift; // = false // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space) + bool ConfigDockingAlwaysTabBar; // = false // [BETA] [FIXME: This currently creates regression with auto-sizing and general overhead] Make every single floating window display within a docking node. + bool ConfigDockingTransparentPayload;// = false // [BETA] Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport cannot be synced. Best used with ConfigViewportsNoAutoMerge. + + // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set) + bool ConfigViewportsNoAutoMerge; // = false; // Set to make all floating imgui windows always create their own viewport. Otherwise, they are merged into the main host viewports when overlapping it. May also set ImGuiViewportFlags_NoAutoMerge on individual viewport. + bool ConfigViewportsNoTaskBarIcon; // = false // Disable default OS task bar icon flag for secondary viewports. When a viewport doesn't want a task bar icon, ImGuiViewportFlags_NoTaskBarIcon will be set on it. + bool ConfigViewportsNoDecoration; // = true // Disable default OS window decoration flag for secondary viewports. When a viewport doesn't want window decorations, ImGuiViewportFlags_NoDecoration will be set on it. Enabling decoration can create subsequent issues at OS levels (e.g. minimum window size). + bool ConfigViewportsNoDefaultParent; // = false // Disable default OS parenting to main viewport for secondary viewports. By default, viewports are marked with ParentViewportId = , expecting the platform backend to setup a parent/child relationship between the OS windows (some backend may ignore this). Set to true if you want the default to be 0, then all viewports will be top-level OS windows. + // Miscellaneous options // (you can visualize and interact with all options in 'Demo->Configuration') bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by backend implementations. @@ -2338,6 +2441,7 @@ struct ImGuiIO IMGUI_API void AddMouseButtonEvent(int button, bool down); // Queue a mouse button change IMGUI_API void AddMouseWheelEvent(float wheel_x, float wheel_y); // Queue a mouse wheel update. wheel_y<0: scroll down, wheel_y>0: scroll up, wheel_x<0: scroll right, wheel_x>0: scroll left. IMGUI_API void AddMouseSourceEvent(ImGuiMouseSource source); // Queue a mouse source change (Mouse/TouchScreen/Pen) + IMGUI_API void AddMouseViewportEvent(ImGuiID id); // Queue a mouse hovered viewport. Requires backend to set ImGuiBackendFlags_HasMouseHoveredViewport to call this (for multi-viewport support). IMGUI_API void AddFocusEvent(bool focused); // Queue a gain/loss of focus for the application (generally based on OS/platform focus of your window) IMGUI_API void AddInputCharacter(unsigned int c); // Queue a new character input IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue a new character input from a UTF-16 character, it can be a surrogate @@ -2386,6 +2490,7 @@ struct ImGuiIO float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. >0 scrolls Up, <0 scrolls Down. Hold SHIFT to turn vertical scroll into horizontal scroll. float MouseWheelH; // Mouse wheel Horizontal. >0 scrolls Left, <0 scrolls Right. Most users don't have a mouse with a horizontal wheel, may not be filled by all backends. ImGuiMouseSource MouseSource; // Mouse actual input peripheral (Mouse/TouchScreen/Pen). + ImGuiID MouseHoveredViewport; // (Optional) Modify using io.AddMouseViewportEvent(). With multi-viewports: viewport the OS mouse is hovering. If possible _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag is much better (few backends can handle that). Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. If you don't imgui will infer the value using the rectangles and last focused time of the viewports it knows about (ignoring other OS windows). bool KeyCtrl; // Keyboard modifier down: Control bool KeyShift; // Keyboard modifier down: Shift bool KeyAlt; // Keyboard modifier down: Alt @@ -2403,12 +2508,14 @@ struct ImGuiIO ImU16 MouseClickedCount[5]; // == 0 (not clicked), == 1 (same as MouseClicked[]), == 2 (double-clicked), == 3 (triple-clicked) etc. when going from !Down to Down ImU16 MouseClickedLastCount[5]; // Count successive number of clicks. Stays valid after mouse release. Reset after another click is done. bool MouseReleased[5]; // Mouse button went from Down to !Down + double MouseReleasedTime[5]; // Time of last released (rarely used! but useful to handle delayed single-click when trying to disambiguate them from double-click). bool MouseDownOwned[5]; // Track if button was clicked inside a dear imgui window or over void blocked by a popup. We don't request mouse capture from the application if click started outside ImGui bounds. bool MouseDownOwnedUnlessPopupClose[5]; // Track if button was clicked inside a dear imgui window. bool MouseWheelRequestAxisSwap; // On a non-Mac system, holding SHIFT requests WheelY to perform the equivalent of a WheelX event. On a Mac system this is already enforced by the system. bool MouseCtrlLeftAsRightClick; // (OSX) Set to true when the current click was a ctrl-click that spawned a simulated right click float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) float MouseDownDurationPrev[5]; // Previous time the mouse button has been down + ImVec2 MouseDragMaxDistanceAbs[5]; // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point (used for moving thresholds) float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. bool AppFocusLost; // Only modify via AddFocusEvent() @@ -2492,6 +2599,28 @@ struct ImGuiSizeCallbackData ImVec2 DesiredSize; // Read-write. Desired size, based on user's mouse position. Write to this field to restrain resizing. }; +// [ALPHA] Rarely used / very advanced uses only. Use with SetNextWindowClass() and DockSpace() functions. +// Important: the content of this class is still highly WIP and likely to change and be refactored +// before we stabilize Docking features. Please be mindful if using this. +// Provide hints: +// - To the platform backend via altered viewport flags (enable/disable OS decoration, OS task bar icons, etc.) +// - To the platform backend for OS level parent/child relationships of viewport. +// - To the docking system for various options and filtering. +struct ImGuiWindowClass +{ + ImGuiID ClassId; // User data. 0 = Default class (unclassed). Windows of different classes cannot be docked with each others. + ImGuiID ParentViewportId; // Hint for the platform backend. -1: use default. 0: request platform backend to not parent the platform. != 0: request platform backend to create a parent<>child relationship between the platform windows. Not conforming backends are free to e.g. parent every viewport to the main viewport or not. + ImGuiID FocusRouteParentWindowId; // ID of parent window for shortcut focus route evaluation, e.g. Shortcut() call from Parent Window will succeed when this window is focused. + ImGuiViewportFlags ViewportFlagsOverrideSet; // Viewport flags to set when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. + ImGuiViewportFlags ViewportFlagsOverrideClear; // Viewport flags to clear when a window of this class owns a viewport. This allows you to enforce OS decoration or task bar icon, override the defaults on a per-window basis. + ImGuiTabItemFlags TabItemFlagsOverrideSet; // [EXPERIMENTAL] TabItem flags to set when a window of this class gets submitted into a dock node tab bar. May use with ImGuiTabItemFlags_Leading or ImGuiTabItemFlags_Trailing. + ImGuiDockNodeFlags DockNodeFlagsOverrideSet; // [EXPERIMENTAL] Dock node flags to set when a window of this class is hosted by a dock node (it doesn't have to be selected!) + bool DockingAlwaysTabBar; // Set to true to enforce single floating windows of this class always having their own docking node (equivalent of setting the global io.ConfigDockingAlwaysTabBar) + bool DockingAllowUnclassed; // Set to true to allow windows of this class to be docked/merged with an unclassed window. // FIXME-DOCK: Move to DockNodeFlags override? + + ImGuiWindowClass() { memset(this, 0, sizeof(*this)); ParentViewportId = (ImGuiID)-1; DockingAllowUnclassed = true; } +}; + // Data payload for Drag and Drop operations: AcceptDragDropPayload(), GetDragDropPayload() struct ImGuiPayload { @@ -2923,7 +3052,7 @@ struct ImGuiSelectionExternalStorage // The maximum line width to bake anti-aliased textures for. Build atlas with ImFontAtlasFlags_NoBakedLines to disable baking. #ifndef IM_DRAWLIST_TEX_LINES_WIDTH_MAX -#define IM_DRAWLIST_TEX_LINES_WIDTH_MAX (63) +#define IM_DRAWLIST_TEX_LINES_WIDTH_MAX (32) #endif // ImDrawCallback: Draw callbacks for advanced uses [configurable type: override in imconfig.h] @@ -2997,7 +3126,6 @@ struct ImDrawChannel ImVector _IdxBuffer; }; - // Split/Merge functions are used to split the draw list into different layers which can be drawn into out of order. // This is used by the Columns/Tables API, so items of each column can be batched together in a same draw call. struct ImDrawListSplitter @@ -3211,7 +3339,7 @@ struct ImDrawList struct ImDrawData { bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. - int CmdListsCount; // Number of ImDrawList* to render (should always be == CmdLists.size) + int CmdListsCount; // Number of ImDrawList* to render int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size ImVector CmdLists; // Array of ImDrawList* to render. The ImDrawLists are owned by ImGuiContext and only pointed to from here. @@ -3232,26 +3360,27 @@ struct ImDrawData // [SECTION] Font API (ImFontConfig, ImFontGlyph, ImFontAtlasFlags, ImFontAtlas, ImFontGlyphRangesBuilder, ImFont) //----------------------------------------------------------------------------- +// A font input/source (we may rename this to ImFontSource in the future) struct ImFontConfig { void* FontData; // // TTF/OTF data int FontDataSize; // // TTF/OTF data size bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself). + bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights. + bool PixelSnapH; // false // Align every glyph AdvanceX to pixel boundaries. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1. int FontNo; // 0 // Index of font within TTF/OTF file + int OversampleH; // 0 (2) // Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1 or 2 depending on size. Note the difference between 2 and 3 is minimal. You can reduce this to 1 for large glyphs save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details. + int OversampleV; // 0 (1) // Rasterize at higher quality for sub-pixel positioning. 0 == auto == 1. This is not really useful as we don't use sub-pixel positions on the Y axis. float SizePixels; // // Size in pixels for rasterizer (more or less maps to the resulting font height). - int OversampleH; // 2 // Rasterize at higher quality for sub-pixel positioning. Note the difference between 2 and 3 is minimal. You can reduce this to 1 for large glyphs save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details. - int OversampleV; // 1 // Rasterize at higher quality for sub-pixel positioning. This is not really useful as we don't use sub-pixel positions on the Y axis. - bool PixelSnapH; // false // Align every glyph AdvanceX to pixel boundaries. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1. ImVec2 GlyphExtraSpacing; // 0, 0 // Extra spacing (in pixels) between glyphs when rendered: essentially add to glyph->AdvanceX. Only X axis is supported for now. ImVec2 GlyphOffset; // 0, 0 // Offset all glyphs from this font input. const ImWchar* GlyphRanges; // NULL // THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list). float GlyphMinAdvanceX; // 0 // Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font float GlyphMaxAdvanceX; // FLT_MAX // Maximum AdvanceX for glyphs - bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights. unsigned int FontBuilderFlags; // 0 // Settings for custom font builder. THIS IS BUILDER IMPLEMENTATION DEPENDENT. Leave as zero if unsure. float RasterizerMultiply; // 1.0f // Linearly brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. This is a silly thing we may remove in the future. float RasterizerDensity; // 1.0f // DPI scale for rasterization, not altering other font metrics: make it easy to swap between e.g. a 100% and a 400% fonts for a zooming display. IMPORTANT: If you increase this it is expected that you increase font scale accordingly, otherwise quality may look lowered. - ImWchar EllipsisChar; // 0 // Explicitly specify unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used. + ImWchar EllipsisChar; // 0 // Explicitly specify Unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used. // [Internal] char Name[40]; // Name (strictly to ease debugging) @@ -3341,8 +3470,8 @@ struct ImFontAtlas IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_font_data, int compressed_font_data_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp. IMGUI_API ImFont* AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter. IMGUI_API void ClearInputData(); // Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts. + IMGUI_API void ClearFonts(); // Clear input+output font data (same as ClearInputData() + glyphs storage, UV coordinates). IMGUI_API void ClearTexData(); // Clear output texture data (CPU side). Saves RAM once the texture has been copied to graphics memory. - IMGUI_API void ClearFonts(); // Clear output font data (glyphs storage, UV coordinates). IMGUI_API void Clear(); // Clear all input and output. // Build atlas, retrieve pixel data. @@ -3375,7 +3504,7 @@ struct ImFontAtlas IMGUI_API const ImWchar* GetGlyphRangesVietnamese(); // Default + Vietnamese characters //------------------------------------------- - // [BETA] Custom Rectangles/Glyphs API + // [ALPHA] Custom Rectangles/Glyphs API //------------------------------------------- // You can request arbitrary rectangles to be packed into the atlas, for your own purposes. @@ -3401,11 +3530,11 @@ struct ImFontAtlas ImTextureID TexID; // User data to refer to the texture once it has been uploaded to user's graphic systems. It is passed back to you during rendering via the ImDrawCmd structure. int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. int TexGlyphPadding; // FIXME: Should be called "TexPackPadding". Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0 (will also need to set AntiAliasedLinesUseTex = false). - bool Locked; // Marked as Locked by ImGui::NewFrame() so attempt to modify the atlas will assert. void* UserData; // Store your own atlas related user-data (if e.g. you have multiple font atlas). // [Internal] // NB: Access texture data via GetTexData*() calls! Which will setup a default font for you. + bool Locked; // Marked as Locked by ImGui::NewFrame() so attempt to modify the atlas will assert. bool TexReady; // Set when texture was built matching current font input bool TexPixelsUseColors; // Tell whether our texture data is known to use colors (rather than just alpha channel), in order to help backend select a format. unsigned char* TexPixelsAlpha8; // 1 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight @@ -3437,39 +3566,39 @@ struct ImFontAtlas struct ImFont { // [Internal] Members: Hot ~20/24 bytes (for CalcTextSize) - ImVector IndexAdvanceX; // 12-16 // out // // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this info, and are often bottleneck in large UI). + ImVector IndexAdvanceX; // 12-16 // out // Sparse. Glyphs->AdvanceX in a directly indexable way (cache-friendly for CalcTextSize functions which only this info, and are often bottleneck in large UI). float FallbackAdvanceX; // 4 // out // = FallbackGlyph->AdvanceX - float FontSize; // 4 // in // // Height of characters/line, set during loading (don't change after loading) + float FontSize; // 4 // in // Height of characters/line, set during loading (don't change after loading) // [Internal] Members: Hot ~28/40 bytes (for RenderText loop) - ImVector IndexLookup; // 12-16 // out // // Sparse. Index glyphs by Unicode code-point. - ImVector Glyphs; // 12-16 // out // // All glyphs. + ImVector IndexLookup; // 12-16 // out // Sparse. Index glyphs by Unicode code-point. + ImVector Glyphs; // 12-16 // out // All glyphs. const ImFontGlyph* FallbackGlyph; // 4-8 // out // = FindGlyph(FontFallbackChar) // [Internal] Members: Cold ~32/40 bytes // Conceptually ConfigData[] is the list of font sources merged to create this font. - ImFontAtlas* ContainerAtlas; // 4-8 // out // // What we has been loaded into - const ImFontConfig* ConfigData; // 4-8 // in // // Pointer within ContainerAtlas->ConfigData to ConfigDataCount instances - short ConfigDataCount; // 2 // in // ~ 1 // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont. + ImFontAtlas* ContainerAtlas; // 4-8 // out // What we has been loaded into + const ImFontConfig* ConfigData; // 4-8 // in // Pointer within ContainerAtlas->ConfigData to ConfigDataCount instances + short ConfigDataCount; // 2 // in // Number of ImFontConfig involved in creating this font. Usually 1, or >1 when merging multiple font sources into one ImFont. short EllipsisCharCount; // 1 // out // 1 or 3 - ImWchar EllipsisChar; // 2-4 // out // = '...'/'.'// Character used for ellipsis rendering. - ImWchar FallbackChar; // 2-4 // out // = FFFD/'?' // Character used if a glyph isn't found. - float EllipsisWidth; // 4 // out // Width - float EllipsisCharStep; // 4 // out // Step between characters when EllipsisCount > 0 + ImWchar EllipsisChar; // 2-4 // out // Character used for ellipsis rendering ('...'). + ImWchar FallbackChar; // 2-4 // out // Character used if a glyph isn't found (U+FFFD, '?') + float EllipsisWidth; // 4 // out // Total ellipsis Width + float EllipsisCharStep; // 4 // out // Step between characters when EllipsisCount > 0 + float Scale; // 4 // in // Base font scale (1.0f), multiplied by the per-window font scale which you can adjust with SetWindowFontScale() + float Ascent, Descent; // 4+4 // out // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] (unscaled) + int MetricsTotalSurface;// 4 // out // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs) bool DirtyLookupTables; // 1 // out // - float Scale; // 4 // in // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetWindowFontScale() - float Ascent, Descent; // 4+4 // out // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] (unscaled) - int MetricsTotalSurface;// 4 // out // // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs) - ImU8 Used4kPagesMap[(IM_UNICODE_CODEPOINT_MAX+1)/4096/8]; // 2 bytes if ImWchar=ImWchar16, 34 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 4K codepoints that has one active glyph. This is mainly used to facilitate iterations across all used codepoints. + ImU8 Used8kPagesMap[(IM_UNICODE_CODEPOINT_MAX+1)/8192/8]; // 1 bytes if ImWchar=ImWchar16, 16 bytes if ImWchar==ImWchar32. Store 1-bit for each block of 4K codepoints that has one active glyph. This is mainly used to facilitate iterations across all used codepoints. // Methods IMGUI_API ImFont(); IMGUI_API ~ImFont(); IMGUI_API const ImFontGlyph*FindGlyph(ImWchar c); IMGUI_API const ImFontGlyph*FindGlyphNoFallback(ImWchar c); - float GetCharAdvance(ImWchar c) { return ((int)c < IndexAdvanceX.Size) ? IndexAdvanceX[(int)c] : FallbackAdvanceX; } - bool IsLoaded() const { return ContainerAtlas != NULL; } - const char* GetDebugName() const { return ConfigData ? ConfigData->Name : ""; } + float GetCharAdvance(ImWchar c) { return ((int)c < IndexAdvanceX.Size) ? IndexAdvanceX[(int)c] : FallbackAdvanceX; } + bool IsLoaded() const { return ContainerAtlas != NULL; } + const char* GetDebugName() const { return ConfigData ? ConfigData->Name : ""; } // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable. // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable. @@ -3498,11 +3627,24 @@ enum ImGuiViewportFlags_ ImGuiViewportFlags_None = 0, ImGuiViewportFlags_IsPlatformWindow = 1 << 0, // Represent a Platform Window ImGuiViewportFlags_IsPlatformMonitor = 1 << 1, // Represent a Platform Monitor (unused yet) - ImGuiViewportFlags_OwnedByApp = 1 << 2, // Platform Window: Is created/managed by the application (rather than a dear imgui backend) + ImGuiViewportFlags_OwnedByApp = 1 << 2, // Platform Window: Is created/managed by the user application? (rather than our backend) + ImGuiViewportFlags_NoDecoration = 1 << 3, // Platform Window: Disable platform decorations: title bar, borders, etc. (generally set all windows, but if ImGuiConfigFlags_ViewportsDecoration is set we only set this on popups/tooltips) + ImGuiViewportFlags_NoTaskBarIcon = 1 << 4, // Platform Window: Disable platform task bar icon (generally set on popups/tooltips, or all windows if ImGuiConfigFlags_ViewportsNoTaskBarIcon is set) + ImGuiViewportFlags_NoFocusOnAppearing = 1 << 5, // Platform Window: Don't take focus when created. + ImGuiViewportFlags_NoFocusOnClick = 1 << 6, // Platform Window: Don't take focus when clicked on. + ImGuiViewportFlags_NoInputs = 1 << 7, // Platform Window: Make mouse pass through so we can drag this window while peaking behind it. + ImGuiViewportFlags_NoRendererClear = 1 << 8, // Platform Window: Renderer doesn't need to clear the framebuffer ahead (because we will fill it entirely). + ImGuiViewportFlags_NoAutoMerge = 1 << 9, // Platform Window: Avoid merging this window into another host window. This can only be set via ImGuiWindowClass viewport flags override (because we need to now ahead if we are going to create a viewport in the first place!). + ImGuiViewportFlags_TopMost = 1 << 10, // Platform Window: Display on top (for tooltips only). + ImGuiViewportFlags_CanHostOtherWindows = 1 << 11, // Viewport can host multiple imgui windows (secondary viewports are associated to a single window). // FIXME: In practice there's still probably code making the assumption that this is always and only on the MainViewport. Will fix once we add support for "no main viewport". + + // Output status flags (from Platform) + ImGuiViewportFlags_IsMinimized = 1 << 12, // Platform Window: Window is minimized, can skip render. When minimized we tend to avoid using the viewport pos/size for clipping window or testing if they are contained in the viewport. + ImGuiViewportFlags_IsFocused = 1 << 13, // Platform Window: Window is focused (last call to Platform_GetWindowFocus() returned true) }; // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. -// - In 'docking' branch with multi-viewport enabled, we extend this concept to have multiple active viewports. +// - With multi-viewport enabled, we extend this concept to have multiple active viewports. // - In the future we will extend this concept further to also represent Platform Monitor and support a "no main platform window" operation mode. // - About Main Area vs Work Area: // - Main Area = entire viewport. @@ -3516,12 +3658,26 @@ struct ImGuiViewport ImVec2 Size; // Main Area: Size of the viewport. ImVec2 WorkPos; // Work Area: Position of the viewport minus task bars, menus bars, status bars (>= Pos) ImVec2 WorkSize; // Work Area: Size of the viewport minus task bars, menu bars, status bars (<= Size) + float DpiScale; // 1.0f = 96 DPI = No extra scale. + ImGuiID ParentViewportId; // (Advanced) 0: no parent. Instruct the platform backend to setup a parent/child relationship between platform windows. + ImDrawData* DrawData; // The ImDrawData corresponding to this viewport. Valid after Render() and until the next call to NewFrame(). // Platform/Backend Dependent Data - void* PlatformHandle; // void* to hold higher-level, platform window handle (e.g. HWND, GLFWWindow*, SDL_Window*) - void* PlatformHandleRaw; // void* to hold lower-level, platform-native window handle (under Win32 this is expected to be a HWND, unused for other platforms) + // Our design separate the Renderer and Platform backends to facilitate combining default backends with each others. + // When our create your own backend for a custom engine, it is possible that both Renderer and Platform will be handled + // by the same system and you may not need to use all the UserData/Handle fields. + // The library never uses those fields, they are merely storage to facilitate backend implementation. + void* RendererUserData; // void* to hold custom data structure for the renderer (e.g. swap chain, framebuffers etc.). generally set by your Renderer_CreateWindow function. + void* PlatformUserData; // void* to hold custom data structure for the OS / platform (e.g. windowing info, render context). generally set by your Platform_CreateWindow function. + void* PlatformHandle; // void* to hold higher-level, platform window handle (e.g. HWND, GLFWWindow*, SDL_Window*), for FindViewportByPlatformHandle(). + void* PlatformHandleRaw; // void* to hold lower-level, platform-native window handle (under Win32 this is expected to be a HWND, unused for other platforms), when using an abstraction layer like GLFW or SDL (where PlatformHandle would be a SDL_Window*) + bool PlatformWindowCreated; // Platform window has been created (Platform_CreateWindow() has been called). This is false during the first frame where a viewport is being created. + bool PlatformRequestMove; // Platform window requested move (e.g. window was moved by the OS / host window manager, authoritative position will be OS window position) + bool PlatformRequestResize; // Platform window requested resize (e.g. window was resized by the OS / host window manager, authoritative size will be OS window size) + bool PlatformRequestClose; // Platform window requested closure (e.g. window was moved by the OS / host window manager, e.g. pressing ALT-F4) ImGuiViewport() { memset(this, 0, sizeof(*this)); } + ~ImGuiViewport() { IM_ASSERT(PlatformUserData == NULL && RendererUserData == NULL); } // Helpers ImVec2 GetCenter() const { return ImVec2(Pos.x + Size.x * 0.5f, Pos.y + Size.y * 0.5f); } @@ -3529,7 +3685,53 @@ struct ImGuiViewport }; //----------------------------------------------------------------------------- -// [SECTION] Platform Dependent Interfaces +// [SECTION] ImGuiPlatformIO + other Platform Dependent Interfaces (ImGuiPlatformMonitor, ImGuiPlatformImeData) +//----------------------------------------------------------------------------- + +// [BETA] (Optional) Multi-Viewport Support! +// If you are new to Dear ImGui and trying to integrate it into your engine, you can probably ignore this for now. +// +// This feature allows you to seamlessly drag Dear ImGui windows outside of your application viewport. +// This is achieved by creating new Platform/OS windows on the fly, and rendering into them. +// Dear ImGui manages the viewport structures, and the backend create and maintain one Platform/OS window for each of those viewports. +// +// See Recap: https://github.com/ocornut/imgui/wiki/Multi-Viewports +// See Glossary https://github.com/ocornut/imgui/wiki/Glossary for details about some of the terminology. +// +// About the coordinates system: +// - When multi-viewports are enabled, all Dear ImGui coordinates become absolute coordinates (same as OS coordinates!) +// - So e.g. ImGui::SetNextWindowPos(ImVec2(0,0)) will position a window relative to your primary monitor! +// - If you want to position windows relative to your main application viewport, use ImGui::GetMainViewport()->Pos as a base position. +// +// Steps to use multi-viewports in your application, when using a default backend from the examples/ folder: +// - Application: Enable feature with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// - Backend: The backend initialization will setup all necessary ImGuiPlatformIO's functions and update monitors info every frame. +// - Application: In your main loop, call ImGui::UpdatePlatformWindows(), ImGui::RenderPlatformWindowsDefault() after EndFrame() or Render(). +// - Application: Fix absolute coordinates used in ImGui::SetWindowPos() or ImGui::SetNextWindowPos() calls. +// +// Steps to use multi-viewports in your application, when using a custom backend: +// - Important: THIS IS NOT EASY TO DO and comes with many subtleties not described here! +// It's also an experimental feature, so some of the requirements may evolve. +// Consider using default backends if you can. Either way, carefully follow and refer to examples/ backends for details. +// - Application: Enable feature with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. +// - Backend: Hook ImGuiPlatformIO's Platform_* and Renderer_* callbacks (see below). +// Set 'io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports' and 'io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports'. +// Update ImGuiPlatformIO's Monitors list every frame. +// Update MousePos every frame, in absolute coordinates. +// - Application: In your main loop, call ImGui::UpdatePlatformWindows(), ImGui::RenderPlatformWindowsDefault() after EndFrame() or Render(). +// You may skip calling RenderPlatformWindowsDefault() if its API is not convenient for your needs. Read comments below. +// - Application: Fix absolute coordinates used in ImGui::SetWindowPos() or ImGui::SetNextWindowPos() calls. +// +// About ImGui::RenderPlatformWindowsDefault(): +// - This function is a mostly a _helper_ for the common-most cases, and to facilitate using default backends. +// - You can check its simple source code to understand what it does. +// It basically iterates secondary viewports and call 4 functions that are setup in ImGuiPlatformIO, if available: +// Platform_RenderWindow(), Renderer_RenderWindow(), Platform_SwapBuffers(), Renderer_SwapBuffers() +// Those functions pointers exists only for the benefit of RenderPlatformWindowsDefault(). +// - If you have very specific rendering needs (e.g. flipping multiple swap-chain simultaneously, unusual sync/threading issues, etc.), +// you may be tempted to ignore RenderPlatformWindowsDefault() and write customized code to perform your renderingg. +// You may decide to setup the platform_io's *RenderWindow and *SwapBuffers pointers and call your functions through those pointers, +// or you may decide to never setup those pointers and call your code directly. They are a convenience, not an obligatory interface. //----------------------------------------------------------------------------- // Access via ImGui::GetPlatformIO() @@ -3538,7 +3740,7 @@ struct ImGuiPlatformIO IMGUI_API ImGuiPlatformIO(); //------------------------------------------------------------------ - // Interface with OS and Platform backend + // Interface with OS and Platform backend (basic) //------------------------------------------------------------------ // Optional: Access OS clipboard @@ -3568,6 +3770,74 @@ struct ImGuiPlatformIO // Written by some backends during ImGui_ImplXXXX_RenderDrawData() call to point backend_specific ImGui_ImplXXXX_RenderState* structure. void* Renderer_RenderState; + + //------------------------------------------------------------------ + // Input - Interface with OS/backends (Multi-Viewport support!) + //------------------------------------------------------------------ + + // For reference, the second column shows which function are generally calling the Platform Functions: + // N = ImGui::NewFrame() ~ beginning of the dear imgui frame: read info from platform/OS windows (latest size/position) + // F = ImGui::Begin(), ImGui::EndFrame() ~ during the dear imgui frame + // U = ImGui::UpdatePlatformWindows() ~ after the dear imgui frame: create and update all platform/OS windows + // R = ImGui::RenderPlatformWindowsDefault() ~ render + // D = ImGui::DestroyPlatformWindows() ~ shutdown + // The general idea is that NewFrame() we will read the current Platform/OS state, and UpdatePlatformWindows() will write to it. + + // The handlers are designed so we can mix and match two imgui_impl_xxxx files, one Platform backend and one Renderer backend. + // Custom engine backends will often provide both Platform and Renderer interfaces together and so may not need to use all functions. + // Platform functions are typically called _before_ their Renderer counterpart, apart from Destroy which are called the other way. + + // Platform Backend functions (e.g. Win32, GLFW, SDL) ------------------- Called by ----- + void (*Platform_CreateWindow)(ImGuiViewport* vp); // . . U . . // Create a new platform window for the given viewport + void (*Platform_DestroyWindow)(ImGuiViewport* vp); // N . U . D // + void (*Platform_ShowWindow)(ImGuiViewport* vp); // . . U . . // Newly created windows are initially hidden so SetWindowPos/Size/Title can be called on them before showing the window + void (*Platform_SetWindowPos)(ImGuiViewport* vp, ImVec2 pos); // . . U . . // Set platform window position (given the upper-left corner of client area) + ImVec2 (*Platform_GetWindowPos)(ImGuiViewport* vp); // N . . . . // + void (*Platform_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); // . . U . . // Set platform window client area size (ignoring OS decorations such as OS title bar etc.) + ImVec2 (*Platform_GetWindowSize)(ImGuiViewport* vp); // N . . . . // Get platform window client area size + void (*Platform_SetWindowFocus)(ImGuiViewport* vp); // N . . . . // Move window to front and set input focus + bool (*Platform_GetWindowFocus)(ImGuiViewport* vp); // . . U . . // + bool (*Platform_GetWindowMinimized)(ImGuiViewport* vp); // N . . . . // Get platform window minimized state. When minimized, we generally won't attempt to get/set size and contents will be culled more easily + void (*Platform_SetWindowTitle)(ImGuiViewport* vp, const char* str); // . . U . . // Set platform window title (given an UTF-8 string) + void (*Platform_SetWindowAlpha)(ImGuiViewport* vp, float alpha); // . . U . . // (Optional) Setup global transparency (not per-pixel transparency) + void (*Platform_UpdateWindow)(ImGuiViewport* vp); // . . U . . // (Optional) Called by UpdatePlatformWindows(). Optional hook to allow the platform backend from doing general book-keeping every frame. + void (*Platform_RenderWindow)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Main rendering (platform side! This is often unused, or just setting a "current" context for OpenGL bindings). 'render_arg' is the value passed to RenderPlatformWindowsDefault(). + void (*Platform_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Call Present/SwapBuffers (platform side! This is often unused!). 'render_arg' is the value passed to RenderPlatformWindowsDefault(). + float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); // N . . . . // (Optional) [BETA] FIXME-DPI: DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI. + void (*Platform_OnChangedViewport)(ImGuiViewport* vp); // . F . . . // (Optional) [BETA] FIXME-DPI: DPI handling: Called during Begin() every time the viewport we are outputting into changes, so backend has a chance to swap fonts to adjust style. + ImVec4 (*Platform_GetWindowWorkAreaInsets)(ImGuiViewport* vp); // N . . . . // (Optional) [BETA] Get initial work area inset for the viewport (won't be covered by main menu bar, dockspace over viewport etc.). Default to (0,0),(0,0). 'safeAreaInsets' in iOS land, 'DisplayCutout' in Android land. + int (*Platform_CreateVkSurface)(ImGuiViewport* vp, ImU64 vk_inst, const void* vk_allocators, ImU64* out_vk_surface); // (Optional) For a Vulkan Renderer to call into Platform code (since the surface creation needs to tie them both). + + // Renderer Backend functions (e.g. DirectX, OpenGL, Vulkan) ------------ Called by ----- + void (*Renderer_CreateWindow)(ImGuiViewport* vp); // . . U . . // Create swap chain, frame buffers etc. (called after Platform_CreateWindow) + void (*Renderer_DestroyWindow)(ImGuiViewport* vp); // N . U . D // Destroy swap chain, frame buffers etc. (called before Platform_DestroyWindow) + void (*Renderer_SetWindowSize)(ImGuiViewport* vp, ImVec2 size); // . . U . . // Resize swap chain, frame buffers etc. (called after Platform_SetWindowSize) + void (*Renderer_RenderWindow)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Clear framebuffer, setup render target, then render the viewport->DrawData. 'render_arg' is the value passed to RenderPlatformWindowsDefault(). + void (*Renderer_SwapBuffers)(ImGuiViewport* vp, void* render_arg); // . . . R . // (Optional) Call Present/SwapBuffers. 'render_arg' is the value passed to RenderPlatformWindowsDefault(). + + // (Optional) Monitor list + // - Updated by: app/backend. Update every frame to dynamically support changing monitor or DPI configuration. + // - Used by: dear imgui to query DPI info, clamp popups/tooltips within same monitor and not have them straddle monitors. + ImVector Monitors; + + //------------------------------------------------------------------ + // Output - List of viewports to render into platform windows + //------------------------------------------------------------------ + + // Viewports list (the list is updated by calling ImGui::EndFrame or ImGui::Render) + // (in the future we will attempt to organize this feature to remove the need for a "main viewport") + ImVector Viewports; // Main viewports, followed by all secondary viewports. +}; + +// (Optional) This is required when enabling multi-viewport. Represent the bounds of each connected monitor/display and their DPI. +// We use this information for multiple DPI support + clamping the position of popups and tooltips so they don't straddle multiple monitors. +struct ImGuiPlatformMonitor +{ + ImVec2 MainPos, MainSize; // Coordinates of the area displayed on this monitor (Min = upper left, Max = bottom right) + ImVec2 WorkPos, WorkSize; // Coordinates without task bars / side bars / menu bars. Used to avoid positioning popups/tooltips inside this region. If you don't have this info, please copy the value for MainPos/MainSize. + float DpiScale; // 1.0f = 96 DPI + void* PlatformHandle; // Backend dependant data (e.g. HMONITOR, GLFWmonitor*, SDL Display Index, NSScreen*) + ImGuiPlatformMonitor() { MainPos = MainSize = WorkPos = WorkSize = ImVec2(0, 0); DpiScale = 1.0f; PlatformHandle = NULL; } }; // (Optional) Support for IME (Input Method Editor) via the platform_io.Platform_SetImeDataFn() function. diff --git a/neo/libs/imgui/imgui_demo.cpp b/neo/libs/imgui/imgui_demo.cpp index d0e41f370..56949942e 100644 --- a/neo/libs/imgui/imgui_demo.cpp +++ b/neo/libs/imgui/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.7 +// dear imgui, v1.91.8 WIP // (demo code) // Help: @@ -94,6 +94,7 @@ Index of this file: // [SECTION] Example App: Fullscreen window / ShowExampleAppFullscreen() // [SECTION] Example App: Manipulating window titles / ShowExampleAppWindowTitles() // [SECTION] Example App: Custom Rendering using ImDrawList API / ShowExampleAppCustomRendering() +// [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace() // [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() // [SECTION] Example App: Assets Browser / ShowExampleAppAssetsBrowser() @@ -211,6 +212,7 @@ static void ShowExampleAppMainMenuBar(); static void ShowExampleAppAssetsBrowser(bool* p_open); static void ShowExampleAppConsole(bool* p_open); static void ShowExampleAppCustomRendering(bool* p_open); +static void ShowExampleAppDockSpace(bool* p_open); static void ShowExampleAppDocuments(bool* p_open); static void ShowExampleAppLog(bool* p_open); static void ShowExampleAppLayout(bool* p_open); @@ -252,6 +254,16 @@ static void HelpMarker(const char* desc) } } +static void ShowDockingDisabledMessage() +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui::Text("ERROR: Docking is not enabled! See Demo > Configuration."); + ImGui::Text("Set io.ConfigFlags |= ImGuiConfigFlags_DockingEnable in your code, or "); + ImGui::SameLine(0.0f, 0.0f); + if (ImGui::SmallButton("click here")) + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; +} + // Helper to wire demo markers located in code to an interactive browser typedef void (*ImGuiDemoMarkerCallback)(const char* file, int line, const char* section, void* user_data); extern ImGuiDemoMarkerCallback GImGuiDemoMarkerCallback; @@ -364,6 +376,7 @@ struct ImGuiDemoWindowData bool ShowAppConsole = false; bool ShowAppCustomRendering = false; bool ShowAppDocuments = false; + bool ShowAppDockSpace = false; bool ShowAppLog = false; bool ShowAppLayout = false; bool ShowAppPropertyEditor = false; @@ -404,7 +417,8 @@ void ImGui::ShowDemoWindow(bool* p_open) // Examples Apps (accessible from the "Examples" menu) if (demo_data.ShowMainMenuBar) { ShowExampleAppMainMenuBar(); } - if (demo_data.ShowAppDocuments) { ShowExampleAppDocuments(&demo_data.ShowAppDocuments); } + if (demo_data.ShowAppDockSpace) { ShowExampleAppDockSpace(&demo_data.ShowAppDockSpace); } // Important: Process the Docking app first, as explicit DockSpace() nodes needs to be submitted early (read comments near the DockSpace function) + if (demo_data.ShowAppDocuments) { ShowExampleAppDocuments(&demo_data.ShowAppDocuments); } // ...process the Document app next, as it may also use a DockSpace() if (demo_data.ShowAppAssetsBrowser) { ShowExampleAppAssetsBrowser(&demo_data.ShowAppAssetsBrowser); } if (demo_data.ShowAppConsole) { ShowExampleAppConsole(&demo_data.ShowAppConsole); } if (demo_data.ShowAppCustomRendering) { ShowExampleAppCustomRendering(&demo_data.ShowAppCustomRendering); } @@ -441,6 +455,7 @@ void ImGui::ShowDemoWindow(bool* p_open) static bool no_nav = false; static bool no_background = false; static bool no_bring_to_front = false; + static bool no_docking = false; static bool unsaved_document = false; ImGuiWindowFlags window_flags = 0; @@ -453,6 +468,7 @@ void ImGui::ShowDemoWindow(bool* p_open) if (no_nav) window_flags |= ImGuiWindowFlags_NoNav; if (no_background) window_flags |= ImGuiWindowFlags_NoBackground; if (no_bring_to_front) window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus; + if (no_docking) window_flags |= ImGuiWindowFlags_NoDocking; if (unsaved_document) window_flags |= ImGuiWindowFlags_UnsavedDocument; if (no_close) p_open = NULL; // Don't pass our bool* to Begin @@ -555,6 +571,44 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Checkbox("io.ConfigNavCursorVisibleAlways", &io.ConfigNavCursorVisibleAlways); ImGui::SameLine(); HelpMarker("Navigation cursor is always visible."); + ImGui::SeparatorText("Docking"); + ImGui::CheckboxFlags("io.ConfigFlags: DockingEnable", &io.ConfigFlags, ImGuiConfigFlags_DockingEnable); + ImGui::SameLine(); + if (io.ConfigDockingWithShift) + HelpMarker("Drag from window title bar or their tab to dock/undock. Hold SHIFT to enable docking.\n\nDrag from window menu button (upper-left button) to undock an entire node (all windows)."); + else + HelpMarker("Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking.\n\nDrag from window menu button (upper-left button) to undock an entire node (all windows)."); + if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) + { + ImGui::Indent(); + ImGui::Checkbox("io.ConfigDockingNoSplit", &io.ConfigDockingNoSplit); + ImGui::SameLine(); HelpMarker("Simplified docking mode: disable window splitting, so docking is limited to merging multiple windows together into tab-bars."); + ImGui::Checkbox("io.ConfigDockingWithShift", &io.ConfigDockingWithShift); + ImGui::SameLine(); HelpMarker("Enable docking when holding Shift only (allow to drop in wider space, reduce visual noise)"); + ImGui::Checkbox("io.ConfigDockingAlwaysTabBar", &io.ConfigDockingAlwaysTabBar); + ImGui::SameLine(); HelpMarker("Create a docking node and tab-bar on single floating windows."); + ImGui::Checkbox("io.ConfigDockingTransparentPayload", &io.ConfigDockingTransparentPayload); + ImGui::SameLine(); HelpMarker("Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport cannot be synced. Best used with ConfigViewportsNoAutoMerge."); + ImGui::Unindent(); + } + + ImGui::SeparatorText("Multi-viewports"); + ImGui::CheckboxFlags("io.ConfigFlags: ViewportsEnable", &io.ConfigFlags, ImGuiConfigFlags_ViewportsEnable); + ImGui::SameLine(); HelpMarker("[beta] Enable beta multi-viewports support. See ImGuiPlatformIO for details."); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::Indent(); + ImGui::Checkbox("io.ConfigViewportsNoAutoMerge", &io.ConfigViewportsNoAutoMerge); + ImGui::SameLine(); HelpMarker("Set to make all floating imgui windows always create their own viewport. Otherwise, they are merged into the main host viewports when overlapping it."); + ImGui::Checkbox("io.ConfigViewportsNoTaskBarIcon", &io.ConfigViewportsNoTaskBarIcon); + ImGui::SameLine(); HelpMarker("Toggling this at runtime is normally unsupported (most platform backends won't refresh the task bar icon state right away)."); + ImGui::Checkbox("io.ConfigViewportsNoDecoration", &io.ConfigViewportsNoDecoration); + ImGui::SameLine(); HelpMarker("Toggling this at runtime is normally unsupported (most platform backends won't refresh the decoration right away)."); + ImGui::Checkbox("io.ConfigViewportsNoDefaultParent", &io.ConfigViewportsNoDefaultParent); + ImGui::SameLine(); HelpMarker("Toggling this at runtime is normally unsupported (most platform backends won't refresh the parenting right away)."); + ImGui::Unindent(); + } + ImGui::SeparatorText("Windows"); ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges); ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires ImGuiBackendFlags_HasMouseCursors for better mouse cursor feedback."); @@ -620,12 +674,16 @@ void ImGui::ShowDemoWindow(bool* p_open) "Those flags are set by the backends (imgui_impl_xxx files) to specify their capabilities.\n" "Here we expose them as read-only fields to avoid breaking interactions with your backend."); + // Make a local copy to avoid modifying actual backend flags. // FIXME: Maybe we need a BeginReadonly() equivalent to keep label bright? ImGui::BeginDisabled(); - ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", &io.BackendFlags, ImGuiBackendFlags_HasGamepad); - ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors); - ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", &io.BackendFlags, ImGuiBackendFlags_HasSetMousePos); - ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &io.BackendFlags, ImGuiBackendFlags_RendererHasVtxOffset); + ImGui::CheckboxFlags("io.BackendFlags: HasGamepad", &io.BackendFlags, ImGuiBackendFlags_HasGamepad); + ImGui::CheckboxFlags("io.BackendFlags: HasMouseCursors", &io.BackendFlags, ImGuiBackendFlags_HasMouseCursors); + ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", &io.BackendFlags, ImGuiBackendFlags_HasSetMousePos); + ImGui::CheckboxFlags("io.BackendFlags: PlatformHasViewports", &io.BackendFlags, ImGuiBackendFlags_PlatformHasViewports); + ImGui::CheckboxFlags("io.BackendFlags: HasMouseHoveredViewport",&io.BackendFlags, ImGuiBackendFlags_HasMouseHoveredViewport); + ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &io.BackendFlags, ImGuiBackendFlags_RendererHasVtxOffset); + ImGui::CheckboxFlags("io.BackendFlags: RendererHasViewports", &io.BackendFlags, ImGuiBackendFlags_RendererHasViewports); ImGui::EndDisabled(); ImGui::TreePop(); @@ -677,6 +735,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::TableNextColumn(); ImGui::Checkbox("No nav", &no_nav); ImGui::TableNextColumn(); ImGui::Checkbox("No background", &no_background); ImGui::TableNextColumn(); ImGui::Checkbox("No bring to front", &no_bring_to_front); + ImGui::TableNextColumn(); ImGui::Checkbox("No docking", &no_docking); ImGui::TableNextColumn(); ImGui::Checkbox("Unsaved document", &unsaved_document); ImGui::EndTable(); } @@ -719,6 +778,7 @@ static void ShowDemoWindowMenuBar(ImGuiDemoWindowData* demo_data) ImGui::MenuItem("Console", NULL, &demo_data->ShowAppConsole); ImGui::MenuItem("Custom rendering", NULL, &demo_data->ShowAppCustomRendering); ImGui::MenuItem("Documents", NULL, &demo_data->ShowAppDocuments); + ImGui::MenuItem("Dockspace", NULL, &demo_data->ShowAppDockSpace); ImGui::MenuItem("Log", NULL, &demo_data->ShowAppLog); ImGui::MenuItem("Property editor", NULL, &demo_data->ShowAppPropertyEditor); ImGui::MenuItem("Simple layout", NULL, &demo_data->ShowAppLayout); @@ -2117,19 +2177,16 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) if (ImGui::TreeNode("Color/Picker Widgets")) { static ImVec4 color = ImVec4(114.0f / 255.0f, 144.0f / 255.0f, 154.0f / 255.0f, 200.0f / 255.0f); + static ImGuiColorEditFlags base_flags = ImGuiColorEditFlags_None; - static bool alpha_preview = true; - static bool alpha_half_preview = false; - static bool drag_and_drop = true; - static bool options_menu = true; - static bool hdr = false; ImGui::SeparatorText("Options"); - ImGui::Checkbox("With Alpha Preview", &alpha_preview); - ImGui::Checkbox("With Half Alpha Preview", &alpha_half_preview); - ImGui::Checkbox("With Drag and Drop", &drag_and_drop); - ImGui::Checkbox("With Options Menu", &options_menu); ImGui::SameLine(); HelpMarker("Right-click on the individual color widget to show options."); - ImGui::Checkbox("With HDR", &hdr); ImGui::SameLine(); HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); - ImGuiColorEditFlags misc_flags = (hdr ? ImGuiColorEditFlags_HDR : 0) | (drag_and_drop ? 0 : ImGuiColorEditFlags_NoDragDrop) | (alpha_half_preview ? ImGuiColorEditFlags_AlphaPreviewHalf : (alpha_preview ? ImGuiColorEditFlags_AlphaPreview : 0)) | (options_menu ? 0 : ImGuiColorEditFlags_NoOptions); + ImGui::CheckboxFlags("ImGuiColorEditFlags_NoAlpha", &base_flags, ImGuiColorEditFlags_NoAlpha); + ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaOpaque", &base_flags, ImGuiColorEditFlags_AlphaOpaque); + ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaNoBg", &base_flags, ImGuiColorEditFlags_AlphaNoBg); + ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaPreviewHalf", &base_flags, ImGuiColorEditFlags_AlphaPreviewHalf); + ImGui::CheckboxFlags("ImGuiColorEditFlags_NoDragDrop", &base_flags, ImGuiColorEditFlags_NoDragDrop); + ImGui::CheckboxFlags("ImGuiColorEditFlags_NoOptions", &base_flags, ImGuiColorEditFlags_NoOptions); ImGui::SameLine(); HelpMarker("Right-click on the individual color widget to show options."); + ImGui::CheckboxFlags("ImGuiColorEditFlags_HDR", &base_flags, ImGuiColorEditFlags_HDR); ImGui::SameLine(); HelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit"); ImGui::SeparatorText("Inline color editor"); @@ -2137,15 +2194,15 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) ImGui::SameLine(); HelpMarker( "Click on the color square to open a color picker.\n" "CTRL+click on individual component to input value.\n"); - ImGui::ColorEdit3("MyColor##1", (float*)&color, misc_flags); + ImGui::ColorEdit3("MyColor##1", (float*)&color, base_flags); IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (HSV, with Alpha)"); ImGui::Text("Color widget HSV with Alpha:"); - ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_DisplayHSV | misc_flags); + ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_DisplayHSV | base_flags); IMGUI_DEMO_MARKER("Widgets/Color/ColorEdit (float display)"); ImGui::Text("Color widget with Float Display:"); - ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | misc_flags); + ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | base_flags); IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with Picker)"); ImGui::Text("Color button with Picker:"); @@ -2153,7 +2210,7 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) "With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\n" "With the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only " "be used for the tooltip and picker popup."); - ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | misc_flags); + ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | base_flags); IMGUI_DEMO_MARKER("Widgets/Color/ColorButton (with custom Picker popup)"); ImGui::Text("Color button with Custom Picker Popup:"); @@ -2173,7 +2230,7 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) } static ImVec4 backup_color; - bool open_popup = ImGui::ColorButton("MyColor##3b", color, misc_flags); + bool open_popup = ImGui::ColorButton("MyColor##3b", color, base_flags); ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x); open_popup |= ImGui::Button("Palette"); if (open_popup) @@ -2185,7 +2242,7 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) { ImGui::Text("MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!"); ImGui::Separator(); - ImGui::ColorPicker4("##picker", (float*)&color, misc_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview); + ImGui::ColorPicker4("##picker", (float*)&color, base_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview); ImGui::SameLine(); ImGui::BeginGroup(); // Lock X position @@ -2227,40 +2284,42 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) ImGui::Text("Color button only:"); static bool no_border = false; ImGui::Checkbox("ImGuiColorEditFlags_NoBorder", &no_border); - ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, misc_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), ImVec2(80, 80)); + ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, base_flags | (no_border ? ImGuiColorEditFlags_NoBorder : 0), ImVec2(80, 80)); IMGUI_DEMO_MARKER("Widgets/Color/ColorPicker"); ImGui::SeparatorText("Color picker"); - static bool alpha = true; - static bool alpha_bar = true; - static bool side_preview = true; + static bool ref_color = false; static ImVec4 ref_color_v(1.0f, 0.0f, 1.0f, 0.5f); - static int display_mode = 0; static int picker_mode = 0; - ImGui::Checkbox("With Alpha", &alpha); - ImGui::Checkbox("With Alpha Bar", &alpha_bar); - ImGui::Checkbox("With Side Preview", &side_preview); - if (side_preview) + static int display_mode = 0; + static ImGuiColorEditFlags color_picker_flags = ImGuiColorEditFlags_AlphaBar; + + ImGui::PushID("Color picker"); + ImGui::CheckboxFlags("ImGuiColorEditFlags_NoAlpha", &color_picker_flags, ImGuiColorEditFlags_NoAlpha); + ImGui::CheckboxFlags("ImGuiColorEditFlags_AlphaBar", &color_picker_flags, ImGuiColorEditFlags_AlphaBar); + ImGui::CheckboxFlags("ImGuiColorEditFlags_NoSidePreview", &color_picker_flags, ImGuiColorEditFlags_NoSidePreview); + if (color_picker_flags & ImGuiColorEditFlags_NoSidePreview) { ImGui::SameLine(); ImGui::Checkbox("With Ref Color", &ref_color); if (ref_color) { ImGui::SameLine(); - ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | misc_flags); + ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | base_flags); } } - ImGui::Combo("Display Mode", &display_mode, "Auto/Current\0None\0RGB Only\0HSV Only\0Hex Only\0"); + + ImGui::Combo("Picker Mode", &picker_mode, "Auto/Current\0ImGuiColorEditFlags_PickerHueBar\0ImGuiColorEditFlags_PickerHueWheel\0"); + ImGui::SameLine(); HelpMarker("When not specified explicitly, user can right-click the picker to change mode."); + + ImGui::Combo("Display Mode", &display_mode, "Auto/Current\0ImGuiColorEditFlags_NoInputs\0ImGuiColorEditFlags_DisplayRGB\0ImGuiColorEditFlags_DisplayHSV\0ImGuiColorEditFlags_DisplayHex\0"); ImGui::SameLine(); HelpMarker( "ColorEdit defaults to displaying RGB inputs if you don't specify a display mode, " "but the user can change it with a right-click on those inputs.\n\nColorPicker defaults to displaying RGB+HSV+Hex " "if you don't specify a display mode.\n\nYou can change the defaults using SetColorEditOptions()."); - ImGui::SameLine(); HelpMarker("When not specified explicitly (Auto/Current mode), user can right-click the picker to change mode."); - ImGuiColorEditFlags flags = misc_flags; - if (!alpha) flags |= ImGuiColorEditFlags_NoAlpha; // This is by default if you call ColorPicker3() instead of ColorPicker4() - if (alpha_bar) flags |= ImGuiColorEditFlags_AlphaBar; - if (!side_preview) flags |= ImGuiColorEditFlags_NoSidePreview; + + ImGuiColorEditFlags flags = base_flags | color_picker_flags; if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar; if (picker_mode == 2) flags |= ImGuiColorEditFlags_PickerHueWheel; if (display_mode == 1) flags |= ImGuiColorEditFlags_NoInputs; // Disable all RGB/HSV/Hex displays @@ -2289,6 +2348,7 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) ImGui::SameLine(); ImGui::SetNextItemWidth(w); ImGui::ColorPicker3("##MyColor##6", (float*)&color, ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha); + ImGui::PopID(); // HSV encoded support (to avoid RGB<>HSV round trips and singularities when S==0 or V==0) static ImVec4 color_hsv(0.23f, 1.0f, 1.0f, 1.0f); // Stored as HSV! @@ -2863,18 +2923,24 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) "IsWindowFocused() = %d\n" "IsWindowFocused(_ChildWindows) = %d\n" "IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\n" + "IsWindowFocused(_ChildWindows|_DockHierarchy) = %d\n" "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n" "IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" + "IsWindowFocused(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n" "IsWindowFocused(_RootWindow) = %d\n" "IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\n" + "IsWindowFocused(_RootWindow|_DockHierarchy) = %d\n" "IsWindowFocused(_AnyWindow) = %d\n", ImGui::IsWindowFocused(), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_DockHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow), ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), + ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow), ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy), + ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy), ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)); // Testing IsWindowHovered() function with its various flags. @@ -2884,10 +2950,13 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n" "IsWindowHovered(_ChildWindows) = %d\n" "IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\n" + "IsWindowHovered(_ChildWindows|_DockHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n" "IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n" + "IsWindowHovered(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n" "IsWindowHovered(_RootWindow) = %d\n" "IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n" + "IsWindowHovered(_RootWindow|_DockHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" "IsWindowHovered(_AnyWindow) = %d\n" "IsWindowHovered(_Stationary) = %d\n", @@ -2896,10 +2965,13 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_DockHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), + ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), + ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow), ImGui::IsWindowHovered(ImGuiHoveredFlags_Stationary)); @@ -2912,10 +2984,13 @@ static void ShowDemoWindowWidgets(ImGuiDemoWindowData* demo_data) // Calling IsItemHovered() after begin returns the hovered status of the title bar. // This is useful in particular if you want to create a context menu associated to the title bar of a window. + // This will also work when docked into a Tab (the Tab replace the Title Bar and guarantee the same properties). static bool test_window = false; ImGui::Checkbox("Hovered/Active tests after Begin() for title bar testing", &test_window); if (test_window) { + // FIXME-DOCK: This window cannot be docked within the ImGui Demo window, this will cause a feedback loop and get them stuck. + // Could we fix this through an ImGuiWindowClass feature? Or an API call to tag our parent as "don't skip items"? ImGui::Begin("Title bar Hovered/Active tests", &test_window); if (ImGui::BeginPopupContextItem()) // <-- This is using IsItemHovered() { @@ -7408,6 +7483,8 @@ static void ShowDemoWindowInputs() ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDown(i)) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); + ImGui::Text("Mouse clicked count:"); + for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseClickedCount[i] > 0) { ImGui::SameLine(); ImGui::Text("b%d: %d", i, io.MouseClickedCount[i]); } // We iterate both legacy native range and named ImGuiKey ranges. This is a little unusual/odd but this allows // displaying the data for old/new backends. @@ -7798,6 +7875,12 @@ void ImGui::ShowAboutWindow(bool* p_open) #ifdef __EMSCRIPTEN__ ImGui::Text("define: __EMSCRIPTEN__"); ImGui::Text("Emscripten: %d.%d.%d", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__); +#endif +#ifdef IMGUI_HAS_VIEWPORT + ImGui::Text("define: IMGUI_HAS_VIEWPORT"); +#endif +#ifdef IMGUI_HAS_DOCK + ImGui::Text("define: IMGUI_HAS_DOCK"); #endif ImGui::Separator(); ImGui::Text("io.BackendPlatformName: %s", io.BackendPlatformName ? io.BackendPlatformName : "NULL"); @@ -7808,7 +7891,19 @@ void ImGui::ShowAboutWindow(bool* p_open) if (io.ConfigFlags & ImGuiConfigFlags_NoMouse) ImGui::Text(" NoMouse"); if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) ImGui::Text(" NoMouseCursorChange"); if (io.ConfigFlags & ImGuiConfigFlags_NoKeyboard) ImGui::Text(" NoKeyboard"); + if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) ImGui::Text(" DockingEnable"); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui::Text(" ViewportsEnable"); + if (io.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) ImGui::Text(" DpiEnableScaleViewports"); + if (io.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ImGui::Text(" DpiEnableScaleFonts"); if (io.MouseDrawCursor) ImGui::Text("io.MouseDrawCursor"); + if (io.ConfigViewportsNoAutoMerge) ImGui::Text("io.ConfigViewportsNoAutoMerge"); + if (io.ConfigViewportsNoTaskBarIcon) ImGui::Text("io.ConfigViewportsNoTaskBarIcon"); + if (io.ConfigViewportsNoDecoration) ImGui::Text("io.ConfigViewportsNoDecoration"); + if (io.ConfigViewportsNoDefaultParent) ImGui::Text("io.ConfigViewportsNoDefaultParent"); + if (io.ConfigDockingNoSplit) ImGui::Text("io.ConfigDockingNoSplit"); + if (io.ConfigDockingWithShift) ImGui::Text("io.ConfigDockingWithShift"); + if (io.ConfigDockingAlwaysTabBar) ImGui::Text("io.ConfigDockingAlwaysTabBar"); + if (io.ConfigDockingTransparentPayload) ImGui::Text("io.ConfigDockingTransparentPayload"); if (io.ConfigMacOSXBehaviors) ImGui::Text("io.ConfigMacOSXBehaviors"); if (io.ConfigNavMoveSetMousePos) ImGui::Text("io.ConfigNavMoveSetMousePos"); if (io.ConfigNavCaptureKeyboard) ImGui::Text("io.ConfigNavCaptureKeyboard"); @@ -7820,7 +7915,10 @@ void ImGui::ShowAboutWindow(bool* p_open) if (io.BackendFlags & ImGuiBackendFlags_HasGamepad) ImGui::Text(" HasGamepad"); if (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) ImGui::Text(" HasMouseCursors"); if (io.BackendFlags & ImGuiBackendFlags_HasSetMousePos) ImGui::Text(" HasSetMousePos"); + if (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) ImGui::Text(" PlatformHasViewports"); + if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)ImGui::Text(" HasMouseHoveredViewport"); if (io.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) ImGui::Text(" RendererHasVtxOffset"); + if (io.BackendFlags & ImGuiBackendFlags_RendererHasViewports) ImGui::Text(" RendererHasViewports"); ImGui::Separator(); ImGui::Text("io.Fonts: %d fonts, Flags: 0x%08X, TexSize: %d,%d", io.Fonts->Fonts.Size, io.Fonts->Flags, io.Fonts->TexWidth, io.Fonts->TexHeight); ImGui::Text("io.DisplaySize: %.2f,%.2f", io.DisplaySize.x, io.DisplaySize.y); @@ -7966,7 +8064,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat("TabBorderSize", &style.TabBorderSize, 0.0f, 1.0f, "%.0f"); ImGui::SliderFloat("TabBarBorderSize", &style.TabBarBorderSize, 0.0f, 2.0f, "%.0f"); - ImGui::SliderFloat("TabBarOverlineSize", &style.TabBarOverlineSize, 0.0f, 2.0f, "%.0f"); + ImGui::SliderFloat("TabBarOverlineSize", &style.TabBarOverlineSize, 0.0f, 3.0f, "%.0f"); ImGui::SameLine(); HelpMarker("Overline is only drawn over the selected tab when ImGuiTabBarFlags_DrawSelectedOverline is set."); ImGui::SeparatorText("Rounding"); @@ -7998,6 +8096,9 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat2("SeparatorTextPadding", (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, "%.0f"); ImGui::SliderFloat("LogSliderDeadzone", &style.LogSliderDeadzone, 0.0f, 12.0f, "%.0f"); + ImGui::SeparatorText("Docking"); + ImGui::SliderFloat("DockingSplitterSize", &style.DockingSeparatorSize, 0.0f, 12.0f, "%.0f"); + ImGui::SeparatorText("Tooltips"); for (int n = 0; n < 2; n++) if (ImGui::TreeNodeEx(n == 0 ? "HoverFlagsForTooltipMouse" : "HoverFlagsForTooltipNav")) @@ -8046,9 +8147,9 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) filter.Draw("Filter colors", ImGui::GetFontSize() * 16); static ImGuiColorEditFlags alpha_flags = 0; - if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine(); - if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_AlphaPreview)) { alpha_flags = ImGuiColorEditFlags_AlphaPreview; } ImGui::SameLine(); - if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine(); + if (ImGui::RadioButton("Opaque", alpha_flags == ImGuiColorEditFlags_AlphaOpaque)) { alpha_flags = ImGuiColorEditFlags_AlphaOpaque; } ImGui::SameLine(); + if (ImGui::RadioButton("Alpha", alpha_flags == ImGuiColorEditFlags_None)) { alpha_flags = ImGuiColorEditFlags_None; } ImGui::SameLine(); + if (ImGui::RadioButton("Both", alpha_flags == ImGuiColorEditFlags_AlphaPreviewHalf)) { alpha_flags = ImGuiColorEditFlags_AlphaPreviewHalf; } ImGui::SameLine(); HelpMarker( "In the color list:\n" "Left-click on color square to open color picker,\n" @@ -9252,6 +9353,8 @@ static void ShowExampleAppConstrainedResize(bool* p_open) else { ImGui::Text("(Hold SHIFT to display a dummy viewport)"); + if (ImGui::IsWindowDocked()) + ImGui::Text("Warning: Sizing Constraints won't work if the window is docked!"); if (ImGui::Button("Set 200x200")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine(); if (ImGui::Button("Set 500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine(); if (ImGui::Button("Set 800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); } @@ -9278,7 +9381,7 @@ static void ShowExampleAppSimpleOverlay(bool* p_open) { static int location = 0; ImGuiIO& io = ImGui::GetIO(); - ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; if (location >= 0) { const float PAD = 10.0f; @@ -9291,6 +9394,7 @@ static void ShowExampleAppSimpleOverlay(bool* p_open) window_pos_pivot.x = (location & 1) ? 1.0f : 0.0f; window_pos_pivot.y = (location & 2) ? 1.0f : 0.0f; ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); + ImGui::SetNextWindowViewport(viewport->ID); window_flags |= ImGuiWindowFlags_NoMove; } else if (location == -2) @@ -9710,6 +9814,134 @@ static void ShowExampleAppCustomRendering(bool* p_open) ImGui::End(); } +//----------------------------------------------------------------------------- +// [SECTION] Example App: Docking, DockSpace / ShowExampleAppDockSpace() +//----------------------------------------------------------------------------- + +// Demonstrate using DockSpace() to create an explicit docking node within an existing window. +// Note: You can use most Docking facilities without calling any API. You DO NOT need to call DockSpace() to use Docking! +// - Drag from window title bar or their tab to dock/undock. Hold SHIFT to disable docking. +// - Drag from window menu button (upper-left button) to undock an entire node (all windows). +// - When io.ConfigDockingWithShift == true, you instead need to hold SHIFT to enable docking. +// About dockspaces: +// - Use DockSpace() to create an explicit dock node _within_ an existing window. +// - Use DockSpaceOverViewport() to create an explicit dock node covering the screen or a specific viewport. +// This is often used with ImGuiDockNodeFlags_PassthruCentralNode. +// - Important: Dockspaces need to be submitted _before_ any window they can host. Submit it early in your frame! (*) +// - Important: Dockspaces need to be kept alive if hidden, otherwise windows docked into it will be undocked. +// e.g. if you have multiple tabs with a dockspace inside each tab: submit the non-visible dockspaces with ImGuiDockNodeFlags_KeepAliveOnly. +// (*) because of this constraint, the implicit \"Debug\" window can not be docked into an explicit DockSpace() node, +// because that window is submitted as part of the part of the NewFrame() call. An easy workaround is that you can create +// your own implicit "Debug##2" window after calling DockSpace() and leave it in the window stack for anyone to use. +void ShowExampleAppDockSpace(bool* p_open) +{ + // READ THIS !!! + // TL;DR; this demo is more complicated than what most users you would normally use. + // If we remove all options we are showcasing, this demo would become: + // void ShowExampleAppDockSpace() + // { + // ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport()); + // } + // In most cases you should be able to just call DockSpaceOverViewport() and ignore all the code below! + // In this specific demo, we are not using DockSpaceOverViewport() because: + // - (1) we allow the host window to be floating/moveable instead of filling the viewport (when opt_fullscreen == false) + // - (2) we allow the host window to have padding (when opt_padding == true) + // - (3) we expose many flags and need a way to have them visible. + // - (4) we have a local menu bar in the host window (vs. you could use BeginMainMenuBar() + DockSpaceOverViewport() + // in your code, but we don't here because we allow the window to be floating) + + static bool opt_fullscreen = true; + static bool opt_padding = false; + static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None; + + // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into, + // because it would be confusing to have two docking targets within each others. + ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking; + if (opt_fullscreen) + { + const ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImGui::SetNextWindowPos(viewport->WorkPos); + ImGui::SetNextWindowSize(viewport->WorkSize); + ImGui::SetNextWindowViewport(viewport->ID); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; + window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; + } + else + { + dockspace_flags &= ~ImGuiDockNodeFlags_PassthruCentralNode; + } + + // When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background + // and handle the pass-thru hole, so we ask Begin() to not render a background. + if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) + window_flags |= ImGuiWindowFlags_NoBackground; + + // Important: note that we proceed even if Begin() returns false (aka window is collapsed). + // This is because we want to keep our DockSpace() active. If a DockSpace() is inactive, + // all active windows docked into it will lose their parent and become undocked. + // We cannot preserve the docking relationship between an active window and an inactive docking, otherwise + // any change of dockspace/settings would lead to windows being stuck in limbo and never being visible. + if (!opt_padding) + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + ImGui::Begin("DockSpace Demo", p_open, window_flags); + if (!opt_padding) + ImGui::PopStyleVar(); + + if (opt_fullscreen) + ImGui::PopStyleVar(2); + + // Submit the DockSpace + ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) + { + ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); + ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags); + } + else + { + ShowDockingDisabledMessage(); + } + + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("Options")) + { + // Disabling fullscreen would allow the window to be moved to the front of other windows, + // which we can't undo at the moment without finer window depth/z control. + ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen); + ImGui::MenuItem("Padding", NULL, &opt_padding); + ImGui::Separator(); + + if (ImGui::MenuItem("Flag: NoDockingOverCentralNode", "", (dockspace_flags & ImGuiDockNodeFlags_NoDockingOverCentralNode) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoDockingOverCentralNode; } + if (ImGui::MenuItem("Flag: NoDockingSplit", "", (dockspace_flags & ImGuiDockNodeFlags_NoDockingSplit) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoDockingSplit; } + if (ImGui::MenuItem("Flag: NoUndocking", "", (dockspace_flags & ImGuiDockNodeFlags_NoUndocking) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoUndocking; } + if (ImGui::MenuItem("Flag: NoResize", "", (dockspace_flags & ImGuiDockNodeFlags_NoResize) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoResize; } + if (ImGui::MenuItem("Flag: AutoHideTabBar", "", (dockspace_flags & ImGuiDockNodeFlags_AutoHideTabBar) != 0)) { dockspace_flags ^= ImGuiDockNodeFlags_AutoHideTabBar; } + if (ImGui::MenuItem("Flag: PassthruCentralNode", "", (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0, opt_fullscreen)) { dockspace_flags ^= ImGuiDockNodeFlags_PassthruCentralNode; } + ImGui::Separator(); + + if (ImGui::MenuItem("Close", NULL, false, p_open != NULL)) + *p_open = false; + ImGui::EndMenu(); + } + HelpMarker( + "When docking is enabled, you can ALWAYS dock MOST window into another! Try it now!" "\n" + "- Drag from window title bar or their tab to dock/undock." "\n" + "- Drag from window menu button (upper-left button) to undock an entire node (all windows)." "\n" + "- Hold SHIFT to disable docking (if io.ConfigDockingWithShift == false, default)" "\n" + "- Hold SHIFT to enable docking (if io.ConfigDockingWithShift == true)" "\n" + "This demo app has nothing to do with enabling docking!" "\n\n" + "This demo app only demonstrate the use of ImGui::DockSpace() which allows you to manually create a docking node _within_ another window." "\n\n" + "Read comments in ShowExampleAppDockSpace() for more details."); + + ImGui::EndMenuBar(); + } + + ImGui::End(); +} + //----------------------------------------------------------------------------- // [SECTION] Example App: Documents Handling / ShowExampleAppDocuments() //----------------------------------------------------------------------------- @@ -9835,11 +10067,25 @@ void ShowExampleAppDocuments(bool* p_open) static ExampleAppDocuments app; // Options + enum Target + { + Target_None, + Target_Tab, // Create documents as local tab into a local tab bar + Target_DockSpaceAndWindow // Create documents as regular windows, and create an embedded dockspace + }; + static Target opt_target = Target_Tab; static bool opt_reorderable = true; static ImGuiTabBarFlags opt_fitting_flags = ImGuiTabBarFlags_FittingPolicyDefault_; + // When (opt_target == Target_DockSpaceAndWindow) there is the possibily that one of our child Document window (e.g. "Eggplant") + // that we emit gets docked into the same spot as the parent window ("Example: Documents"). + // This would create a problematic feedback loop because selecting the "Eggplant" tab would make the "Example: Documents" tab + // not visible, which in turn would stop submitting the "Eggplant" window. + // We avoid this problem by submitting our documents window even if our parent window is not currently visible. + // Another solution may be to make the "Example: Documents" window use the ImGuiWindowFlags_NoDocking. + bool window_contents_visible = ImGui::Begin("Example: Documents", p_open, ImGuiWindowFlags_MenuBar); - if (!window_contents_visible) + if (!window_contents_visible && opt_target != Target_DockSpaceAndWindow) { ImGui::End(); return; @@ -9883,6 +10129,12 @@ void ShowExampleAppDocuments(bool* p_open) doc.DoForceClose(); ImGui::PopID(); } + ImGui::PushItemWidth(ImGui::GetFontSize() * 12); + ImGui::Combo("Output", (int*)&opt_target, "None\0TabBar+Tabs\0DockSpace+Window\0"); + ImGui::PopItemWidth(); + bool redock_all = false; + if (opt_target == Target_Tab) { ImGui::SameLine(); ImGui::Checkbox("Reorderable Tabs", &opt_reorderable); } + if (opt_target == Target_DockSpaceAndWindow) { ImGui::SameLine(); redock_all = ImGui::Button("Redock all"); } ImGui::Separator(); @@ -9896,7 +10148,8 @@ void ShowExampleAppDocuments(bool* p_open) // hole for one-frame, both in the tab-bar and in tab-contents when closing a tab/window. // The rarely used SetTabItemClosed() function is a way to notify of programmatic closure to avoid the one-frame hole. - // Submit Tab Bar and Tabs + // Tabs + if (opt_target == Target_Tab) { ImGuiTabBarFlags tab_bar_flags = (opt_fitting_flags) | (opt_reorderable ? ImGuiTabBarFlags_Reorderable : 0); tab_bar_flags |= ImGuiTabBarFlags_DrawSelectedOverline; @@ -9939,6 +10192,53 @@ void ShowExampleAppDocuments(bool* p_open) ImGui::EndTabBar(); } } + else if (opt_target == Target_DockSpaceAndWindow) + { + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DockingEnable) + { + app.NotifyOfDocumentsClosedElsewhere(); + + // Create a DockSpace node where any window can be docked + ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); + ImGui::DockSpace(dockspace_id); + + // Create Windows + for (int doc_n = 0; doc_n < app.Documents.Size; doc_n++) + { + MyDocument* doc = &app.Documents[doc_n]; + if (!doc->Open) + continue; + + ImGui::SetNextWindowDockID(dockspace_id, redock_all ? ImGuiCond_Always : ImGuiCond_FirstUseEver); + ImGuiWindowFlags window_flags = (doc->Dirty ? ImGuiWindowFlags_UnsavedDocument : 0); + bool visible = ImGui::Begin(doc->Name, &doc->Open, window_flags); + + // Cancel attempt to close when unsaved add to save queue so we can display a popup. + if (!doc->Open && doc->Dirty) + { + doc->Open = true; + app.CloseQueue.push_back(doc); + } + + app.DisplayDocContextMenu(doc); + if (visible) + app.DisplayDocContents(doc); + + ImGui::End(); + } + } + else + { + ShowDockingDisabledMessage(); + } + } + + // Early out other contents + if (!window_contents_visible) + { + ImGui::End(); + return; + } // Display renaming UI if (app.RenamingDoc != NULL) diff --git a/neo/libs/imgui/imgui_draw.cpp b/neo/libs/imgui/imgui_draw.cpp index 344eae9e8..997c9b768 100644 --- a/neo/libs/imgui/imgui_draw.cpp +++ b/neo/libs/imgui/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.7 +// dear imgui, v1.91.8 WIP // (drawing and font code) /* @@ -222,6 +222,8 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_TabDimmed] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); colors[ImGuiCol_TabDimmedSelected] = ImLerp(colors[ImGuiCol_TabSelected], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.50f, 0.50f, 0.50f, 0.00f); + colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_HeaderActive] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); + colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -285,6 +287,8 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst) colors[ImGuiCol_TabDimmed] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); colors[ImGuiCol_TabDimmedSelected] = ImLerp(colors[ImGuiCol_TabSelected], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.53f, 0.53f, 0.87f, 0.00f); + colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); + colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -349,6 +353,8 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_TabDimmed] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); colors[ImGuiCol_TabDimmedSelected] = ImLerp(colors[ImGuiCol_TabSelected], colors[ImGuiCol_TitleBg], 0.40f); colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.26f, 0.59f, 1.00f, 0.00f); + colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); + colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -1669,8 +1675,7 @@ void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 // Accept null ranges if (text_begin == text_end || text_begin[0] == 0) return; - if (text_end == NULL) - text_end = text_begin + strlen(text_begin); + // No need to strlen() here: font->RenderText() will do it and may early out. // Pull default font/size from the shared ImDrawListSharedData instance if (font == NULL) @@ -1693,7 +1698,7 @@ void ImDrawList::AddText(ImFont* font, float font_size, const ImVec2& pos, ImU32 void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end) { - AddText(NULL, 0.0f, pos, col, text_begin, text_end); + AddText(_Data->Font, _Data->FontSize, pos, col, text_begin, text_end); } void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& p_min, const ImVec2& p_max, const ImVec2& uv_min, const ImVec2& uv_max, ImU32 col) @@ -2375,8 +2380,8 @@ ImFontConfig::ImFontConfig() { memset(this, 0, sizeof(*this)); FontDataOwnedByAtlas = true; - OversampleH = 2; - OversampleV = 1; + OversampleH = 0; // Auto == 1 or 2 depending on size + OversampleV = 0; // Auto == 1 GlyphMaxAdvanceX = FLT_MAX; RasterizerMultiply = 1.0f; RasterizerDensity = 1.0f; @@ -2521,6 +2526,7 @@ void ImFontAtlas::ClearTexData() void ImFontAtlas::ClearFonts() { IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); + ClearInputData(); Fonts.clear_delete(); TexReady = false; } @@ -2573,8 +2579,7 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) IM_ASSERT(!Locked && "Cannot modify a locked ImFontAtlas between NewFrame() and EndFrame/Render()!"); IM_ASSERT(font_cfg->FontData != NULL && font_cfg->FontDataSize > 0); IM_ASSERT(font_cfg->SizePixels > 0.0f && "Is ImFontConfig struct correctly initialized?"); - IM_ASSERT(font_cfg->OversampleH > 0 && font_cfg->OversampleV > 0 && "Is ImFontConfig struct correctly initialized?"); - IM_ASSERT(font_cfg->RasterizerDensity > 0.0f); + IM_ASSERT(font_cfg->RasterizerDensity > 0.0f && "Is ImFontConfig struct correctly initialized?"); // Create new font if (!font_cfg->MergeMode) @@ -2820,6 +2825,13 @@ void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsig *data = table[*data]; } +void ImFontAtlasBuildGetOversampleFactors(const ImFontConfig* cfg, int* out_oversample_h, int* out_oversample_v) +{ + // Automatically disable horizontal oversampling over size 36 + *out_oversample_h = (cfg->OversampleH != 0) ? cfg->OversampleH : (cfg->SizePixels * cfg->RasterizerDensity > 36.0f || cfg->PixelSnapH) ? 1 : 2; + *out_oversample_v = (cfg->OversampleV != 0) ? cfg->OversampleV : 1; +} + #ifdef IMGUI_ENABLE_STB_TRUETYPE // Temporary data for one source font (multiple source fonts can be merged into one destination ImFont) // (C++03 doesn't allow instancing ImVector<> with function-local types so we declare the type here.) @@ -2985,15 +2997,19 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) buf_rects_out_n += src_tmp.GlyphsCount; buf_packedchars_out_n += src_tmp.GlyphsCount; - // Convert our ranges in the format stb_truetype wants + // Automatic selection of oversampling parameters ImFontConfig& cfg = atlas->ConfigData[src_i]; + int oversample_h, oversample_v; + ImFontAtlasBuildGetOversampleFactors(&cfg, &oversample_h, &oversample_v); + + // Convert our ranges in the format stb_truetype wants src_tmp.PackRange.font_size = cfg.SizePixels * cfg.RasterizerDensity; src_tmp.PackRange.first_unicode_codepoint_in_range = 0; src_tmp.PackRange.array_of_unicode_codepoints = src_tmp.GlyphsList.Data; src_tmp.PackRange.num_chars = src_tmp.GlyphsList.Size; src_tmp.PackRange.chardata_for_range = src_tmp.PackedChars; - src_tmp.PackRange.h_oversample = (unsigned char)cfg.OversampleH; - src_tmp.PackRange.v_oversample = (unsigned char)cfg.OversampleV; + src_tmp.PackRange.h_oversample = (unsigned char)oversample_h; + src_tmp.PackRange.v_oversample = (unsigned char)oversample_v; // Gather the sizes of all rectangles we will need to pack (this loop is based on stbtt_PackFontRangesGatherRects) const float scale = (cfg.SizePixels > 0.0f) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels * cfg.RasterizerDensity) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels * cfg.RasterizerDensity); @@ -3002,9 +3018,9 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) int x0, y0, x1, y1; const int glyph_index_in_font = stbtt_FindGlyphIndex(&src_tmp.FontInfo, src_tmp.GlyphsList[glyph_i]); IM_ASSERT(glyph_index_in_font != 0); - stbtt_GetGlyphBitmapBoxSubpixel(&src_tmp.FontInfo, glyph_index_in_font, scale * cfg.OversampleH, scale * cfg.OversampleV, 0, 0, &x0, &y0, &x1, &y1); - src_tmp.Rects[glyph_i].w = (stbrp_coord)(x1 - x0 + pack_padding + cfg.OversampleH - 1); - src_tmp.Rects[glyph_i].h = (stbrp_coord)(y1 - y0 + pack_padding + cfg.OversampleV - 1); + stbtt_GetGlyphBitmapBoxSubpixel(&src_tmp.FontInfo, glyph_index_in_font, scale * oversample_h, scale * oversample_v, 0, 0, &x0, &y0, &x1, &y1); + src_tmp.Rects[glyph_i].w = (stbrp_coord)(x1 - x0 + pack_padding + oversample_h - 1); + src_tmp.Rects[glyph_i].h = (stbrp_coord)(y1 - y0 + pack_padding + oversample_v - 1); total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h; } } @@ -3689,7 +3705,7 @@ ImFont::ImFont() Scale = 1.0f; Ascent = Descent = 0.0f; MetricsTotalSurface = 0; - memset(Used4kPagesMap, 0, sizeof(Used4kPagesMap)); + memset(Used8kPagesMap, 0, sizeof(Used8kPagesMap)); } ImFont::~ImFont() @@ -3709,7 +3725,7 @@ void ImFont::ClearOutputData() DirtyLookupTables = true; Ascent = Descent = 0.0f; MetricsTotalSurface = 0; - memset(Used4kPagesMap, 0, sizeof(Used4kPagesMap)); + memset(Used8kPagesMap, 0, sizeof(Used8kPagesMap)); } static ImWchar FindFirstExistingGlyph(ImFont* font, const ImWchar* candidate_chars, int candidate_chars_count) @@ -3732,17 +3748,17 @@ void ImFont::BuildLookupTable() IndexAdvanceX.clear(); IndexLookup.clear(); DirtyLookupTables = false; - memset(Used4kPagesMap, 0, sizeof(Used4kPagesMap)); + memset(Used8kPagesMap, 0, sizeof(Used8kPagesMap)); GrowIndex(max_codepoint + 1); for (int i = 0; i < Glyphs.Size; i++) { int codepoint = (int)Glyphs[i].Codepoint; IndexAdvanceX[codepoint] = Glyphs[i].AdvanceX; - IndexLookup[codepoint] = (ImWchar)i; + IndexLookup[codepoint] = (ImU16)i; // Mark 4K page as used - const int page_n = codepoint / 4096; - Used4kPagesMap[page_n >> 3] |= 1 << (page_n & 7); + const int page_n = codepoint / 8192; + Used8kPagesMap[page_n >> 3] |= 1 << (page_n & 7); } // Create a glyph to handle TAB @@ -3756,7 +3772,7 @@ void ImFont::BuildLookupTable() tab_glyph.Codepoint = '\t'; tab_glyph.AdvanceX *= IM_TABSIZE; IndexAdvanceX[(int)tab_glyph.Codepoint] = (float)tab_glyph.AdvanceX; - IndexLookup[(int)tab_glyph.Codepoint] = (ImWchar)(Glyphs.Size - 1); + IndexLookup[(int)tab_glyph.Codepoint] = (ImU16)(Glyphs.Size - 1); } // Mark special glyphs as not visible (note that AddGlyph already mark as non-visible glyphs with zero-size polygons) @@ -3804,15 +3820,15 @@ void ImFont::BuildLookupTable() } } -// API is designed this way to avoid exposing the 4K page size +// API is designed this way to avoid exposing the 8K page size // e.g. use with IsGlyphRangeUnused(0, 255) bool ImFont::IsGlyphRangeUnused(unsigned int c_begin, unsigned int c_last) { - unsigned int page_begin = (c_begin / 4096); - unsigned int page_last = (c_last / 4096); + unsigned int page_begin = (c_begin / 8192); + unsigned int page_last = (c_last / 8192); for (unsigned int page_n = page_begin; page_n <= page_last; page_n++) - if ((page_n >> 3) < sizeof(Used4kPagesMap)) - if (Used4kPagesMap[page_n >> 3] & (1 << (page_n & 7))) + if ((page_n >> 3) < sizeof(Used8kPagesMap)) + if (Used8kPagesMap[page_n >> 3] & (1 << (page_n & 7))) return false; return true; } @@ -3829,7 +3845,7 @@ void ImFont::GrowIndex(int new_size) if (new_size <= IndexLookup.Size) return; IndexAdvanceX.resize(new_size, -1.0f); - IndexLookup.resize(new_size, (ImWchar)-1); + IndexLookup.resize(new_size, (ImU16)-1); } // x0/y0/x1/y1 are offset from the character upper-left layout position, in pixels. Therefore x0/y0 are often fairly close to zero. @@ -3872,6 +3888,7 @@ void ImFont::AddGlyph(const ImFontConfig* cfg, ImWchar codepoint, float x0, floa glyph.U1 = u1; glyph.V1 = v1; glyph.AdvanceX = advance_x; + IM_ASSERT(Glyphs.Size < 0xFFFF); // IndexLookup[] hold 16-bit values and -1 is reserved. // Compute rough surface usage metrics (+1 to account for average padding, +0.99 to round) // We use (U1-U0)*TexWidth instead of X1-X0 to account for oversampling. @@ -3885,13 +3902,13 @@ void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst) IM_ASSERT(IndexLookup.Size > 0); // Currently this can only be called AFTER the font has been built, aka after calling ImFontAtlas::GetTexDataAs*() function. unsigned int index_size = (unsigned int)IndexLookup.Size; - if (dst < index_size && IndexLookup.Data[dst] == (ImWchar)-1 && !overwrite_dst) // 'dst' already exists + if (dst < index_size && IndexLookup.Data[dst] == (ImU16)-1 && !overwrite_dst) // 'dst' already exists return; if (src >= index_size && dst >= index_size) // both 'dst' and 'src' don't exist -> no-op return; GrowIndex(dst + 1); - IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : (ImWchar)-1; + IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : (ImU16)-1; IndexAdvanceX[dst] = (src < index_size) ? IndexAdvanceX.Data[src] : 1.0f; } @@ -3900,8 +3917,8 @@ const ImFontGlyph* ImFont::FindGlyph(ImWchar c) { if (c >= (size_t)IndexLookup.Size) return FallbackGlyph; - const ImWchar i = IndexLookup.Data[c]; - if (i == (ImWchar)-1) + const ImU16 i = IndexLookup.Data[c]; + if (i == (ImU16)-1) return FallbackGlyph; return &Glyphs.Data[i]; } @@ -3910,8 +3927,8 @@ const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) { if (c >= (size_t)IndexLookup.Size) return NULL; - const ImWchar i = IndexLookup.Data[c]; - if (i == (ImWchar)-1) + const ImU16 i = IndexLookup.Data[c]; + if (i == (ImU16)-1) return NULL; return &Glyphs.Data[i]; } @@ -4125,15 +4142,15 @@ void ImFont::RenderChar(ImDrawList* draw_list, float size, const ImVec2& pos, Im // Note: as with every ImDrawList drawing function, this expects that the font atlas texture is bound. void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) { - if (!text_end) - text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls. - // Align to be pixel perfect float x = IM_TRUNC(pos.x); float y = IM_TRUNC(pos.y); if (y > clip_rect.w) return; + if (!text_end) + text_end = text_begin + strlen(text_begin); // ImGui:: functions generally already provides a valid text_end, so this is merely to handle direct calls. + const float scale = size / FontSize; const float line_height = FontSize * scale; const float origin_x = x; @@ -4316,6 +4333,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im // - RenderArrow() // - RenderBullet() // - RenderCheckMark() +// - RenderArrowDockMenu() // - RenderArrowPointingAt() // - RenderRectFilledRangeH() // - RenderRectFilledWithHole() @@ -4390,6 +4408,14 @@ void ImGui::RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half } } +// This is less wide than RenderArrow() and we use in dock nodes instead of the regular RenderArrow() to denote a change of functionality, +// and because the saved space means that the left-most tab label can stay at exactly the same position as the label of a loose window. +void ImGui::RenderArrowDockMenu(ImDrawList* draw_list, ImVec2 p_min, float sz, ImU32 col) +{ + draw_list->AddRectFilled(p_min + ImVec2(sz * 0.20f, sz * 0.15f), p_min + ImVec2(sz * 0.80f, sz * 0.30f), col); + RenderArrowPointingAt(draw_list, p_min + ImVec2(sz * 0.50f, sz * 0.85f), ImVec2(sz * 0.30f, sz * 0.40f), ImGuiDir_Down, col); +} + static inline float ImAcos01(float x) { if (x <= 0.0f) return IM_PI * 0.5f; @@ -4475,6 +4501,17 @@ void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, if (fill_R && fill_D) draw_list->AddRectFilled(ImVec2(inner.Max.x, inner.Max.y), ImVec2(outer.Max.x, outer.Max.y), col, rounding, ImDrawFlags_RoundCornersBottomRight); } +ImDrawFlags ImGui::CalcRoundingFlagsForRectInRect(const ImRect& r_in, const ImRect& r_outer, float threshold) +{ + bool round_l = r_in.Min.x <= r_outer.Min.x + threshold; + bool round_r = r_in.Max.x >= r_outer.Max.x - threshold; + bool round_t = r_in.Min.y <= r_outer.Min.y + threshold; + bool round_b = r_in.Max.y >= r_outer.Max.y - threshold; + return ImDrawFlags_RoundCornersNone + | ((round_t && round_l) ? ImDrawFlags_RoundCornersTopLeft : 0) | ((round_t && round_r) ? ImDrawFlags_RoundCornersTopRight : 0) + | ((round_b && round_l) ? ImDrawFlags_RoundCornersBottomLeft : 0) | ((round_b && round_r) ? ImDrawFlags_RoundCornersBottomRight : 0); +} + // Helper for ColorPicker4() // NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that. // Spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding altogether. diff --git a/neo/libs/imgui/imgui_internal.h b/neo/libs/imgui/imgui_internal.h index f3f915d7e..a8c44f359 100644 --- a/neo/libs/imgui/imgui_internal.h +++ b/neo/libs/imgui/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.91.7 +// dear imgui, v1.91.8 WIP // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. @@ -130,10 +130,17 @@ Index of this file: // [SECTION] Forward declarations //----------------------------------------------------------------------------- +// Utilities +// (other types which are not forwarded declared are: ImBitArray<>, ImSpan<>, ImSpanAllocator<>, ImPool<>, ImChunkStream<>) struct ImBitVector; // Store 1-bit per value struct ImRect; // An axis-aligned rectangle (2 points) +struct ImGuiTextIndex; // Maintain a line index for a text buffer. + +// ImDrawList/ImFontAtlas struct ImDrawDataBuilder; // Helper to build a ImDrawData instance struct ImDrawListSharedData; // Data shared between all ImDrawList instances + +// ImGui struct ImGuiBoxSelectState; // Box-selection state (currently used by multi-selection, could potentially be used by others) struct ImGuiColorMod; // Stacked color modifier, backup of modified data so we can restore it struct ImGuiContext; // Main Dear ImGui context @@ -141,6 +148,10 @@ struct ImGuiContextHook; // Hook for extensions like ImGuiTestEngine struct ImGuiDataVarInfo; // Variable information (e.g. to access style variables from an enum) struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum struct ImGuiDeactivatedItemData; // Data for IsItemDeactivated()/IsItemDeactivatedAfterEdit() function. +struct ImGuiDockContext; // Docking system context +struct ImGuiDockRequest; // Docking system dock/undock queued request +struct ImGuiDockNode; // Docking system node (hold a list of Windows OR two child dock nodes) +struct ImGuiDockNodeSettings; // Storage for a dock node in .ini file (we preserve those even if the associated dock node isn't active during the session) struct ImGuiErrorRecoveryState; // Storage of stack sizes for error handling and recovery struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup() struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box @@ -172,12 +183,14 @@ struct ImGuiTreeNodeStackData; // Temporary storage for TreeNode(). struct ImGuiTypingSelectState; // Storage for GetTypingSelectRequest() struct ImGuiTypingSelectRequest; // Storage for GetTypingSelectRequest() (aimed to be public) struct ImGuiWindow; // Storage for one window +struct ImGuiWindowDockStyle; // Storage for window-style data which needs to be stored for docking purpose struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame, in practice we currently keep it for each window) struct ImGuiWindowSettings; // Storage for a window .ini settings (we keep one of those even if the actual window wasn't instanced during this session) // Enumerations // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. enum ImGuiLocKey : int; // -> enum ImGuiLocKey // Enum: a localization entry for translation. +typedef int ImGuiDataAuthority; // -> enum ImGuiDataAuthority_ // Enum: for storing the source authority (dock node vs window) of a field typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical // Flags @@ -211,6 +224,9 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer // [SECTION] Macros //----------------------------------------------------------------------------- +// Internal Drag and Drop payload types. String starting with '_' are reserved for Dear ImGui. +#define IMGUI_PAYLOAD_TYPE_WINDOW "_IMWINDOW" // Payload == ImGuiWindow* + // Debug Printing Into TTY // (since IMGUI_VERSION_NUM >= 18729: IMGUI_DEBUG_LOG was reworked into IMGUI_DEBUG_PRINTF (and removed framecount from it). If you were using a #define IMGUI_DEBUG_LOG please rename) #ifndef IMGUI_DEBUG_PRINTF @@ -222,7 +238,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #endif // Debug Logging for ShowDebugLogWindow(). This is designed for relatively rare events so please don't spam. -#define IMGUI_DEBUG_LOG_ERROR(...) do { ImGuiContext& g2 = *GImGui; if (g2.DebugLogFlags & ImGuiDebugLogFlags_EventError) IMGUI_DEBUG_LOG(__VA_ARGS__); else g2.DebugLogSkippedErrors++; } while (0) +#define IMGUI_DEBUG_LOG_ERROR(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventError) IMGUI_DEBUG_LOG(__VA_ARGS__); else g.DebugLogSkippedErrors++; } while (0) #define IMGUI_DEBUG_LOG_ACTIVEID(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_FOCUS(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) @@ -232,6 +248,8 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #define IMGUI_DEBUG_LOG_IO(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_FONT(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFont) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_INPUTROUTING(...) do{if (g.DebugLogFlags & ImGuiDebugLogFlags_EventInputRouting)IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_DOCKING(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventDocking) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_VIEWPORT(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventViewport) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) // Static Asserts #define IM_STATIC_ASSERT(_COND) static_assert(_COND, "") @@ -462,7 +480,7 @@ static inline double ImRsqrt(double x) { return 1.0 / sqrt(x); } template static inline T ImMin(T lhs, T rhs) { return lhs < rhs ? lhs : rhs; } template static inline T ImMax(T lhs, T rhs) { return lhs >= rhs ? lhs : rhs; } template static inline T ImClamp(T v, T mn, T mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } -template static inline T ImLerp(T a, T b, float t) { return (T)(a + (b - a) * (T)t); } +template static inline T ImLerp(T a, T b, float t) { return (T)(a + (b - a) * t); } template static inline void ImSwap(T& a, T& b) { T tmp = a; a = b; b = tmp; } template static inline T ImAddClampOverflow(T a, T b, T mn, T mx) { if (b < 0 && (a < mn - b)) return mn; if (b > 0 && (a > mx - b)) return mx; return a + b; } template static inline T ImSubClampOverflow(T a, T b, T mn, T mx) { if (b > 0 && (a < mn + b)) return mn; if (b < 0 && (a > mx + b)) return mx; return a - b; } @@ -736,6 +754,7 @@ struct ImGuiTextIndex // Helper: ImGuiStorage IMGUI_API ImGuiStoragePair* ImLowerBound(ImGuiStoragePair* in_begin, ImGuiStoragePair* in_end, ImGuiID key); + //----------------------------------------------------------------------------- // [SECTION] ImDrawList support //----------------------------------------------------------------------------- @@ -892,7 +911,7 @@ enum ImGuiItemStatusFlags_ enum ImGuiHoveredFlagsPrivate_ { ImGuiHoveredFlags_DelayMask_ = ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay, - ImGuiHoveredFlags_AllowedMaskForIsWindowHovered = ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary, + ImGuiHoveredFlags_AllowedMaskForIsWindowHovered = ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_DockHierarchy | ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary, ImGuiHoveredFlags_AllowedMaskForIsItemHovered = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayMask_, }; @@ -1188,6 +1207,9 @@ enum ImGuiNextWindowDataFlags_ ImGuiNextWindowDataFlags_HasScroll = 1 << 7, ImGuiNextWindowDataFlags_HasChildFlags = 1 << 8, ImGuiNextWindowDataFlags_HasRefreshPolicy = 1 << 9, + ImGuiNextWindowDataFlags_HasViewport = 1 << 10, + ImGuiNextWindowDataFlags_HasDock = 1 << 11, + ImGuiNextWindowDataFlags_HasWindowClass = 1 << 12, }; // Storage for SetNexWindow** functions @@ -1197,17 +1219,22 @@ struct ImGuiNextWindowData ImGuiCond PosCond; ImGuiCond SizeCond; ImGuiCond CollapsedCond; + ImGuiCond DockCond; ImVec2 PosVal; ImVec2 PosPivotVal; ImVec2 SizeVal; ImVec2 ContentSizeVal; ImVec2 ScrollVal; ImGuiChildFlags ChildFlags; + bool PosUndock; bool CollapsedVal; ImRect SizeConstraintRect; ImGuiSizeCallback SizeCallback; void* SizeCallbackUserData; float BgAlphaVal; // Override background alpha + ImGuiID ViewportId; + ImGuiID DockId; + ImGuiWindowClass WindowClass; ImVec2 MenuBarOffsetMinVal; // (Always on) This is not exposed publicly, so we don't clear it and it doesn't have a corresponding flag (could we? for consistency?) ImGuiWindowRefreshFlags RefreshFlagsVal; @@ -1385,6 +1412,7 @@ enum ImGuiInputEventType ImGuiInputEventType_MousePos, ImGuiInputEventType_MouseWheel, ImGuiInputEventType_MouseButton, + ImGuiInputEventType_MouseViewport, ImGuiInputEventType_Key, ImGuiInputEventType_Text, ImGuiInputEventType_Focus, @@ -1405,6 +1433,7 @@ enum ImGuiInputSource struct ImGuiInputEventMousePos { float PosX, PosY; ImGuiMouseSource MouseSource; }; struct ImGuiInputEventMouseWheel { float WheelX, WheelY; ImGuiMouseSource MouseSource; }; struct ImGuiInputEventMouseButton { int Button; bool Down; ImGuiMouseSource MouseSource; }; +struct ImGuiInputEventMouseViewport { ImGuiID HoveredViewportID; }; struct ImGuiInputEventKey { ImGuiKey Key; bool Down; float AnalogValue; }; struct ImGuiInputEventText { unsigned int Char; }; struct ImGuiInputEventAppFocused { bool Focused; }; @@ -1419,6 +1448,7 @@ struct ImGuiInputEvent ImGuiInputEventMousePos MousePos; // if Type == ImGuiInputEventType_MousePos ImGuiInputEventMouseWheel MouseWheel; // if Type == ImGuiInputEventType_MouseWheel ImGuiInputEventMouseButton MouseButton; // if Type == ImGuiInputEventType_MouseButton + ImGuiInputEventMouseViewport MouseViewport; // if Type == ImGuiInputEventType_MouseViewport ImGuiInputEventKey Key; // if Type == ImGuiInputEventType_Key ImGuiInputEventText Text; // if Type == ImGuiInputEventType_Text ImGuiInputEventAppFocused AppFocused; // if Type == ImGuiInputEventType_Focus @@ -1812,8 +1842,155 @@ struct IMGUI_API ImGuiMultiSelectState // [SECTION] Docking support //----------------------------------------------------------------------------- +#define DOCKING_HOST_DRAW_CHANNEL_BG 0 // Dock host: background fill +#define DOCKING_HOST_DRAW_CHANNEL_FG 1 // Dock host: decorations and contents + #ifdef IMGUI_HAS_DOCK -// + +// Extend ImGuiDockNodeFlags_ +enum ImGuiDockNodeFlagsPrivate_ +{ + // [Internal] + ImGuiDockNodeFlags_DockSpace = 1 << 10, // Saved // A dockspace is a node that occupy space within an existing user window. Otherwise the node is floating and create its own window. + ImGuiDockNodeFlags_CentralNode = 1 << 11, // Saved // The central node has 2 main properties: stay visible when empty, only use "remaining" spaces from its neighbor. + ImGuiDockNodeFlags_NoTabBar = 1 << 12, // Saved // Tab bar is completely unavailable. No triangle in the corner to enable it back. + ImGuiDockNodeFlags_HiddenTabBar = 1 << 13, // Saved // Tab bar is hidden, with a triangle in the corner to show it again (NB: actual tab-bar instance may be destroyed as this is only used for single-window tab bar) + ImGuiDockNodeFlags_NoWindowMenuButton = 1 << 14, // Saved // Disable window/docking menu (that one that appears instead of the collapse button) + ImGuiDockNodeFlags_NoCloseButton = 1 << 15, // Saved // Disable close button + ImGuiDockNodeFlags_NoResizeX = 1 << 16, // // + ImGuiDockNodeFlags_NoResizeY = 1 << 17, // // + ImGuiDockNodeFlags_DockedWindowsInFocusRoute= 1 << 18, // // Any docked window will be automatically be focus-route chained (window->ParentWindowForFocusRoute set to this) so Shortcut() in this window can run when any docked window is focused. + + // Disable docking/undocking actions in this dockspace or individual node (existing docked nodes will be preserved) + // Those are not exposed in public because the desirable sharing/inheriting/copy-flag-on-split behaviors are quite difficult to design and understand. + // The two public flags ImGuiDockNodeFlags_NoDockingOverCentralNode/ImGuiDockNodeFlags_NoDockingSplit don't have those issues. + ImGuiDockNodeFlags_NoDockingSplitOther = 1 << 19, // // Disable this node from splitting other windows/nodes. + ImGuiDockNodeFlags_NoDockingOverMe = 1 << 20, // // Disable other windows/nodes from being docked over this node. + ImGuiDockNodeFlags_NoDockingOverOther = 1 << 21, // // Disable this node from being docked over another window or non-empty node. + ImGuiDockNodeFlags_NoDockingOverEmpty = 1 << 22, // // Disable this node from being docked over an empty node (e.g. DockSpace with no other windows) + ImGuiDockNodeFlags_NoDocking = ImGuiDockNodeFlags_NoDockingOverMe | ImGuiDockNodeFlags_NoDockingOverOther | ImGuiDockNodeFlags_NoDockingOverEmpty | ImGuiDockNodeFlags_NoDockingSplit | ImGuiDockNodeFlags_NoDockingSplitOther, + + // Masks + ImGuiDockNodeFlags_SharedFlagsInheritMask_ = ~0, + ImGuiDockNodeFlags_NoResizeFlagsMask_ = (int)ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_NoResizeX | ImGuiDockNodeFlags_NoResizeY, + + // When splitting, those local flags are moved to the inheriting child, never duplicated + ImGuiDockNodeFlags_LocalFlagsTransferMask_ = (int)ImGuiDockNodeFlags_NoDockingSplit | ImGuiDockNodeFlags_NoResizeFlagsMask_ | (int)ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton, + ImGuiDockNodeFlags_SavedFlagsMask_ = ImGuiDockNodeFlags_NoResizeFlagsMask_ | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton, +}; + +// Store the source authority (dock node vs window) of a field +enum ImGuiDataAuthority_ +{ + ImGuiDataAuthority_Auto, + ImGuiDataAuthority_DockNode, + ImGuiDataAuthority_Window, +}; + +enum ImGuiDockNodeState +{ + ImGuiDockNodeState_Unknown, + ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow, + ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing, + ImGuiDockNodeState_HostWindowVisible, +}; + +// sizeof() 156~192 +struct IMGUI_API ImGuiDockNode +{ + ImGuiID ID; + ImGuiDockNodeFlags SharedFlags; // (Write) Flags shared by all nodes of a same dockspace hierarchy (inherited from the root node) + ImGuiDockNodeFlags LocalFlags; // (Write) Flags specific to this node + ImGuiDockNodeFlags LocalFlagsInWindows; // (Write) Flags specific to this node, applied from windows + ImGuiDockNodeFlags MergedFlags; // (Read) Effective flags (== SharedFlags | LocalFlagsInNode | LocalFlagsInWindows) + ImGuiDockNodeState State; + ImGuiDockNode* ParentNode; + ImGuiDockNode* ChildNodes[2]; // [Split node only] Child nodes (left/right or top/bottom). Consider switching to an array. + ImVector Windows; // Note: unordered list! Iterate TabBar->Tabs for user-order. + ImGuiTabBar* TabBar; + ImVec2 Pos; // Current position + ImVec2 Size; // Current size + ImVec2 SizeRef; // [Split node only] Last explicitly written-to size (overridden when using a splitter affecting the node), used to calculate Size. + ImGuiAxis SplitAxis; // [Split node only] Split axis (X or Y) + ImGuiWindowClass WindowClass; // [Root node only] + ImU32 LastBgColor; + + ImGuiWindow* HostWindow; + ImGuiWindow* VisibleWindow; // Generally point to window which is ID is == SelectedTabID, but when CTRL+Tabbing this can be a different window. + ImGuiDockNode* CentralNode; // [Root node only] Pointer to central node. + ImGuiDockNode* OnlyNodeWithWindows; // [Root node only] Set when there is a single visible node within the hierarchy. + int CountNodeWithWindows; // [Root node only] + int LastFrameAlive; // Last frame number the node was updated or kept alive explicitly with DockSpace() + ImGuiDockNodeFlags_KeepAliveOnly + int LastFrameActive; // Last frame number the node was updated. + int LastFrameFocused; // Last frame number the node was focused. + ImGuiID LastFocusedNodeId; // [Root node only] Which of our child docking node (any ancestor in the hierarchy) was last focused. + ImGuiID SelectedTabId; // [Leaf node only] Which of our tab/window is selected. + ImGuiID WantCloseTabId; // [Leaf node only] Set when closing a specific tab/window. + ImGuiID RefViewportId; // Reference viewport ID from visible window when HostWindow == NULL. + ImGuiDataAuthority AuthorityForPos :3; + ImGuiDataAuthority AuthorityForSize :3; + ImGuiDataAuthority AuthorityForViewport :3; + bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) + bool IsFocused :1; + bool IsBgDrawnThisFrame :1; + bool HasCloseButton :1; // Provide space for a close button (if any of the docked window has one). Note that button may be hidden on window without one. + bool HasWindowMenuButton :1; + bool HasCentralNodeChild :1; + bool WantCloseAll :1; // Set when closing all tabs at once. + bool WantLockSizeOnce :1; + bool WantMouseMove :1; // After a node extraction we need to transition toward moving the newly created host window + bool WantHiddenTabBarUpdate :1; + bool WantHiddenTabBarToggle :1; + + ImGuiDockNode(ImGuiID id); + ~ImGuiDockNode(); + bool IsRootNode() const { return ParentNode == NULL; } + bool IsDockSpace() const { return (MergedFlags & ImGuiDockNodeFlags_DockSpace) != 0; } + bool IsFloatingNode() const { return ParentNode == NULL && (MergedFlags & ImGuiDockNodeFlags_DockSpace) == 0; } + bool IsCentralNode() const { return (MergedFlags & ImGuiDockNodeFlags_CentralNode) != 0; } + bool IsHiddenTabBar() const { return (MergedFlags & ImGuiDockNodeFlags_HiddenTabBar) != 0; } // Hidden tab bar can be shown back by clicking the small triangle + bool IsNoTabBar() const { return (MergedFlags & ImGuiDockNodeFlags_NoTabBar) != 0; } // Never show a tab bar + bool IsSplitNode() const { return ChildNodes[0] != NULL; } + bool IsLeafNode() const { return ChildNodes[0] == NULL; } + bool IsEmpty() const { return ChildNodes[0] == NULL && Windows.Size == 0; } + ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } + + void SetLocalFlags(ImGuiDockNodeFlags flags) { LocalFlags = flags; UpdateMergedFlags(); } + void UpdateMergedFlags() { MergedFlags = SharedFlags | LocalFlags | LocalFlagsInWindows; } +}; + +// List of colors that are stored at the time of Begin() into Docked Windows. +// We currently store the packed colors in a simple array window->DockStyle.Colors[]. +// A better solution may involve appending into a log of colors in ImGuiContext + store offsets into those arrays in ImGuiWindow, +// but it would be more complex as we'd need to double-buffer both as e.g. drop target may refer to window from last frame. +enum ImGuiWindowDockStyleCol +{ + ImGuiWindowDockStyleCol_Text, + ImGuiWindowDockStyleCol_TabHovered, + ImGuiWindowDockStyleCol_TabFocused, + ImGuiWindowDockStyleCol_TabSelected, + ImGuiWindowDockStyleCol_TabSelectedOverline, + ImGuiWindowDockStyleCol_TabDimmed, + ImGuiWindowDockStyleCol_TabDimmedSelected, + ImGuiWindowDockStyleCol_TabDimmedSelectedOverline, + ImGuiWindowDockStyleCol_COUNT +}; + +// We don't store style.Alpha: dock_node->LastBgColor embeds it and otherwise it would only affect the docking tab, which intuitively I would say we don't want to. +struct ImGuiWindowDockStyle +{ + ImU32 Colors[ImGuiWindowDockStyleCol_COUNT]; +}; + +struct ImGuiDockContext +{ + ImGuiStorage Nodes; // Map ID -> ImGuiDockNode*: Active nodes + ImVector Requests; + ImVector NodesSettings; + bool WantFullRebuild; + ImGuiDockContext() { memset(this, 0, sizeof(*this)); } +}; + #endif // #ifdef IMGUI_HAS_DOCK //----------------------------------------------------------------------------- @@ -1824,10 +2001,24 @@ struct IMGUI_API ImGuiMultiSelectState // Every instance of ImGuiViewport is in fact a ImGuiViewportP. struct ImGuiViewportP : public ImGuiViewport { + ImGuiWindow* Window; // Set when the viewport is owned by a window (and ImGuiViewportFlags_CanHostOtherWindows is NOT set) + int Idx; + int LastFrameActive; // Last frame number this viewport was activated by a window + int LastFocusedStampCount; // Last stamp number from when a window hosted by this viewport was focused (by comparing this value between two viewport we have an implicit viewport z-order we use as fallback) + ImGuiID LastNameHash; + ImVec2 LastPos; + ImVec2 LastSize; + float Alpha; // Window opacity (when dragging dockable windows/viewports we make them transparent) + float LastAlpha; + bool LastFocusedHadNavWindow;// Instead of maintaining a LastFocusedWindow (which may harder to correctly maintain), we merely store weither NavWindow != NULL last time the viewport was focused. + short PlatformMonitor; int BgFgDrawListsLastFrame[2]; // Last frame number the background (0) and foreground (1) draw lists were used ImDrawList* BgFgDrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays. ImDrawData DrawDataP; ImDrawDataBuilder DrawDataBuilder; // Temporary data while building final ImDrawData + ImVec2 LastPlatformPos; + ImVec2 LastPlatformSize; + ImVec2 LastRendererSize; // Per-viewport work area // - Insets are >= 0.0f values, distance from viewport corners to work area. @@ -1838,8 +2029,9 @@ struct ImGuiViewportP : public ImGuiViewport ImVec2 BuildWorkInsetMin; // Work Area inset accumulator for current frame, to become next frame's WorkInset ImVec2 BuildWorkInsetMax; // " - ImGuiViewportP() { BgFgDrawListsLastFrame[0] = BgFgDrawListsLastFrame[1] = -1; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; } - ~ImGuiViewportP() { if (BgFgDrawLists[0]) IM_DELETE(BgFgDrawLists[0]); if (BgFgDrawLists[1]) IM_DELETE(BgFgDrawLists[1]); } + ImGuiViewportP() { Window = NULL; Idx = -1; LastFrameActive = BgFgDrawListsLastFrame[0] = BgFgDrawListsLastFrame[1] = LastFocusedStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; LastFocusedHadNavWindow = false; PlatformMonitor = -1; BgFgDrawLists[0] = BgFgDrawLists[1] = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } + ~ImGuiViewportP() { if (BgFgDrawLists[0]) IM_DELETE(BgFgDrawLists[0]); if (BgFgDrawLists[1]) IM_DELETE(BgFgDrawLists[1]); } + void ClearRequestFlags() { PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; } // Calculate work rect pos/size given a set of offset (we have 1 pair of offset for rect locked from last frame data, and 1 pair for currently building rect) ImVec2 CalcWorkRectPos(const ImVec2& inset_min) const { return ImVec2(Pos.x + inset_min.x, Pos.y + inset_min.y); } @@ -1862,14 +2054,19 @@ struct ImGuiViewportP : public ImGuiViewport struct ImGuiWindowSettings { ImGuiID ID; - ImVec2ih Pos; + ImVec2ih Pos; // NB: Settings position are stored RELATIVE to the viewport! Whereas runtime ones are absolute positions. ImVec2ih Size; + ImVec2ih ViewportPos; + ImGuiID ViewportId; + ImGuiID DockId; // ID of last known DockNode (even if the DockNode is invisible because it has only 1 active window), or 0 if none. + ImGuiID ClassId; // ID of window class if specified + short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible. bool Collapsed; bool IsChild; bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context) bool WantDelete; // Set to invalidate/delete the settings entry - ImGuiWindowSettings() { memset(this, 0, sizeof(*this)); } + ImGuiWindowSettings() { memset(this, 0, sizeof(*this)); DockOrder = -1; } char* GetName() { return (char*)(this + 1); } }; @@ -1905,6 +2102,9 @@ enum ImGuiLocKey : int ImGuiLocKey_WindowingUntitled, ImGuiLocKey_OpenLink_s, ImGuiLocKey_CopyLink, + ImGuiLocKey_DockingHideTabBar, + ImGuiLocKey_DockingHoldShiftToDock, + ImGuiLocKey_DockingDragToUndockOrMoveNode, ImGuiLocKey_COUNT }; @@ -1949,8 +2149,8 @@ enum ImGuiDebugLogFlags_ ImGuiDebugLogFlags_EventIO = 1 << 7, ImGuiDebugLogFlags_EventFont = 1 << 8, ImGuiDebugLogFlags_EventInputRouting = 1 << 9, - ImGuiDebugLogFlags_EventDocking = 1 << 10, // Unused in this branch - ImGuiDebugLogFlags_EventViewport = 1 << 11, // Unused in this branch + ImGuiDebugLogFlags_EventDocking = 1 << 10, + ImGuiDebugLogFlags_EventViewport = 1 << 11, ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventFont | ImGuiDebugLogFlags_EventInputRouting | ImGuiDebugLogFlags_EventDocking | ImGuiDebugLogFlags_EventViewport, ImGuiDebugLogFlags_OutputToTTY = 1 << 20, // Also send output to TTY @@ -1985,6 +2185,7 @@ struct ImGuiMetricsConfig bool ShowDrawCmdBoundingBoxes = true; bool ShowTextEncodingViewer = false; bool ShowAtlasTintedWithTextColor = false; + bool ShowDockingNodes = false; int ShowWindowsRectsType = -1; int ShowTablesRectsType = -1; int HighlightMonitorIdx = -1; @@ -2044,15 +2245,18 @@ struct ImGuiContext ImGuiIO IO; ImGuiPlatformIO PlatformIO; ImGuiStyle Style; + ImGuiConfigFlags ConfigFlagsCurrFrame; // = g.IO.ConfigFlags at the time of NewFrame() + ImGuiConfigFlags ConfigFlagsLastFrame; ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window. float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Base text height. float FontScale; // == FontSize / Font->FontSize - float CurrentDpiScale; // Current window/viewport DpiScale + float CurrentDpiScale; // Current window/viewport DpiScale == CurrentViewport->DpiScale ImDrawListSharedData DrawListSharedData; double Time; int FrameCount; int FrameCountEnded; + int FrameCountPlatformEnded; int FrameCountRendered; ImGuiID WithinEndChildID; // Set within EndChild() bool WithinFrameScope; // Set by NewFrame(), cleared by EndFrame() @@ -2081,7 +2285,7 @@ struct ImGuiContext ImGuiWindow* HoveredWindow; // Window the mouse is hovering. Will typically catch mouse inputs. ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set. ImGuiWindow* HoveredWindowBeforeClear; // Window the mouse is hovering. Filled even with _NoMouse. This is currently useful for multi-context compositors. - ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindow. + ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindowDockTree. ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window. ImVec2 WheelingWindowRefMousePos; int WheelingWindowStartFrame; // This may be set one frame before WheelingWindow is != NULL @@ -2158,7 +2362,16 @@ struct ImGuiContext ImVectorTreeNodeStack; // Stack for TreeNode() // Viewports - ImVector Viewports; // Active viewports (Size==1 in 'master' branch). Each viewports hold their copy of ImDrawData. + ImVector Viewports; // Active viewports (always 1+, and generally 1 unless multi-viewports are enabled). Each viewports hold their copy of ImDrawData. + ImGuiViewportP* CurrentViewport; // We track changes of viewport (happening in Begin) so we can call Platform_OnChangedViewport() + ImGuiViewportP* MouseViewport; + ImGuiViewportP* MouseLastHoveredViewport; // Last known viewport that was hovered by mouse (even if we are not hovering any viewport any more) + honoring the _NoInputs flag. + ImGuiID PlatformLastFocusedViewportId; + ImGuiPlatformMonitor FallbackMonitor; // Virtual monitor used as fallback if backend doesn't provide monitor information. + ImRect PlatformMonitorsFullWorkRect; // Bounding box of all platform monitors + int ViewportCreatedCount; // Unique sequential creation counter (mostly for testing/debugging) + int PlatformWindowsCreatedCount; // Unique sequential creation counter (mostly for testing/debugging) + int ViewportFocusedStampCount; // Every time the front-most window changes, we stamp its viewport with an incrementing counter // Keyboard/Gamepad Navigation bool NavCursorVisible; // Nav focus cursor/rectangle is visible? We hide it after a mouse click. We show it after a nav move. @@ -2328,6 +2541,12 @@ struct ImGuiContext // Platform support ImGuiPlatformImeData PlatformImeData; // Data updated by current frame ImGuiPlatformImeData PlatformImeDataPrev; // Previous frame data. When changed we call the platform_io.Platform_SetImeDataFn() handler. + ImGuiID PlatformImeViewport; + + // Extensions + // FIXME: We could provide an API to register one slot in an array held in ImGuiContext? + ImGuiDockContext DockContext; + void (*DockNodeWindowMenuHandler)(ImGuiContext* ctx, ImGuiDockNode* node, ImGuiTabBar* tab_bar); // Settings bool SettingsLoaded; @@ -2386,6 +2605,7 @@ struct ImGuiContext ImGuiMetricsConfig DebugMetricsConfig; ImGuiIDStackTool DebugIDStackTool; ImGuiDebugAllocInfo DebugAllocInfo; + ImGuiDockNode* DebugHoveredDockNode; // Hovered dock node. // Misc float FramerateSecPerFrame[60]; // Calculate estimate of framerate for user over the last 60 frames.. @@ -2449,6 +2669,12 @@ struct IMGUI_API ImGuiWindowTempData ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin() ImU32 ModalDimBgColor; + // Status flags + ImGuiItemStatusFlags WindowItemStatusFlags; + ImGuiItemStatusFlags ChildItemStatusFlags; + ImGuiItemStatusFlags DockTabItemStatusFlags; + ImRect DockTabItemRect; + // Local parameters stacks // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. float ItemWidth; // Current item width (>0.0: width in pixels, <0.0: align xx pixels to the right of window). @@ -2463,9 +2689,13 @@ struct IMGUI_API ImGuiWindow ImGuiContext* Ctx; // Parent UI context (needs to be set explicitly by parent). char* Name; // Window name, owned by the window. ImGuiID ID; // == ImHashStr(Name) - ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_ + ImGuiWindowFlags Flags, FlagsPreviousFrame; // See enum ImGuiWindowFlags_ ImGuiChildFlags ChildFlags; // Set when window is a child window. See enum ImGuiChildFlags_ + ImGuiWindowClass WindowClass; // Advanced users only. Set with SetNextWindowClass() ImGuiViewportP* Viewport; // Always set in Begin(). Inactive windows may have a NULL value here if their viewport was discarded. + ImGuiID ViewportId; // We backup the viewport id (since the viewport may disappear or never be created if the window is inactive) + ImVec2 ViewportPos; // We backup the viewport position (since the viewport may disappear or never be created if the window is inactive) + int ViewportAllowPlatformMonitorExtend; // Reset to -1 every frame (index is guaranteed to be valid between NewFrame..EndFrame), only used in the Appearing frame of a tooltip/popup to enforce clamping to a given monitor ImVec2 Pos; // Position (always rounded-up to nearest pixel) ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) ImVec2 SizeFull; // Size when non collapsed @@ -2481,6 +2711,7 @@ struct IMGUI_API ImGuiWindow float DecoInnerSizeX1, DecoInnerSizeY1; // Applied AFTER/OVER InnerRect. Specialized for Tables as they use specialized form of clipping and frozen rows/columns are inside InnerRect (and not part of regular decoration sizes). int NameBufLen; // Size of buffer storing Name. May be larger than strlen(Name)! ImGuiID MoveId; // == window->GetID("#MOVE") + ImGuiID TabId; // == window->GetID("#TAB") ImGuiID ChildId; // ID of corresponding item in parent window (for navigation to return from child window to parent window) ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) ImVec2 Scroll; @@ -2490,6 +2721,7 @@ struct IMGUI_API ImGuiWindow ImVec2 ScrollTargetEdgeSnapDist; // 0.0f = no snapping, >0.0f snapping threshold ImVec2 ScrollbarSizes; // Size taken by each scrollbars on their smaller axis. Pay attention! ScrollbarSizes.x == width of the vertical scrollbar, ScrollbarSizes.y = height of the horizontal scrollbar. bool ScrollbarX, ScrollbarY; // Are scrollbars visible? + bool ViewportOwned; bool Active; // Set to true on Begin(), unless Collapsed bool WasActive; bool WriteAccessed; // Set to true when any widget access the current window @@ -2519,6 +2751,7 @@ struct IMGUI_API ImGuiWindow ImGuiCond SetWindowPosAllowFlags : 8; // store acceptable condition flags for SetNextWindowPos() use. ImGuiCond SetWindowSizeAllowFlags : 8; // store acceptable condition flags for SetNextWindowSize() use. ImGuiCond SetWindowCollapsedAllowFlags : 8; // store acceptable condition flags for SetNextWindowCollapsed() use. + ImGuiCond SetWindowDockAllowFlags : 8; // store acceptable condition flags for SetNextWindowDock() use. ImVec2 SetWindowPosVal; // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size) ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0, 0) when positioning from top-left corner; ImVec2(0.5f, 0.5f) for centering; ImVec2(1, 1) for bottom right. @@ -2538,11 +2771,15 @@ struct IMGUI_API ImGuiWindow ImVec2ih HitTestHoleOffset; int LastFrameActive; // Last frame number the window was Active. + int LastFrameJustFocused; // Last frame number the window was made Focused. float LastTimeActive; // Last timestamp the window was Active (using float as we don't need high precision there) float ItemWidthDefault; ImGuiStorage StateStorage; ImVector ColumnsStorage; float FontWindowScale; // User scale multiplier per-window, via SetWindowFontScale() + float FontWindowScaleParents; + float FontDpiScale; + float FontRefSize; // This is a copy of window->CalcFontSize() at the time of Begin(), trying to phase out CalcFontSize() especially as it may be called on non-current window. int SettingsOffset; // Offset into SettingsWindows[] (offsets are always valid as we only grow the array from the back) ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer) @@ -2551,6 +2788,7 @@ struct IMGUI_API ImGuiWindow ImGuiWindow* ParentWindowInBeginStack; ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes. ImGuiWindow* RootWindowPopupTree; // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child. + ImGuiWindow* RootWindowDockTree; // Point to ourself or first ancestor that is not a child window. Cross through dock nodes. ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active. ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. ImGuiWindow* ParentWindowForFocusRoute; // Set to manual link a window to its logical parent so that Shortcut() chain are honoerd (e.g. Tool linked to Document) @@ -2565,6 +2803,17 @@ struct IMGUI_API ImGuiWindow int MemoryDrawListVtxCapacity; bool MemoryCompacted; // Set when window extraneous data have been garbage collected + // Docking + bool DockIsActive :1; // When docking artifacts are actually visible. When this is set, DockNode is guaranteed to be != NULL. ~~ (DockNode != NULL) && (DockNode->Windows.Size > 1). + bool DockNodeIsVisible :1; + bool DockTabIsVisible :1; // Is our window visible this frame? ~~ is the corresponding tab selected? + bool DockTabWantClose :1; + short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible. + ImGuiWindowDockStyle DockStyle; + ImGuiDockNode* DockNode; // Which node are we docked into. Important: Prefer testing DockIsActive in many cases as this will still be set when the dock node is hidden. + ImGuiDockNode* DockNodeAsHost; // Which node are we owning (for parent windows) + ImGuiID DockId; // Backup of last valid DockNode->ID, so single window remember their dock node id even when they are not bound any more + public: ImGuiWindow(ImGuiContext* context, const char* name); ~ImGuiWindow(); @@ -2577,7 +2826,7 @@ struct IMGUI_API ImGuiWindow // We don't use g.FontSize because the window may be != g.CurrentWindow. ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } - float CalcFontSize() const { ImGuiContext& g = *Ctx; float scale = g.FontBaseSize * FontWindowScale; if (ParentWindow) scale *= ParentWindow->FontWindowScale; return scale; } + float CalcFontSize() const { ImGuiContext& g = *Ctx; return g.FontBaseSize * FontWindowScale * FontDpiScale * FontWindowScaleParents; } ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight)); } ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight; return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight); } }; @@ -2600,13 +2849,16 @@ enum ImGuiTabItemFlagsPrivate_ ImGuiTabItemFlags_SectionMask_ = ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing, ImGuiTabItemFlags_NoCloseButton = 1 << 20, // Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout) ImGuiTabItemFlags_Button = 1 << 21, // Used by TabItemButton, change the tab item behavior to mimic a button + ImGuiTabItemFlags_Invisible = 1 << 22, // To reserve space e.g. with ImGuiTabItemFlags_Leading + ImGuiTabItemFlags_Unsorted = 1 << 23, // [Docking] Trailing tabs with the _Unsorted flag will be sorted based on the DockOrder of their Window. }; -// Storage for one active tab item (sizeof() 40 bytes) +// Storage for one active tab item (sizeof() 48 bytes) struct ImGuiTabItem { ImGuiID ID; ImGuiTabItemFlags Flags; + ImGuiWindow* Window; // When TabItem is part of a DockNode's TabBar, we hold on to a window. int LastFrameVisible; int LastFrameSelected; // This allows us to infer an ordered list of the last activated tabs with little maintenance float Offset; // Position relative to beginning of tab @@ -2863,6 +3115,7 @@ struct IMGUI_API ImGuiTable ImGuiTableDrawChannelIdx DummyDrawChannel; // Redirect non-visible columns here. ImGuiTableDrawChannelIdx Bg2DrawChannelCurrent; // For Selectable() and other widgets drawing across columns after the freezing line. Index within DrawSplitter.Channels[] ImGuiTableDrawChannelIdx Bg2DrawChannelUnfrozen; + ImS8 NavLayer; // ImGuiNavLayer at the time of BeginTable(). bool IsLayoutLocked; // Set by TableUpdateLayout() which is called when beginning the first row. bool IsInsideRow; // Set when inside TableBeginRow()/TableEndRow(). bool IsInitializing; @@ -2965,7 +3218,8 @@ namespace ImGui // If this ever crashes because g.CurrentWindow is NULL, it means that either: // - ImGui::NewFrame() has never been called, which is illegal. // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal. - IMGUI_API ImGuiIO& GetIOEx(ImGuiContext* ctx); + IMGUI_API ImGuiIO& GetIOEx(ImGuiContext* ctx); + IMGUI_API ImGuiPlatformIO& GetPlatformIOEx(ImGuiContext* ctx); inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; } inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; } IMGUI_API ImGuiWindow* FindWindowByID(ImGuiID id); @@ -2973,7 +3227,7 @@ namespace ImGui IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window); IMGUI_API void UpdateWindowSkipRefresh(ImGuiWindow* window); IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window); - IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy); + IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy); IMGUI_API bool IsWindowWithinBeginStackOf(ImGuiWindow* window, ImGuiWindow* potential_parent); IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below); IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); @@ -2982,7 +3236,7 @@ namespace ImGui IMGUI_API void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond = 0); IMGUI_API void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size); IMGUI_API void SetWindowHiddenAndSkipItemsForCurrentFrame(ImGuiWindow* window); - inline void SetWindowParentWindowForFocusRoute(ImGuiWindow* window, ImGuiWindow* parent_window) { window->ParentWindowForFocusRoute = parent_window; } + inline void SetWindowParentWindowForFocusRoute(ImGuiWindow* window, ImGuiWindow* parent_window) { window->ParentWindowForFocusRoute = parent_window; } // You may also use SetNextWindowClass()'s FocusRouteParentWindowId field. inline ImRect WindowRectAbsToRel(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x - off.x, r.Min.y - off.y, r.Max.x - off.x, r.Max.y - off.y); } inline ImRect WindowRectRelToAbs(ImGuiWindow* window, const ImRect& r) { ImVec2 off = window->DC.CursorStartPos; return ImRect(r.Min.x + off.x, r.Min.y + off.y, r.Max.x + off.x, r.Max.y + off.y); } inline ImVec2 WindowPosAbsToRel(ImGuiWindow* window, const ImVec2& p) { ImVec2 off = window->DC.CursorStartPos; return ImVec2(p.x - off.x, p.y - off.y); } @@ -3004,9 +3258,8 @@ namespace ImGui // Fonts, drawing IMGUI_API void SetCurrentFont(ImFont* font); inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; } - inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); return GetForegroundDrawList(); } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches. - IMGUI_API ImDrawList* GetBackgroundDrawList(ImGuiViewport* viewport); // get background draw list for the given viewport. this draw list will be the first rendering one. Useful to quickly draw shapes/text behind dear imgui contents. - IMGUI_API ImDrawList* GetForegroundDrawList(ImGuiViewport* viewport); // get foreground draw list for the given viewport. this draw list will be the last rendered one. Useful to quickly draw shapes/text over dear imgui contents. + IMGUI_API void PushPasswordFont(); + inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { return GetForegroundDrawList(window->Viewport); } IMGUI_API void AddDrawListToDrawDataEx(ImDrawData* draw_data, ImVector* out_list, ImDrawList* draw_list); // Init @@ -3018,6 +3271,7 @@ namespace ImGui IMGUI_API void UpdateHoveredWindowAndCaptureFlags(); IMGUI_API void FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_viewport, ImGuiWindow** out_hovered_window, ImGuiWindow** out_hovered_window_under_moving_window); IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window); + IMGUI_API void StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock); IMGUI_API void UpdateMouseMovingWindowNewFrame(); IMGUI_API void UpdateMouseMovingWindowEndFrame(); @@ -3027,8 +3281,13 @@ namespace ImGui IMGUI_API void CallContextHooks(ImGuiContext* context, ImGuiContextHookType type); // Viewports + IMGUI_API void TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos, const ImVec2& old_size, const ImVec2& new_size); IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); + IMGUI_API void DestroyPlatformWindow(ImGuiViewportP* viewport); IMGUI_API void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport); + IMGUI_API void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport); + IMGUI_API const ImGuiPlatformMonitor* GetViewportPlatformMonitor(ImGuiViewport* viewport); + IMGUI_API ImGuiViewportP* FindHoveredViewportFromPlatformWindowStack(const ImVec2& mouse_platform_pos); // Settings IMGUI_API void MarkIniSettingsDirty(); @@ -3085,7 +3344,7 @@ namespace ImGui IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id, ImGuiItemFlags item_flags); IMGUI_API bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags = 0); IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id); - IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags in_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); + IMGUI_API void SetLastItemData(ImGuiID item_id, ImGuiItemFlags item_flags, ImGuiItemStatusFlags status_flags, const ImRect& item_rect); IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_w, float default_h); IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); IMGUI_API void PushMultiItemsWidths(int components, float width_full); @@ -3243,6 +3502,61 @@ namespace ImGui IMGUI_API bool TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id); IMGUI_API ImGuiKeyRoutingData* GetShortcutRoutingData(ImGuiKeyChord key_chord); + // Docking + // (some functions are only declared in imgui.cpp, see Docking section) + IMGUI_API void DockContextInitialize(ImGuiContext* ctx); + IMGUI_API void DockContextShutdown(ImGuiContext* ctx); + IMGUI_API void DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs); // Use root_id==0 to clear all + IMGUI_API void DockContextRebuildNodes(ImGuiContext* ctx); + IMGUI_API void DockContextNewFrameUpdateUndocking(ImGuiContext* ctx); + IMGUI_API void DockContextNewFrameUpdateDocking(ImGuiContext* ctx); + IMGUI_API void DockContextEndFrame(ImGuiContext* ctx); + IMGUI_API ImGuiID DockContextGenNodeID(ImGuiContext* ctx); + IMGUI_API void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); + IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); + IMGUI_API void DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); + IMGUI_API void DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref = true); + IMGUI_API void DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); + IMGUI_API bool DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload_window, ImGuiDockNode* payload_node, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos); + IMGUI_API ImGuiDockNode*DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id); + IMGUI_API void DockNodeWindowMenuHandler_Default(ImGuiContext* ctx, ImGuiDockNode* node, ImGuiTabBar* tab_bar); + IMGUI_API bool DockNodeBeginAmendTabBar(ImGuiDockNode* node); + IMGUI_API void DockNodeEndAmendTabBar(); + inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } + inline bool DockNodeIsInHierarchyOf(ImGuiDockNode* node, ImGuiDockNode* parent) { while (node) { if (node == parent) return true; node = node->ParentNode; } return false; } + inline int DockNodeGetDepth(const ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; } + inline ImGuiID DockNodeGetWindowMenuButtonId(const ImGuiDockNode* node) { return ImHashStr("#COLLAPSE", 0, node->ID); } + inline ImGuiDockNode* GetWindowDockNode() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DockNode; } + IMGUI_API bool GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window); + IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open); + IMGUI_API void BeginDockableDragDropSource(ImGuiWindow* window); + IMGUI_API void BeginDockableDragDropTarget(ImGuiWindow* window); + IMGUI_API void SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond); + + // Docking - Builder function needs to be generally called before the node is used/submitted. + // - The DockBuilderXXX functions are designed to _eventually_ become a public API, but it is too early to expose it and guarantee stability. + // - Do not hold on ImGuiDockNode* pointers! They may be invalidated by any split/merge/remove operation and every frame. + // - To create a DockSpace() node, make sure to set the ImGuiDockNodeFlags_DockSpace flag when calling DockBuilderAddNode(). + // You can create dockspace nodes (attached to a window) _or_ floating nodes (carry its own window) with this API. + // - DockBuilderSplitNode() create 2 child nodes within 1 node. The initial node becomes a parent node. + // - If you intend to split the node immediately after creation using DockBuilderSplitNode(), make sure + // to call DockBuilderSetNodeSize() beforehand. If you don't, the resulting split sizes may not be reliable. + // - Call DockBuilderFinish() after you are done. + IMGUI_API void DockBuilderDockWindow(const char* window_name, ImGuiID node_id); + IMGUI_API ImGuiDockNode*DockBuilderGetNode(ImGuiID node_id); + inline ImGuiDockNode* DockBuilderGetCentralNode(ImGuiID node_id) { ImGuiDockNode* node = DockBuilderGetNode(node_id); if (!node) return NULL; return DockNodeGetRootNode(node)->CentralNode; } + IMGUI_API ImGuiID DockBuilderAddNode(ImGuiID node_id = 0, ImGuiDockNodeFlags flags = 0); + IMGUI_API void DockBuilderRemoveNode(ImGuiID node_id); // Remove node and all its child, undock all windows + IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiID node_id, bool clear_settings_refs = true); + IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the remaining root node (node_id). + IMGUI_API void DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos); + IMGUI_API void DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size); + IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_at_opposite_dir); // Create 2 child nodes in this parent node. + IMGUI_API void DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs); + IMGUI_API void DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs); + IMGUI_API void DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name); + IMGUI_API void DockBuilderFinish(ImGuiID node_id); + // [EXPERIMENTAL] Focus Scope // This is generally used to identify a unique input location (for e.g. a selection set) // There is one per window (automatically set in Begin), but: @@ -3355,9 +3669,11 @@ namespace ImGui IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags); IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API ImGuiTabItem* TabBarFindTabByOrder(ImGuiTabBar* tab_bar, int order); + IMGUI_API ImGuiTabItem* TabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBar* tab_bar); IMGUI_API ImGuiTabItem* TabBarGetCurrentTab(ImGuiTabBar* tab_bar); inline int TabBarGetTabOrder(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) { return tab_bar->Tabs.index_from_ptr(tab); } IMGUI_API const char* TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); + IMGUI_API void TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window); IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); IMGUI_API void TabBarQueueFocus(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); @@ -3366,6 +3682,7 @@ namespace ImGui IMGUI_API void TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, ImGuiTabItem* tab, ImVec2 mouse_pos); IMGUI_API bool TabBarProcessReorder(ImGuiTabBar* tab_bar); IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window); + IMGUI_API void TabItemSpacing(const char* str_id, ImGuiTabItemFlags flags, float width); IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker); IMGUI_API ImVec2 TabItemCalcSize(ImGuiWindow* window); IMGUI_API void TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col); @@ -3394,8 +3711,10 @@ namespace ImGui IMGUI_API void RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col); IMGUI_API void RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz); IMGUI_API void RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col); + IMGUI_API void RenderArrowDockMenu(ImDrawList* draw_list, ImVec2 p_min, float sz, ImU32 col); IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); IMGUI_API void RenderRectFilledWithHole(ImDrawList* draw_list, const ImRect& outer, const ImRect& inner, ImU32 col, float rounding); + IMGUI_API ImDrawFlags CalcRoundingFlagsForRectInRect(const ImRect& r_in, const ImRect& r_outer, float threshold); // Widgets IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); @@ -3409,7 +3728,7 @@ namespace ImGui // Widgets: Window Decorations IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos); - IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos); + IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_node); IMGUI_API void Scrollbar(ImGuiAxis axis); IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 avail_v, ImS64 contents_v, ImDrawFlags draw_rounding_flags = 0); IMGUI_API ImRect GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis); @@ -3501,6 +3820,7 @@ namespace ImGui IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); IMGUI_API void DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* data_id, const void* data_id_end); IMGUI_API void DebugNodeColumns(ImGuiOldColumns* columns); + IMGUI_API void DebugNodeDockNode(ImGuiDockNode* node, const char* label); IMGUI_API void DebugNodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, const ImDrawList* draw_list, const char* label); IMGUI_API void DebugNodeDrawCmdShowMeshAndBoundingBox(ImDrawList* out_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, bool show_mesh, bool show_aabb); IMGUI_API void DebugNodeFont(ImFont* font); @@ -3517,6 +3837,7 @@ namespace ImGui IMGUI_API void DebugNodeWindowsList(ImVector* windows, const char* label); IMGUI_API void DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack); IMGUI_API void DebugNodeViewport(ImGuiViewportP* viewport); + IMGUI_API void DebugNodePlatformMonitor(ImGuiPlatformMonitor* monitor, const char* label, int idx); IMGUI_API void DebugRenderKeyboardPreview(ImDrawList* draw_list); IMGUI_API void DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb); @@ -3561,6 +3882,7 @@ IMGUI_API void ImFontAtlasBuildRender8bppRectFromString(ImFontAtlas* atlas, IMGUI_API void ImFontAtlasBuildRender32bppRectFromString(ImFontAtlas* atlas, int x, int y, int w, int h, const char* in_str, char in_marker_char, unsigned int in_marker_pixel_value); IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor); IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride); +IMGUI_API void ImFontAtlasBuildGetOversampleFactors(const ImFontConfig* cfg, int* out_oversample_h, int* out_oversample_v); //----------------------------------------------------------------------------- // [SECTION] Test Engine specific hooks (imgui_test_engine) diff --git a/neo/libs/imgui/imgui_tables.cpp b/neo/libs/imgui/imgui_tables.cpp index efef5ff74..5411e8a1b 100644 --- a/neo/libs/imgui/imgui_tables.cpp +++ b/neo/libs/imgui/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.7 +// dear imgui, v1.91.8 WIP // (tables and columns code) /* @@ -374,6 +374,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->ColumnsCount = columns_count; table->IsLayoutLocked = false; table->InnerWidth = inner_width; + table->NavLayer = (ImS8)outer_window->DC.NavLayerCurrent; temp_data->UserOuterSize = outer_size; // Instance data (for instance 0, TableID == TableInstanceID) @@ -1050,7 +1051,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) const int column_n = table->DisplayOrderToIndex[order_n]; ImGuiTableColumn* column = &table->Columns[column_n]; - column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main); // Use Count NOT request so Header line changes layer when frozen + // Initial nav layer: using FreezeRowsCount, NOT FreezeRowsRequest, so Header line changes layer when frozen + column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : (ImGuiNavLayer)table->NavLayer); if (offset_x_frozen && table->FreezeColumnsCount == visible_n) { @@ -1493,7 +1495,7 @@ void ImGui::EndTable() if (inner_window != outer_window) { short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask; - inner_window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // So empty table don't appear to navigate differently. + inner_window->DC.NavLayersActiveMask |= 1 << table->NavLayer; // So empty table don't appear to navigate differently. g.CurrentTable = NULL; // To avoid error recovery recursing EndChild(); g.CurrentTable = table; @@ -2032,7 +2034,7 @@ void ImGui::TableEndRow(ImGuiTable* table) if (unfreeze_rows_request) { for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main; + table->Columns[column_n].NavLayerCurrent = table->NavLayer; const float y0 = ImMax(table->RowPosY2 + 1, table->InnerClipRect.Min.y); table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y; diff --git a/neo/libs/imgui/imgui_widgets.cpp b/neo/libs/imgui/imgui_widgets.cpp index dd27400cd..9fce722a1 100644 --- a/neo/libs/imgui/imgui_widgets.cpp +++ b/neo/libs/imgui/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.7 +// dear imgui, v1.91.8 WIP // (widgets code) /* @@ -516,7 +516,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool flags |= (item_flags & ImGuiItemFlags_ButtonRepeat) ? ImGuiButtonFlags_PressedOnClick : ImGuiButtonFlags_PressedOnDefault_; ImGuiWindow* backup_hovered_window = g.HoveredWindow; - const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindow == window; + const bool flatten_hovered_children = (flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredWindow && g.HoveredWindow->RootWindowDockTree == window->RootWindowDockTree; if (flatten_hovered_children) g.HoveredWindow = window; @@ -874,7 +874,8 @@ bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos) return pressed; } -bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) +// The Collapse button also functions as a Dock Menu button. +bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_node) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; @@ -887,16 +888,21 @@ bool ImGui::CollapseButton(ImGuiID id, const ImVec2& pos) return pressed; // Render + //bool is_dock_menu = (window->DockNodeAsHost && !window->Collapsed); ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); ImU32 text_col = GetColorU32(ImGuiCol_Text); if (hovered || held) window->DrawList->AddRectFilled(bb.Min, bb.Max, bg_col); RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact); - RenderArrow(window->DrawList, bb.Min, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); + + if (dock_node) + RenderArrowDockMenu(window->DrawList, bb.Min, g.FontSize, text_col); + else + RenderArrow(window->DrawList, bb.Min, text_col, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); // Switch to moving the window after mouse is moved beyond the initial drag threshold if (IsItemActive() && IsMouseDragging(0)) - StartMouseMovingWindow(window); + StartMouseMovingWindowOrNode(window, dock_node, true); // Undock from window/collapse menu button return pressed; } @@ -909,15 +915,17 @@ ImGuiID ImGui::GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis) // Return scrollbar rectangle, must only be called for corresponding axis if window->ScrollbarX/Y is set. ImRect ImGui::GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis) { + ImGuiContext& g = *GImGui; const ImRect outer_rect = window->Rect(); const ImRect inner_rect = window->InnerRect; - const float border_size = window->WindowBorderSize; const float scrollbar_size = window->ScrollbarSizes[axis ^ 1]; // (ScrollbarSizes.x = width of Y scrollbar; ScrollbarSizes.y = height of X scrollbar) IM_ASSERT(scrollbar_size > 0.0f); + const float border_size = IM_ROUND(window->WindowBorderSize * 0.5f); + const float border_top = (window->Flags & ImGuiWindowFlags_MenuBar) ? IM_ROUND(g.Style.FrameBorderSize * 0.5f) : 0.0f; if (axis == ImGuiAxis_X) - return ImRect(inner_rect.Min.x, ImMax(outer_rect.Min.y, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x - border_size, outer_rect.Max.y - border_size); + return ImRect(inner_rect.Min.x + border_size, ImMax(outer_rect.Min.y + border_size, outer_rect.Max.y - border_size - scrollbar_size), inner_rect.Max.x - border_size, outer_rect.Max.y - border_size); else - return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y, outer_rect.Max.x - border_size, inner_rect.Max.y - border_size); + return ImRect(ImMax(outer_rect.Min.x, outer_rect.Max.x - border_size - scrollbar_size), inner_rect.Min.y + border_top, outer_rect.Max.x - border_size, inner_rect.Max.y - border_size); } void ImGui::Scrollbar(ImGuiAxis axis) @@ -1638,7 +1646,7 @@ void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end const float separator_thickness = style.SeparatorTextBorderSize; const ImVec2 min_size(label_size.x + extra_w + padding.x * 2.0f, ImMax(label_size.y + padding.y * 2.0f, separator_thickness)); const ImRect bb(pos, ImVec2(window->WorkRect.Max.x, pos.y + min_size.y)); - const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImFloor((style.SeparatorTextSize - label_size.y) * 0.5f)); + const float text_baseline_y = ImTrunc((bb.GetHeight() - label_size.y) * style.SeparatorTextAlign.y + 0.99999f); //ImMax(padding.y, ImTrunc((style.SeparatorTextSize - label_size.y) * 0.5f)); ItemSize(min_size, text_baseline_y); if (!ItemAdd(bb, id)) return; @@ -4244,6 +4252,23 @@ void ImGuiInputTextCallbackData::InsertChars(int pos, const char* new_text, cons BufTextLen += new_text_len; } +void ImGui::PushPasswordFont() +{ + ImGuiContext& g = *GImGui; + ImFont* in_font = g.Font; + ImFont* out_font = &g.InputTextPasswordFont; + const ImFontGlyph* glyph = in_font->FindGlyph('*'); + out_font->FontSize = in_font->FontSize; + out_font->Scale = in_font->Scale; + out_font->Ascent = in_font->Ascent; + out_font->Descent = in_font->Descent; + out_font->ContainerAtlas = in_font->ContainerAtlas; + out_font->FallbackGlyph = glyph; + out_font->FallbackAdvanceX = glyph->AdvanceX; + IM_ASSERT(out_font->Glyphs.Size == 0 && out_font->IndexAdvanceX.Size == 0 && out_font->IndexLookup.Size == 0); + PushFont(out_font); +} + // Return false to discard a character. static bool InputTextFilterCharacter(ImGuiContext* ctx, unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, bool input_source_is_clipboard) { @@ -4654,19 +4679,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Password pushes a temporary font with only a fallback glyph if (is_password && !is_displaying_hint) - { - const ImFontGlyph* glyph = g.Font->FindGlyph('*'); - ImFont* password_font = &g.InputTextPasswordFont; - password_font->FontSize = g.Font->FontSize; - password_font->Scale = g.Font->Scale; - password_font->Ascent = g.Font->Ascent; - password_font->Descent = g.Font->Descent; - password_font->ContainerAtlas = g.Font->ContainerAtlas; - password_font->FallbackGlyph = glyph; - password_font->FallbackAdvanceX = glyph->AdvanceX; - IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty()); - PushFont(password_font); - } + PushPasswordFont(); // Process mouse inputs and character inputs if (g.ActiveId == id) @@ -5299,6 +5312,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ g.PlatformImeData.WantVisible = true; g.PlatformImeData.InputPos = ImVec2(cursor_screen_pos.x - 1.0f, cursor_screen_pos.y - g.FontSize); g.PlatformImeData.InputLineHeight = g.FontSize; + g.PlatformImeViewport = window->Viewport->ID; } } } @@ -5902,7 +5916,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl if ((flags & ImGuiColorEditFlags_NoLabel)) Text("Current"); - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf | ImGuiColorEditFlags_NoTooltip; + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaMask_ | ImGuiColorEditFlags_NoTooltip; ColorButton("##current", col_v4, (flags & sub_flags_to_forward), ImVec2(square_sz * 3, square_sz * 2)); if (ref_col != NULL) { @@ -5942,7 +5956,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl if ((flags & ImGuiColorEditFlags_NoInputs) == 0) { PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; + ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_AlphaMask_ | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoSmallPreview; ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; if (flags & ImGuiColorEditFlags_DisplayRGB || (flags & ImGuiColorEditFlags_DisplayMask_) == 0) if (ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_DisplayRGB)) @@ -6118,8 +6132,8 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl bool hovered, held; bool pressed = ButtonBehavior(bb, id, &hovered, &held); - if (flags & ImGuiColorEditFlags_NoAlpha) - flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf); + if (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaOpaque)) + flags &= ~(ImGuiColorEditFlags_AlphaNoBg | ImGuiColorEditFlags_AlphaPreviewHalf); ImVec4 col_rgb = col; if (flags & ImGuiColorEditFlags_InputHSV) @@ -6138,14 +6152,17 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col_rgb.w < 1.0f) { float mid_x = IM_ROUND((bb_inner.Min.x + bb_inner.Max.x) * 0.5f); - RenderColorRectWithAlphaCheckerboard(window->DrawList, ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawFlags_RoundCornersRight); + if ((flags & ImGuiColorEditFlags_AlphaNoBg) == 0) + RenderColorRectWithAlphaCheckerboard(window->DrawList, ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawFlags_RoundCornersRight); + else + window->DrawList->AddRectFilled(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col_rgb), rounding, ImDrawFlags_RoundCornersRight); window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_rgb_without_alpha), rounding, ImDrawFlags_RoundCornersLeft); } else { // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha - ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col_rgb : col_rgb_without_alpha; - if (col_source.w < 1.0f) + ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaOpaque) ? col_rgb_without_alpha : col_rgb; + if (col_source.w < 1.0f && (flags & ImGuiColorEditFlags_AlphaNoBg) == 0) RenderColorRectWithAlphaCheckerboard(window->DrawList, bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding); else window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding); @@ -6175,7 +6192,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl // Tooltip if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered && IsItemHovered(ImGuiHoveredFlags_ForTooltip)) - ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); + ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_AlphaMask_)); return pressed; } @@ -6216,7 +6233,8 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2); ImVec4 cf(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); - ColorButton("##preview", cf, (flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); + ImGuiColorEditFlags flags_to_forward = ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_AlphaMask_; + ColorButton("##preview", cf, (flags & flags_to_forward) | ImGuiColorEditFlags_NoTooltip, sz); SameLine(); if ((flags & ImGuiColorEditFlags_InputRGB) || !(flags & ImGuiColorEditFlags_InputMask_)) { @@ -6937,13 +6955,9 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_SpanAvailWidth)) size.x = ImMax(label_size.x, max_x - min_x); - // Text stays at the submission position, but bounding box may be extended on both sides - const ImVec2 text_min = pos; - const ImVec2 text_max(min_x + size.x, pos.y + size.y); - // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable. // FIXME: Not part of layout so not included in clipper calculation, but ItemSize currently doesn't allow offsetting CursorPos. - ImRect bb(min_x, pos.y, text_max.x, text_max.y); + ImRect bb(min_x, pos.y, min_x + size.x, pos.y + size.y); if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0) { const float spacing_x = span_all_columns ? 0.0f : style.ItemSpacing.x; @@ -7079,8 +7093,9 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl PopColumnsBackground(); } + // Text stays at the submission position. Alignment/clipping extents ignore SpanAllColumns. if (is_visible) - RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb); + RenderTextClipped(pos, ImVec2(window->WorkRect.Max.x, pos.y + size.y), label, NULL, &label_size, style.SelectableTextAlign, &bb); // Automatically close popups if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_NoAutoClosePopups) && (g.LastItemData.ItemFlags & ImGuiItemFlags_AutoClosePopups)) @@ -8622,12 +8637,14 @@ bool ImGui::BeginMenuBar() IM_ASSERT(!window->DC.MenuBarAppending); BeginGroup(); // Backup position on layer 0 // FIXME: Misleading to use a group for that backup/restore - PushID("##menubar"); + PushID("##MenuBar"); // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect. // We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy. + const float border_top = ImMax(IM_ROUND(window->WindowBorderSize * 0.5f - window->TitleBarHeight), 0.0f); + const float border_half = IM_ROUND(window->WindowBorderSize * 0.5f); ImRect bar_rect = window->MenuBarRect(); - ImRect clip_rect(IM_ROUND(bar_rect.Min.x + window->WindowBorderSize), IM_ROUND(bar_rect.Min.y + window->WindowBorderSize), IM_ROUND(ImMax(bar_rect.Min.x, bar_rect.Max.x - ImMax(window->WindowRounding, window->WindowBorderSize))), IM_ROUND(bar_rect.Max.y)); + ImRect clip_rect(ImFloor(bar_rect.Min.x + border_half), ImFloor(bar_rect.Min.y + border_top), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - ImMax(window->WindowRounding, border_half))), ImFloor(bar_rect.Max.y)); clip_rect.ClipWith(window->OuterRectClipped); PushClipRect(clip_rect.Min, clip_rect.Max, false); @@ -8702,10 +8719,10 @@ bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, Im IM_ASSERT(dir != ImGuiDir_None); ImGuiWindow* bar_window = FindWindowByName(name); + ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)(viewport_p ? viewport_p : GetMainViewport()); if (bar_window == NULL || bar_window->BeginCount == 0) { // Calculate and set window size/position - ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)(viewport_p ? viewport_p : GetMainViewport()); ImRect avail_rect = viewport->GetBuildWorkRect(); ImGuiAxis axis = (dir == ImGuiDir_Up || dir == ImGuiDir_Down) ? ImGuiAxis_Y : ImGuiAxis_X; ImVec2 pos = avail_rect.Min; @@ -8723,7 +8740,8 @@ bool ImGui::BeginViewportSideBar(const char* name, ImGuiViewport* viewport_p, Im viewport->BuildWorkInsetMax[axis] += axis_size; } - window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; + window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking; + SetNextWindowViewport(viewport->ID); // Enforce viewport so we don't create our own viewport when ImGuiConfigFlags_ViewportsNoMerge is set. PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0)); // Lift normal size constraint bool is_open = Begin(name, NULL, window_flags); @@ -8737,6 +8755,9 @@ bool ImGui::BeginMainMenuBar() ImGuiContext& g = *GImGui; ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport(); + // Notify of viewport change so GetFrameHeight() can be accurate in case of DPI change + SetCurrentViewport(NULL, viewport); + // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. // FIXME: This could be generalized as an opt-in way to clamp window->DC.CursorStartPos to avoid SafeArea? // FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV calibration in OS settings. @@ -8760,7 +8781,7 @@ void ImGui::EndMainMenuBar() // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window // FIXME: With this strategy we won't be able to restore a NULL focus. ImGuiContext& g = *GImGui; - if (g.CurrentWindow == g.NavWindow && g.NavLayer == ImGuiNavLayer_Main && !g.NavAnyRequest) + if (g.CurrentWindow == g.NavWindow && g.NavLayer == ImGuiNavLayer_Main && !g.NavAnyRequest && g.ActiveId == 0) FocusTopMostWindowUnderOne(g.NavWindow, NULL, NULL, ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild); End(); @@ -8787,7 +8808,7 @@ static bool IsRootOfOpenMenuSet() const ImGuiPopupData* upper_popup = &g.OpenPopupStack[g.BeginPopupStack.Size]; if (window->DC.NavLayerCurrent != upper_popup->ParentNavLayer) return false; - return upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu) && ImGui::IsWindowChildOf(upper_popup->Window, window, true); + return upper_popup->Window && (upper_popup->Window->Flags & ImGuiWindowFlags_ChildMenu) && ImGui::IsWindowChildOf(upper_popup->Window, window, true, false); } bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) @@ -9129,8 +9150,10 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, // - TabBarCalcMaxTabWidth() [Internal] // - TabBarFindTabById() [Internal] // - TabBarFindTabByOrder() [Internal] +// - TabBarFindMostRecentlySelectedTabForActiveWindow() [Internal] // - TabBarGetCurrentTab() [Internal] // - TabBarGetTabName() [Internal] +// - TabBarAddTab() [Internal] // - TabBarRemoveTab() [Internal] // - TabBarCloseTab() [Internal] // - TabBarScrollClamp() [Internal] @@ -9252,7 +9275,8 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG // Ensure correct ordering when toggling ImGuiTabBarFlags_Reorderable flag, or when a new tab was added while being not reorderable if ((flags & ImGuiTabBarFlags_Reorderable) != (tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (tab_bar->TabsAddedNew && !(flags & ImGuiTabBarFlags_Reorderable))) - ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder); + if ((flags & ImGuiTabBarFlags_DockNode) == 0) // FIXME: TabBar with DockNode can now be hybrid + ImQsort(tab_bar->Tabs.Data, tab_bar->Tabs.Size, sizeof(ImGuiTabItem), TabItemComparerByBeginOrder); tab_bar->TabsAddedNew = false; // Flags @@ -9534,6 +9558,10 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) tab_bar->VisibleTabId = tab_bar->SelectedTabId; tab_bar->VisibleTabWasSubmitted = false; + // CTRL+TAB can override visible tab temporarily + if (g.NavWindowingTarget != NULL && g.NavWindowingTarget->DockNode && g.NavWindowingTarget->DockNode->TabBar == tab_bar) + tab_bar->VisibleTabId = scroll_to_tab_id = g.NavWindowingTarget->TabId; + // Apply request requests if (scroll_to_tab_id != 0) TabBarScrollToTab(tab_bar, scroll_to_tab_id, sections); @@ -9579,11 +9607,11 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) // Dockable windows uses Name/ID in the global namespace. Non-dockable items use the ID stack. static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window) { - IM_ASSERT(docked_window == NULL); // master branch only - IM_UNUSED(docked_window); - if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) + if (docked_window != NULL) { - ImGuiID id = ImHashStr(label); + IM_UNUSED(tab_bar); + IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode); + ImGuiID id = docked_window->TabId; KeepAliveID(id); return id; } @@ -9617,6 +9645,20 @@ ImGuiTabItem* ImGui::TabBarFindTabByOrder(ImGuiTabBar* tab_bar, int order) return &tab_bar->Tabs[order]; } +// FIXME: See references to #2304 in TODO.txt +ImGuiTabItem* ImGui::TabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBar* tab_bar) +{ + ImGuiTabItem* most_recently_selected_tab = NULL; + for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) + { + ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; + if (most_recently_selected_tab == NULL || most_recently_selected_tab->LastFrameSelected < tab->LastFrameSelected) + if (tab->Window && tab->Window->WasActive) + most_recently_selected_tab = tab; + } + return most_recently_selected_tab; +} + ImGuiTabItem* ImGui::TabBarGetCurrentTab(ImGuiTabBar* tab_bar) { if (tab_bar->LastTabItemIdx < 0 || tab_bar->LastTabItemIdx >= tab_bar->Tabs.Size) @@ -9626,12 +9668,35 @@ ImGuiTabItem* ImGui::TabBarGetCurrentTab(ImGuiTabBar* tab_bar) const char* ImGui::TabBarGetTabName(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) { + if (tab->Window) + return tab->Window->Name; if (tab->NameOffset == -1) return "N/A"; IM_ASSERT(tab->NameOffset < tab_bar->TabsNames.Buf.Size); return tab_bar->TabsNames.Buf.Data + tab->NameOffset; } +// The purpose of this call is to register tab in advance so we can control their order at the time they appear. +// Otherwise calling this is unnecessary as tabs are appending as needed by the BeginTabItem() function. +void ImGui::TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(TabBarFindTabByID(tab_bar, window->TabId) == NULL); + IM_ASSERT(g.CurrentTabBar != tab_bar); // Can't work while the tab bar is active as our tab doesn't have an X offset yet, in theory we could/should test something like (tab_bar->CurrFrameVisible < g.FrameCount) but we'd need to solve why triggers the commented early-out assert in BeginTabBarEx() (probably dock node going from implicit to explicit in same frame) + + if (!window->HasCloseButton) + tab_flags |= ImGuiTabItemFlags_NoCloseButton; // Set _NoCloseButton immediately because it will be used for first-frame width calculation. + + ImGuiTabItem new_tab; + new_tab.ID = window->TabId; + new_tab.Flags = tab_flags; + new_tab.LastFrameVisible = tab_bar->CurrFrameVisible; // Required so BeginTabBar() doesn't ditch the tab + if (new_tab.LastFrameVisible == -1) + new_tab.LastFrameVisible = g.FrameCount - 1; + new_tab.Window = window; // Required so tab bar layout can compute the tab width before tab submission + tab_bar->Tabs.push_back(new_tab); +} + // The *TabId fields are already set by the docking system _before_ the actual TabItem was created, so we clear them regardless. void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id) { @@ -9921,7 +9986,7 @@ bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags f IM_ASSERT_USER_ERROR(tab_bar, "Needs to be called between BeginTabBar() and EndTabBar()!"); return false; } - IM_ASSERT(!(flags & ImGuiTabItemFlags_Button)); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead! + IM_ASSERT((flags & ImGuiTabItemFlags_Button) == 0); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead! bool ret = TabItemEx(tab_bar, label, p_open, flags, NULL); if (ret && !(flags & ImGuiTabItemFlags_NoPushId)) @@ -9967,6 +10032,23 @@ bool ImGui::TabItemButton(const char* label, ImGuiTabItemFlags flags) return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder, NULL); } +void ImGui::TabItemSpacing(const char* str_id, ImGuiTabItemFlags flags, float width) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return; + + ImGuiTabBar* tab_bar = g.CurrentTabBar; + if (tab_bar == NULL) + { + IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); + return; + } + SetNextItemWidth(width); + TabItemEx(tab_bar, str_id, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder | ImGuiTabItemFlags_Invisible, NULL); +} + bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window) { // Layout whole tab bar if not already done @@ -10031,11 +10113,14 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, const bool is_tab_button = (flags & ImGuiTabItemFlags_Button) != 0; tab->LastFrameVisible = g.FrameCount; tab->Flags = flags; + tab->Window = docked_window; // Append name _WITH_ the zero-terminator + // (regular tabs are permitted in a DockNode tab bar, but window tabs not permitted in a non-DockNode tab bar) if (docked_window != NULL) { - IM_ASSERT(docked_window == NULL); // master branch only + IM_ASSERT(tab_bar->Flags & ImGuiTabBarFlags_DockNode); + tab->NameOffset = -1; } else { @@ -10060,7 +10145,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, tab_bar->VisibleTabWasSubmitted = true; // On the very first frame of a tab bar we let first tab contents be visible to minimize appearing glitches - if (!tab_contents_visible && tab_bar->SelectedTabId == 0 && tab_bar_appearing) + if (!tab_contents_visible && tab_bar->SelectedTabId == 0 && tab_bar_appearing && docked_window == NULL) if (tab_bar->Tabs.Size == 1 && !(tab_bar->Flags & ImGuiTabBarFlags_AutoSelectNewTabs)) tab_contents_visible = true; @@ -10108,30 +10193,85 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, } // Click to Select a tab - // Allow the close button to overlap ImGuiButtonFlags button_flags = ((is_tab_button ? ImGuiButtonFlags_PressedOnClickRelease : ImGuiButtonFlags_PressedOnClick) | ImGuiButtonFlags_AllowOverlap); - if (g.DragDropActive) + if (g.DragDropActive && !g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW)) // FIXME: May be an opt-in property of the payload to disable this button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); + bool hovered, held, pressed; + if (flags & ImGuiTabItemFlags_Invisible) + hovered = held = pressed = false; + else + pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); if (pressed && !is_tab_button) TabBarQueueFocus(tab_bar, tab); - // Drag and drop: re-order tabs - if (held && !tab_appearing && IsMouseDragging(0)) + // Transfer active id window so the active id is not owned by the dock host (as StartMouseMovingWindow() + // will only do it on the drag). This allows FocusWindow() to be more conservative in how it clears active id. + if (held && docked_window && g.ActiveId == id && g.ActiveIdIsJustActivated) + g.ActiveIdWindow = docked_window; + + // Drag and drop a single floating window node moves it + ImGuiDockNode* node = docked_window ? docked_window->DockNode : NULL; + const bool single_floating_window_node = node && node->IsFloatingNode() && (node->Windows.Size == 1); + if (held && single_floating_window_node && IsMouseDragging(0, 0.0f)) + { + // Move + StartMouseMovingWindow(docked_window); + } + else if (held && !tab_appearing && IsMouseDragging(0)) { - if (!g.DragDropActive && (tab_bar->Flags & ImGuiTabBarFlags_Reorderable)) + // Drag and drop: re-order tabs + int drag_dir = 0; + float drag_distance_from_edge_x = 0.0f; + if (!g.DragDropActive && ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (docked_window != NULL))) { // While moving a tab it will jump on the other side of the mouse, so we also test for MouseDelta.x if (g.IO.MouseDelta.x < 0.0f && g.IO.MousePos.x < bb.Min.x) { + drag_dir = -1; + drag_distance_from_edge_x = bb.Min.x - g.IO.MousePos.x; TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); } else if (g.IO.MouseDelta.x > 0.0f && g.IO.MousePos.x > bb.Max.x) { + drag_dir = +1; + drag_distance_from_edge_x = g.IO.MousePos.x - bb.Max.x; TabBarQueueReorderFromMousePos(tab_bar, tab, g.IO.MousePos); } } + + // Extract a Dockable window out of it's tab bar + const bool can_undock = docked_window != NULL && !(docked_window->Flags & ImGuiWindowFlags_NoMove) && !(node->MergedFlags & ImGuiDockNodeFlags_NoUndocking); + if (can_undock) + { + // We use a variable threshold to distinguish dragging tabs within a tab bar and extracting them out of the tab bar + bool undocking_tab = (g.DragDropActive && g.DragDropPayload.SourceId == id); + if (!undocking_tab) //&& (!g.IO.ConfigDockingWithShift || g.IO.KeyShift) + { + float threshold_base = g.FontSize; + float threshold_x = (threshold_base * 2.2f); + float threshold_y = (threshold_base * 1.5f) + ImClamp((ImFabs(g.IO.MouseDragMaxDistanceAbs[0].x) - threshold_base * 2.0f) * 0.20f, 0.0f, threshold_base * 4.0f); + //GetForegroundDrawList()->AddRect(ImVec2(bb.Min.x - threshold_x, bb.Min.y - threshold_y), ImVec2(bb.Max.x + threshold_x, bb.Max.y + threshold_y), IM_COL32_WHITE); // [DEBUG] + + float distance_from_edge_y = ImMax(bb.Min.y - g.IO.MousePos.y, g.IO.MousePos.y - bb.Max.y); + if (distance_from_edge_y >= threshold_y) + undocking_tab = true; + if (drag_distance_from_edge_x > threshold_x) + if ((drag_dir < 0 && TabBarGetTabOrder(tab_bar, tab) == 0) || (drag_dir > 0 && TabBarGetTabOrder(tab_bar, tab) == tab_bar->Tabs.Size - 1)) + undocking_tab = true; + } + + if (undocking_tab) + { + // Undock + // FIXME: refactor to share more code with e.g. StartMouseMovingWindow + DockContextQueueUndockWindow(&g, docked_window); + g.MovingWindow = docked_window; + SetActiveID(g.MovingWindow->MoveId, g.MovingWindow); + g.ActiveIdClickOffset -= g.MovingWindow->Pos - bb.Min; + g.ActiveIdNoClearOnFocusLoss = true; + SetActiveIdUsingAllKeyboardKeys(); + } + } } #if 0 @@ -10145,36 +10285,64 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, #endif // Render tab shape - ImDrawList* display_draw_list = window->DrawList; - const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabDimmed)); - TabItemBackground(display_draw_list, bb, flags, tab_col); - if (tab_contents_visible && (tab_bar->Flags & ImGuiTabBarFlags_DrawSelectedOverline) && style.TabBarOverlineSize > 0.0f) + const bool is_visible = (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) && !(flags & ImGuiTabItemFlags_Invisible); + if (is_visible) { - float x_offset = IM_TRUNC(0.4f * style.TabRounding); - if (x_offset < 2.0f * g.CurrentDpiScale) - x_offset = 0.0f; - float y_offset = 1.0f * g.CurrentDpiScale; - display_draw_list->AddLine(bb.GetTL() + ImVec2(x_offset, y_offset), bb.GetTR() + ImVec2(-x_offset, y_offset), GetColorU32(tab_bar_focused ? ImGuiCol_TabSelectedOverline : ImGuiCol_TabDimmedSelectedOverline), style.TabBarOverlineSize); - } - RenderNavCursor(bb, id); + ImDrawList* display_draw_list = window->DrawList; + const ImU32 tab_col = GetColorU32((held || hovered) ? ImGuiCol_TabHovered : tab_contents_visible ? (tab_bar_focused ? ImGuiCol_TabSelected : ImGuiCol_TabDimmedSelected) : (tab_bar_focused ? ImGuiCol_Tab : ImGuiCol_TabDimmed)); + TabItemBackground(display_draw_list, bb, flags, tab_col); + if (tab_contents_visible && (tab_bar->Flags & ImGuiTabBarFlags_DrawSelectedOverline) && style.TabBarOverlineSize > 0.0f) + { + // Might be moved to TabItemBackground() ? + ImVec2 tl = bb.GetTL() + ImVec2(0, 1.0f * g.CurrentDpiScale); + ImVec2 tr = bb.GetTR() + ImVec2(0, 1.0f * g.CurrentDpiScale); + ImU32 overline_col = GetColorU32(tab_bar_focused ? ImGuiCol_TabSelectedOverline : ImGuiCol_TabDimmedSelectedOverline); + if (style.TabRounding > 0.0f) + { + float rounding = style.TabRounding; + display_draw_list->PathArcToFast(tl + ImVec2(+rounding, +rounding), rounding, 7, 9); + display_draw_list->PathArcToFast(tr + ImVec2(-rounding, +rounding), rounding, 9, 11); + display_draw_list->PathStroke(overline_col, 0, style.TabBarOverlineSize); + } + else + { + display_draw_list->AddLine(tl - ImVec2(0.5f, 0.5f), tr - ImVec2(0.5f, 0.5f), overline_col, style.TabBarOverlineSize); + } + } + RenderNavCursor(bb, id); - // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget. - const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); - if (tab_bar->SelectedTabId != tab->ID && hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)) && !is_tab_button) - TabBarQueueFocus(tab_bar, tab); + // Select with right mouse button. This is so the common idiom for context menu automatically highlight the current widget. + const bool hovered_unblocked = IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup); + if (tab_bar->SelectedTabId != tab->ID && hovered_unblocked && (IsMouseClicked(1) || IsMouseReleased(1)) && !is_tab_button) + TabBarQueueFocus(tab_bar, tab); - if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) - flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; + if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) + flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; - // Render tab label, process close button - const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, id) : 0; - bool just_closed; - bool text_clipped; - TabItemLabelAndCloseButton(display_draw_list, bb, tab_just_unsaved ? (flags & ~ImGuiTabItemFlags_UnsavedDocument) : flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped); - if (just_closed && p_open != NULL) - { - *p_open = false; - TabBarCloseTab(tab_bar, tab); + // Render tab label, process close button + const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, docked_window ? docked_window->ID : id) : 0; + bool just_closed; + bool text_clipped; + TabItemLabelAndCloseButton(display_draw_list, bb, tab_just_unsaved ? (flags & ~ImGuiTabItemFlags_UnsavedDocument) : flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped); + if (just_closed && p_open != NULL) + { + *p_open = false; + TabBarCloseTab(tab_bar, tab); + } + + // Forward Hovered state so IsItemHovered() after Begin() can work (even though we are technically hovering our parent) + // That state is copied to window->DockTabItemStatusFlags by our caller. + if (docked_window && (hovered || g.HoveredId == close_button_id)) + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; + + // Tooltip + // (Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer-> seems ok) + // (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores) + // FIXME: This is a mess. + // FIXME: We may want disabled tab to still display the tooltip? + if (text_clipped && g.HoveredId == id && !held) + if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) + SetItemTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); } // Restore main window position so user can draw there @@ -10182,15 +10350,6 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, PopClipRect(); window->DC.CursorPos = backup_main_cursor_pos; - // Tooltip - // (Won't work over the close button because ItemOverlap systems messes up with HoveredIdTimer-> seems ok) - // (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores) - // FIXME: This is a mess. - // FIXME: We may want disabled tab to still display the tooltip? - if (text_clipped && g.HoveredId == id && !held) - if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) - SetItemTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); - IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected if (is_tab_button) return pressed; @@ -10211,6 +10370,16 @@ void ImGui::SetTabItemClosed(const char* label) if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id)) tab->WantClose = true; // Will be processed by next call to TabBarLayout() } + else if (ImGuiWindow* window = FindWindowByName(label)) + { + if (window->DockIsActive) + if (ImGuiDockNode* node = window->DockNode) + { + ImGuiID tab_id = TabBarCalcTabID(node->TabBar, label, window); + TabBarRemoveTab(node->TabBar, tab_id); + window->DockTabWantClose = true; + } + } } ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker) @@ -10225,10 +10394,9 @@ ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button_or_unsave return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y); } -ImVec2 ImGui::TabItemCalcSize(ImGuiWindow*) +ImVec2 ImGui::TabItemCalcSize(ImGuiWindow* window) { - IM_ASSERT(0); // This function exists to facilitate merge with 'docking' branch. - return ImVec2(0.0f, 0.0f); + return TabItemCalcSize(window->Name, window->HasCloseButton || (window->Flags & ImGuiWindowFlags_UnsavedDocument)); } void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col) diff --git a/neo/libs/imgui/misc/debuggers/imgui.natvis b/neo/libs/imgui/misc/debuggers/imgui.natvis index 13b636008..94d17a8f6 100644 --- a/neo/libs/imgui/misc/debuggers/imgui.natvis +++ b/neo/libs/imgui/misc/debuggers/imgui.natvis @@ -55,4 +55,8 @@ More information at: https://docs.microsoft.com/en-us/visualstudio/debugger/crea {{Name {Name,s} Active {(Active||WasActive)?1:0,d} Child {(Flags & 0x01000000)?1:0,d} Popup {(Flags & 0x04000000)?1:0,d} Hidden {(Hidden)?1:0,d}} + + {{ID {ID,x} Pos=({Pos.x,g} {Pos.y,g}) Size=({Size.x,g} {Size.y,g}) Parent {(ParentNode==0)?0:ParentNode->ID,x} Childs {(ChildNodes[0] != 0)+(ChildNodes[1] != 0)} Windows {Windows.Size} } + + diff --git a/neo/libs/imgui/misc/freetype/imgui_freetype.cpp b/neo/libs/imgui/misc/freetype/imgui_freetype.cpp index 997bc4340..029991bbd 100644 --- a/neo/libs/imgui/misc/freetype/imgui_freetype.cpp +++ b/neo/libs/imgui/misc/freetype/imgui_freetype.cpp @@ -33,7 +33,7 @@ // - For correct results you need to be using sRGB and convert to linear space in the pixel shader output. // - The default dear imgui styles will be impacted by this change (alpha values will need tweaking). -// FIXME: cfg.OversampleH, OversampleV are not supported (but perhaps not so necessary with this rasterizer). +// FIXME: cfg.OversampleH, OversampleV are not supported, but generally not necessary with this rasterizer because Hinting makes everything look better. #include "imgui.h" #ifndef IMGUI_DISABLE @@ -104,6 +104,9 @@ static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_ // Code //------------------------------------------------------------------------- +#define FT_CEIL(X) (((X + 63) & -64) / 64) // From SDL_ttf: Handy routines for converting from fixed point +#define FT_SCALEFACTOR 64.0f + namespace { // Glyph metrics: @@ -182,9 +185,6 @@ namespace float InvRasterizationDensity; }; - // From SDL_ttf: Handy routines for converting from fixed point - #define FT_CEIL(X) (((X + 63) & -64) / 64) - bool FreeTypeFont::InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_font_builder_flags) { FT_Error error = FT_New_Memory_Face(ft_library, (uint8_t*)cfg.FontData, (uint32_t)cfg.FontDataSize, (uint32_t)cfg.FontNo, &Face); @@ -316,7 +316,7 @@ namespace out_glyph_info->Height = (int)ft_bitmap->rows; out_glyph_info->OffsetX = Face->glyph->bitmap_left; out_glyph_info->OffsetY = -Face->glyph->bitmap_top; - out_glyph_info->AdvanceX = (float)FT_CEIL(slot->advance.x); + out_glyph_info->AdvanceX = (float)slot->advance.x / FT_SCALEFACTOR; out_glyph_info->IsColored = (ft_bitmap->pixel_mode == FT_PIXEL_MODE_BGRA); return ft_bitmap; diff --git a/neo/sys/sys_imgui.cpp b/neo/sys/sys_imgui.cpp index 353b380e2..541727f94 100644 --- a/neo/sys/sys_imgui.cpp +++ b/neo/sys/sys_imgui.cpp @@ -40,6 +40,8 @@ #include "renderer/tr_local.h" // glconfig #include "ui/DeviceContext.h" +#include "tools/edit_public.h" + extern void Com_DrawDhewm3SettingsMenu(); // in framework/dhewm3SettingsMenu.cpp extern void Com_OpenCloseDhewm3SettingsMenu( bool open ); // ditto @@ -170,6 +172,57 @@ void ShowWarningOverlay( const char* text ) warningOverlayStartPos = ImGui::GetMousePos(); } +static idStr infoOverlayText; +static double infoOverlayStartTime = -100.0; +static ImVec2 infoOverlayStartPos; +static bool infoOverlayOpen = false; + +static void UpdateInfoOverlay() +{ + if ( !infoOverlayOpen ) { + return; + } + + if ( ImGui::GetTime() - infoOverlayStartTime > 4.0f ) { + infoOverlayOpen = false; + return; + } + + // also hide if a key was pressed or maybe even if the mouse was moved (too much) + ImVec2 mdv = ImGui::GetMousePos() - infoOverlayStartPos; // Mouse Delta Vector + float mouseDelta = sqrtf( mdv.x * mdv.x + mdv.y * mdv.y ); + const float fontSize = ImGui::GetFontSize(); + if ( mouseDelta > fontSize * 10.0f ) { + infoOverlayStartTime = -100.0f; + infoOverlayOpen = false; + return; + } + + ImVec2 pos = ImGui::GetMainViewport()->WorkPos; + pos.x += fontSize; + pos.y += fontSize; + ImGui::SetNextWindowPos( pos, ImGuiCond_Always ); //, ImVec2(0.5f, 0.5f) ); + ImGui::PushStyleColor( ImGuiCol_WindowBg, ImVec4(0.4f, 0.4f, 0.4f, 0.6f) ); + + int winFlags = ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove + | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize; + ImGui::Begin("InfoOverlay", NULL, winFlags); + + ImGui::TextUnformatted( infoOverlayText.c_str() ); + + ImGui::End(); + + ImGui::PopStyleColor(); // WindowBg +} + +void ShowInfoOverlay( const char* text ) +{ + infoOverlayText = text; + infoOverlayStartTime = ImGui::GetTime(); + infoOverlayStartPos = ImGui::GetMousePos(); + infoOverlayOpen = true; +} + static float GetDefaultScale() { if ( glConfig.winWidth != glConfig.vidWidth ) { @@ -227,6 +280,12 @@ bool Init(void* _sdlWindow, void* sdlGlContext) io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + // TODO: make configurable so it's not always enabled by default? + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; + //io.ConfigViewportsNoAutoMerge = true; + //io.ConfigViewportsNoTaskBarIcon = true; + static idStr iniPath; iniPath = cvarSystem->GetCVarString( "fs_configpath" ); iniPath += "/imgui.ini"; @@ -319,7 +378,7 @@ void NewFrame() // so ImGui also recognizes internally that all windows are closed // and e.g. ImGuiCond_Appearing works as intended static int framesAfterAllWindowsClosed = 0; - if ( openImguiWindows == 0 ) { + if ( openImguiWindows == 0 && !infoOverlayOpen ) { if ( framesAfterAllWindowsClosed > 1 ) return; else @@ -360,6 +419,7 @@ void NewFrame() haveNewFrame = true; UpdateWarningOverlay(); + UpdateInfoOverlay(); if (openImguiWindows & D3_ImGuiWin_Settings) { Com_DrawDhewm3SettingsMenu(); @@ -371,10 +431,19 @@ void NewFrame() if(!show_demo_window) CloseWindow(D3_ImGuiWin_Demo); } + + if (openImguiWindows & D3_ImGuiWin_AnyEditor) { + ImGuiTools::DrawToolWindows(); + } } bool keybindModeEnabled = false; +// this is true if the right mouse button is pressed while an ImGui window is open +// but the cursor is not over a window. +// used to allow looking around when a tool or the settings menu is open +static bool rmbPressed = false; + // called with every SDL event by Sys_GetEvent() // returns true if ImGui has handled the event (so it shouldn't be handled by D3) bool ProcessEvent(const void* sdlEvent) @@ -383,13 +452,29 @@ bool ProcessEvent(const void* sdlEvent) return false; const SDL_Event* ev = (const SDL_Event*)sdlEvent; + ImGuiIO& io = ImGui::GetIO(); + + // allow looking and moving around while right mouse button is pressed, even if + // an ImGui window is open (unless the cursor is currently over an ImGui window) + if ( !io.WantCaptureMouse && (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_RMASK) ) + { + // prevent the hidden cursor from interacting with ImGui windows + io.ConfigFlags |= ImGuiConfigFlags_NoMouse; + rmbPressed = true; + return false; + } + + // if right mouse button isn't pressed anymore, restore ability to use the mouse in ImGui windows + io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse; + rmbPressed = false; + // ImGui_ImplSDL2_ProcessEvent() doc says: // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. - bool imguiUsedEvent = ImGui_ImplSDLx_ProcessEvent( ev ); + if ( keybindModeEnabled ) { // in keybind mode, all input events are passed to Doom3 so it can translate them // to internal events and we can access and use them to create a new binding @@ -413,8 +498,6 @@ bool ProcessEvent(const void* sdlEvent) hadKeyDownEvent = true; } if( imguiUsedEvent ) { - ImGuiIO& io = ImGui::GetIO(); - if ( io.WantCaptureMouse ) { switch( ev->type ) { case SDL_MOUSEMOTION: @@ -435,9 +518,9 @@ bool ProcessEvent(const void* sdlEvent) case SDL_KEYDOWN: //case SDL_KEYUP: NOTE: see above why key up events are passed to the engine #if SDL_VERSION_ATLEAST(3, 0, 0) - if ( ev->key.key < SDLK_F1 || ev->key.key > SDLK_F12) { + if ( ev->key.key < SDLK_F1 || ev->key.key > SDLK_F12 ) { #else - if ( ev->key.keysym.sym < SDLK_F1 || ev->key.keysym.sym > SDLK_F12) { + if ( ev->key.keysym.sym < SDLK_F1 || ev->key.keysym.sym > SDLK_F12 ) { #endif // F1 - F12 are passed to the engine so its shortcuts // (like quickload or screenshot) still work @@ -461,27 +544,21 @@ void SetKeyBindMode( bool enable ) bool ShouldShowCursor() { + if ( openImguiWindows == 0 ) { + return false; // no open ImGui window => no ImGui cursor + } if ( sessLocal.GetActiveMenu() == nullptr ) { - // when ingame, render the ImGui/SDL/system cursor if an ImGui window is open - // because dhewm3 does *not* render its own cursor outside ImGui windows. - // additionally, only show it if an ImGui window has focus - this allows you - // to click outside the ImGui window to give Doom3 focus and look around. - // You can get focus on the ImGui window again by clicking while the invisible - // cursor is over the window (things in it still get highlighted), or by - // opening the main (Esc) or by opening the Dhewm3 Settings window (F10, usually), - // which will either open it focused or give an ImGui window focus if it - // was open but unfocused. - // TODO: Might be nice to have a keyboard shortcut to give focus to any open - // ImGui window, maybe Pause? - return openImguiWindows != 0 && ImGui::IsWindowFocused( ImGuiFocusedFlags_AnyWindow ); + // we're ingame + // if one of our ImGui windows is open (tool or settings menu), usually the + // cursor is shown and all events go to ImGui (no moving around the world) + // UNLESS the right mouse button is pressed (outside of an ImGui window) + // so you still have a way to look around the world when needed + return !rmbPressed; } else { // if we're in a menu (probably main menu), dhewm3 renders a cursor for it, // so only show the ImGui cursor when the mouse cursor is over an ImGui window // or in one of the black bars where Doom3's cursor isn't rendered in // non 4:3 resolutions - if ( openImguiWindows == 0 ) { - return false; // no open ImGui window => no ImGui cursor - } if ( ImGui::GetIO().WantCaptureMouse ) { return true; // over an ImGui window => definitely want ImGui cursor } @@ -558,6 +635,18 @@ void EndFrame() ImGui_ImplOpenGL2_RenderDrawData( ImGui::GetDrawData() ); + // Update and Render additional Platform Windows + // (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere. + // For this specific demo app we could also call SDL_GL_MakeCurrent(window, gl_context) directly) + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + SDL_Window* backup_current_window = SDL_GL_GetCurrentWindow(); + SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + SDL_GL_MakeCurrent(backup_current_window, backup_current_context); + } + if ( curArrayBuffer != 0 ) { qglBindBufferARB( GL_ARRAY_BUFFER_ARB, curArrayBuffer ); } @@ -579,6 +668,9 @@ void OpenWindow( D3ImGuiWindow win ) switch ( win ) { case D3_ImGuiWin_Settings: Com_OpenCloseDhewm3SettingsMenu( true ); + break; + case D3_ImGuiWin_PDAEditor: + break; // TODO: other windows that need explicit opening } @@ -594,6 +686,9 @@ void CloseWindow( D3ImGuiWindow win ) switch ( win ) { case D3_ImGuiWin_Settings: Com_OpenCloseDhewm3SettingsMenu( false ); + break; + case D3_ImGuiWin_PDAEditor: + break; // TODO: other windows that need explicit closing } diff --git a/neo/sys/sys_imgui.h b/neo/sys/sys_imgui.h index bd0db4e58..183e40bf9 100644 --- a/neo/sys/sys_imgui.h +++ b/neo/sys/sys_imgui.h @@ -12,7 +12,21 @@ enum D3ImGuiWindow { D3_ImGuiWin_None = 0, D3_ImGuiWin_Settings = 1, // advanced dhewm3 settings menu D3_ImGuiWin_Demo = 2, // ImGui demo window - // next should be 4, then 8, etc so a bitmask can be used + D3_ImGuiWin_LightEditor = 4, // new ingame Light Editor + D3_ImGuiWin_AfEditor = 8, // new AF Editor + D3_ImGuiWin_PDAEditor = 16, // new PDA Editor + D3_ImGuiWin_ParticleEditor = 32, // new Particle Editor + D3_ImGuiWin_ScriptEditor = 64, // new Script Editor + D3_ImGuiWin_DeclBrowser = 128, // new Decl Browser + D3_ImGuiWin_MaterialEditor = 256, // next should be 512, then 1024, etc so a bitmask can be used + + D3_ImGuiWin_AnyEditor = D3_ImGuiWin_LightEditor + | D3_ImGuiWin_AfEditor + | D3_ImGuiWin_PDAEditor + | D3_ImGuiWin_ParticleEditor + | D3_ImGuiWin_ScriptEditor + | D3_ImGuiWin_DeclBrowser + | D3_ImGuiWin_MaterialEditor // to decide whether to call DrawToolWindows() }; #ifndef IMGUI_DISABLE @@ -64,6 +78,10 @@ extern void SetScale( float scale ); // disappears after a few seconds or when a key is pressed or the mouse is moved extern void ShowWarningOverlay( const char* text ); +// show a little overlay-window at the upper left of the screen showing the given text +// disappears after a few seconds or when a key is pressed or the mouse is moved +extern void ShowInfoOverlay( const char* text ); + enum Style { Dhewm3, ImGui_Default, diff --git a/neo/sys/win32/rc/doom.aps b/neo/sys/win32/rc/doom.aps new file mode 100644 index 000000000..4ee4097d8 Binary files /dev/null and b/neo/sys/win32/rc/doom.aps differ diff --git a/neo/sys/win32/win_main.cpp b/neo/sys/win32/win_main.cpp index 816572fde..3e7a349dc 100644 --- a/neo/sys/win32/win_main.cpp +++ b/neo/sys/win32/win_main.cpp @@ -1281,15 +1281,25 @@ int SDL_main(int argc, char *argv[]) { // Level Editor RadiantRun(); } - else if (com_editors & EDITOR_MATERIAL ) { - //BSM Nerve: Add support for the material editor - MaterialEditorRun(); - } else { +#ifdef IMGUI_DISABLE // DG: unless ImGui is disabled, the ImGui-based versions are used instead + if ( com_editors & EDITOR_MATERIAL ) { + //BSM Nerve: Add support for the material editor + MaterialEditorRun(); + } if ( com_editors & EDITOR_LIGHT ) { // in-game Light Editor LightEditorRun(); } + if ( com_editors & EDITOR_AF ) { + // in-game Articulated Figure Editor + AFEditorRun(); + } + if ( com_editors & EDITOR_PDA ) { + // in-game PDA Editor + PDAEditorRun(); + } +#endif if ( com_editors & EDITOR_SOUND ) { // in-game Sound Editor SoundEditorRun(); @@ -1298,10 +1308,7 @@ int SDL_main(int argc, char *argv[]) { // in-game Declaration Browser DeclBrowserRun(); } - if ( com_editors & EDITOR_AF ) { - // in-game Articulated Figure Editor - AFEditorRun(); - } + if ( com_editors & EDITOR_PARTICLE ) { // in-game Particle Editor ParticleEditorRun(); @@ -1310,10 +1317,6 @@ int SDL_main(int argc, char *argv[]) { // in-game Script Editor ScriptEditorRun(); } - if ( com_editors & EDITOR_PDA ) { - // in-game PDA Editor - PDAEditorRun(); - } } } #endif diff --git a/neo/tools/edit_public.h b/neo/tools/edit_public.h index be36df30a..ff91e6146 100644 --- a/neo/tools/edit_public.h +++ b/neo/tools/edit_public.h @@ -44,6 +44,55 @@ If you have questions concerning this license or the applicable additional terms class idProgram; class idInterpreter; +/* +========================== + ImGui-based Tools +========================== +*/ + +namespace ImGuiTools +{ + +// things in impl need to be used in at least one other file, but should generally not be used +namespace impl +{ + + void SetReleaseToolMouse( bool doRelease ); + +} //namespace impl + +bool ReleaseMouseForTools(); + +bool AreEditorsActive(); + +void DrawToolWindows(); + +void LightEditorInit( const idDict *dict ); + +void AfEditorInit(); + +// in-game PDA Editor +void PDAEditorInit( const idDict *spawnArgs ); + +void ParticleEditorInit( const idDict *spawnArgs ); + +void ScriptEditorInit( const idDict *spawnArgs ); + +void DeclBrowserInit( const idDict* spawnArgs ); + +void MaterialEditorInit(); +void MaterialEditorDraw(); +void MaterialEditorShutdown(); +void MaterialEditorPrintConsole( const char* msg ); + +} + + +/* +========================== + Old MFC-based Tools +========================== +*/ // Radiant Level Editor void RadiantInit( void ); diff --git a/neo/tools/imgui/ImGuiTools.cpp b/neo/tools/imgui/ImGuiTools.cpp new file mode 100644 index 000000000..2cea47ac0 --- /dev/null +++ b/neo/tools/imgui/ImGuiTools.cpp @@ -0,0 +1,242 @@ +/* +=========================================================================== + +Doom 3 BFG Edition GPL Source Code +Copyright (C) 2016 Daniel Gibson +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). + +Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 BFG Edition Source Code. If not, see . + +In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + + +//#include "../imgui/BFGimgui.h" + +#include "framework/Common.h" +#include "framework/Game.h" + +#include "idlib/CmdArgs.h" + +#include "sys/sys_imgui.h" + +#include "afeditor/AfEditor.h" +#include "lighteditor/LightEditor.h" +#include "pdaeditor/PDAEditor.h" +#include "particleeditor/ParticleEditor.h" +#include "scripteditor/ScriptEditor.h" +#include "decleditor/DeclBrowser.h" + +static bool releaseMouse = false; + +#if 0 // currently this doesn't make too much sense +void ShowEditors_f( const idCmdArgs& args ) +{ + showToolWindows = true; +} +#endif // 0 + +namespace ImGuiTools +{ + +// things in impl need to be used in at least one other file, but should generally not be touched +namespace impl +{ + +void SetReleaseToolMouse( bool doRelease ) +{ + releaseMouse = doRelease; +} + +} //namespace impl + +static idEntity* GetSelectedEntity( const idDict* dict, const char* spawnclass ) +{ + idEntity* selEntities[16] = {}; + int numEnts = gameEdit->GetSelectedEntities(selEntities, 16); + const char* name = NULL; + if ( dict != NULL ) { + name = dict->GetString( "name", NULL ); + if ( spawnclass == NULL ) { + spawnclass = dict->GetString( "spawnclass", NULL ); + } + } else if ( spawnclass == NULL ) { // both NULL + assert( 0 && "GetSelectedEntity() needs either a spawnclass or an entity dict!" ); + return NULL; + } + + if ( spawnclass == NULL && name == NULL ) { + // we may have had a dict, but it had no name, so what can you do.. + // TODO: log warning? + return NULL; + } + + for ( int i=0; iEntityGetSpawnArgs( ent ); + if ( spawnArgs != NULL ) { + // TODO: theoretically spawnArgs == dict could work, as the dict passed here *is* + // the entity's spawnargs - they call common->InitTool( EDITOR_FOO, &spawnArgs ); + if ( spawnclass != NULL ) { + const char* entClass = spawnArgs->GetString( "spawnclass", NULL ); + if ( entClass != NULL && idStr::Icmp(spawnclass, entClass) == 0 ) { + const char* entName = spawnArgs->GetString( "name", NULL ); + if ( name == NULL + || (entName != NULL && idStr::Icmp(name, entName) == 0) ) + { + return ent; + } + } + } else { // match just by name + const char* entName = spawnArgs->GetString( "name", NULL ); + if ( entName != NULL && idStr::Icmp(name, entName) == 0 ) { + return ent; + } + } + } + } + + return NULL; +} + +// TODO: replace with sys_imgui's openImguiWindows ? +bool AreEditorsActive() +{ + return cvarSystem->GetCVarInteger( "g_editEntityMode" ) > 0 || com_editors != 0; +} + +bool ReleaseMouseForTools() +{ + return AreEditorsActive() && releaseMouse; +} + +void DrawToolWindows() +{ + if( LightEditor::Instance().IsShown() ) + { + LightEditor::Instance().Draw(); + } + if( AfEditor::Instance().IsShown() ) + { + AfEditor::Instance().Draw(); + } + if ( PDAEditor::Instance().IsShown() ) + { + PDAEditor::Instance().Draw(); + } + if ( ParticleEditor::Instance().IsShown() ) + { + ParticleEditor::Instance().Draw(); + } + if ( ScriptEditor::Instance().IsShown() ) + { + ScriptEditor::Instance().Draw(); + } + if ( DeclBrowser::Instance().IsShown() ) { + DeclBrowser::Instance().Draw(); + } + ImGuiTools::MaterialEditorDraw(); +} + +void LightEditorInit( const idDict* dict ) +{ + if( dict == NULL ) { + return; + } + + idEntity* ent = GetSelectedEntity(dict, "idLight"); + if( ent == NULL ) { + return; + } + + // NOTE: we can't access idEntity (it's just a declaration), because it should + // be game/mod specific. but we can at least check the spawnclass from the dict. + assert( idStr::Icmp( dict->GetString( "spawnclass" ), "idLight" ) == 0 + && "LightEditorInit() must only be called with light entities or NULL!" ); + + LightEditor::Instance().ShowIt( true ); + impl::SetReleaseToolMouse( true ); + + LightEditor::ReInit( dict, ent ); + + D3::ImGuiHooks::OpenWindow( D3::ImGuiHooks::D3_ImGuiWin_LightEditor ); +} + +void AfEditorInit() // TODO: why no passed spawnargs? +{ + D3::ImGuiHooks::OpenWindow( D3::ImGuiHooks::D3_ImGuiWin_AfEditor ); + AfEditor::Instance().ShowIt( true ); + impl::SetReleaseToolMouse( true ); +} + +void PDAEditorInit(const idDict* dict) +{ + PDAEditor::Instance().Reset(); + PDAEditor::Instance().ShowIt( true ); + impl::SetReleaseToolMouse( true ); + + D3::ImGuiHooks::OpenWindow(D3::ImGuiHooks::D3_ImGuiWin_PDAEditor); +} + +void ParticleEditorInit(const idDict* spawnArgs) +{ + ParticleEditor::Instance().Reset(); + ParticleEditor::Instance().ShowIt( true ); + + impl::SetReleaseToolMouse(true); + + if ( spawnArgs ) { + idStr str = spawnArgs->GetString("model"); + str.StripFileExtension(); + ParticleEditor::Instance().SelectParticle(str); + ParticleEditor::Instance().SetParticleVisualization(static_cast(ParticleEditor::SELECTED)); + } + + cvarSystem->SetCVarBool("r_useCachedDynamicModels", false); + + D3::ImGuiHooks::OpenWindow(D3::ImGuiHooks::D3_ImGuiWin_ParticleEditor); +} + +void ScriptEditorInit(const idDict* spawnArgs) +{ + ScriptEditor::Instance().Reset(); + ScriptEditor::Instance().ShowIt( true ); + + impl::SetReleaseToolMouse(true); + + if ( spawnArgs ) { + const char *str = spawnArgs->GetString( "script" ); + ScriptEditor::Instance().OpenFile( str ); + } + + D3::ImGuiHooks::OpenWindow(D3::ImGuiHooks::D3_ImGuiWin_ScriptEditor); +} + +void DeclBrowserInit(const idDict* spawnArgs) +{ + DeclBrowser::Instance().Reset(); + DeclBrowser::Instance().ShowIt( true ); + + impl::SetReleaseToolMouse(true); + + D3::ImGuiHooks::OpenWindow(D3::ImGuiHooks::D3_ImGuiWin_DeclBrowser); +} + +} //namespace ImGuiTools diff --git a/neo/tools/imgui/afeditor/AfBodyEditor.cpp b/neo/tools/imgui/afeditor/AfBodyEditor.cpp new file mode 100644 index 000000000..667833519 --- /dev/null +++ b/neo/tools/imgui/afeditor/AfBodyEditor.cpp @@ -0,0 +1,357 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "AfBodyEditor.h" + +#include "framework/Game.h" +#include "idlib/geometry/TraceModel.h" +#include "renderer/Model.h" + +#define ARRAY_COUNT IM_ARRAYSIZE + +static const char* bodyTypeNames[] = +{ + "none", + "box", + "octahedron", + "dedecahedron", + "cylinder", + "cone", + "bone", + "polygon", + "polygonvolume", + "custom" +}; + +static const char* modifyJointsNames[] = +{ + "axis", + "origin", + "both" +}; + +static const char* afVecTypeNames[] = +{ + "coordinates", + "joint", + "bone center", +}; + +static bool ModelTypeItemGetter( void* data, int index, const char** out ); + +namespace ImGuiTools +{ +AfBodyEditor::AfBodyEditor( idDeclAF* newDecl, idDeclAF_Body* newBody ) + : decl( newDecl ) + , body( newBody ) + , positionType( 0 ) + , modifyJointType( 0 ) + , comboJoint1( 0 ) + , comboJoint2( 0 ) + , originBoneCenterJoint1( 0 ) + , originBoneCenterJoint2( 0 ) + , originJoint( 0 ) + , contentWidget( MakePhysicsContentsSelector() ) +{ + InitJointLists(); +} + +AfBodyEditor::~AfBodyEditor() +{ +} + +bool AfBodyEditor::Do() +{ + ImGui::PushID( body->name ); + + bool changed = false; + + ImGui::InputTextStr( "##rename", &renameBody ); + ImGui::SameLine(); + if( ImGui::Button( "Rename" ) ) + { + if( renameBody.Allocated() > 0 ) + { + decl->RenameBody( body->name, renameBody.c_str() ); + renameBody.Clear(); + } + changed = true; + } + + if( ImGui::CollapsingHeader( "Collision Model" ) ) + { + changed = CollisionModel() || changed; + } + + if( ImGui::CollapsingHeader( "Position" ) ) + { + changed = Position() || changed; + } + + if( ImGui::CollapsingHeader( "Collision Detection" ) ) + { + changed = ImGui::Checkbox( "Self Collision", &body->selfCollision ) || changed; + changed = DoMultiSelect( &contentWidget, &body->contents ) || changed; + } + + if( ImGui::CollapsingHeader( "Friction" ) ) + { + changed = ImGui::DragFloat( "Linear", &body->linearFriction ) || changed; + changed = ImGui::DragFloat( "Angular", &body->angularFriction ) || changed; + changed = ImGui::DragFloat( "Contact", &body->contactFriction ) || changed; + changed = InputAfVector( "Friction Direction", &body->frictionDirection ) || changed; + changed = InputAfVector( "Motor Direction", &body->contactMotorDirection ) || changed; + } + + if( ImGui::CollapsingHeader( "Joints" ) ) + { + if( ImGui::Combo( "Modified Joint", &comboJoint1, StringListItemGetter, &joints, joints.Num() ) ) + { + body->jointName = joints[comboJoint1]; + changed = true; + } + changed = ImGui::Combo( "Modify", ( int* )&body->jointMod, modifyJointsNames, ARRAY_COUNT( modifyJointsNames ) ) || changed; + changed = ImGui::InputTextStr( "Contained Joints", &body->containedJoints ) || changed; + } + + ImGui::PopID(); + + return changed; +} + +void AfBodyEditor::InitJointLists() +{ + const idRenderModel* model = gameEdit->ANIM_GetModelFromName( decl->model.c_str() ); + if( !model ) + { + return; + } + + const int numJoints = model->NumJoints(); + for( int i = 0; i < numJoints; i++ ) + { + const char* jointName = model->GetJointName( ( jointHandle_t )i ); + joints[joints.Append( jointName )].ToLower(); + } +} + +bool AfBodyEditor::Position() +{ + bool changed = false; + + ImGui::Columns( 2, "positionColumns2" ); + + changed = ImGui::Combo( "Type", ( int* )&body->origin.type, afVecTypeNames, ARRAY_COUNT( afVecTypeNames ) ) || changed; + + ImGui::NextColumn(); + changed = PositionProperty() || changed; + ImGui::NextColumn(); + changed = PitchYawRoll() || changed; + + ImGui::Columns( 1 ); + + return changed; +} + +bool AfBodyEditor::PositionProperty() +{ + bool changed = false; + + idAFVector* afVec = &body->origin; + + switch( afVec->type ) + { + case idAFVector::VEC_COORDS: + changed = ImGui::DragFloat3( "Coordinates", ( float* )&afVec->ToVec3() ) || changed; + break; + case idAFVector::VEC_JOINT: + if( ImGui::BeginCombo( "Joint", afVec->joint1.c_str() ) ) + { + for( int n = 0; n < joints.Num(); n++ ) + { + bool is_selected = ( afVec->joint1 == joints[n] ); + if( ImGui::Selectable( joints[n], is_selected ) ) + { + changed = true; + afVec->joint1 = joints[n]; + } + if( is_selected ) + { + ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch) + } + } + ImGui::EndCombo(); + } + break; + case idAFVector::VEC_BONECENTER: + case idAFVector::VEC_BONEDIR: + if( ImGui::BeginCombo( "Joint 1", afVec->joint1.c_str() ) ) + { + for( int n = 0; n < joints.Num(); n++ ) + { + bool is_selected = ( afVec->joint1 == joints[n] ); + if( ImGui::Selectable( joints[n], is_selected ) ) + { + changed = true; + afVec->joint1 = joints[n]; + } + if( is_selected ) + { + ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch) + } + } + ImGui::EndCombo(); + } + if( ImGui::BeginCombo( "Joint 2", afVec->joint2.c_str() ) ) + { + for( int n = 0; n < joints.Num(); n++ ) + { + bool is_selected = ( afVec->joint2 == joints[n] ); + if( ImGui::Selectable( joints[n], is_selected ) ) + { + changed = true; + afVec->joint2 = joints[n]; + } + if( is_selected ) + { + ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch) + } + } + ImGui::EndCombo(); + } + break; + default: + ImGui::Text( "*Unknown Type*" ); + } + + return changed; +} + +bool AfBodyEditor::PitchYawRoll() +{ + bool changed = false; + const ImGuiStyle& style = ImGui::GetStyle(); + const float w_full = ImGui::CalcItemWidth(); + const float square_sz = ImGui::GetFrameHeight(); + const float w_button = square_sz + style.ItemInnerSpacing.x; + const float w_inputs = w_full - w_button; + + const float widthItemOne = Max( 1.0f, ( float )( int )( ( w_inputs - ( style.ItemInnerSpacing.x ) * 2 ) / 2.0f ) ); + const float widthItemLast = Max( 1.0f, ( float )( int )( w_inputs - ( widthItemOne + style.ItemInnerSpacing.x ) ) ); + + ImGui::SetNextItemWidth( widthItemOne ); + changed = ImGui::DragFloat( "##p", &body->angles.pitch, 1.0f, 0.0f, 360.0f, "pitch: %.3f" ) || changed; + ImGui::SameLine(); + ImGui::SetNextItemWidth( widthItemOne ); + changed = ImGui::DragFloat( "##y", &body->angles.yaw, 1.0f, 0.0f, 360.0f, "yaw: %.3f" ) || changed; + ImGui::SameLine(); + ImGui::SetNextItemWidth( widthItemLast ); + changed = ImGui::DragFloat( "##r", &body->angles.roll, 1.0f, 0.0f, 360.0f, "roll: %.3f" ); + + return changed; +} + +bool AfBodyEditor::CollisionModel() +{ + bool changed = false; + + ImGui::PushID( "CollisionModel" ); + + ImGui::Columns( 2, "collisonColumns2" ); + changed = ImGui::Combo( "Model Type", &body->modelType, ModelTypeItemGetter, nullptr, ARRAY_COUNT( bodyTypeNames ) ) || changed; + changed = ImGui::DragFloat( "Density", &body->density ) || changed; + ImGui::NextColumn(); + changed = CollisionModelSize() || changed; + ImGui::Columns( 1 ); + + ImGui::PopID(); + + return changed; +} + +bool AfBodyEditor::CollisionModelSize() +{ + bool changed = false; + + switch( body->modelType ) + { + case TRM_INVALID: + ImGui::Text( "Provide a valid model type." ); + break; + case TRM_BOX: + case TRM_OCTAHEDRON: + case TRM_DODECAHEDRON: + changed = InputAfVector( "min", &body->v1 ) || changed; + changed = InputAfVector( "max", &body->v2 ) || changed; + ImGui::NewLine(); + break; + case TRM_CYLINDER: + case TRM_CONE: + changed = InputAfVector( "min", &body->v1 ) || changed; + changed = InputAfVector( "max", &body->v2 ) || changed; + changed = ImGui::InputInt( "Sides", &body->numSides ) || changed; + break; + case TRM_BONE: + changed = InputAfVector( "min", &body->v1 ) || changed; + changed = InputAfVector( "max", &body->v2 ) || changed; + changed = ImGui::DragFloat( "Width", &body->width ) || changed; + break; + case TRM_POLYGON: + ImGui::TextColored( ImVec4( 1, 0, 0, 1 ), "Polygon models are not supported :(" ); + break; + case TRM_POLYGONVOLUME: + ImGui::TextColored( ImVec4( 1, 0, 0, 1 ), "Polygon Volume models are not supported :(" ); + break; + case TRM_CUSTOM: + ImGui::TextColored( ImVec4( 1, 0, 0, 1 ), "Custom models are not supported :(" ); + break; + } + + return changed; +} + +bool AfBodyEditor::InputAfVector( const char* label, idAFVector* vec ) +{ + return ImGui::DragFloat3( label, ( float* )&vec->ToVec3() ); +} + +} + + +static bool ModelTypeItemGetter( void* data, int index, const char** out ) +{ + if( index < 0 || index >= ARRAY_COUNT( bodyTypeNames ) ) + { + return false; + } + + *out = bodyTypeNames[index]; + + return true; +} diff --git a/neo/tools/imgui/afeditor/AfBodyEditor.h b/neo/tools/imgui/afeditor/AfBodyEditor.h new file mode 100644 index 000000000..f684c2c26 --- /dev/null +++ b/neo/tools/imgui/afeditor/AfBodyEditor.h @@ -0,0 +1,75 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#pragma once + +#include "../util/ImGui_IdWidgets.h" + +#include "idlib/containers/StrList.h" +#include "framework/DeclAF.h" + +namespace ImGuiTools +{ + +class AfBodyEditor +{ +public: + AfBodyEditor( idDeclAF* newDecl, idDeclAF_Body* newBody ); + ~AfBodyEditor(); + + bool Do(); + +private: + + void InitJointLists(); + + bool Position(); + bool PositionProperty(); + bool PitchYawRoll(); + bool CollisionModel(); + bool CollisionModelSize(); + bool InputAfVector( const char* label, idAFVector* vec ); + + idDeclAF* decl; + idDeclAF_Body* body; + int positionType; + int modifyJointType; + int comboJoint1; + int comboJoint2; + int originBoneCenterJoint1; + int originBoneCenterJoint2; + int originJoint; + MultiSelectWidget contentWidget; + + idStr modifiedJoint; + idStr renameBody; + idStrList joints; +}; + +} diff --git a/neo/tools/imgui/afeditor/AfConstraintEditor.cpp b/neo/tools/imgui/afeditor/AfConstraintEditor.cpp new file mode 100644 index 000000000..4692f470d --- /dev/null +++ b/neo/tools/imgui/afeditor/AfConstraintEditor.cpp @@ -0,0 +1,384 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "AfConstraintEditor.h" + +#include "framework/Game.h" +#include "renderer/Model.h" + +#define ARRAY_COUNT IM_ARRAYSIZE + +static const char* constraintTypeNames[] = +{ + "Invalid", + "Fixed", + "Ball and Socket Joint", + "Universal Joint", + "Hinge", + "Slider", + "Spring" +}; + +static const char* afVecTypeNames[] = +{ + "coordinates", + "joint", + "bone center", + "bone direction" +}; + +static const char* constraintLimitTypeNames[] = +{ + "none", + "cone", + "pyramid" +}; + +namespace ImGuiTools +{ + +static bool ConstraintLimitTypeGetter( void* data, int index, const char** out ); + +AfConstraintEditor::AfConstraintEditor( idDeclAF* newDecl, idDeclAF_Constraint* newConstraint ) + : decl( newDecl ) + , constraint( newConstraint ) + , anchorType( 0 ) + , shaft1Type( 0 ) + , shaft2Type( 0 ) + , limitOrientationType( 0 ) + , body1( 0 ) + , body2( 0 ) + , jointAnchor1( 0 ) + , jointAnchor2( 0 ) + , jointShaft1( 0 ) +{ + InitJointLists(); +} + +AfConstraintEditor::~AfConstraintEditor() +{ +} + +bool AfConstraintEditor::Do() +{ + ImGui::PushID( constraint->name.c_str() ); + + bool changed = false; + + ImGui::InputTextStr( "##rename", &renameConstraint ); + + ImGui::SameLine(); + + if( ImGui::Button( "Rename" ) ) + { + if( renameConstraint.Allocated() > 0 ) + { + decl->RenameConstraint( constraint->name, renameConstraint.c_str() ); + renameConstraint.Clear(); + } + changed = true; + } + + if( ImGui::CollapsingHeader( "General" ) ) + { + ImGui::Columns( 2, "constraintGeneral2" ); + changed = ImGui::Combo( "Type##General", ( int* )&constraint->type, constraintTypeNames, ARRAY_COUNT( constraintTypeNames ) ) || changed; + changed = ImGui::DragFloat( "Friction##Friction", &constraint->friction ) || changed; + + if( constraint->type == DECLAF_CONSTRAINT_SPRING ) + { + changed = ImGui::DragFloat( "Stretch", &constraint->stretch ) || changed; + ImGui::SameLine(); + HelpMarker( "Spring constant when the spring is stretched" ); + changed = ImGui::DragFloat( "Compress", &constraint->compress, 0.001f, 0.0f, 1.0f ) || changed; + ImGui::SameLine(); + HelpMarker( "Spring constant when the spring is compressed" ); + changed = ImGui::DragFloat( "Damping", &constraint->damping, 0.001f, 0.0f, 1.0f ) || changed; + ImGui::SameLine(); + HelpMarker( "Spring damping" ); + changed = ImGui::DragFloat( "Length", &constraint->restLength, 0.001f, 0.0f, 1.0f ) || changed; + ImGui::SameLine(); + HelpMarker( "Rest length of the spring" ); + changed = ImGui::DragFloat( "Min Length", &constraint->minLength, 0.001f, 0.0f, 1.0f ) || changed; + ImGui::SameLine(); + HelpMarker( "Minimum length of the spring" ); + changed = ImGui::DragFloat( "Max Length", &constraint->maxLength, 0.001f, 0.0f, 1.0f ) || changed; + ImGui::SameLine(); + HelpMarker( "Maximum length of the spring" ); + } + + ImGui::NextColumn(); + if( ImGui::BeginCombo( "Body 1", constraint->body1.c_str() ) ) + { + for( int n = 0; n < decl->bodies.Num(); n++ ) + { + bool isSelected = ( constraint->body1 == decl->bodies[n]->name ); + if( ImGui::Selectable( decl->bodies[n]->name, isSelected ) ) + { + constraint->body1 = decl->bodies[n]->name; + changed = true; + } + if( isSelected ) + { + ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch) + } + } + ImGui::EndCombo(); + } + + if( ImGui::BeginCombo( "Body 2", constraint->body2.c_str() ) ) + { + for( int n = 0; n < decl->bodies.Num(); n++ ) + { + bool isSelected = ( constraint->body2 == decl->bodies[n]->name ); + if( ImGui::Selectable( decl->bodies[n]->name, isSelected ) ) + { + constraint->body2 = decl->bodies[n]->name; + changed = true; + } + if( isSelected ) + { + ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch) + } + } + ImGui::EndCombo(); + } + + ImGui::Columns( 1 ); + } + + if( ImGui::CollapsingHeader( "Anchor" ) ) + { + ImGui::PushID( "Anchor1" ); + ImGui::Columns( 2, "anchorColumn2" ); + changed = ImGui::Combo( "Type##anchor", ( int* )&constraint->anchor.type, afVecTypeNames, ARRAY_COUNT( afVecTypeNames ) ) || changed; + ImGui::NextColumn(); + changed = InputAfVector( &constraint->anchor ) || changed; + ImGui::Columns( 1 ); + ImGui::PopID(); + } + + if( constraint->type == DECLAF_CONSTRAINT_SPRING ) + { + if( ImGui::CollapsingHeader( "Anchor2" ) ) + { + ImGui::PushID( "Anchor2" ); + ImGui::Columns( 2, "anchorColumn2" ); + changed = ImGui::Combo( "Type##anchor", ( int* )&constraint->anchor2.type, afVecTypeNames, ARRAY_COUNT( afVecTypeNames ) ) || changed; + ImGui::NextColumn(); + changed = InputAfVector( &constraint->anchor2 ) || changed; + ImGui::Columns( 1 ); + ImGui::PopID(); + } + } + + if( constraint->type == DECLAF_CONSTRAINT_UNIVERSALJOINT ) + { + if( ImGui::CollapsingHeader( "Shaft 1" ) ) + { + ImGui::PushID( "Shaft 1" ); + ImGui::Columns( 2, "shaft12" ); + changed = ImGui::Combo( "Type", ( int* )&constraint->shaft[0].type, afVecTypeNames, ARRAY_COUNT( afVecTypeNames ) ) || changed; + ImGui::NextColumn(); + changed = InputAfVector( &constraint->shaft[0] ) || changed; + ImGui::Columns( 1 ); + ImGui::PopID(); + } + + if( ImGui::CollapsingHeader( "Shaft 2" ) ) + { + ImGui::PushID( "Shaft 2" ); + ImGui::Columns( 2, "shaft22" ); + changed = ImGui::Combo( "Type", ( int* )&constraint->shaft[1].type, afVecTypeNames, ARRAY_COUNT( afVecTypeNames ) ) || changed; + ImGui::NextColumn(); + changed = InputAfVector( &constraint->shaft[1] ) || changed; + ImGui::Columns( 1 ); + ImGui::PopID(); + } + } + + if( constraint->type == DECLAF_CONSTRAINT_HINGE ) + { + if( ImGui::CollapsingHeader( "Limit Orientation" ) ) + { + ImGui::DragFloat3( "Limit Angles", &constraint->limitAngles[0] ); + ImGui::SameLine(); + HelpMarker( "Specifies a V-shaped limit." + "\nThe first angle specifies the center of the V-shaped limit." + "\nThe angle which specifies the width of the V-shaped limit follows." + "\nThe V-shape is attached to body2. Next a shaft is specified which" + "\nis attached to body1 and is constrained to always stay within the V-shape." + "\nThe orientation of this shaft is specified with the third angle." ); + } + } + else + { + if( ImGui::CollapsingHeader( "Limit Type" ) ) + { + ImGui::PushID( "LimitType" ); + ImGui::Columns( 2, "limit2" ); + changed = ImGui::Combo( "Type", ( int* )&constraint->limit, ConstraintLimitTypeGetter, nullptr, ARRAY_COUNT( constraintLimitTypeNames ) - 1 ) || changed; + ImGui::NextColumn(); + switch( constraint->limit ) + { + case idDeclAF_Constraint::LIMIT_NONE: + break; + case idDeclAF_Constraint::LIMIT_CONE: + changed = ImGui::DragFloat3( "Cone##Limit", ( float* )&constraint->limitAngles[0], 0, -359.0f, 360.0f ) || changed; + break; + case idDeclAF_Constraint::LIMIT_PYRAMID: + changed = ImGui::DragFloat3( "Pyramid##Limit", ( float* )&constraint->limitAngles[0], 0, -359.0f, 360.0f ) || changed; + break; + } + ImGui::Columns( 1 ); + ImGui::PopID(); + } + + if( ImGui::CollapsingHeader( "Limit Orientation" ) ) + { + ImGui::PushID( "LimitO" ); + ImGui::Columns( 2, "limitOrientation2" ); + changed = ImGui::Combo( "Type", ( int* )&constraint->limitAxis.type, afVecTypeNames, ARRAY_COUNT( afVecTypeNames ) ) || changed; + ImGui::NextColumn(); + changed = InputAfVector( &constraint->limitAxis ) || changed; + ImGui::Columns( 1 ); + ImGui::PopID(); + } + } + + ImGui::PopID(); + + return changed; +} + +bool AfConstraintEditor::InputAfVector( idAFVector* afVec ) +{ + bool changed = false; + + switch( afVec->type ) + { + case idAFVector::VEC_COORDS: + changed = changed || ImGui::DragFloat3( "Coordinates", ( float* )&afVec->ToVec3() ); + break; + case idAFVector::VEC_JOINT: + if( ImGui::BeginCombo( "Joint", afVec->joint1.c_str() ) ) + { + for( int n = 0; n < joints.Num(); n++ ) + { + bool is_selected = ( afVec->joint1 == joints[n] ); + if( ImGui::Selectable( joints[n], is_selected ) ) + { + afVec->joint1 = joints[n]; + changed = true; + } + if( is_selected ) + { + ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch) + } + } + ImGui::EndCombo(); + } + break; + case idAFVector::VEC_BONECENTER: + case idAFVector::VEC_BONEDIR: + if( ImGui::BeginCombo( "Joint 1", afVec->joint1.c_str() ) ) + { + for( int n = 0; n < joints.Num(); n++ ) + { + bool is_selected = ( afVec->joint1 == joints[n] ); + if( ImGui::Selectable( joints[n], is_selected ) ) + { + afVec->joint1 = joints[n]; + changed = true; + } + if( is_selected ) + { + ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch) + } + } + ImGui::EndCombo(); + } + if( ImGui::BeginCombo( "Joint 2", afVec->joint2.c_str() ) ) + { + for( int n = 0; n < joints.Num(); n++ ) + { + bool is_selected = ( afVec->joint2 == joints[n] ); + if( ImGui::Selectable( joints[n], is_selected ) ) + { + afVec->joint2 = joints[n]; + changed = true; + } + if( is_selected ) + { + ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch) + } + } + ImGui::EndCombo(); + } + break; + default: + ImGui::Text( "*Unknown Type*" ); + } + + return changed; +} + +void AfConstraintEditor::InitJointLists() +{ + const idRenderModel* model = gameEdit->ANIM_GetModelFromName( decl->model.c_str() ); + if( !model ) + { + return; + } + + joints.Clear(); + + const int numJoints = model->NumJoints(); + for( int i = 0; i < numJoints; i++ ) + { + const char* jointName = model->GetJointName( ( jointHandle_t )i ); + joints[joints.Append( jointName )].ToLower(); + } +} + +bool ConstraintLimitTypeGetter( void* data, int index, const char** out ) +{ + index += 1; + + if( index < 0 || index > ARRAY_COUNT( constraintLimitTypeNames ) ) + { + *out = constraintLimitTypeNames[0]; + return false; + } + + *out = constraintLimitTypeNames[index]; + return true; +} + +} diff --git a/neo/tools/imgui/afeditor/AfConstraintEditor.h b/neo/tools/imgui/afeditor/AfConstraintEditor.h new file mode 100644 index 000000000..b1e048fcb --- /dev/null +++ b/neo/tools/imgui/afeditor/AfConstraintEditor.h @@ -0,0 +1,68 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#pragma once + +#include "../util/ImGui_IdWidgets.h" + +#include "framework/DeclAF.h" +#include "idlib/containers/StrList.h" + +namespace ImGuiTools +{ + +class AfConstraintEditor +{ +public: + + AfConstraintEditor( idDeclAF* newDecl, idDeclAF_Constraint* newConstraint ); + ~AfConstraintEditor(); + + bool Do(); + bool InputAfVector( idAFVector* afVec ); + void InitJointLists(); + +private: + + idDeclAF* decl; + idDeclAF_Constraint* constraint; + int anchorType; + int shaft1Type; + int shaft2Type; + int limitOrientationType; + int body1; + int body2; + int jointAnchor1; + int jointAnchor2; + int jointShaft1; + idStr renameConstraint; + idStrList joints; +}; + +} diff --git a/neo/tools/imgui/afeditor/AfEditor.cpp b/neo/tools/imgui/afeditor/AfEditor.cpp new file mode 100644 index 000000000..04438204f --- /dev/null +++ b/neo/tools/imgui/afeditor/AfEditor.cpp @@ -0,0 +1,566 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "AfEditor.h" + +#include "../util/ImGui_IdWidgets.h" + +#include "framework/FileSystem.h" +#include "framework/Game.h" + +namespace ImGuiTools +{ + +static bool BodyItemGetter( void* data, int index, const char** items ); +static bool ConstraintItemGetter( void* data, int index, const char** items ); + +static bool CVarCheckBox( const char* label, const char* cvarname ); + +AfEditor::AfEditor() + : isShown( false ) + , fileSelection( 0 ) + , currentAf( 0 ) + , currentConstraint( 0 ) + , currentBodySelection( 0 ) + , currentEntity( 0 ) + , decl( nullptr ) + , body( nullptr ) + , constraint( nullptr ) + , propertyEditor( nullptr ) +{ +} + +AfEditor::~AfEditor() +{ + if( propertyEditor ) + { + delete propertyEditor; + } + bodyEditors.DeleteContents( true ); + constraintEditors.DeleteContents( true ); +} + +void AfEditor::Init() +{ +} + +void AfEditor::Draw() +{ + bool showTool = isShown; + + if( ImGui::Begin( "AF Editor", &showTool, ImGuiWindowFlags_MenuBar ) ) + { + impl::SetReleaseToolMouse( true ); + + bool changedAf = false; + bool openedAfBrowser = false; + bool clickedNew = false; + + if( ImGui::BeginMenuBar() ) + { + if( ImGui::BeginMenu( "File" ) ) + { + if( ImGui::MenuItem( "New", "Ctrl+N" ) ) + { + clickedNew = true; + } + + if( ImGui::MenuItem( "Open..", "Ctrl+O" ) ) + { + afList.shouldPopulate = true; + openedAfBrowser = true; + } + + if( ImGui::MenuItem( "Save", "Ctrl+S" ) ) + { + if( decl ) + { + decl->Save(); + } + } + + // When the editor is closed. it should also set g_editEntities with ~= EDITOR_AF. + if( ImGui::MenuItem( "Close", "Ctrl+W" ) ) + { + showTool = false; + } + + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + + if( clickedNew ) + { + ImGui::OpenPopup( "New Articulated Figure" ); + } + + if( ImGui::BeginPopupModal( "New Articulated Figure" ) ) + { + if( afFiles.Num() == 0 ) + { + idFileList* files = fileSystem->ListFiles( "af", ".af", true, true ); + for( int i = 0; i < files->GetNumFiles(); i++ ) + { + afFiles.Append( files->GetFile( i ) ); + } + fileSystem->FreeFileList( files ); + } + + ImGui::BeginListBox( "##afFileSelect" ); + for( int i = 0; i < afFiles.Num(); i++ ) + { + if( ImGui::ListBox( "Files", &fileSelection, StringListItemGetter, &afFiles, afFiles.Num() ) ) + { + fileName = afFiles[fileSelection]; + } + } + ImGui::EndListBox(); + + ImGui::SameLine(); + ImGui::SmallButton( "New File" ); + + ImGui::InputTextStr( "AF Name", &fileName ); + if( ImGui::Button( "Create" ) ) + { + idStr afName = fileName; + afName.StripPath(); + afName.StripFileExtension(); + + if( !afName.IsEmpty() ) + { + idDeclAF* newDecl = static_cast( const_cast( declManager->FindType( DECL_AF, afName.c_str(), false ) ) ); + if( !newDecl ) + { + // create it + newDecl = static_cast( declManager->CreateNewDecl( DECL_AF, afName.c_str(), fileName.c_str() ) ); + } + + afList.names.Append( afName ); + currentAf = afList.names.Num() - 1; + + OnNewDecl( newDecl ); + + ImGui::CloseCurrentPopup(); + } + } + + ImGui::SameLine(); + if( ImGui::Button( "Close" ) ) + { + afFiles.Clear(); + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + + if( openedAfBrowser ) + { + ImGui::OpenPopup( "Articulated Figure Browser" ); + } + + if( ImGui::BeginPopup( "Articulated Figure Browser" ) ) + { + afList.populate(); + afList.shouldPopulate = false; + if( afList.names.Num() > 0 ) + { + ImGui::Combo( "Articulated Figures", ¤tAf, &StringListItemGetter, &afList, afList.names.Num() ); + if( ImGui::Button( "Select" ) ) + { + idDeclAF* newDecl = static_cast( const_cast( declManager->FindType( DECL_AF, afList.names[currentAf], false ) ) ); + if( newDecl ) + { + OnNewDecl( newDecl ); + } + ImGui::CloseCurrentPopup(); + } + } + else + { + ImGui::Text( "There are no articulated figures!" ); + } + + ImGui::SameLine(); + + if( ImGui::Button( "Close" ) ) + { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + if( decl ) + { + if( ImGui::BeginTabBar( "Tab Bar" ) ) + { + if( ImGui::BeginTabItem( "View" ) ) + { + // the CVars must be fetched by name because they're from the Game DLL + // TODO: alternatively, the cvars could be fetched in Init() and kept around + CVarCheckBox( "Show Bodies", "af_showBodies" ); + CVarCheckBox( "Show Body Names", "af_showBodyNames" ); + CVarCheckBox( "Show Constraints", "af_showConstraints" ); + CVarCheckBox( "Show Constraint Names", "af_showConstraintNames" ); + CVarCheckBox( "Show Limits", "af_showLimits" ); + CVarCheckBox( "Show Active ", "af_showActive" ); + CVarCheckBox( "Show Trees", "af_showTrees" ); + CVarCheckBox( "Show Mass", "af_showMass" ); + ImGui::EndTabItem(); + } + + if( ImGui::BeginTabItem( "Properties" ) ) + { + if( propertyEditor ) + { + changedAf = changedAf || propertyEditor->Do(); + } + ImGui::EndTabItem(); + } + + if( ImGui::BeginTabItem( "Bodies##Tab" ) ) + { + if( decl->bodies.Num() > 0 ) + { + ImGui::Combo( "Body", ¤tBodySelection, BodyItemGetter, decl, decl->bodies.Num() ); + } + + ImGui::SameLine(); + + static char bodyName[256] = { '\0' }; + + if( ImGui::Button( "New Body" ) ) + { + ImGui::OpenPopup( "Create a New Body" ); + } + if( ImGui::BeginPopup( "Create a New Body" ) ) + { + ImGui::Text( "Create a new body for the articulated figure" ); + ImGui::InputText( "Body Name", &bodyName[0], 256 ); + if( ImGui::Button( "Create" ) ) + { + // added a new body + if( strlen( bodyName ) > 0 ) + { + // TODO: Should do some data validation on the body name. + decl->NewBody( bodyName ); + bodyName[0] = '\0'; + currentBodySelection = decl->bodies.Num() - 1; + bodyEditors.Append( new AfBodyEditor( decl, decl->bodies[currentBodySelection] ) ); + ImGui::CloseCurrentPopup(); + } + } + ImGui::SameLine(); + if( ImGui::Button( "Cancel" ) ) + { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + ImGui::Separator(); + + if( bodyEditors.Num() > currentBodySelection ) + { + changedAf = changedAf || bodyEditors[currentBodySelection]->Do(); + + if( ImGui::Button( "Delete Body" ) ) + { + ImGui::OpenPopup( "Delete Body##2" ); + } + + if( ImGui::BeginPopupModal( "Delete Body##2" ) ) + { + ImGui::Text( "Are you sure you want to delete body %s?", decl->bodies[currentBodySelection]->name.c_str() ); + + if( ImGui::Button( "Yes" ) ) + { + delete bodyEditors[currentBodySelection]; + bodyEditors.RemoveIndex( currentBodySelection ); + decl->DeleteBody( decl->bodies[currentBodySelection]->name.c_str() ); + currentBodySelection -= 1; + if( currentBodySelection < 0 ) + { + currentBodySelection = 0; + } + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if( ImGui::Button( "No" ) ) + { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + } + + ImGui::EndTabItem(); + } + + if( ImGui::BeginTabItem( "Constraints" ) ) + { + if( decl->constraints.Num() > 0 ) + { + ImGui::Combo( "Constraint", ¤tConstraint, ConstraintItemGetter, decl, decl->constraints.Num() ); + } + + static char constraintName[256] = { '\0' }; + + if( ImGui::Button( "New" ) ) + { + ImGui::OpenPopup( "New Constraint" ); + } + + if( ImGui::BeginPopupModal( "New Constraint" ) ) + { + ImGui::Text( "Create a new body for the articulated figure" ); + ImGui::InputText( "Constraint Name", &constraintName[0], 256 ); + + if( ImGui::Button( "Create" ) ) + { + // added a new body + if( strlen( constraintName ) > 0 ) + { + decl->NewConstraint( constraintName ); + constraintEditors.Append( new AfConstraintEditor( decl, decl->constraints[decl->constraints.Num() - 1] ) ); + currentConstraint = constraintEditors.Num() - 1; + } + constraintName[0] = '\0'; + + ImGui::CloseCurrentPopup(); + } + + ImGui::SameLine(); + + if( ImGui::Button( "Cancel" ) ) + { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + + ImGui::SameLine(); + + if( ImGui::Button( "Delete" ) ) + { + ImGui::OpenPopup( "Delete Constraint##2" ); + } + + if( ImGui::BeginPopupModal( "Delete Constraint##2" ) ) + { + ImGui::Text( "Are you sure you want to delete constraint %s?", decl->constraints[currentConstraint]->name.c_str() ); + + if( ImGui::Button( "Yes" ) ) + { + delete constraintEditors[currentConstraint]; + constraintEditors.RemoveIndex( currentConstraint ); + decl->DeleteConstraint( decl->constraints[currentConstraint]->name.c_str() ); + currentConstraint -= 1; + if( currentConstraint < 0 ) + { + currentConstraint = 0; + } + ImGui::CloseCurrentPopup(); + } + + ImGui::SameLine(); + + if( ImGui::Button( "No" ) ) + { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + + if( decl->constraints.Num() > currentConstraint ) + { + changedAf = constraintEditors[currentConstraint]->Do(); + } + + ImGui::EndTabItem(); + } + + ImGui::EndTabBar(); + } + + ImGui::NewLine(); + ImGui::Separator(); + ImGui::NewLine(); + + try + { + if( ImGui::Button( "Spawn" ) ) + { + if( afList.names.Num() > currentAf ) + { + gameEdit->AF_SpawnEntity( afList.names[currentAf].c_str() ); + } + } + + ImGui::SameLine(); + + if( ImGui::Button( "Kill" ) ) + { + cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "deleteSelected\n" ); + } + + if( changedAf ) + { + if( afList.names.Num() > currentAf ) + { + gameEdit->AF_UpdateEntities( afList.names[currentAf].c_str() ); + } + } + } + catch( idException& exception ) + { + common->DWarning( "AfEditor exception %s", exception.error ); + } + } + } + ImGui::End(); + + if( isShown && !showTool ) + { + // TODO: do the same as when pressing cancel? + isShown = showTool; + impl::SetReleaseToolMouse( false ); + D3::ImGuiHooks::CloseWindow( D3::ImGuiHooks::D3_ImGuiWin_AfEditor ); + } +} + +void AfEditor::OnNewDecl( idDeclAF* newDecl ) +{ + decl = newDecl; + if( propertyEditor ) + { + delete propertyEditor; + } + bodyEditors.DeleteContents( true ); + constraintEditors.DeleteContents( true ); + + propertyEditor = new AfPropertyEditor( newDecl ); + + for( int i = 0; i < decl->bodies.Num(); i++ ) + { + bodyEditors.Append( new AfBodyEditor( decl, decl->bodies[i] ) ); + } + + for( int i = 0; i < decl->constraints.Num(); i++ ) + { + constraintEditors.Append( new AfConstraintEditor( decl, decl->constraints[i] ) ); + } +} + +AfEditor& AfEditor::Instance() +{ + static AfEditor instance; + return instance; +} + +void AfEditor::Enable( const idCmdArgs& args ) +{ + AfEditor::Instance().ShowIt( true ); +} + +void AfEditor::AfList::populate() +{ + if( !shouldPopulate ) + { + return; + } + + names.Clear(); + + int count = declManager->GetNumDecls( DECL_AF ); + for( int i = 0; i < count; i++ ) + { + names.Append( static_cast( declManager->DeclByIndex( DECL_AF, i, false ) )->GetName() ); + } +} + +bool BodyItemGetter( void* data, int index, const char** itemName ) +{ + idDeclAF* decl = reinterpret_cast( data ); + if( !decl ) + { + return false; + } + + if( index > decl->bodies.Num() ) + { + return false; + } + + idDeclAF_Body* body = decl->bodies[index]; + + *itemName = body->name.c_str(); + + return true; +} + +bool ConstraintItemGetter( void* data, int index, const char** items ) +{ + idDeclAF* decl = reinterpret_cast( data ); + if( !decl ) + { + return false; + } + + if( index > decl->constraints.Num() ) + { + return false; + } + + idDeclAF_Constraint* constraint = decl->constraints[index]; + + *items = constraint->name.c_str(); + + return true; +} + +static bool CVarCheckBox( const char* label, const char* cvarName ) +{ + idCVar* cvar = cvarSystem->Find( cvarName ); + if( cvar != NULL ) + { + bool value = cvar->GetBool(); + if( ImGui::Checkbox( label, &value ) ) + { + cvar->SetBool( value ); + return true; + } + } + return false; +} + +} diff --git a/neo/tools/imgui/afeditor/AfEditor.h b/neo/tools/imgui/afeditor/AfEditor.h new file mode 100644 index 000000000..0200be062 --- /dev/null +++ b/neo/tools/imgui/afeditor/AfEditor.h @@ -0,0 +1,124 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef EDITOR_TOOLS_AFEDITOR_H_ +#define EDITOR_TOOLS_AFEDITOR_H_ + +#include "../../edit_public.h" + +#include "AfBodyEditor.h" +#include "AfConstraintEditor.h" +#include "AfPropertyEditor.h" + +#include "framework/DeclAF.h" + +namespace ImGuiTools +{ + +/** +* Articulated figure imgui editor. +*/ +class AfEditor +{ +public: + virtual ~AfEditor(); + + void Init(); + + void ShowIt( bool show ); + + bool IsShown() const; + + void Draw(); + +public: + + static AfEditor& Instance(); + static void Enable( const idCmdArgs& args ); + +public: + + AfEditor( AfEditor const& ) = delete; + void operator=( AfEditor const& ) = delete; + +private: + + struct AfList + { + AfList() : names(), shouldPopulate( false ) {} + void populate(); + + idList names; + bool shouldPopulate; + }; + + AfEditor(); + + void OnNewDecl( idDeclAF* newDecl ); + + bool isShown; + int fileSelection; + int currentAf; + int currentConstraint; + int currentBodySelection; + int currentEntity; + idDeclAF* decl; + idDeclAF_Body* body; + idDeclAF_Constraint* constraint; + + // Editor dialogs + AfPropertyEditor* propertyEditor; + idList bodyEditors; + idList constraintEditors; + + AfList afList; // list with idDeclAF names + idList afFiles; + idStr fileName; + + struct IndexEntityDef + { + int index; + idStr name; + }; + idList entities; +}; + +inline void AfEditor::ShowIt( bool show ) +{ + isShown = show; +} + +inline bool AfEditor::IsShown() const +{ + return isShown; +} + +} + +#endif diff --git a/neo/tools/imgui/afeditor/AfPropertyEditor.cpp b/neo/tools/imgui/afeditor/AfPropertyEditor.cpp new file mode 100644 index 000000000..cc7e15aac --- /dev/null +++ b/neo/tools/imgui/afeditor/AfPropertyEditor.cpp @@ -0,0 +1,134 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "AfPropertyEditor.h" + +namespace ImGuiTools +{ +AfPropertyEditor::AfPropertyEditor( idDeclAF* newDecl ) + : decl( newDecl ) + , contentWidget( MakePhysicsContentsSelector() ) + , clipMaskWidget( MakePhysicsContentsSelector() ) + , linearTolerance( 0 ) + , angularTolerance( 0 ) + , currentModel( 0 ) +{ + contentWidget.UpdateWithBitFlags( decl->contents ); + clipMaskWidget.UpdateWithBitFlags( decl->clipMask ); + + UpdateModelDefList(); +} + +AfPropertyEditor::~AfPropertyEditor() +{ +} + +bool AfPropertyEditor::Do() +{ + bool changed = false; + + if( ImGui::CollapsingHeader( "MD5" ) ) + { + if( ImGui::Combo( "Models", ¤tModel, StringListItemGetter, &modelDefs, modelDefs.Num() ) ) + { + decl->model = modelDefs[currentModel]; + changed = true; + } + ImGui::InputTextStr( "Skin", &decl->skin, ImGuiInputTextFlags_CharsNoBlank ); + } + + if( ImGui::CollapsingHeader( "Default Collision Detection" ) ) + { + ImGui::Checkbox( "Self Collision", &decl->selfCollision ); + ImGui::BeginListBox( "Contents" ); + changed = DoMultiSelect( &contentWidget, &decl->contents ) || changed; + ImGui::EndListBox(); + + ImGui::BeginListBox( "Clip Mask" ); + changed = DoMultiSelect( &clipMaskWidget, &decl->clipMask ) || changed; + ImGui::EndListBox(); + } + + if( ImGui::CollapsingHeader( "Default Friction" ) ) + { + changed = ImGui::DragFloat( "Linear", &decl->defaultLinearFriction, 0.01f, -100000.f, 100000.f, "%.2f" ) || changed; + changed = ImGui::DragFloat( "Angular", &decl->defaultAngularFriction, 0.01f, -100000.f, 100000.f, "%.2f" ) || changed; + changed = ImGui::DragFloat( "Contact", &decl->defaultContactFriction, 0.01f, -100000.f, 100000.f, "%.2f" ) || changed; + changed = ImGui::DragFloat( "Constraint", &decl->defaultConstraintFriction, 0.01f, -100000.f, 100000.f, "%.2f" ) || changed; + } + + if( ImGui::CollapsingHeader( "Mass" ) ) + { + changed = changed || ImGui::InputFloat( "Total Mass", &decl->totalMass, 1.0f, 50.0f, "%.0f" ); + ImGui::SameLine(); + HelpMarker( "The total mass of the articulated figure.\n" + "If the total mass is set to a value greater than\n" + "zero then the mass of each body is scaled such that\n" + "the total mass of the articulated figure equals the given mass." ); + } + + if( ImGui::CollapsingHeader( "Suspend Speed" ) ) + { + changed = changed || ImGui::InputFloat2( "Linear Velocity", ( float* )&decl->suspendVelocity, "%.0f" ); + changed = changed || ImGui::InputFloat2( "Linear Acceleration", ( float* )&decl->suspendAcceleration, "%.0f" ); + } + + // TODO(Stephen): Figure out what these properties are for. + //if (ImGui::CollapsingHeader("Suspend Movement")) + //{ + // ImGui::InputInt("No Move Time", (int*)&decl->noMoveTime); + // ImGui::InputInt("Linear Tolerance", &linearTolerance); + // ImGui::InputInt("Angular Tolerance", &angularTolerance); + // ImGui::InputInt("Minimum Move Time", (int*)&decl->minMoveTime); + // ImGui::InputInt("Maximum Move Time", (int*)&decl->maxMoveTime); + //} + + return changed; +} + +void AfPropertyEditor::UpdateModelDefList() +{ + // update the model list. + const int totalModelDefs = declManager->GetNumDecls( DECL_MODELDEF ); + modelDefs.AssureSize( totalModelDefs ); + for( int i = 0; i < totalModelDefs; i++ ) + { + const idDecl* modelDef = declManager->DeclByIndex( DECL_MODELDEF, i, false ); + if( modelDef ) + { + modelDefs[i] = modelDef->GetName(); + if( decl->model == modelDef->GetName() ) + { + currentModel = i; + } + } + } +} + +} diff --git a/neo/tools/imgui/afeditor/AfPropertyEditor.h b/neo/tools/imgui/afeditor/AfPropertyEditor.h new file mode 100644 index 000000000..8f2460c3e --- /dev/null +++ b/neo/tools/imgui/afeditor/AfPropertyEditor.h @@ -0,0 +1,65 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef EDITOR_TOOLS_AFPROPERTYEDITOR_H_ +#define EDITOR_TOOLS_AFPROPERTYEDITOR_H_ + +#include "../util/ImGui_IdWidgets.h" + +#include "framework/DeclAF.h" +#include "idlib/containers/StrList.h" + +namespace ImGuiTools +{ + +class AfPropertyEditor +{ +public: + AfPropertyEditor( idDeclAF* newDecl ); + ~AfPropertyEditor(); + + bool Do(); + +private: + + void UpdateModelDefList(); + + idDeclAF* decl; + MultiSelectWidget contentWidget; + MultiSelectWidget clipMaskWidget; + int linearTolerance; + int angularTolerance; + int currentModel; + + idStrList modelDefs; +}; + +} + +#endif diff --git a/neo/tools/imgui/decleditor/DeclBrowser.cpp b/neo/tools/imgui/decleditor/DeclBrowser.cpp new file mode 100644 index 000000000..103769317 --- /dev/null +++ b/neo/tools/imgui/decleditor/DeclBrowser.cpp @@ -0,0 +1,625 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../util/ImGui_IdWidgets.h" +#include "sys/sys_imgui.h" + +#include "framework/FileSystem.h" + +#include "PathTreeCtrl.h" +#include "DeclNew.h" +#include "DeclEditor.h" +#include "DeclBrowser.h" + +const int DECLTYPE_SHIFT = 24; +const int DECLINDEX_MASK = ( 1 << DECLTYPE_SHIFT ) - 1; +const int DECLTYPE_SCRIPT = 126; +const int DECLTYPE_GUI = 127; + +#define GetIdFromTypeAndIndex( type, index ) ( ( (int)type << DECLTYPE_SHIFT ) | index ) +#define GetTypeFromId( id ) ( (declType_t) ( (int)id >> DECLTYPE_SHIFT ) ) +#define GetIndexFromId( id ) ( (int)id & DECLINDEX_MASK ) + +namespace ImGuiTools { + +bool DeclBrowserOnToolTipNotify( void *data, TreeNode *item, idStr &tooltipText ) { + return reinterpret_cast(data)->OnToolTipNotify( item, tooltipText ); +} +void DeclBrowserOnTreeSelChanged( void *data, bool doubleClicked ) { + reinterpret_cast(data)->OnTreeSelChanged( doubleClicked ); +} +void DeclBrowserOnTreeContextMenu( void *data, TreeNode *item ) { + // nop +} +void DeclBrowserOnTreeBeginDrag( void *data, TreeNode *item ) { + // nop +} +void DeclBrowserOnTreeEndDrag( void *data, TreeNode *source, TreeNode *destination ) { + // nop +} +void DeclBrowserOnTreeInput( void *data, bool prepare, TreeNode *item ) { + // nop +} + +/* +================ +DeclBrowser::DeclBrowser +================ +*/ +DeclBrowser::DeclBrowser() + : isShown(false) + , statusBarText() + , declTree() + , findNameStatic() + , findTextStatic() + , findNameEdit() + , findTextEdit() + , findButtonEnabled(false) + , editButtonEnabled(false) + , newButtonEnabled(false) + , reloadButtonEnabled(false) + , cancelButtonEnabled(false) + , baseDeclTree() + , numListedDecls(0) + , findNameString() + , findTextString() + , declNewDlg() + , declEditorDlg() +{ +} + +DeclBrowser& DeclBrowser::Instance() { + static DeclBrowser instance; + return instance; +} + +/* +================ +DeclBrowser::Draw +================ +*/ +void DeclBrowser::Draw() { + bool showTool; + + showTool = isShown; + + if ( ImGui::Begin( "Decl Browser", &showTool, ImGuiWindowFlags_AlwaysAutoResize ) ) { + impl::SetReleaseToolMouse( true ); + + if ( ImGui::BeginChild( "Decl Tree", ImVec2( 300, 400 ) ) ) { + ImGui::SetItemTooltip( "decl browser" ); + declTree.Draw( DeclBrowserOnToolTipNotify, DeclBrowserOnTreeSelChanged, DeclBrowserOnTreeContextMenu, DeclBrowserOnTreeBeginDrag, DeclBrowserOnTreeEndDrag, DeclBrowserOnTreeInput, this ); + ImGui::EndChild(); + } + + if ( ImGui::InputTextStr( "Search names", &findNameEdit ) ) { + } + ImGui::SetItemTooltip( "search for declarations with matching name, use meta characters: *, ? and [abc...]" ); + if ( ImGui::InputTextStr( "Search text", &findTextEdit ) ) { + } + ImGui::SetItemTooltip( "search for declarations containing text" ); + if ( ImGui::Button( "Find" ) ) { + OnBnClickedFind(); + } + ImGui::SetItemTooltip( "find declarations matching the search strings" ); + ImGui::SameLine(); + if ( ImGui::Button( "Edit" ) ) { + OnBnClickedEdit(); + } + ImGui::SetItemTooltip( "edit selected declaration" ); + ImGui::SameLine(); + if ( ImGui::Button( "New" ) ) { + OnBnClickedNew(); + } + ImGui::SetItemTooltip( "create new declaration" ); + ImGui::SameLine(); + if ( ImGui::Button( "Reload" ) ) { + ReloadDeclarations(); + } + ImGui::SetItemTooltip( "reload declarations" ); + ImGui::SameLine(); + if ( ImGui::Button( "Close" ) ) { + showTool = false; + } + + if ( declNewDlg.Draw() ) { + OnBnClickedNewAccepted(); + } + + if ( declEditorDlg.Draw() ) { + // nothing to do + } + } + ImGui::End(); + + if ( isShown && !showTool ) + { + isShown = showTool; + impl::SetReleaseToolMouse( false ); + D3::ImGuiHooks::CloseWindow( D3::ImGuiHooks::D3_ImGuiWin_DeclBrowser ); + } +} + +/* +================ +DeclBrowser::AddDeclTypeToTree +================ +*/ +template< class type > +int idListDeclSortCompare( const type *a, const type *b ) { + return idStr::IcmpPath( (*a)->GetName(), (*b)->GetName() ); +} + +void DeclBrowser::AddDeclTypeToTree( declType_t type, const char *root, PathTreeCtrl &tree ) { + int i; + idList decls; + idPathTreeStack stack; + idStr rootStr, declName; + + decls.SetNum( declManager->GetNumDecls( type ) ); + for ( i = 0; i < decls.Num(); i++ ) { + decls[i] = declManager->DeclByIndex( type, i, false ); + } + decls.Sort( idListDeclSortCompare ); + + rootStr = root; + rootStr += "/"; + + stack.PushRoot( NULL ); + + for ( i = 0; i < decls.Num(); i++) { + declName = rootStr + decls[i]->GetName(); + + declName.BackSlashesToSlashes(); + declName.Strip(' '); + + tree.AddPathToTree( declName, GetIdFromTypeAndIndex( type, decls[i]->Index() ), stack ); + } +} + +/* +================ +DeclBrowser::AddScriptsToTree +================ +*/ +void DeclBrowser::AddScriptsToTree( PathTreeCtrl &tree ) { + int i; + idPathTreeStack stack; + idStr scriptName; + idFileList *files; + + files = fileSystem->ListFilesTree( "script", ".script", true ); + + stack.PushRoot( NULL ); + + for ( i = 0; i < files->GetNumFiles(); i++) { + scriptName = files->GetFile( i ); + + scriptName.BackSlashesToSlashes(); + scriptName.StripFileExtension(); + + tree.AddPathToTree( scriptName, GetIdFromTypeAndIndex( DECLTYPE_SCRIPT, i ), stack ); + } + + fileSystem->FreeFileList( files ); +} + +/* +================ +DeclBrowser::AddGUIsToTree +================ +*/ +void DeclBrowser::AddGUIsToTree( PathTreeCtrl &tree ) { + int i; + idPathTreeStack stack; + idStr scriptName; + idFileList *files; + + files = fileSystem->ListFilesTree( "guis", ".gui", true ); + + stack.PushRoot( NULL ); + + for ( i = 0; i < files->GetNumFiles(); i++) { + scriptName = files->GetFile( i ); + + scriptName.BackSlashesToSlashes(); + scriptName.StripFileExtension(); + + tree.AddPathToTree( scriptName, GetIdFromTypeAndIndex( DECLTYPE_GUI, i ), stack ); + } + + fileSystem->FreeFileList( files ); +} + +/* +================ +DeclBrowser::InitBaseDeclTree +================ +*/ +void DeclBrowser::InitBaseDeclTree( void ) { + int i; + + numListedDecls = 0; + baseDeclTree.DeleteAllItems(); + + for ( i = 0; i < declManager->GetNumDeclTypes(); i++ ) { + AddDeclTypeToTree( (declType_t)i, declManager->GetDeclNameFromType( (declType_t)i ), baseDeclTree ); + } + + AddScriptsToTree( baseDeclTree ); + AddGUIsToTree( baseDeclTree ); +} + +/* +================ +DeclBrowser::GetDeclName +================ +*/ +void DeclBrowser::GetDeclName( TreeNode *item, idStr &typeName, idStr &declName ) const { + TreeNode *parent; + idStr itemName; + + declName.Clear(); + for( parent = declTree.GetParentItem( item ); parent && parent != declTree.GetRootItem(); parent = declTree.GetParentItem( parent ) ) { + itemName = declTree.GetItemText( item ); + declName = itemName + "/" + declName; + item = parent; + } + declName.Strip( '/' ); + typeName = declTree.GetItemText( item ); +} + +/* +================ +DeclBrowser::GetDeclFromTreeItem +================ +*/ +const idDecl *DeclBrowser::GetDeclFromTreeItem( TreeNode *item ) const { + int id, index; + declType_t type; + const idDecl *decl; + + if ( !item ) { + return NULL; + } + + if ( declTree.GetChildItem( item ) ) { + return NULL; + } + + id = declTree.GetItemData( item ); + type = GetTypeFromId( id ); + index = GetIndexFromId( id ); + + if ( type < 0 || type >= declManager->GetNumDeclTypes() ) { + return NULL; + } + + decl = declManager->DeclByIndex( type, index, false ); + + return decl; +} + +/* +================ +DeclBrowser::GetSelectedDecl +================ +*/ +const idDecl *DeclBrowser::GetSelectedDecl( void ) const { + return GetDeclFromTreeItem( declTree.GetSelectedItem() ); +} + +/* +================ +DeclBrowser::EditSelected +================ +*/ +void DeclBrowser::EditSelected( void ) { + int id, index; + idDict spawnArgs; + const idDecl *decl; + declType_t type; + TreeNode *item; + + item = declTree.GetSelectedItem(); + + if ( declTree.GetChildItem( item ) ) { + return; + } + + id = declTree.GetItemData( item ); + type = GetTypeFromId( id ); + index = GetIndexFromId( id ); + + switch( type ) { + case DECL_AF: { + decl = declManager->DeclByIndex( type, index, false ); + spawnArgs.Set( "articulatedFigure", decl->GetName() ); + ImGuiTools::AfEditorInit(); // TODO: pass spawnArgs + break; + } + case DECL_PARTICLE: { + decl = declManager->DeclByIndex( type, index, false ); + spawnArgs.Set( "model", decl->GetName() ); + ImGuiTools::ParticleEditorInit( &spawnArgs ); + break; + } + case DECL_PDA: { + decl = declManager->DeclByIndex( type, index, false ); + spawnArgs.Set( "pda", decl->GetName() ); + ImGuiTools::PDAEditorInit( &spawnArgs ); + break; + } + case DECLTYPE_SCRIPT: + case DECLTYPE_GUI: { + idStr typeName, declName; + GetDeclName( item, typeName, declName ); + idStr fileName = typeName + "/" + declName + ( ( type == DECLTYPE_SCRIPT ) ? ".script" : ".gui" ); + spawnArgs.Set( "script", fileName ); + ImGuiTools::ScriptEditorInit( &spawnArgs ); + break; + } + default: { + decl = declManager->DeclByIndex( type, index, false ); + + declEditorDlg.Start( const_cast( decl ) ); + break; + } + } +} + +/* +================ +DeclBrowserCompareDecl +================ +*/ +bool DeclBrowserCompareDecl( void *data, TreeNode *item, const char *name ) { + return reinterpret_cast(data)->CompareDecl( item, name ); +} + +/* +================ +DeclBrowser::CompareDecl +================ +*/ +bool DeclBrowser::CompareDecl( TreeNode *item, const char *name ) const { + if ( findNameString.Length() ) { + if ( !idStr::Filter( findNameString, name, false ) ) { + return false; + } + } + + if ( findTextString.Length() ) { + int id, index; + declType_t type; + + id = declTree.GetItemData( item ); + type = GetTypeFromId( id ); + index = GetIndexFromId( id ); + + if ( type == DECLTYPE_SCRIPT || type == DECLTYPE_GUI ) { + // search for the text in the script or gui + idStr text; + void *buffer; + if ( fileSystem->ReadFile( idStr( name ) + ( ( type == DECLTYPE_SCRIPT ) ? ".script" : ".gui" ), &buffer ) == -1 ) { + return false; + } + text = (char *) buffer; + fileSystem->FreeFile( buffer ); + if ( text.Find( findTextString, false ) == -1 ) { + return false; + } + } else { + // search for the text in the decl + const idDecl *decl = declManager->DeclByIndex( type, index, false ); + char *declText = (char *)_alloca( ( decl->GetTextLength() + 1 ) * sizeof( char ) ); + decl->GetText( declText ); + if ( idStr::FindText( declText, findTextString, false ) == -1 ) { + return false; + } + } + } + + return true; +} + +/* +================ +DeclBrowser::Reset +================ +*/ +void DeclBrowser::Reset() { + + InitBaseDeclTree(); + + findNameString = "*"; + findNameEdit = findNameString; + + findTextString = ""; + findTextEdit = findTextString; + + numListedDecls = baseDeclTree.SearchTree( DeclBrowserCompareDecl, this, declTree ); + + statusBarText = va( "%d decls listed", numListedDecls ); +} + +/* +================ +DeclBrowser::ReloadDeclarations +================ +*/ +void DeclBrowser::ReloadDeclarations( void ) { + InitBaseDeclTree(); + OnBnClickedFind(); +} + +// DeclBrowser message handlers + +/* +================ +DeclBrowser::OnToolTipNotify +================ +*/ +bool DeclBrowser::OnToolTipNotify( TreeNode *item, idStr &tooltipText ) const { + if ( item ) { + const idDecl *decl = GetDeclFromTreeItem( item ); + + if ( !decl ) { + return false; + } + + tooltipText = va( "%s, line: %d", decl->GetFileName(), decl->GetLineNum() ); + + return true; + } + + return false; +} + +/* +================ +DeclBrowser::OnTreeSelChanged +================ +*/ +void DeclBrowser::OnTreeSelChanged( bool doubleClicked ) { + + const idDecl *decl = GetSelectedDecl(); + if ( decl ) { + statusBarText = va( "%d decls listed - %s, line: %d", numListedDecls, decl->GetFileName(), decl->GetLineNum() ); + findNameEdit = va( "%s/%s", declManager->GetDeclNameFromType( decl->GetType() ), decl->GetName() ); + } else { + TreeNode *item = declTree.GetSelectedItem(); + idStr typeName, declName; + GetDeclName( item, typeName, declName ); + findNameEdit = va( "%s/%s*", typeName.c_str(), declName.c_str() ); + statusBarText = va( "%d decls listed", numListedDecls ); + } + + if ( doubleClicked ) { + OnBnClickedEdit(); + } +} + +/* +================ +DeclBrowser::OnTreeDblclk +================ +*/ +void DeclBrowser::OnTreeDblclk() { + // post a message as if the edit button was clicked to make sure the editor gets focus + //PostMessage( WM_COMMAND, ( BN_CLICKED << 16 ) | editButton.GetDlgCtrlID(), 0 ); +} + +/* +================ +DeclBrowser::OnBnClickedFind +================ +*/ +void DeclBrowser::OnBnClickedFind() { + idStr windowText; + + windowText = findNameEdit; + findNameString = windowText; + findNameString.Strip( ' ' ); + + windowText = findTextEdit; + findTextString = windowText; + findTextString.Strip( ' ' ); + + numListedDecls = baseDeclTree.SearchTree( DeclBrowserCompareDecl, this, declTree ); + + statusBarText = va( "%d decls listed", numListedDecls ); +} + +/* +================ +DeclBrowser::OnBnClickedEdit +================ +*/ +void DeclBrowser::OnBnClickedEdit() { + EditSelected(); +} + +/* +================ +DeclBrowser::OnBnClickedNew +================ +*/ +void DeclBrowser::OnBnClickedNew() { + TreeNode *item; + idStr typeName, declName; + const idDecl *decl; + + declNewDlg.SetDeclTree( &baseDeclTree ); + + item = declTree.GetSelectedItem(); + if ( item ) { + GetDeclName( item, typeName, declName ); + declNewDlg.SetDefaultType( typeName ); + declNewDlg.SetDefaultName( declName ); + } + + decl = GetSelectedDecl(); + if ( decl ) { + declNewDlg.SetDefaultFile( decl->GetFileName() ); + } + + declNewDlg.Start(); +} + +void DeclBrowser::OnBnClickedNewAccepted() { + const idDecl *decl = declNewDlg.GetNewDecl(); + + if ( decl ) { + idStr declName = declManager->GetDeclNameFromType( decl->GetType() ); + declName += "/"; + declName += decl->GetName(); + + int id = GetIdFromTypeAndIndex( decl->GetType(), decl->Index() ); + + baseDeclTree.InsertPathIntoTree( declName, id ); + TreeNode *item = declTree.InsertPathIntoTree( declName, id ); + declTree.SelectItem( item ); + + EditSelected(); + } +} + +/* +================ +DeclBrowser::OnBnClickedReload +================ +*/ +void DeclBrowser::OnBnClickedReload() { + + declManager->Reload( false ); + + ReloadDeclarations(); +} + +} diff --git a/neo/tools/imgui/decleditor/DeclBrowser.h b/neo/tools/imgui/decleditor/DeclBrowser.h new file mode 100644 index 000000000..d010b52eb --- /dev/null +++ b/neo/tools/imgui/decleditor/DeclBrowser.h @@ -0,0 +1,108 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __DECLBROWSER_H__ +#define __DECBROWSER_H__ + +#include "../../edit_public.h" + +#include "PathTreeCtrl.h" +#include "DeclNew.h" +#include "DeclEditor.h" + +namespace ImGuiTools { + +// DeclBrowser dialog + +class DeclBrowser { +public: + DeclBrowser(); // standard constructor + + static DeclBrowser& Instance(); + + void ReloadDeclarations( void ); + bool CompareDecl( TreeNode *item, const char *name ) const; + bool OnToolTipNotify( TreeNode *item, idStr &tooltipText ) const; + void OnTreeSelChanged( bool doubleClicked ); + + void Reset(); + void Draw(); + + void ShowIt(bool show) { + isShown = show; + } + bool IsShown() { + return isShown; + } + +private: + void OnTreeDblclk(); + void OnBnClickedFind(); + void OnBnClickedEdit(); + void OnBnClickedNew(); + void OnBnClickedNewAccepted(); + void OnBnClickedReload(); + +private: + bool isShown; + idStr statusBarText; + PathTreeCtrl declTree; + idStr findNameStatic; + idStr findTextStatic; + idStr findNameEdit; + idStr findTextEdit; + bool findButtonEnabled; + bool editButtonEnabled; + bool newButtonEnabled; + bool reloadButtonEnabled; + bool cancelButtonEnabled; + + + PathTreeCtrl baseDeclTree; + int numListedDecls; + idStr findNameString; + idStr findTextString; + + DeclNew declNewDlg; + DeclEditor declEditorDlg; + +private: + void AddDeclTypeToTree( declType_t type, const char *root, PathTreeCtrl &tree ); + void AddScriptsToTree( PathTreeCtrl &tree ); + void AddGUIsToTree( PathTreeCtrl &tree ); + void InitBaseDeclTree( void ); + + void GetDeclName( TreeNode *item, idStr &typeName, idStr &declName ) const; + const idDecl * GetDeclFromTreeItem( TreeNode *item ) const; + const idDecl * GetSelectedDecl( void ) const; + void EditSelected( void ); +}; + +} + +#endif /* !__DECLBROWSER_H__ */ diff --git a/neo/tools/imgui/decleditor/DeclEditor.cpp b/neo/tools/imgui/decleditor/DeclEditor.cpp new file mode 100644 index 000000000..c8021b30c --- /dev/null +++ b/neo/tools/imgui/decleditor/DeclEditor.cpp @@ -0,0 +1,371 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../util/ImGui_IdWidgets.h" + +#include "PathTreeCtrl.h" +#include "DeclBrowser.h" + +namespace ImGuiTools { + +// DeclEditor dialog + +/* +================ +DeclEditor::DeclEditor +================ +*/ +DeclEditor::DeclEditor() + : isShown(false) + , windowText() + , statusBarText() + , declEdit() + , testButtonEnabled(false) + , okButtonEnabled(false) + , cancelButtonEnabled(true) + , errorText() + , decl(NULL) + , firstLine(0) +{ + declEdit.Init(); +} + +/* +================ +DeclEditor::TestDecl +================ +*/ +bool DeclEditor::TestDecl( const idStr &declText ) { + idLexer src( LEXFL_NOSTRINGCONCAT ); + idToken token; + int indent; + + errorText.Clear(); + + src.LoadMemory( declText, declText.Length(), "decl text" ); + + indent = 0; + while( src.ReadToken( &token ) ) { + if ( token == "{" ) { + indent++; + } else if ( token == "}" ) { + indent--; + } + } + + if ( indent < 0 ) { + errorText = "Missing opening brace!"; + return false; + } + if ( indent > 0 ) { + errorText = "Missing closing brace!"; + return false; + } + return true; +} + +/* +================ +DeclEditor::UpdateStatusBar +================ +*/ +void DeclEditor::UpdateStatusBar( void ) { + int line, column, character; + + if ( decl ) { + declEdit.GetCursorPos( line, column, character ); + statusBarText = va( "Line: %d, Column: %d", decl->GetLineNum() + line, column ); + } +} + +/* +================ +DeclEditor::Reset +================ +*/ +void DeclEditor::Reset() { + + windowText.Clear(); + + testButtonEnabled = false; + okButtonEnabled = false; + + UpdateStatusBar(); +} + +/* +================ +DeclEditor::Start +================ +*/ +void DeclEditor::Start( idDecl *decl ) { + int numLines = 0; + int numCharsPerLine = 0; + int maxCharsPerLine = 0; + idStr declText; + + this->decl = decl; + + ShowIt( true ); + + switch( decl->GetType() ) { + case DECL_ENTITYDEF: + //declEdit.SetStringColor( SRE_COLOR_BLUE, SRE_COLOR_DARK_CYAN ); + declEdit.LoadKeyWordsFromFile( "editors/entity.def" ); + break; + case DECL_MATERIAL: + declEdit.LoadKeyWordsFromFile( "editors/material.def" ); + break; + case DECL_SKIN: + declEdit.LoadKeyWordsFromFile( "editors/skin.def" ); + break; + case DECL_SOUND: + declEdit.LoadKeyWordsFromFile( "editors/sound.def" ); + break; + case DECL_FX: + declEdit.LoadKeyWordsFromFile( "editors/fx.def" ); + break; + case DECL_PARTICLE: + declEdit.LoadKeyWordsFromFile( "editors/particle.def" ); + break; + case DECL_AF: + declEdit.LoadKeyWordsFromFile( "editors/af.def" ); + break; + case DECL_TABLE: + declEdit.LoadKeyWordsFromFile( "editors/table.def" ); + break; + case DECL_MODELDEF: + declEdit.LoadKeyWordsFromFile( "editors/model.def" ); + break; + default: + declEdit.LoadKeyWordsFromFile( va( "editors/%s.def", declManager->GetDeclNameFromType( decl->GetType() ) ) ); + break; + } + + firstLine = decl->GetLineNum(); + + char *localDeclText = (char *)_alloca( ( decl->GetTextLength() + 1 ) * sizeof( char ) ); + decl->GetText( localDeclText ); + declText = localDeclText; + + declEdit.SetText( declText ); + + for( const char *ptr = declText.c_str(); *ptr; ptr++ ) { + if ( *ptr == '\r' ) { + if ( numCharsPerLine > maxCharsPerLine ) { + maxCharsPerLine = numCharsPerLine; + } + numCharsPerLine = 0; + numLines++; + } else if ( *ptr == '\t' ) { + numCharsPerLine += TAB_SIZE; + } else { + numCharsPerLine++; + } + } + + windowText = va( "Declaration Editor (%s, line %d)", decl->GetFileName(), decl->GetLineNum() ); + + testButtonEnabled = false; + okButtonEnabled = false; + + UpdateStatusBar(); + + //declEdit.SetFocus(); + + ImGui::OpenPopup( "Declaration Editor" ); +} + +bool DeclEditor::Draw() { + bool accepted = false; + + if ( !isShown ) { + return false; + } + + if ( ImGui::BeginPopup( "Declaration Editor" ) ) { + + declEdit.Draw(); + if ( declEdit.IsEdited() ) { + testButtonEnabled = true; + okButtonEnabled = true; + } + + ImGui::BeginDisabled( !testButtonEnabled ); + if ( ImGui::Button( "Test" ) ) { + OnBnClickedTest(); + } + ImGui::EndDisabled(); + + ImGui::BeginDisabled( !okButtonEnabled ); + if ( ImGui::Button( "Save" ) ) { + if ( OnBnClickedOk() ) { + accepted = true; + ImGui::CloseCurrentPopup(); + } + } + bool subPopupAccepted = false; + if ( ImGui::BeginPopup( "Warning saving" ) ) { + ImGui::TextUnformatted( va( "Declaration file %s has been modified outside of the editor.\r\nReload declarations and save?", decl->GetFileName() ) ); + + if ( ImGui::Button( "OK" ) ) { + OnBnClickedOkAccepted(); + subPopupAccepted = true; + ImGui::CloseCurrentPopup(); + } + if ( ImGui::Button( "Cancel" ) ) { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + if ( subPopupAccepted ) { + accepted = true; + ImGui::CloseCurrentPopup(); + } + ImGui::EndDisabled(); + ImGui::SameLine(); + ImGui::BeginDisabled( !cancelButtonEnabled ); + if ( ImGui::Button( "Cancel" ) ) { + OnBnClickedCancel(); + ImGui::CloseCurrentPopup(); + } + ImGui::EndDisabled(); + + ImGui::EndPopup(); + } + + if ( accepted ) + { + isShown = false; + } + + return accepted; +} + +// DeclEditor message handlers + +/* +================ +DeclEditor::OnBnClickedTest +================ +*/ +void DeclEditor::OnBnClickedTest() { + idStr declText; + + if ( decl ) { + + declEdit.GetText( declText ); + + if ( !TestDecl( declText ) ) { + return; + } + + char *oldDeclText = (char *)_alloca( ( decl->GetTextLength() + 1 ) * sizeof( char ) ); + decl->GetText( oldDeclText ); + decl->SetText( declText ); + decl->Invalidate(); + declManager->DeclByIndex( decl->GetType(), decl->Index(), true ); + decl->SetText( oldDeclText ); + decl->Invalidate(); + common->Printf( "tested %s\n", decl->GetName() ); + + testButtonEnabled = false; + } +} + +/* +================ +DeclEditor::OnBnClickedOk +================ +*/ +bool DeclEditor::OnBnClickedOk() { + idStr declText; + + if ( decl ) { + + declEdit.GetText( declText ); + + if ( !TestDecl( declText ) ) { + return false; + } + + if ( decl->SourceFileChanged() ) { + ImGui::OpenPopup( "Warning saving" ); + return false; + } + + decl->SetText( declText ); + if ( !decl->ReplaceSourceFileText() ) { + errorText = va( "Couldn't save: %s.\r\nMake sure the declaration file is not read-only.", decl->GetFileName() ); + return false; + } + decl->Invalidate(); + } + + okButtonEnabled = false; + return true; +} + +void DeclEditor::OnBnClickedOkAccepted() { + idStr declText; + + if ( decl ) { + + declEdit.GetText( declText ); + + declManager->Reload( false ); + DeclBrowser::Instance().ReloadDeclarations(); + + decl->SetText( declText ); + if ( !decl->ReplaceSourceFileText() ) { + errorText = va( "Couldn't save: %s.\r\nMake sure the declaration file is not read-only.", decl->GetFileName() ); + return; + } + decl->Invalidate(); + } + + okButtonEnabled = false; +} + +/* +================ +DeclEditor::OnBnClickedCancel +================ +*/ +void DeclEditor::OnBnClickedCancel() { + if ( okButtonEnabled ) { + /* + if ( MessageBox( "Cancel changes?", "Cancel", MB_YESNO | MB_ICONQUESTION ) != IDYES ) { + return; + }*/ + } + //OnCancel(); +} + +} diff --git a/neo/tools/imgui/decleditor/DeclEditor.h b/neo/tools/imgui/decleditor/DeclEditor.h new file mode 100644 index 000000000..206e742b8 --- /dev/null +++ b/neo/tools/imgui/decleditor/DeclEditor.h @@ -0,0 +1,80 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __DECLEDITOR_H__ +#define __DECLEDITOR_H__ + +#include "../util/SyntaxRichEditCtrl.h" + +namespace ImGuiTools { + +// DeclEditor dialog + +class DeclEditor { +public: + DeclEditor(); // standard constructor + + void Reset(); + void Start( idDecl* decl ); + + bool Draw(); + + void ShowIt(bool show) { + isShown = show; + } + bool IsShown() { + return isShown; + } + +private: + void OnBnClickedTest(); + bool OnBnClickedOk(); + void OnBnClickedOkAccepted(); + void OnBnClickedCancel(); + +private: + bool isShown; + idStr windowText; + idStr statusBarText; + SyntaxRichEditCtrl declEdit; + bool testButtonEnabled; + bool okButtonEnabled; + bool cancelButtonEnabled; + idStr errorText; + + idDecl * decl; + int firstLine; + +private: + bool TestDecl( const idStr &declText ); + void UpdateStatusBar( void ); +}; + +} + +#endif /* !__DECLEDITOR_H__ */ diff --git a/neo/tools/imgui/decleditor/DeclNew.cpp b/neo/tools/imgui/decleditor/DeclNew.cpp new file mode 100644 index 000000000..d3f6e54fe --- /dev/null +++ b/neo/tools/imgui/decleditor/DeclNew.cpp @@ -0,0 +1,273 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../util/ImGui_IdWidgets.h" + +#include "framework/FileSystem.h" + +#include "PathTreeCtrl.h" +#include "DeclNew.h" +#include "DeclBrowser.h" + +namespace ImGuiTools { + +/* +================ +DeclNew::DeclNew +================ +*/ +DeclNew::DeclNew() + : typeList() + , typeListSel(-1) + , nameEdit() + , fileEdit() + , errorText() + , declTree(NULL) + , defaultType() + , defaultName() + , newDecl(NULL) + , state(DONE) + , selectDlg( DECL_AF, "af", ".af", "AFs" ) +{ +} + +/* +================ +DeclNew::InitTypeList +================ +*/ +void DeclNew::InitTypeList( void ) { + int i; + + typeList.Clear(); + for ( i = 0; i < declManager->GetNumDeclTypes(); i++ ) { + typeList.Append( declManager->GetDeclNameFromType( (declType_t)i ) ); + } + typeListSel = -1; +} + +/* +================ +DeclNew::Reset +================ +*/ +void DeclNew::Reset() { + InitTypeList(); + + typeListSel = typeList.FindIndex( defaultType ); + nameEdit = defaultName; + fileEdit = defaultFile; + newDecl = NULL; + state = DONE; +} + +void DeclNew::Start() { + Reset(); + + state = NAME; + + ImGui::OpenPopup("New Declaration"); +} + +bool DeclNew::Draw() { + if ( state == DONE ) { + return false; + } + + bool accepted = false; + bool canceled = false; + + if ( ImGui::BeginPopupModal( "New Declaration", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::TextColored( ImVec4( 1, 0, 0, 1 ), "%s", errorText.c_str() ); + + const char *previewValue = typeListSel == -1 ? NULL : typeList[typeListSel].c_str(); + + if ( ImGui::BeginCombo( "Type##typeListSelect", previewValue ) ) { + ImGui::SetItemTooltip( "select the declaration type to create" ); + + for ( int i = 0; i < typeList.Num(); i++ ) { + bool selected = ( i == typeListSel ); + + ImGui::PushID( i ); + if ( ImGui::Selectable( typeList[i].c_str(), selected ) ) { + typeListSel = i; + } + if ( selected ) { + ImGui::SetItemDefaultFocus(); + } + ImGui::PopID(); + } + + ImGui::EndCombo(); + } + + if ( ImGui::InputTextStr( "Name", &nameEdit ) ) { + + } + ImGui::SetItemTooltip( "enter a name for the new declaration" ); + if ( ImGui::InputTextStr( "File", &fileEdit ) ) { + + } + ImGui::SetItemTooltip( "enter the name of the file to add the declaration to" ); + ImGui::SameLine(); + if ( ImGui::Button( "..." ) ) { + //selectDlg.Start(); + OnBnClickedFile(); + } + ImGui::SetItemTooltip( "select existing file to add the declaration to" ); + + if ( selectDlg.Draw() ) { + // accepted + //fileEdit = fileSystem->OSPathToRelativePath( dlgFile.m_ofn.lpstrFile ); + fileEdit = selectDlg.GetDecl()->GetFileName(); + } + + if ( ImGui::Button( "OK" ) ) { + OnBnClickedOk(); + if ( newDecl ) { + accepted = true; + state = DONE; + ImGui::CloseCurrentPopup(); + } + } + ImGui::SetItemTooltip( "create new declaration" ); + ImGui::SameLine(); + if ( ImGui::Button( "Cancel" ) ) { + accepted = false; + state = DONE; + ImGui::CloseCurrentPopup(); + } + ImGui::SetItemTooltip( "cancel" ); + + ImGui::EndPopup(); + } + + return accepted; +} + +// DeclNew message handlers + +/* +================ +DeclNew::OnBnClickedFile +================ +*/ +void DeclNew::OnBnClickedFile() { + idStr typeName, folder, ext; + idStr path; + + errorText.Clear(); + + if ( typeListSel == -1 ) { + errorText = "Select a declaration type first."; + return; + } + + declType_t type = declManager->GetDeclTypeFromName( typeName ); + if ( type >= declManager->GetNumDeclTypes() ) { + errorText = "Unknown declaration type."; + return; + } + + switch( type ) { + // FIXME: SteelStorm2 has a _v1 suffix for materials, def and fx - why? + case DECL_TABLE: folder = "materials"; ext = ".mtr"; break; + case DECL_MATERIAL: folder = "materials"; ext = ".mtr"; break; + case DECL_SKIN: folder = "skins"; ext = ".skin"; break; + case DECL_SOUND: folder = "sound"; ext = ".sndshd"; break; + case DECL_ENTITYDEF: folder = "def"; ext = ".def|.decl"; break; + case DECL_MODELDEF: folder = "def"; ext = ".def"; break; + case DECL_FX: folder = "fx"; ext = ".fx"; break; + case DECL_PARTICLE: folder = "particles"; ext = ".prt"; break; + case DECL_AF: folder = "af"; ext = ".af"; break; + default: folder = "def"; ext = ".decl"; break; + } + + path = fileSystem->RelativePathToOSPath( folder ); + path += "\\*"; + + selectDlg.Start(); +} + +/* +================ +DeclNew::OnBnClickedOk +================ +*/ +void DeclNew::OnBnClickedOk() { + idStr typeName, declName, fileName; + + errorText.Clear(); + + if ( !declTree ) { + errorText = "No declaration tree available."; + return; + } + + if ( typeListSel == -1 ) { + errorText = "No declaration type selected."; + return; + } + typeName = typeList[typeListSel]; + + declName = nameEdit; + if ( declName.Length() == 0 ) { + errorText = "No declaration name specified."; + return; + } + + fileName = fileEdit; + if ( fileName.Length() == 0 ) { + errorText = "No file name specified."; + return; + } + + if ( declTree->FindItem( idStr( typeName + "/" + declName ) ) ) { + errorText = "Declaration already exists."; + return; + } + + declType_t type = declManager->GetDeclTypeFromName( typeName ); + if ( type >= declManager->GetNumDeclTypes() ) { + errorText = "Unknown declaration type."; + return; + } + + newDecl = declManager->CreateNewDecl( type, declName, fileName ); +} + +/* +================ +DeclNew::OnBnClickedCancel +================ +*/ +void DeclNew::OnBnClickedCancel() { + +} + +} diff --git a/neo/tools/imgui/decleditor/DeclNew.h b/neo/tools/imgui/decleditor/DeclNew.h new file mode 100644 index 000000000..756065bd1 --- /dev/null +++ b/neo/tools/imgui/decleditor/DeclNew.h @@ -0,0 +1,81 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __DECLNEW_H__ +#define __DECLNEW_H__ + +namespace ImGuiTools { + +// DeclNew dialog + +class DeclNew { + +public: + DeclNew(); // standard constructor + + void SetDeclTree( PathTreeCtrl *tree ) { declTree = tree; } + void SetDefaultType( const char *type ) { defaultType = type; } + void SetDefaultName( const char *name ) { defaultName = name; } + void SetDefaultFile( const char *file ) { defaultFile = file; } + idDecl * GetNewDecl( void ) const { return newDecl; } + + void Reset(); + void Start(); + bool Draw(); + +private: + void OnBnClickedFile(); + void OnBnClickedOk(); + void OnBnClickedCancel(); + +private: + enum state_t { DONE = 0, NAME }; + + idStrList typeList; + int typeListSel; + idStr nameEdit; + idStr fileEdit; + + idStr errorText; + + PathTreeCtrl * declTree; + idStr defaultType; + idStr defaultName; + idStr defaultFile; + idDecl * newDecl; + state_t state; + + DeclNewSelect selectDlg; + +private: + void InitTypeList( void ); +}; + +} + +#endif /* !__DECLNEW_H__ */ diff --git a/neo/tools/imgui/decleditor/PathTreeCtrl.cpp b/neo/tools/imgui/decleditor/PathTreeCtrl.cpp new file mode 100644 index 000000000..809bb195c --- /dev/null +++ b/neo/tools/imgui/decleditor/PathTreeCtrl.cpp @@ -0,0 +1,207 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../util/ImGui_IdWidgets.h" + +#include "PathTreeCtrl.h" + +namespace ImGuiTools { + +/* +================ +PathTreeCtrl::FindItem + +Find the given path in the tree. +================ +*/ +TreeNode *PathTreeCtrl::FindItem( const idStr &pathName ) { + int lastSlash; + idStr path, tmpPath, itemName; + TreeNode *item, *parentItem; + + parentItem = NULL; + item = GetChildItem( GetRootItem() ); + + lastSlash = pathName.Last( '/' ); + + while( item && lastSlash > path.Length() ) { + itemName = GetItemText( item ); + tmpPath = path + itemName; + if ( pathName.Icmpn( tmpPath, tmpPath.Length() ) == 0 ) { + parentItem = item; + item = GetChildItem( item ); + path = tmpPath + "/"; + } else { + item = GetNextSiblingItem( item ); + } + } + + for ( item = GetChildItem( parentItem ); item; item = GetNextSiblingItem( item ) ) { + itemName = GetItemText( item ); + if ( pathName.Icmp( path + itemName ) == 0 ) { + return item; + } + } + + return NULL; +} + +/* +================ +TreeCtrl::InsertPathIntoTree + +Inserts a new item going from the root down the tree only creating paths where necessary. +This is slow and should only be used to insert single items. +================ +*/ +TreeNode *PathTreeCtrl::InsertPathIntoTree( const idStr &pathName, const int id ) { + int lastSlash; + idStr path, tmpPath, itemName; + TreeNode *item, *parentItem; + + parentItem = NULL; + item = GetRootItem(); + + lastSlash = pathName.Last( '/' ); + + while( item && lastSlash > path.Length() ) { + itemName = GetItemText( item ); + tmpPath = path + itemName; + if ( pathName.Icmpn( tmpPath, tmpPath.Length() ) == 0 ) { + parentItem = item; + item = GetChildItem( item ); + path = tmpPath + "/"; + } else { + item = GetNextSiblingItem( item ); + } + } + + while( lastSlash > path.Length() ) { + pathName.Mid( path.Length(), pathName.Length(), tmpPath ); + tmpPath.Left( tmpPath.Find( '/' ), itemName ); + parentItem = InsertItem( itemName, parentItem ); + path += itemName + "/"; + } + + pathName.Mid( path.Length(), pathName.Length(), itemName ); + item = InsertItem( itemName, parentItem ); // TODO: insert sorted? + SetItemData( item, id ); + + return item; +} + +/* +================ +TreeCtrl::AddPathToTree + +Adds a new item to the tree. +Assumes new paths after the current stack path do not yet exist. +================ +*/ +TreeNode *PathTreeCtrl::AddPathToTree( const idStr &pathName, const int id, idPathTreeStack &stack ) { + int lastSlash; + idStr itemName, tmpPath; + TreeNode *item; + + lastSlash = pathName.Last( '/' ); + + while( stack.Num() > 1 ) { + if ( pathName.Icmpn( stack.TopName(), stack.TopNameLength() ) == 0 ) { + break; + } + stack.Pop(); + } + + while( lastSlash > stack.TopNameLength() ) { + pathName.Mid( stack.TopNameLength(), pathName.Length(), tmpPath ); + tmpPath.Left( tmpPath.Find( '/' ), itemName ); + item = InsertItem( itemName, stack.TopItem() ); + stack.Push( item, itemName ); + } + + pathName.Mid( stack.TopNameLength(), pathName.Length(), itemName ); + item = InsertItem( itemName, stack.TopItem() ); + SetItemData( item, id ); + + return item; +} + +/* +================ +PathTreeCtrl::SearchTree + +Search the tree using the search string. +Adds the matched tree items to the result tree. +Returns the number of items added to the result tree. +================ +*/ +int PathTreeCtrl::SearchTree( treeItemCompare_t compare, void *data, PathTreeCtrl &result ) { + idPathTreeStack stack, searchStack; + TreeNode *item, *child; + idStr name; + int id, numItems; + + numItems = 0; + result.DeleteAllItems(); + stack.PushRoot( NULL ); + + item = GetRootItem(); + searchStack.PushRoot( item ); + id = 0; + + while( searchStack.Num() > 0 ) { + + for ( child = GetChildItem( item ); child; child = GetChildItem( child ) ) { + if ( item != GetRootItem() ) { + searchStack.Push( item, GetItemText( item ) ); + } + item = child; + } + + name = searchStack.TopName(); + name += GetItemText( item ); + id = GetItemData( item ); + + if ( compare( data, item, name ) ) { + result.AddPathToTree( name, id, stack ); + numItems++; + } + + for ( item = GetNextSiblingItem( item ); item == NULL; ) { + item = GetNextSiblingItem( searchStack.TopItem() ); + searchStack.Pop(); + if ( searchStack.Num() <= 0 ) { + return numItems; + } + } + } + + return numItems; +} + +} diff --git a/neo/tools/imgui/decleditor/PathTreeCtrl.h b/neo/tools/imgui/decleditor/PathTreeCtrl.h new file mode 100644 index 000000000..819aed12e --- /dev/null +++ b/neo/tools/imgui/decleditor/PathTreeCtrl.h @@ -0,0 +1,95 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __PATHTREECTRL_H__ +#define __PATHTREECTRL_H__ + +#include "idlib/Str.h" +#include "idlib/Heap.h" +#include "idlib/containers/Hierarchy.h" +#include "../util/TreeCtrl.h" + +namespace ImGuiTools { + +/* +=============================================================================== + + Tree Control for path names. + +=============================================================================== +*/ + +class idPathTreeStack { +public: + idPathTreeStack( void ) { size = 0; } + + void PushRoot( TreeNode *root ); + void Push( TreeNode *item, const char *name ); + void Pop( void ) { size--; } + TreeNode * TopItem( void ) const { return stackItem[size-1]; } + const char * TopName( void ) const { return stackName[size-1]; } + int TopNameLength( void ) const { return stackName[size-1].Length(); } + int Num( void ) const { return size; } + +private: + int size; + TreeNode * stackItem[128]; + idStr stackName[128]; +}; + +ID_INLINE void idPathTreeStack::PushRoot( TreeNode *root ) { + assert( size == 0 ); + stackItem[size] = root; + stackName[size] = ""; + size++; +} + +ID_INLINE void idPathTreeStack::Push( TreeNode *item, const char *name ) { + assert( size < 127 ); + stackItem[size] = item; + stackName[size] = stackName[size-1] + name + "/"; + size++; +} + +class PathTreeCtrl : public TreeCtrl { +public: + PathTreeCtrl() + { + } + ~PathTreeCtrl() { + } + + TreeNode * FindItem( const idStr &pathName ); + TreeNode * InsertPathIntoTree( const idStr &pathName, const int id ); + TreeNode * AddPathToTree( const idStr &pathName, const int id, idPathTreeStack &stack ); + int SearchTree( treeItemCompare_t compare, void *data, PathTreeCtrl &result ); +}; + +} + +#endif /* !__PATHTREECTRL_H__ */ diff --git a/neo/tools/imgui/lighteditor/LightEditor.cpp b/neo/tools/imgui/lighteditor/LightEditor.cpp new file mode 100644 index 000000000..114a44291 --- /dev/null +++ b/neo/tools/imgui/lighteditor/LightEditor.cpp @@ -0,0 +1,798 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. +Copyright (C) 2015 Daniel Gibson +Copyright (C) 2020-2023 Robert Beckebans + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "LightEditor.h" + +#include "sys/sys_imgui.h" +#include "../util/ImGui_IdWidgets.h" + +#include "idlib/math/Vector.h" + +#include "framework/Game.h" +#include "framework/DeclEntityDef.h" +#include "renderer/Material.h" +#include "renderer/Image.h" + +namespace ImGuiTools +{ + +void LightInfo::Defaults() +{ + lightType = LIGHT_POINT; + + strTexture = ""; + equalRadius = true; + explicitStartEnd = false; + lightStart.Zero(); + lightEnd.Zero(); + lightUp.Zero(); + lightRight.Zero(); + lightTarget.Zero(); + lightCenter.Zero(); + color[0] = color[1] = color[2] = 1.0f; + + lightRadius.Zero(); + castShadows = true; + skipSpecular = false; + hasCenter = false; + //lightStyle = -1; +} + + +void LightInfo::DefaultPoint() +{ + idVec3 oldColor = color; + Defaults(); + color = oldColor; + lightType = LIGHT_POINT; + lightRadius[0] = lightRadius[1] = lightRadius[2] = 300; + equalRadius = true; +} + +void LightInfo::DefaultProjected() +{ + idVec3 oldColor = color; + Defaults(); + color = oldColor; + lightType = LIGHT_SPOT; + lightTarget[2] = -256; + lightUp[1] = -128; + lightRight[0] = -128; +} + +void LightInfo::DefaultSun() +{ + idVec3 oldColor = color; + Defaults(); + color = oldColor; + lightType = LIGHT_SUN; + lightCenter.Set( 4, 4, 32 ); + lightRadius[0] = lightRadius[1] = 2048; + lightRadius[2] = 1024; + equalRadius = false; +} + +void LightInfo::FromDict( const idDict* e ) +{ + e->GetVector( "origin", "", origin ); + + lightRadius.Zero(); + lightTarget.Zero(); + lightRight.Zero(); + lightUp.Zero(); + lightStart.Zero(); + lightEnd.Zero(); + lightCenter.Zero(); + + castShadows = !e->GetBool( "noshadows" ); + skipSpecular = e->GetBool( "nospecular" ); + + strTexture = e->GetString( "texture" ); + + bool isParallel = e->GetBool( "parallel" ); + + if( !e->GetVector( "_color", "", color ) ) + { + color[0] = color[1] = color[2] = 1.0f; + // NOTE: like the game, imgui uses color values between 0.0 and 1.0 + // even though it displays them as 0 to 255 + } + + if( e->GetVector( "light_right", "", lightRight ) ) + { + // projected light + lightType = LIGHT_SPOT; + e->GetVector( "light_target", "", lightTarget ); + e->GetVector( "light_up", "", lightUp ); + if( e->GetVector( "light_start", "", lightStart ) ) + { + // explicit start and end points + explicitStartEnd = true; + if( !e->GetVector( "light_end", "", lightEnd ) ) + { + // no end, use target + lightEnd = lightTarget; + } + } + else + { + explicitStartEnd = false; + // create a start a quarter of the way to the target + lightStart = lightTarget * 0.25; + lightEnd = lightTarget; + } + } + else + { + lightType = isParallel ? LIGHT_SUN : LIGHT_POINT; + + if( e->GetVector( "light_radius", "", lightRadius ) ) + { + equalRadius = ( lightRadius.x == lightRadius.y && lightRadius.x == lightRadius.z ); + } + else + { + float radius = e->GetFloat( "light" ); + if( radius == 0 ) + { + radius = 300; + } + lightRadius[0] = lightRadius[1] = lightRadius[2] = radius; + equalRadius = true; + } + if( e->GetVector( "light_center", "", lightCenter ) ) + { + hasCenter = true; + } + } + + // RB: Quake 1 light styles + //lightStyle = e->GetInt( "style", "-1" ); +} + +// the returned idDict is supposed to be used by idGameEdit::EntityChangeSpawnArgs() +// and thus will contain pairs with value "" if the key should be removed from entity +void LightInfo::ToDict( idDict* e ) +{ + e->SetVector( "origin", origin ); + + // idGameEdit::EntityChangeSpawnArgs() will delete key/value from entity, + // if value is "" => use DELETE_VAL for readability + static const char* DELETE_VAL = ""; + + e->Set( "light", DELETE_VAL ); // we always use "light_radius" instead + + e->Set( "noshadows", ( !castShadows ) ? "1" : "0" ); + e->Set( "nospecular", ( skipSpecular ) ? "1" : "0" ); + + if( strTexture.Length() > 0 ) + { + e->Set( "texture", strTexture ); + } + else + { + e->Set( "texture", DELETE_VAL ); + } + + // NOTE: e->SetVector() uses precision of 2, not enough for color + e->Set( "_color", color.ToString( 4 ) ); + + if( lightType == LIGHT_POINT || lightType == LIGHT_SUN ) + { + if( !equalRadius ) + { + e->SetVector( "light_radius", lightRadius ); + } + else + { + idVec3 tmp( lightRadius[0], lightRadius[0], lightRadius[0] ); // x, y and z have the same value + e->SetVector( "light_radius", tmp ); + } + + if( hasCenter ) + { + e->SetVector( "light_center", lightCenter ); + } + else + { + e->Set( "light_center", DELETE_VAL ); + } + + e->Set( "parallel", ( lightType == LIGHT_SUN ) ? "1" : DELETE_VAL ); + + // get rid of all the projected light specific stuff + e->Set( "light_target", DELETE_VAL ); + e->Set( "light_up", DELETE_VAL ); + e->Set( "light_right", DELETE_VAL ); + e->Set( "light_start", DELETE_VAL ); + e->Set( "light_end", DELETE_VAL ); + } + else + { + e->SetVector( "light_target", lightTarget ); + e->SetVector( "light_up", lightUp ); + e->SetVector( "light_right", lightRight ); + if( explicitStartEnd ) + { + e->SetVector( "light_start", lightStart ); + e->SetVector( "light_end", lightEnd ); + } + else + { + e->Set( "light_start", DELETE_VAL ); + e->Set( "light_end", DELETE_VAL ); + } + + // get rid of the pointlight specific stuff + e->Set( "light_radius", DELETE_VAL ); + e->Set( "light_center", DELETE_VAL ); + e->Set( "parallel", DELETE_VAL ); + } + +#if 0 // DG: dhewm3 doesn't have this + // RB: Quake 1 light styles + if( lightStyle != -1 && lightType != LIGHT_SUN ) + { + e->SetInt( "style", lightStyle ); + } + else + { + e->Set( "style", DELETE_VAL ); + } +#endif +} + +LightInfo::LightInfo() +{ + Defaults(); +} + + +// ########### LightEditor ############# + +LightEditor& LightEditor::Instance() +{ + static LightEditor instance; + return instance; +} + + +// static +void LightEditor::ReInit( const idDict* dict, idEntity* light ) +{ + // TODO: if the lighteditor is currently shown, show a warning first about saving current changes to the last light? + Instance().Init( dict, light ); +} + +void LightEditor::Init( const idDict* dict, idEntity* light ) +{ + Reset(); + + if( textureNames.Num() == 0 ) + { + LoadLightTextures(); + } +#if 0 // dhewm3 doesn't have Quake lightstyles + if( styleNames.Num() == 0 ) + { + LoadLightStyles(); + } +#endif + + if( dict ) + { + original.FromDict( dict ); + cur.FromDict( dict ); + + gameEdit->EntityGetOrigin( light, entityPos ); + + const char* name = dict->GetString( "name", NULL ); + entityName = name ? name : gameEdit->GetUniqueEntityName( "light" ); + title = idStr::Format( "Light Editor: %s at (%s)###LightEditor", entityName.c_str(), entityPos.ToString() ); + + currentTextureIndex = 0; + currentTexture = NULL; + if( original.strTexture.Length() > 0 ) + { + const char* curTex = original.strTexture.c_str(); + for( int i = 0; i < textureNames.Num(); ++i ) + { + if( textureNames[i] == curTex ) + { + currentTextureIndex = i + 1; // remember, 0 is "" + LoadCurrentTexture(); + break; + } + } + } + +#if 0 // dhewm3 doesn't have this + // RB: light styles + if( original.lightStyle >= 0 ) + { + currentStyleIndex = original.lightStyle + 1; + } +#endif + } + + this->lightEntity = light; +} + +void LightEditor::Reset() +{ + title = "Light Editor: no Light selected!"; + entityPos.x = idMath::INFINITY; + entityPos.y = idMath::INFINITY; + entityPos.z = idMath::INFINITY; + + original.Defaults(); + cur.Defaults(); + + lightEntity = NULL; + currentTextureIndex = 0; + currentTexture = NULL; + //currentTextureMaterial = NULL; + //currentStyleIndex = 0; +} + +static idImage* GetLightEditorImage( const idMaterial* mat ) +{ + // this is similar to what the original light editor does; rbd3bfg iterates through all stages to find a non-null image + idImage* ret = (mat->GetNumStages() > 0) ? mat->GetStage(0)->texture.image : NULL; + return ret ? ret : mat->GetEditorImage(); +} + +void LightEditor::LoadLightTextures() +{ + textureNames.Clear(); + + int count = declManager->GetNumDecls( DECL_MATERIAL ); + + for( int i = 0; i < count; i++ ) + { + // just get the name of the light material + const idMaterial* mat = declManager->MaterialByIndex( i, false ); + + idStr matName = mat->GetName(); + matName.ToLower(); // FIXME: why? (this is from old doom3 code) + + if( matName.Icmpn( "lights/", strlen( "lights/" ) ) == 0 || matName.Icmpn( "fogs/", strlen( "fogs/" ) ) == 0 ) + { + // actually load the material + const idMaterial* material = declManager->FindMaterial( matName, false ); + if( material != NULL ) + { + // check if the material has textures or is just a leftover from the development + idImage* editorImage = GetLightEditorImage( mat ); + if( editorImage->texnum == idImage::TEXTURE_NOT_LOADED ) + { + //editorImage->DeferredLoadImage(); // this is BFG-specific + + // TODO: use this instead? otherwise defaulted isn't set + editorImage->ActuallyLoadImage( true, true ); + } + + if( !editorImage->defaulted ) + { + textureNames.Append( matName ); + } + } + } + } + + textureNames.Sort(); // FIXME: this is ugly, get a proper sort function? +} + +// static +bool LightEditor::TextureItemsGetter( void* data, int idx, const char** outText ) +{ + LightEditor* self = static_cast( data ); + if( idx == 0 ) + { + *outText = ""; + return true; + } + + // as index 0 has special purpose, the "real" index is one less + --idx; + + if( idx < 0 || idx >= self->textureNames.Num() ) + { + *outText = ""; + return false; + } + + *outText = self->textureNames[idx].c_str(); + + return true; +} + +void LightEditor::LoadCurrentTexture() +{ + currentTexture = NULL; + + if( currentTextureIndex > 0 && cur.strTexture.Length() > 0 ) + { + const idMaterial* mat = declManager->FindMaterial( cur.strTexture, false ); + if( mat != NULL ) + { + currentTexture = GetLightEditorImage( mat ); +#if 0 // DG: this is some hack in rbd3bfg, see their commit 329d822d3228f108 + if( currentTexture ) // FIXME: we don't actually use currentTextureMaterial here + { + // RB: create extra 2D material of the image for UI rendering + + // HACK that deserves being called a hack + idStr uiName( "lighteditor/" ); + uiName += currentTexture->imgName; + + currentTextureMaterial = declManager->FindMaterial( uiName, true ); + } +#endif + } + } +} + +#if 0 // dhewm3 doesn't have Quake lightstyles +void LightEditor::LoadLightStyles() +{ + styleNames.Clear(); + + const idDeclEntityDef* decl = static_cast( declManager->FindType( DECL_ENTITYDEF, "light", false ) ); + if( decl == NULL ) + { + return; + } + + int numStyles = decl->dict.GetInt( "num_styles", "0" ); + if( numStyles > 0 ) + { + for( int i = 0; i < numStyles; i++ ) + { + idStr style = decl->dict.GetString( va( "light_style%d", i ) ); + styleNames.Append( style ); + } + } + else + { + // RB: it's not defined in entityDef light so use predefined Quake 1 table + for( int i = 0; i < 12; i++ ) + { + idStr style( predef_lightstylesinfo[ i ] ); + styleNames.Append( style ); + } + } +} +#endif + +#if 0 // dhewm3 doesn't have Quake lightstyles +// static +bool LightEditor::StyleItemsGetter( void* data, int idx, const char** outText ) +{ + LightEditor* self = static_cast( data ); + if( idx == 0 ) + { + *outText = ""; + return true; + } + + // as index 0 has special purpose, the "real" index is one less + --idx; + + if( idx < 0 || idx >= self->styleNames.Num() ) + { + *outText = ""; + return false; + } + + *outText = self->styleNames[idx].c_str(); + + return true; +} +#endif + +void LightEditor::TempApplyChanges() +{ + if( lightEntity != NULL ) + { + idDict d; + cur.ToDict( &d ); + + gameEdit->EntityChangeSpawnArgs( lightEntity, &d ); + gameEdit->EntityUpdateChangeableSpawnArgs( lightEntity, NULL ); + } +} + +void LightEditor::SaveChanges() +{ + idDict d; + cur.ToDict( &d ); + if( entityName[0] != '\0' ) + { + gameEdit->MapCopyDictToEntity( entityName, &d ); + } + else if( entityPos.x != idMath::INFINITY ) + { + assert(0 && "unimplemented"); +#if 0 // DG: FIXME what is this about?! + entityName = gameEdit->GetUniqueEntityName( "light" ); + d.Set( "name", entityName ); + + // RB: this is really HACKY + gameEdit->MapCopyDictToEntityAtOrigin( entityPos, &d ); +#endif + } + + gameEdit->MapSave(); +} + +void LightEditor::CancelChanges() +{ + if( lightEntity != NULL ) + { + idDict d; + original.ToDict( &d ); + + gameEdit->EntityChangeSpawnArgs( lightEntity, &d ); + gameEdit->EntityUpdateChangeableSpawnArgs( lightEntity, NULL ); + } +} + +// a kinda ugly hack to get a float* (as used by imgui) from idVec3 +static float* vecToArr( idVec3& v ) +{ + return &v.x; +} + +void LightEditor::Draw() +{ + bool showTool = isShown; + if( ImGui::Begin( title, &showTool ) ) //, ImGuiWindowFlags_ShowBorders ) ) + { + bool changes = false; + + // RB: handle arrow key inputs like in TrenchBroom +#if 0 // FIXME: port this to whatever ImGui does now + ImGuiIO& io = ImGui::GetIO(); + + if( io.KeysDown[K_ESCAPE] ) + { + CancelChanges(); + showTool = false; + } + + // TODO use view direction like just global values + if( io.KeysDown[K_LALT] ) + { + if( io.KeysDown[K_UPARROW] ) + { + cur.origin.z += 1; + changes = true; + } + else if( io.KeysDown[K_DOWNARROW] ) + { + cur.origin.z -= 1; + changes = true; + } + } + else if( io.KeysDown[K_RIGHTARROW] ) + { + cur.origin.x += 1; + changes = true; + } + else if( io.KeysDown[K_LEFTARROW] ) + { + cur.origin.x -= 1; + changes = true; + } + else if( io.KeysDown[K_UPARROW] ) + { + cur.origin.y += 1; + changes = true; + } + else if( io.KeysDown[K_DOWNARROW] ) + { + cur.origin.y -= 1; + changes = true; + } +#endif + + ImGui::SeparatorText( "Light Volume" ); + + ImGui::Spacing(); + + int lightSelectionRadioBtn = cur.lightType; + + changes |= ImGui::RadioButton( "Point Light", &lightSelectionRadioBtn, 0 ); + ImGui::SameLine(); + changes |= ImGui::RadioButton( "Spot Light", &lightSelectionRadioBtn, 1 ); + ImGui::SetItemTooltip( "aka Projected Light" ); + ImGui::SameLine(); + changes |= ImGui::RadioButton( "Sun Light", &lightSelectionRadioBtn, 2 ); + ImGui::SetItemTooltip( "Parallel Point Light" ); + + ImGui::Indent(); + + ImGui::Spacing(); + + if( lightSelectionRadioBtn == LIGHT_POINT || lightSelectionRadioBtn == LIGHT_SUN ) + { + if( lightSelectionRadioBtn == LIGHT_POINT && lightSelectionRadioBtn != cur.lightType ) + { + cur.DefaultPoint(); + currentTextureIndex = 0; // cur.DefaultPoint() resets cur.strTexture to "", so sync this here + currentTexture = NULL; + changes = true; + } + else if( lightSelectionRadioBtn == LIGHT_SUN && lightSelectionRadioBtn != cur.lightType ) + { + cur.DefaultSun(); + currentTextureIndex = 0; // cur.DefaultPoint() resets cur.strTexture to "", so sync this here + currentTexture = NULL; + changes = true; + } + + ImGui::PushItemWidth( -1.0f ); // align end of Drag* with right window border + + changes |= ImGui::Checkbox( "Equilateral Radius", &cur.equalRadius ); + ImGui::Text( "Radius:" ); + ImGui::Indent(); + if( cur.equalRadius ) + { + if( ImGui::DragFloat( "##radEquil", &cur.lightRadius.x, 1.0f, 0.0f, 10000.0f, "%.1f" ) ) + { + cur.lightRadius.z = cur.lightRadius.y = cur.lightRadius.x; + changes = true; + } + } + else + { + changes |= ImGui::DragVec3( "##radXYZ", cur.lightRadius ); + } + ImGui::Unindent(); + + ImGui::Spacing(); + + //changes |= ImGui::Checkbox( "Parallel", &cur.isParallel ); // now handled as "sun" + + //ImGui::Spacing(); + + changes |= ImGui::Checkbox( "Center", &cur.hasCenter ); + if( cur.hasCenter ) + { + ImGui::Indent(); + changes |= ImGui::DragVec3( "##centerXYZ", cur.lightCenter, 1.0f, 0.0f, 10000.0f, "%.1f" ); + ImGui::Unindent(); + } + ImGui::PopItemWidth(); // back to default alignment on right side + } + else if( lightSelectionRadioBtn == LIGHT_SPOT ) + { + if( cur.lightType != lightSelectionRadioBtn ) + { + cur.DefaultProjected(); + currentTextureIndex = 0; // cur.DefaultPoint() resets cur.strTexture to "", so sync this here + currentTexture = NULL; + changes = true; + } + + changes |= ImGui::DragVec3( "Target", cur.lightTarget, 1.0f, 0.0f, 0.0f, "%.1f" ); + changes |= ImGui::DragVec3( "Right", cur.lightRight, 1.0f, 0.0f, 0.0f, "%.1f" ); + changes |= ImGui::DragVec3( "Up", cur.lightUp, 1.0f, 0.0f, 0.0f, "%.1f" ); + + ImGui::Spacing(); + + changes |= ImGui::Checkbox( "Explicit start/end points", &cur.explicitStartEnd ); + + ImGui::Spacing(); + if( cur.explicitStartEnd ) + { + changes |= ImGui::DragVec3( "Start", cur.lightStart, 1.0f, 0.0f, 0.0f, "%.1f" ); + changes |= ImGui::DragVec3( "End", cur.lightEnd, 1.0f, 0.0f, 0.0f, "%.1f" ); + } + } + + cur.lightType = ELightType( lightSelectionRadioBtn ); + + ImGui::Unindent(); + + ImGui::SeparatorText( "Color & Texturing" ); + + changes |= ImGui::ColorEdit3( "Color", vecToArr( cur.color ) ); + + ImGui::Spacing(); + + if( ImGui::Combo( "Texture", ¤tTextureIndex, TextureItemsGetter, this, textureNames.Num() + 1 ) ) + { + changes = true; + + // -1 because 0 is "" + cur.strTexture = ( currentTextureIndex > 0 ) ? textureNames[currentTextureIndex - 1] : ""; + LoadCurrentTexture(); + } + + if( /*currentTextureMaterial != nullptr &&*/ currentTexture != nullptr ) + { + ImVec2 size( currentTexture->uploadWidth, currentTexture->uploadHeight ); + + //ImGui::Image( ( void* )currentTextureMaterial, size, ImVec2( 0, 0 ), ImVec2( 1, 1 ), + ImGui::Image( currentTexture->texnum, size, ImVec2( 0, 0 ), ImVec2( 1, 1 ), + ImColor( 255, 255, 255, 255 ), ImColor( 255, 255, 255, 128 ) ); + } + +#if 0 // dhewm3 has no Quake lightstyles + ImGui::SeparatorText( "Flicker Style" ); + + if( ImGui::Combo( "Style", ¤tStyleIndex, StyleItemsGetter, this, styleNames.Num() + 1 ) ) + { + changes = true; + + // -1 because 0 is "" + cur.lightStyle = ( currentStyleIndex > 0 ) ? currentStyleIndex - 1 : -1; + } +#endif + + ImGui::SeparatorText( "Misc Options" ); + + changes |= ImGui::Checkbox( "Cast Shadows", &cur.castShadows ); + changes |= ImGui::Checkbox( "Skip Specular", &cur.skipSpecular ); + + // TODO: allow multiple lights selected at the same time + "apply different" button? + // then only the changed attribute (e.g. color) would be set to all lights, + // but they'd keep their other individual properties (eg radius) + + ImGui::Spacing(); + + if( ImGui::Button( "Save to .map" ) ) + { + SaveChanges(); + showTool = false; + } + else if( ImGui::SameLine(), ImGui::Button( "Cancel" ) ) + { + CancelChanges(); + showTool = false; + } + else if( changes ) + { + TempApplyChanges(); + } + } + ImGui::End(); + + if( isShown && !showTool ) + { + isShown = showTool; + impl::SetReleaseToolMouse( false ); + D3::ImGuiHooks::CloseWindow( D3::ImGuiHooks::D3_ImGuiWin_LightEditor ); + } +} + +} //namespace ImGuiTools diff --git a/neo/tools/imgui/lighteditor/LightEditor.h b/neo/tools/imgui/lighteditor/LightEditor.h new file mode 100644 index 000000000..bc6da93f9 --- /dev/null +++ b/neo/tools/imgui/lighteditor/LightEditor.h @@ -0,0 +1,153 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. +Copyright (C) 2015 Daniel Gibson +Copyright (C) 2016-2023 Robert Beckebans + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +// a GUI light editor, based loosely on the one from original Doom3 (neo/tools/radiant/LightDlg.*) +// LightInfo was CLightInfo, the LightEditor itself was written from scratch. + +#ifndef NEO_TOOLS_EDITORS_LIGHTEDITOR_H_ +#define NEO_TOOLS_EDITORS_LIGHTEDITOR_H_ + +#include "idlib/Dict.h" +#include "../../edit_public.h" + +class idImage; +class idMaterial; +class idEntity; + +namespace ImGuiTools +{ + +enum ELightType +{ + LIGHT_POINT, + LIGHT_SPOT, + LIGHT_SUN +}; + +class LightInfo +{ +public: + ELightType lightType; + + idStr strTexture; + bool equalRadius; + bool explicitStartEnd; + idVec3 lightStart; + idVec3 lightEnd; + idVec3 lightUp; + idVec3 lightRight; + idVec3 lightTarget; + idVec3 lightCenter; + idVec3 color; + idVec3 origin; + + idVec3 lightRadius; + bool castShadows; + bool skipSpecular; + bool hasCenter; + //int lightStyle; DG: dhewm3 doesn't have Quake lightstyles + + LightInfo(); + + void Defaults(); + void DefaultPoint(); + void DefaultProjected(); + void DefaultSun(); + void FromDict( const idDict* e ); + void ToDict( idDict* e ); +}; + +class LightEditor +{ +private: + bool isShown; + + idStr title; + idStr entityName; + idVec3 entityPos; + + LightInfo original; + LightInfo cur; // current status of the light + + idEntity* lightEntity; + + idList textureNames; + int currentTextureIndex; + idImage* currentTexture; + //const idMaterial* currentTextureMaterial; // DG: that was rbd3bfg-specific + +#if 0 // DG: dhewm3 doesn't have Quake lightstyles + // RB: light style support + idList styleNames; + int currentStyleIndex; + + void LoadLightStyles(); + static bool StyleItemsGetter( void* data, int idx, const char** out_text ); +#endif + + void Init( const idDict* dict, idEntity* light ); + void Reset(); + + void LoadLightTextures(); + static bool TextureItemsGetter( void* data, int idx, const char** out_text ); + void LoadCurrentTexture(); + + void TempApplyChanges(); + void SaveChanges(); + void CancelChanges(); + + LightEditor() + { + isShown = false; + + Reset(); + } + +public: + + static LightEditor& Instance(); + static void ReInit( const idDict* dict, idEntity* light ); + + inline void ShowIt( bool show ) + { + isShown = show; + } + + inline bool IsShown() const + { + return isShown; + } + + void Draw(); +}; + +} //namespace ImGuiTools + +#endif diff --git a/neo/tools/imgui/materialeditor/ConsoleView.cpp b/neo/tools/imgui/materialeditor/ConsoleView.cpp new file mode 100644 index 000000000..e2932e6a9 --- /dev/null +++ b/neo/tools/imgui/materialeditor/ConsoleView.cpp @@ -0,0 +1,290 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#include "../../libs/ImGuiColorTextEdit/TextEditor.h" + +#include "../util/ImGui_IdWidgets.h" + +#include "ConsoleView.h" + +#define EDIT_HEIGHT 25 + +namespace ImGuiTools { + +/** +* Constructor for ConsoleView. +*/ +ConsoleView::ConsoleView() + : editConsole(NULL) + , limitConsoleLines(100) + , editInputLastKeyDownTime(0) +{ +} + +/** +* Destructor for ConsoleView. +*/ +ConsoleView::~ConsoleView() { +} + +void ConsoleView::Init() { + editConsole = new TextEditor(); + editConsole->SetReadOnly( true ); + editConsole->SetShowLineNumber( false ); + editConsole->SetShowCurrentLine( false ); + editConsole->SetColorizerEnable( false ); + editConsole->SetShowWhitespaces( false ); + + limitConsoleLines = 100; +} + +/** +* Adds text to the end of the console output window. +* @param msg The text to append. +* \todo: BMatt Nerve: Fix scroll code so the output window will scroll as text +* is added if the cursor is at the end of the window. +*/ +void ConsoleView::AddText( const char *msg ) { + + idStr work; + + work = msg; + work.RemoveColors(); + + auto lines = editConsole->GetTextLines(); + + if ( lines.size() > limitConsoleLines ) { + int numDelete = lines.size() - limitConsoleLines; + lines.erase( lines.begin(), lines.begin() + numDelete ); + } + for ( int i = 0; i < work.Length(); ) { + int j = work.Find('\n', i); + if (j == -1) { + break; + } + idStr line; + work.Mid(i, j - i, line); + + lines.push_back( std::string( line.c_str() ) ); + i = j + 1; + } + + TextEditor::Coordinates sel = TextEditor::Coordinates( (int)lines.size() - 1, 0 ); + editConsole->SetTextLines( lines ); + editConsole->SetCursorPosition( sel ); +} + +/** +* Replaces the text in the console window with the specified text. +* @param text The text to place in the console window. +*/ +void ConsoleView::SetConsoleText ( const idStr &text ) { + editInput = text; +} + +/** +* Executes the specified console command. If the command is passed +* as a parameter then it is executed otherwise the command in the +* input box is executed. +* @param cmd The text to execute. If this string is empty then the +* input edit box text is used. +*/ +void ConsoleView::ExecuteCommand ( const idStr &cmd ) { + + idStr str; + if ( cmd.Length() > 0 ) { + str = cmd; + } else { + str = editInput; + } + + if ( str != "" ) { + editInput.Clear(); + common->Printf( "%s\n", str.c_str() ); + + // avoid adding multiple identical commands in a row + int index = consoleHistory.Num(); + + if ( index == 0 || str != consoleHistory[index-1]) { + // keep the history to 16 commands, removing the oldest command + if ( consoleHistory.Num () > 16 ) { + consoleHistory.RemoveIndex ( 0 ); + } + currentHistoryPosition = consoleHistory.Append( str ); + } else { + currentHistoryPosition = consoleHistory.Num() - 1; + } + + currentCommand.Clear (); + + bool propogateCommand = true; + + // process some of our own special commands + if ( str.Icmp( "clear" ) == 0 ) { + editConsole->SetText( std::string( "" ) ); + } + else if ( str.Icmp ( "edit" ) == 0) { + propogateCommand = false; + } + if ( propogateCommand ) { + cmdSystem->BufferCommandText( CMD_EXEC_NOW, str ); + } + } +} + +void ConsoleView::Draw( const ImVec2 &pos, const ImVec2 &size ) { + editConsole->Render( "###Lines", ImVec2( size.x, size.y-EDIT_HEIGHT ) ); + if ( ImGui::InputTextStr( "###Input", &editInput, ImGuiInputTextFlags_ElideLeft ) ) { + } + ImGui::SetItemDefaultFocus(); + ImGui::SetItemKeyOwner( ImGuiKey_Enter ); + ImGui::SetItemKeyOwner( ImGuiKey_UpArrow ); + ImGui::SetItemKeyOwner( ImGuiKey_DownArrow ); + ImGui::SetItemKeyOwner( ImGuiKey_Tab ); + ImGui::SetItemKeyOwner( ImGuiKey_PageUp ); + ImGui::SetItemKeyOwner( ImGuiKey_PageDown ); + + // keep auto focus on the input box + if (ImGui::IsItemHovered() || (ImGui::IsWindowFocused() && !ImGui::IsAnyItemActive() && !ImGui::IsMouseClicked(0))) + ImGui::SetKeyboardFocusHere(-1); + + PreTranslateMessage(); + if ( selectEditInput ) { + selectEditInput = false; + } + if ( ImGui::IsWindowFocused() ) { + MaterialEditorSetActiveWindow( ME_WINDOW_CONSOLE ); + } +} + +/** +* Handles keyboard input to process the "Enter" key to execute +* commands and command history. +*/ +bool ConsoleView::PreTranslateMessage() { + int timeEnd = Sys_Milliseconds(); + int elapsed = timeEnd - editInputLastKeyDownTime; + int keydownTime = 200; + + if ( ImGui::IsKeyDown( ImGuiKey_Enter ) ) { + if ( elapsed > keydownTime ) { + this->ExecuteCommand(); + editInputLastKeyDownTime = timeEnd; + } + return true; + } + + if ( ImGui::IsKeyDown( ImGuiKey_UpArrow ) ) { + if ( elapsed > keydownTime ) { + // save off the current in-progress command so we can get back to it + if ( saveCurrentCommand == true ) { + currentCommand = editInput; + saveCurrentCommand = false; + } + + if ( consoleHistory.Num () > 0 ) { + editInput = consoleHistory[currentHistoryPosition]; + + selectEditInput = true; + } + + if ( currentHistoryPosition > 0 ) { + --currentHistoryPosition; + } + editInputLastKeyDownTime = timeEnd; + } + return true; + } + + if ( ImGui::IsKeyDown( ImGuiKey_DownArrow ) ) { + if ( elapsed > keydownTime ) { + int selLocation = 0; + if ( currentHistoryPosition < consoleHistory.Num () - 1 ) { + ++currentHistoryPosition; + editInput = consoleHistory[currentHistoryPosition]; + selLocation = consoleHistory[currentHistoryPosition].Length(); + } + else { + editInput = currentCommand; + selLocation = currentCommand.Length(); + currentCommand.Clear (); + saveCurrentCommand = true; + } + + selectEditInput = true; + editInputLastKeyDownTime = timeEnd; + } + return true; + } + if ( ImGui::IsKeyDown( ImGuiKey_Tab ) ) { + if ( elapsed > keydownTime ) { + common->Printf ( "Command History\n----------------\n" ); + for ( int i = 0 ; i < consoleHistory.Num ();i++ ) + { + common->Printf ( "[cmd %d]: %s\n" , i , consoleHistory[i].c_str() ); + } + common->Printf ( "----------------\n" ); + editInputLastKeyDownTime = timeEnd; + } + return true; + } + if ( ImGui::IsKeyDown( ImGuiKey_PageDown ) ) { + if ( elapsed > keydownTime ) { + editConsole->MoveDown( 10 ); + editInputLastKeyDownTime = timeEnd; + } + return true; + } + + if ( ImGui::IsKeyDown( ImGuiKey_PageUp ) ) { + if ( elapsed > keydownTime ) { + editConsole->MoveUp( 10 ); + editInputLastKeyDownTime = timeEnd; + } + return true; + } + + if ( ImGui::IsKeyDown( ImGuiKey_Home ) ) { + if ( elapsed > keydownTime ) { + editConsole->MoveTop(); + editInputLastKeyDownTime = timeEnd; + } + return true; + } + + if ( ImGui::IsKeyDown( ImGuiKey_End ) ) { + if ( elapsed > keydownTime ) { + editConsole->MoveBottom(); + editInputLastKeyDownTime = timeEnd; + } + return true; + } + + return false; +} + +} diff --git a/neo/tools/imgui/materialeditor/ConsoleView.h b/neo/tools/imgui/materialeditor/ConsoleView.h new file mode 100644 index 000000000..569b8ecfc --- /dev/null +++ b/neo/tools/imgui/materialeditor/ConsoleView.h @@ -0,0 +1,83 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef CONSOLEVIEW_H_ +#define CONSOLEVIEW_H_ + +#include "../util/ImGui_IdWidgets.h" +#include "sys/sys_imgui.h" + +#include "MaterialEditor.h" +#include "../util/SyntaxRichEditCtrl.h" + +class TextEditor; + +namespace ImGuiTools { + +/** +* View in the Material Editor that functions as a Doom III +* console. It allows users to view console output as well as issue +* console commands to the engine. +*/ +class ConsoleView +{ +public: + + TextEditor * editConsole; + idStr editInput; + + idStr consoleStr; + idStrList consoleHistory; + idStr currentCommand; + int currentHistoryPosition; + bool saveCurrentCommand; + +public: + ConsoleView(); + ~ConsoleView(); + + void Init(); + + // Public Operations + void AddText( const char* msg ); + void SetConsoleText( const idStr &text ); + void ExecuteCommand( const idStr &cmd = "" ); + + void Draw( const ImVec2 &pos, const ImVec2 &size ); + +private: + int limitConsoleLines; + bool selectEditInput; + int editInputLastKeyDownTime; + +private: + bool PreTranslateMessage(); +}; + +} + +#endif /* !CONSOLEVIEW_H_ */ diff --git a/neo/tools/imgui/materialeditor/FindDialog.cpp b/neo/tools/imgui/materialeditor/FindDialog.cpp new file mode 100644 index 000000000..6327ee8ab --- /dev/null +++ b/neo/tools/imgui/materialeditor/FindDialog.cpp @@ -0,0 +1,181 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../util/ImGui_IdWidgets.h" + +#include "FindDialog.h" + +#include "MEMainFrame.h" + +namespace ImGuiTools { + +/** +* Constructor for FindDialog. +*/ +FindDialog::FindDialog(MEMainFrame *pParent) + : visible(false) + , focus(false) + , message(0) +{ + registry.Init("MaterialEditor_Find"); + parent = pParent; +} + +/** +* Destructor for FindDialog. +*/ +FindDialog::~FindDialog() { +} + +/** +* Creates and instance of the find dialog. +*/ +void FindDialog::Start() { + visible = true; + focus = true; + message = 0; + LoadFindSettings(); +} + +bool FindDialog::Draw( const ImVec2 &pos, const ImVec2 &size ) { + bool accepted = false; + + if (!visible) { + return accepted; + } + + ImVec2 oldCursorPos = ImGui::GetCursorPos(); + + ImGui::SetCursorPos( pos ); + // FIXME: find a better color + ImGui::PushStyleColor(ImGuiCol_FrameBg, IM_COL32(50, 50, 50, 255)); + bool ret = ImGui::BeginChild( "Find", size, ImGuiChildFlags_Borders | ImGuiChildFlags_FrameStyle ); + if ( ret ) { + + if ( ImGui::InputTextStr( "Find What", &searchData.searchText ) ) { + message = 0; + } + if ( focus ) { + ImGui::SetKeyboardFocusHere( -1 ); + focus = false; + } + + bool nameOnly = searchData.nameOnly; + if ( ImGui::Checkbox( "Name Only", &nameOnly ) ) { + searchData.nameOnly = nameOnly; + message = 0; + } + + ImGui::Text( "Look In:" ); + if ( ImGui::RadioButton( "Current Material", &searchData.searchScope, 0 ) ) { + message = 0; + } + ImGui::SameLine(); + if ( ImGui::RadioButton( "Open Materials", &searchData.searchScope, 1 ) ) { + message = 0; + } + ImGui::SameLine(); + if ( ImGui::RadioButton( "All Materials", &searchData.searchScope, 2 ) ) { + message = 0; + } + + if ( ImGui::Button( "Find Next" ) ) { + OnBnClickedFindNext(); + } + ImGui::SameLine(); + if ( ImGui::Button( "Cancel" ) ) { + OnCancel(); + visible = false; + message = 0; + accepted = true; + } + + if ( message ) { + ImGui::TextColored( ImVec4( 1, 0, 0, 1 ), "Unable to find '%s'", searchData.searchText.c_str() ); + } + } + ImGui::EndChild(); + ImGui::PopStyleColor(); + + ImGui::SetCursorPos( oldCursorPos ); + + return accepted; +} + +void FindDialog::UnableToFind() { + message = 1; +} + +/** +* Triggers a search based on the parameters in the dialog. +*/ +void FindDialog::OnBnClickedFindNext() { + + searchData.searched = false; + parent->FindNext(&searchData); +} + +/** +* Saves the search parameters and closes the find dialog. +*/ +void FindDialog::OnCancel() +{ + SaveFindSettings(); + + parent->CloseFind(); +} + +/** +* Loads the search parameters from the registry and makes sure the controls are properly +* initialized. +*/ +void FindDialog::LoadFindSettings() { + registry.Load(); + + searchData.searchText = registry.GetString("searchText"); + searchData.nameOnly = (int)registry.GetFloat("nameOnly"); + searchData.searchScope = (int)registry.GetFloat("searchScope"); + + //registry.GetWindowPlacement("findDialog", GetSafeHwnd()); +} + +/** +* Saves the search parameters to the registry. +*/ +void FindDialog::SaveFindSettings() { + + registry.SetString("searchText", searchData.searchText); + registry.SetFloat("nameOnly", searchData.nameOnly); + registry.SetFloat("searchScope", searchData.searchScope); + + //registry.SetWindowPlacement("findDialog", GetSafeHwnd()); + + registry.Save(); +} + +} // namespace ImGuiTools diff --git a/neo/tools/imgui/materialeditor/FindDialog.h b/neo/tools/imgui/materialeditor/FindDialog.h new file mode 100644 index 000000000..90d598d96 --- /dev/null +++ b/neo/tools/imgui/materialeditor/FindDialog.h @@ -0,0 +1,79 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef FINDDIALOG_H_ +#define FINDDIALOG_H_ + +#include "sys/sys_imgui.h" +#include "MaterialEditor.h" +#include "../util/RegistryOptions.h" + +namespace ImGuiTools { + +class MEMainFrame; + +/** +* Dialog that provides an input box and several checkboxes to define +* the parameters of a search. These parameters include: text string, search +* scope and search only name flag. +*/ +class FindDialog +{ + +public: + FindDialog(MEMainFrame *pParent); + virtual ~FindDialog(); + + void Start(); + + bool Draw( const ImVec2 &pos, const ImVec2 &size ); + + void UnableToFind(); + +protected: + + //Messages + void OnBnClickedFindNext(); + void OnCancel(); + + //Protected Operations + void LoadFindSettings(); + void SaveFindSettings(); + +protected: + bool visible; + bool focus; + int message; + MEMainFrame* parent; + MaterialSearchData_t searchData; + rvRegistryOptions registry; +}; + +} + +#endif /* !FINDDIALOG_H_ */ diff --git a/neo/tools/imgui/materialeditor/MEMainFrame.cpp b/neo/tools/imgui/materialeditor/MEMainFrame.cpp new file mode 100644 index 000000000..ee2985a25 --- /dev/null +++ b/neo/tools/imgui/materialeditor/MEMainFrame.cpp @@ -0,0 +1,907 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#include "sys/sys_imgui.h" + +#include "MaterialEditor.h" +#include "MEMainFrame.h" +#include "MaterialDef.h" + +#include "framework/CmdSystem.h" + +#define TAB_CONTROL 0x1006 + +/* +BEGIN_MESSAGE_MAP(MEMainFrame, CFrameWnd) + ON_WM_CREATE() + ON_WM_SETFOCUS() + ON_WM_DESTROY() + ON_WM_SIZE() + + ON_NOTIFY(TCN_SELCHANGE, TAB_CONTROL, OnTcnSelChange) + + ON_COMMAND(ID_ME_FILE_EXIT, OnFileExit) + ON_COMMAND(ID_ME_FILE_SAVEMATERIAL, OnFileSaveMaterial) + ON_COMMAND(ID_ME_FILE_SAVEFILE, OnFileSaveFile) + ON_COMMAND(ID_ME_FILE_SAVE, OnFileSaveAll) + ON_UPDATE_COMMAND_UI(ID_ME_FILE_SAVEMATERIAL, OnFileSaveMaterialUpdate ) + ON_UPDATE_COMMAND_UI(ID_ME_FILE_SAVEFILE, OnFileSaveFileUpdate ) + ON_UPDATE_COMMAND_UI(ID_ME_FILE_SAVE, OnFileSaveAllUpdate ) + + ON_COMMAND(ID_ME_PREVIEW_APPLYCHANGES, OnApplyMaterial) + ON_COMMAND(ID_ME_PREVIEW_APPLYFILE, OnApplyFile) + ON_COMMAND(ID_ME_PREVIEW_APPLYALL, OnApplyAll) + ON_UPDATE_COMMAND_UI(ID_ME_PREVIEW_APPLYCHANGES, OnApplyMaterialUpdate ) + ON_UPDATE_COMMAND_UI(ID_ME_PREVIEW_APPLYFILE, OnApplyFileUpdate ) + ON_UPDATE_COMMAND_UI(ID_ME_PREVIEW_APPLYALL, OnApplyAllUpdate ) + + ON_COMMAND(ID_ME_EDIT_CUT, OnEditCut) + ON_COMMAND(ID_ME_EDIT_COPY, OnEditCopy) + ON_COMMAND(ID_ME_EDIT_PASTE, OnEditPaste) + ON_COMMAND(ID_ME_EDIT_DELETE, OnEditDelete) + ON_COMMAND(ID_ME_EDIT_RENAME, OnEditRename) + ON_UPDATE_COMMAND_UI(ID_ME_EDIT_CUT, OnEditCutUpdate) + ON_UPDATE_COMMAND_UI(ID_ME_EDIT_COPY, OnEditCopyUpdate) + ON_UPDATE_COMMAND_UI(ID_ME_EDIT_PASTE, OnEditPasteUpdate) + ON_UPDATE_COMMAND_UI(ID_ME_EDIT_DELETE, OnEditDeleteUpdate) + ON_UPDATE_COMMAND_UI(ID_ME_EDIT_RENAME, OnEditRenameUpdate) + + ON_COMMAND(ID_ME_EDIT_FIND, OnEditFind) + ON_COMMAND(ID_ME_EDIT_FIND_NEXT, OnEditFindNext) + + ON_COMMAND(ID_ME_EDIT_UNDO, OnEditUndo) + ON_COMMAND(ID_ME_EDIT_REDO, OnEditRedo) + ON_UPDATE_COMMAND_UI(ID_ME_EDIT_UNDO, OnEditUndoUpdate ) + ON_UPDATE_COMMAND_UI(ID_ME_EDIT_REDO, OnEditRedoUpdate ) + + ON_COMMAND(ID_VIEW_INCLUDEFILENAME, OnViewIncludeFile) + ON_COMMAND(ID_PREVIEW_RELOADARBPROGRAMS, OnReloadArbPrograms) + ON_COMMAND(ID_PREVIEW_RELOADIMAGES, OnReloadImages ) +END_MESSAGE_MAP() + +static UINT indicators[] = +{ + ID_SEPARATOR, // status line indicator + ID_INDICATOR_CAPS, + ID_INDICATOR_NUM, + ID_INDICATOR_SCRL, +};*/ + +namespace ImGuiTools { + +/** +* Constructor for MEMainFrame. Initialize some member data and load the options. +*/ +MEMainFrame::MEMainFrame() { + + currentDoc = NULL; + m_find = NULL; + + searchData.searched = false; + + activeWindow = ME_WINDOW_NONE; + + options.Load(); +} + +/** +* Destructor for MEMainFrame. +*/ +MEMainFrame::~MEMainFrame() { +} + +/** +* Called to add console text to the console view. +* @param msg The text that is to be added to the console. +*/ +void MEMainFrame::PrintConsoleMessage(const char *msg) { + m_consoleView->AddText(msg); +} + +/** +* Sets a few window styles for the main window during the creation process. +*/ +bool MEMainFrame::PreCreateWindow() { + return true; +} + +/** +* Called by the MFC framework to allow the window to create any client windows. This method +* creates all of the spliter windows and registers all of the views with the document manager. +*/ +void MEMainFrame::OnCreateClient() { + includeFileInMaterialList = true; + + /* + CCreateContext consoleContext; + consoleContext.m_pNewViewClass = RUNTIME_CLASS(ConsoleView); + */ + m_consoleView = new ConsoleView(); + m_consoleView->Init(); + //m_consoleView->ShowWindow(SW_HIDE); + /* + m_tabs.Create(TCS_BOTTOM | TCS_FLATBUTTONS | WS_CHILD | WS_VISIBLE, CRect(0, 0, 0, 0), this, TAB_CONTROL); + m_tabs.InsertItem(0, "Editor"); + m_tabs.InsertItem(1, "Console"); + m_tabs.SetFont(materialEditorFont); + + m_splitterWnd.CreateStatic(this, 2, 1); + + + m_editSplitter.CreateStatic(&m_splitterWnd, 1, 2, WS_CHILD | WS_VISIBLE | WS_BORDER, m_splitterWnd.IdFromRowCol(0, 0)); + + if(!m_editSplitter.CreateView(0, 0, RUNTIME_CLASS(MaterialTreeView), CSize(300, 200), pContext)) { + TRACE0("Failed to create material list pane\n"); + return FALSE; + } + + if(!m_editSplitter.CreateView(0, 1, RUNTIME_CLASS(MaterialEditView), CSize(200, 200), pContext)) { + TRACE0("Failed to create stage property pane\n"); + return FALSE; + } + + + m_previewSplitter.CreateStatic(&m_splitterWnd, 1, 2, WS_CHILD | WS_VISIBLE | WS_BORDER, m_splitterWnd.IdFromRowCol(1, 0)); + + if(!m_previewSplitter.CreateView(0, 0, RUNTIME_CLASS(MaterialPreviewPropView), CSize(300, 200), pContext)) { + TRACE0("Failed to create preview property pane\n"); + return FALSE; + } + + if(!m_previewSplitter.CreateView(0, 1, RUNTIME_CLASS(MaterialPreviewView), CSize(100, 200), pContext)) { + TRACE0("Failed to create preview pane\n"); + return FALSE; + } + */ + //Get references to all of the views + m_materialTreeView = new MaterialTreeView(); //(MaterialTreeView*)m_editSplitter.GetPane(0, 0); + m_materialTreeView->OnCreate(); + m_previewPropertyView = new MaterialPreviewPropView();// (MaterialPreviewPropView*)m_previewSplitter.GetPane(0, 0); + m_materialPreviewView = new MaterialPreviewView(); //(MaterialPreviewView*)m_previewSplitter.GetPane(0, 1); + + m_materialEditView = new MaterialEditView(); // (MaterialEditView*)m_editSplitter.GetPane(0, 1); + m_materialEditView->OnCreate(); + m_stageView = m_materialEditView->m_stageView; + m_materialPropertyView = m_materialEditView->m_materialPropertyView; + //m_materialEditSplitter = &m_materialEditView->m_editSplitter; + + //Load the splitter positions from the registry + + editSplitterPos = 300; + editSplitterWidth = 500 + m_materialEditView->m_editSplitterWidth; + editSplitterHeight = 200; + previewSplitterPos = 300; + previewSplitterPos = 500; + previewSplitterWidth = 500; + previewSplitterHeight = 500; + + int val = options.GetMaterialEditHeight(); + if(val <= 0) + val = 300; + //m_splitterWnd.SetRowInfo(0, val, 0); + editSplitterHeight = val; + + val = options.GetMaterialTreeWidth(); + if(val <= 0) + val = 300; + editSplitterPos = val; + + val = options.GetStageWidth(); + if(val <= 0) + val = 200; + m_materialEditView->m_editSplitterPos = val; + + val = options.GetPreviewPropertiesWidth(); + if(val <= 0) + val = 300; + previewSplitterPos = val; + + //Register the views with the document manager + materialDocManager.RegisterMaterialView(this); + materialDocManager.RegisterMaterialView(m_materialTreeView); + materialDocManager.RegisterMaterialView(m_stageView); + materialDocManager.RegisterMaterialView(m_materialPropertyView); + materialDocManager.RegisterMaterialView(m_materialPreviewView); + materialDocManager.RegisterMaterialView(m_materialEditView); + + //Let the stage window know about the prop window + m_stageView->SetMaterialPropertyView(m_materialPropertyView); + + //Let the preview props now about the preview window + m_previewPropertyView->RegisterPreviewView(m_materialPreviewView); + m_previewPropertyView->InitializePropTree(); + //m_previewPropertyView->GetPropertyTreeCtrl().SetColumn(120); + + MaterialDefManager::InitializeMaterialDefLists(); + + //Some prop tree initialization + + val = options.GetMaterialPropHeadingWidth(); + if(val <= 0) + val = 200; + //m_materialPropertyView->GetPropertyTreeCtrl().SetColumn(val); + m_materialPropertyView->LoadSettings(); + + val = options.GetPreviewPropHeadingWidth(); + if(val <= 0) + val = 120; + //m_previewPropertyView->GetPropertyTreeCtrl().SetColumn(val); + + // Build the material list + m_materialTreeView->InitializeMaterialList( includeFileInMaterialList ); + + //SetActiveView(m_materialTreeView); +} + +/** +* Called by the framework while the window is being created. This methods +* creates the tool bars and status bars +* /todo Bmatt Nerve: Need to get the toolbars to work correctly. +*/ +int MEMainFrame::OnCreate() { + m_stageView = new StageView(); + + return 0; +} + +/** +* Called by the MFC framework while the window is being destroyed. This method +* saves the splitter and window positions. +*/ +void MEMainFrame::OnDestroy() { + int cur; + int min; + + cur = editSplitterHeight; + options.SetMaterialEditHeight(cur); + + cur = editSplitterPos; + options.SetMaterialTreeWidth(cur); + + cur = m_materialEditView->m_editSplitterPos; + options.SetStageWidth(cur); + + cur = previewSplitterPos; + options.SetPreviewPropertiesWidth(cur); + + + //cur = m_materialPropertyView->GetPropertyTreeCtrl().GetColumn(); + //options.SetMaterialPropHeadingWidth(cur); + + //cur = m_previewPropertyView->GetPropertyTreeCtrl().GetColumn(); + //options.SetPreviewPropHeadingWidth(cur); + + //options.SetWindowPlacement ( "mainframe", m_hWnd ); + options.Save(); + + m_materialPropertyView->SaveSettings(); + + MaterialDefManager::DestroyMaterialDefLists(); +} + +void MEMainFrame::Draw() { + if( ImGui::BeginMenuBar() ) + { + if( ImGui::BeginMenu( "File" ) ) + { + ImGui::BeginDisabled( !IsFileSaveMaterialEnabled() ); + if( ImGui::MenuItem( "Save Material" ) ) { + OnFileSaveMaterial(); + } + ImGui::EndDisabled(); + + ImGui::BeginDisabled( !IsFileSaveEnabled() ); + if ( ImGui::MenuItem( "Save File", "Ctrl+S" ) ) { + OnFileSaveFile(); + } + ImGui::EndDisabled(); + + ImGui::BeginDisabled( !IsFileSaveAllEnabled() ); + if ( ImGui::MenuItem( "Save All" ) ) { + OnFileSaveAll(); + } + ImGui::EndDisabled(); + + ImGui::Separator(); + + if( ImGui::MenuItem( "Exit", "Ctrl+W" ) || ImGui::Shortcut( ImGuiMod_Ctrl | ImGuiKey_W ) ) { + OnFileExit(); + } + + ImGui::EndMenu(); + } + if ( ImGui::BeginMenu( "Edit" ) ) { + ImGui::BeginDisabled( !IsEditUndoEnabled() ); + ImGui::SetNextItemShortcut( ImGuiMod_Ctrl | ImGuiKey_Z ); + if ( ImGui::MenuItem( "Undo", "Ctrl+Z" ) || ImGui::Shortcut( ImGuiMod_Ctrl | ImGuiKey_Z ) ) { + OnEditUndo(); + } + ImGui::EndDisabled(); + + ImGui::BeginDisabled( !IsEditRedoEnabled() ); + ImGui::SetNextItemShortcut( ImGuiMod_Ctrl | ImGuiKey_Y ); + ImGui::MenuItem( "Redo", "Ctrl-Y" ); + if ( ImGui::Shortcut( ImGuiMod_Ctrl | ImGuiKey_Y ) || ImGui::Shortcut( ImGuiMod_Shift | ImGuiMod_Ctrl | ImGuiKey_Z ) ) { + OnEditRedo(); + } + ImGui::EndDisabled(); + + ImGui::Separator(); + + ImGui::BeginDisabled( !IsEditCutEnabled() ); + ImGui::SetNextItemShortcut( ImGuiMod_Ctrl | ImGuiKey_X ); + if ( ImGui::MenuItem( "Cut", "Ctrl+X" ) || ImGui::Shortcut( ImGuiMod_Ctrl | ImGuiKey_X ) ) { + OnEditCut(); + } + ImGui::EndDisabled(); + + ImGui::BeginDisabled( !IsEditCopyEnabled() ); + ImGui::SetNextItemShortcut( ImGuiMod_Ctrl | ImGuiKey_C ); + if ( ImGui::MenuItem( "Copy", "Ctrl+C" ) || ImGui::Shortcut( ImGuiMod_Ctrl | ImGuiKey_C ) ) { + OnEditCopy(); + } + ImGui::EndDisabled(); + ImGui::BeginDisabled( !IsEditPasteEnabled() ); + ImGui::SetNextItemShortcut( ImGuiMod_Ctrl | ImGuiKey_V ); + if ( ImGui::MenuItem( "Paste", "Ctrl+V" ) || ImGui::Shortcut( ImGuiMod_Ctrl | ImGuiKey_V ) ) { + OnEditPaste(); + } + ImGui::EndDisabled(); + ImGui::BeginDisabled( !IsEditDeleteEnabled() ); + ImGui::SetNextItemShortcut( ImGuiKey_Delete ); + if ( ImGui::MenuItem( "Delete", "Del" ) || ImGui::Shortcut( ImGuiKey_Delete ) ) { + OnEditDelete(); + } + ImGui::EndDisabled(); + ImGui::BeginDisabled( !IsEditRenameEnabled() ); + ImGui::SetNextItemShortcut( ImGuiKey_F2 ); + if ( ImGui::MenuItem( "Rename", "F2" ) || ImGui::Shortcut( ImGuiKey_F2 ) ) { + OnEditRename(); + } + ImGui::EndDisabled(); + + ImGui::Separator(); + + ImGui::SetNextItemShortcut( ImGuiMod_Ctrl | ImGuiKey_F ); + if ( ImGui::MenuItem( "Find", "Ctrl+F" ) || ImGui::Shortcut( ImGuiMod_Ctrl | ImGuiKey_F ) ) { + OnEditFind(); + } + + ImGui::EndMenu(); + } + if ( ImGui::BeginMenu( "Material View" ) ) { + if ( ImGui::Checkbox( "Include Filename", &includeFileInMaterialList ) ) { + OnViewIncludeFile(); + } + + ImGui::EndMenu(); + } + if ( ImGui::BeginMenu( "Preview" ) ) { + ImGui::BeginDisabled( !IsApplyAllEnabled() ); + ImGui::SetNextItemShortcut( ImGuiMod_Ctrl | ImGuiKey_A ); + if ( ImGui::MenuItem( "Apply Material", "Ctrl+A" ) ) { + OnApplyMaterial(); + } + ImGui::EndDisabled(); + + ImGui::BeginDisabled( !IsApplyFileEnabled() ); + if ( ImGui::MenuItem( "Apply File" ) ) { + OnApplyFile(); + } + ImGui::EndDisabled(); + + ImGui::BeginDisabled( !IsApplyAllEnabled() ); + if ( ImGui::MenuItem( "Apply All" ) ) { + OnApplyAll(); + } + ImGui::EndDisabled(); + + ImGui::Separator(); + + ImGui::SetNextItemShortcut( ImGuiMod_Ctrl | ImGuiKey_R ); + if ( ImGui::MenuItem( "Reload ARB Programs", "Ctrl+R" ) ) { + OnReloadArbPrograms(); + } + + ImGui::SetNextItemShortcut( ImGuiMod_Ctrl | ImGuiKey_I ); + if ( ImGui::MenuItem( "Reload Images", "Ctrl+I" ) ) { + OnReloadImages(); + } + + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + + // debug + /*ImGui::Text( "Focus: %s", + activeWindow == ME_WINDOW_NONE ? "None" + : activeWindow == ME_WINDOW_TREE ? "Tree" + : activeWindow == ME_WINDOW_TEXT_EDIT ? "Text Edit" + : activeWindow == ME_WINDOW_PROP ? "Prop" + : activeWindow == ME_WINDOW_PREVIEW ? "Preview" + : activeWindow == ME_WINDOW_PREVIEW_PROP ? "Preview Prop" + : activeWindow == ME_WINDOW_STAGE ? "Stage" + : "Console" );*/ + + if ( ImGui::BeginTabBar( "MainTabBar" ) ) { + + if ( ImGui::BeginTabItem( "Editor" ) ) { + float splitterButtonWidthOrHeight = 8.0f; + ImVec2 windowSize = ImVec2( Max( editSplitterWidth, previewSplitterWidth ) + splitterButtonWidthOrHeight, editSplitterHeight + previewSplitterHeight + splitterButtonWidthOrHeight * 2.0f ); + + if ( ImGui::BeginChild( "editorSplitters", windowSize ) ) { + ImGui::PushStyleVar( ImGuiStyleVar_ItemSpacing, ImVec2( 0, 0 ) ); + + // m_editSplitter: splitter with 1 row, 2 columns (at cell 0,0 of m_splitterWnd) + + // m_materialTreeView : MaterialTreeView at 0, 0 of m_editSplitter + // Size 300, 200 + if ( m_materialTreeView->Draw( ImVec2( editSplitterPos, editSplitterHeight ) ) ) { + + } + + ImGui::SameLine(); + ImGui::InvisibleButton( "editSplitter", ImVec2( splitterButtonWidthOrHeight, editSplitterHeight ) ); + if ( ImGui::IsItemActive() ) { + editSplitterPos += ImGui::GetIO().MouseDelta.x; + } + ImGui::SameLine(); + + // m_materialEditView : MaterialEditView at 0, 1 of m_editSplitter + // Size 200, 200 + if ( m_materialEditView->Draw( ImVec2( 0, editSplitterHeight ) ) ) { + + } + + if ( m_find ) { + m_find->Draw( ImVec2( previewSplitterPos, 0 ), ImVec2( editSplitterWidth, editSplitterHeight ) ); + } + + ImGui::InvisibleButton( "editRowHSplitter", ImVec2( -1, splitterButtonWidthOrHeight ) ); + if ( ImGui::IsItemActive() ) { + editSplitterHeight += ImGui::GetIO().MouseDelta.y; + } + // m_previewSplitter : splitter with 1 row, 2 columns(at cell 1, 0 of m_splitterWnd) + // m_previewPropertyView : MaterialPreviewPropView at 0, 0 of m_previewSplitter + // Size 300, 200 + + if ( m_previewPropertyView->Draw( ImVec2( previewSplitterPos, previewSplitterHeight ) ) ) { + + } + + ImGui::SameLine(); + ImGui::InvisibleButton( "previewSplitter", ImVec2( splitterButtonWidthOrHeight, previewSplitterHeight ) ); + if ( ImGui::IsItemActive() ) { + previewSplitterPos += ImGui::GetIO().MouseDelta.x; + } + ImGui::SameLine(); + + // m_materialPreviewView : MaterialPreviewView at 0, 1 of m_previewSplitter + // Size 200, 200 + if ( m_materialPreviewView->Draw( ImVec2( windowSize.x - previewSplitterPos, previewSplitterHeight ) ) ) { + + } + + ImGui::InvisibleButton( "previewRowHSplitter", ImVec2( -1, splitterButtonWidthOrHeight ) ); + if ( ImGui::IsItemActive() ) { + previewSplitterHeight += ImGui::GetIO().MouseDelta.y; + } + + ImGui::PopStyleVar(); + } + ImGui::EndChild(); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Console")) + { + m_consoleView->Draw(ImVec2(0, 0), ImVec2(800, 600)); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } +} + +void MEMainFrame::SetActiveWindow( MaterialEditorWindow_t window ) { + activeWindow = window; +} + +/** +* Shuts down the material editor. +* /todo BMatt Nerve: Need to warn the user if a file is modified. +*/ +void MEMainFrame::OnFileExit() { + OnDestroy(); + cmdSystem->BufferCommandText( CMD_EXEC_NOW, "quit\n" ); +} + +/** +* Saves the selected material. +*/ +void MEMainFrame::OnFileSaveMaterial() { + MaterialDoc* material = materialDocManager.GetCurrentMaterialDoc(); + if(material) { + materialDocManager.SaveMaterial(material); + } +} + +/** +* Saves the selected file. +*/ +void MEMainFrame::OnFileSaveFile() { + idStr filename = m_materialTreeView->GetSaveFilename(); + if(filename.Length() > 0) { + materialDocManager.SaveFile(filename); + } +} + +/** +* Saves all modified materials. +*/ +void MEMainFrame::OnFileSaveAll() { + materialDocManager.SaveAllMaterials(); +} + +/** +* Enables the save material menu item if a material is selected and has been modified. +*/ +bool MEMainFrame::IsFileSaveMaterialEnabled() { + MaterialDoc* pDoc = materialDocManager.GetCurrentMaterialDoc(); + + return pDoc && pDoc->modified; +} + +/** +* Enables the Save File menu item if the current file contains a modified material. +*/ +bool MEMainFrame::IsFileSaveEnabled() { + return (m_materialTreeView->CanSaveFile()); +} + +/** +* Enables the Save All menu item if there are any materials that have been modified. +*/ +bool MEMainFrame::IsFileSaveAllEnabled() { + return (materialDocManager.IsAnyModified()); +} + +/** +* Apply the selected material. +*/ +void MEMainFrame::OnApplyMaterial() { + MaterialDoc* material = materialDocManager.GetCurrentMaterialDoc(); + if(material) { + materialDocManager.ApplyMaterial(material); + } +} + +/** +* Applies all modified materials in the selected file. +*/ +void MEMainFrame::OnApplyFile() { + idStr filename = m_materialTreeView->GetSaveFilename(); + if(filename.Length() > 0) { + materialDocManager.ApplyFile(filename); + } +} + +/** +* Applies all modified materials. +*/ +void MEMainFrame::OnApplyAll() { + materialDocManager.ApplyAll(); +} + +/** +* Enables the Apply Material menu item if the current material has an apply waiting. +*/ +bool MEMainFrame::IsApplyMaterialEnabled() { + MaterialDoc* pDoc = materialDocManager.GetCurrentMaterialDoc(); + + return (pDoc && pDoc->applyWaiting); +} + +/** +* Enables the apply file menu item if the current file contains any materials +* that need to be applied. +*/ +bool MEMainFrame::IsApplyFileEnabled() { + MaterialDoc* pDoc = materialDocManager.GetCurrentMaterialDoc(); + + return (pDoc && materialDocManager.DoesFileNeedApply(pDoc->renderMaterial->GetFileName())); +} + +/** +* Enables the apply all menu item if there are any materials that need +* to be applied. +*/ +bool MEMainFrame::IsApplyAllEnabled() { + return (materialDocManager.DoesAnyNeedApply()); +} + +/** +* Performs a cut operation on the selected material. +*/ +void MEMainFrame::OnEditCut() { + switch ( activeWindow ) { + case ME_WINDOW_TREE: + m_materialTreeView->OnCut(); + break; + case ME_WINDOW_TEXT_EDIT: + m_materialEditView->OnCut(); + break; + default: + break; + } +} + +/** +* Performs a copy operation on the selected material or stage. +*/ +void MEMainFrame::OnEditCopy() { + switch ( activeWindow ) { + case ME_WINDOW_STAGE: + m_stageView->OnCopy(); + break; + case ME_WINDOW_TREE: + m_materialTreeView->OnCopy(); + break; + case ME_WINDOW_TEXT_EDIT: + m_materialEditView->OnCopy(); + break; + default: + break; + } +} + +/** +* Performs a paste operation on the selected material or stage. +*/ +void MEMainFrame::OnEditPaste() { + switch ( activeWindow ) { + case ME_WINDOW_STAGE: + m_stageView->OnPaste(); + break; + case ME_WINDOW_TREE: + m_materialTreeView->OnPaste(); + break; + case ME_WINDOW_TEXT_EDIT: + m_materialEditView->OnPaste(); + break; + default: + break; + } +} + +/** +* Performs a delete operation on the selected material or stage. +*/ +void MEMainFrame::OnEditDelete() { + switch ( activeWindow ) { + case ME_WINDOW_STAGE: + m_stageView->OnDeleteStage(); + return; + case ME_WINDOW_TREE: + m_materialTreeView->OnDeleteMaterial(); + break; + case ME_WINDOW_TEXT_EDIT: + m_materialEditView->OnDelete(); + break; + default: + break; + } +} + +/** +* Performs a rename operation on the selected material or stage. +*/ +void MEMainFrame::OnEditRename() { + switch ( activeWindow ) { + case ME_WINDOW_STAGE: + m_stageView->OnRenameStage(); + break; + case ME_WINDOW_TREE: + m_materialTreeView->OnRenameMaterial(); + break; + default: + break; + } +} + + +/** +* Enable the cut menu item if a material is selected. +*/ +bool MEMainFrame::IsEditCutEnabled() { + return m_stageView->CanCut() || m_materialTreeView->CanCut() || m_materialEditView->CanCut(); +} + +/** +* Enables the copy menu item if a material or stage is selected. +*/ +bool MEMainFrame::IsEditCopyEnabled() { + return m_stageView->CanCopy() || m_materialTreeView->CanCopy() || m_materialEditView->CanCopy(); +} + +/** +* Enables a paste operation when a material or stage has been copied. +*/ +bool MEMainFrame::IsEditPasteEnabled() { + return m_stageView->CanPaste() || m_materialTreeView->CanPaste() || m_materialEditView->CanPaste(); +} + +/** +* Enables a delete operation when a material or stage is selected. +*/ +bool MEMainFrame::IsEditDeleteEnabled() { + return m_stageView->CanDelete() || m_materialTreeView->CanDelete() || m_materialEditView->CanDelete(); +} + +/** +* Enables a rename operation when a material, folder or stage is selected. +*/ +bool MEMainFrame::IsEditRenameEnabled() { + return m_stageView->CanRename() || m_materialTreeView->CanRename(); +} + +/** +* Opens the find dialog. +*/ +void MEMainFrame::OnEditFind() { + + if (m_find== NULL) + { + m_find = new FindDialog( this ); + } + + m_find->Start(); +} + +/** +* Performs a search with the previously selected search parameters. +*/ +void MEMainFrame::OnEditFindNext() { + FindNext(NULL); +} + +/** +* Performs an undo operation. +*/ +void MEMainFrame::OnEditUndo() { + //Check for undo operation on special windows + switch ( activeWindow ) { + case ME_WINDOW_TEXT_EDIT: + m_materialEditView->m_textView.Undo(); + break; + case ME_WINDOW_TREE: + case ME_WINDOW_PROP: + case ME_WINDOW_PREVIEW: + case ME_WINDOW_PREVIEW_PROP: + case ME_WINDOW_STAGE: + case ME_WINDOW_CONSOLE: + materialDocManager.Undo(); + break; + default: + break; + } +} + +/** +* Performs a redo operation. +*/ +void MEMainFrame::OnEditRedo() { + //Check for redo operation on special windows + switch ( activeWindow ) { + case ME_WINDOW_TEXT_EDIT: + m_materialEditView->m_textView.Redo(); + break; + case ME_WINDOW_TREE: + case ME_WINDOW_PROP: + case ME_WINDOW_PREVIEW: + case ME_WINDOW_PREVIEW_PROP: + case ME_WINDOW_STAGE: + case ME_WINDOW_CONSOLE: + materialDocManager.Redo(); + break; + default: + break; + } +} + +/** +* Enables the undo menu item if an undo is available. +*/ +bool MEMainFrame::IsEditUndoEnabled() { + return m_materialEditView->m_textView.CanUndo() || materialDocManager.IsUndoAvailable(); +} + +/** +* Enables the redo menu item if a redo is available. +*/ +bool MEMainFrame::IsEditRedoEnabled() { + return m_materialEditView->m_textView.CanRedo() || materialDocManager.IsRedoAvailable(); +} + +/** +* Toggles between including the file into the material list and not. +*/ +void MEMainFrame::OnViewIncludeFile() { + m_materialTreeView->InitializeMaterialList( includeFileInMaterialList ); +} + +/** +* Executes the reloadARBPrograms console command for convinience. +*/ +void MEMainFrame::OnReloadArbPrograms() { + cmdSystem->BufferCommandText(CMD_EXEC_NOW, "reloadARBprograms"); +} + +/** +* Executes the reloadImages command to reload images that have been changed outside +* of the editor. +*/ +void MEMainFrame::OnReloadImages() { + cmdSystem->BufferCommandText(CMD_EXEC_NOW, "reloadImages"); +} + +/** +* Called by the find dialog when it is closing. +*/ +void MEMainFrame::CloseFind() { + +} + +/** +* Begins a search based on the provided parameters or the previously used +* parameters. +*/ +void MEMainFrame::FindNext(MaterialSearchData_t* search) { + + if(search) { + searchData = *search; + } else { + if(!searchData.searched) { + return; + } + } + + //The material tree controls the searching + if(!m_materialTreeView->FindNextMaterial(&searchData)) { + m_find->UnableToFind(); + } + searchData.searched = true; +} + +/** +* Called when the selected material has changed. +* @param pMaterial The newly selected material. +*/ +void MEMainFrame::MV_OnMaterialSelectionChange(MaterialDoc* pMaterial) { + +} + +} diff --git a/neo/tools/imgui/materialeditor/MEMainFrame.h b/neo/tools/imgui/materialeditor/MEMainFrame.h new file mode 100644 index 000000000..9aa79d7b7 --- /dev/null +++ b/neo/tools/imgui/materialeditor/MEMainFrame.h @@ -0,0 +1,166 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef MEMAINFRAME_H_ +#define MEMAINFRAME_H_ + +#include "MaterialEditor.h" +#include "MaterialTreeView.h" +#include "MaterialPropTreeView.h" +#include "MaterialPreviewView.h" +#include "StageView.h" +#include "MaterialPreviewPropView.h" +#include "MEOptions.h" +#include "ConsoleView.h" +#include "FindDialog.h" +//#include "../common/PropTree/PropTreeView.h" +#include "MaterialDocManager.h" +#include "MaterialEditView.h" + +namespace ImGuiTools { + +/** +* The main window for the material editor. +*/ +class MEMainFrame : public MaterialView +{ + +public: + MEMainFrame(); + virtual ~MEMainFrame(); + + //Public Operations + void PrintConsoleMessage(const char *msg); + + bool PreCreateWindow(); + void OnCreateClient(); + + //Message Handlers + int OnCreate(); + void OnDestroy(); + + void SetActiveWindow(MaterialEditorWindow_t window); + + void Draw(); + + //Menu Message Handlers + void OnFileExit(); + void OnFileSaveMaterial(); + void OnFileSaveFile(); + void OnFileSaveAll(); + bool IsFileSaveMaterialEnabled(); + bool IsFileSaveEnabled(); + bool IsFileSaveAllEnabled(); + + void OnApplyMaterial(); + void OnApplyFile(); + void OnApplyAll(); + bool IsApplyMaterialEnabled(); + bool IsApplyFileEnabled(); + bool IsApplyAllEnabled(); + + void OnEditCut(); + void OnEditCopy(); + void OnEditPaste(); + void OnEditDelete(); + void OnEditRename(); + bool IsEditCutEnabled(); + bool IsEditCopyEnabled(); + bool IsEditPasteEnabled(); + bool IsEditDeleteEnabled(); + bool IsEditRenameEnabled(); + + void OnEditFind(); + void OnEditFindNext(); + + void OnEditUndo(); + void OnEditRedo(); + bool IsEditUndoEnabled(); + bool IsEditRedoEnabled(); + + void OnViewIncludeFile(); + void OnReloadArbPrograms(); + void OnReloadImages(); + +private: + //Methods for Find interactions + friend FindDialog; + void CloseFind(); + void FindNext(MaterialSearchData_t* search); + + //MaterialView Interface + virtual void MV_OnMaterialSelectionChange(MaterialDoc* pMaterial); + +private: + //Status and Toolbars + //CStatusBar m_wndStatusBar; + //CToolBar m_wndToolBar; + + //Splitter windows + /* + CTabCtrl m_tabs; + CSplitterWnd m_splitterWnd; + CSplitterWnd m_editSplitter; + CSplitterWnd m_previewSplitter; + CSplitterWnd* m_materialEditSplitter; + */ + float editSplitterWidth; + float editSplitterPos; + float editSplitterHeight; + float previewSplitterWidth; + float previewSplitterPos; + float previewSplitterHeight; + + //Child Views + MaterialTreeView* m_materialTreeView; + StageView* m_stageView; + MaterialPropTreeView* m_materialPropertyView; + MaterialPreviewView* m_materialPreviewView; + MaterialPreviewPropView* m_previewPropertyView; + ConsoleView* m_consoleView; + + MaterialEditView* m_materialEditView; + + //Find Data + FindDialog* m_find; + MaterialSearchData_t searchData; + + //Document Management + MaterialDocManager materialDocManager; + MaterialDoc* currentDoc; + + //Options + MEOptions options; + + bool includeFileInMaterialList; + + MaterialEditorWindow_t activeWindow; +}; + +} + +#endif /* !MEMAINFRAME_H_ */ diff --git a/neo/tools/imgui/materialeditor/MEOptions.cpp b/neo/tools/imgui/materialeditor/MEOptions.cpp new file mode 100644 index 000000000..4d1a1047b --- /dev/null +++ b/neo/tools/imgui/materialeditor/MEOptions.cpp @@ -0,0 +1,92 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#include "sys/sys_imgui.h" + +#include "../util/RegistryOptions.h" +#include "MEOptions.h" + +namespace ImGuiTools { + +/** +* Constructor for MEOptions. +*/ +MEOptions::MEOptions ( ) { + + registry.Init("MaterialEditor"); + + materialTreeWidth = 0; + stageWidth = 0; + previewPropertiesWidth = 0; + materialEditHeight = 0; + materialPropHeadingWidth = 0; + previewPropHeadingWidth = 0; + +} + +/** +* Destructor for MEOptions. +*/ +MEOptions::~MEOptions() { +} + +/** +* Saves the material editor options to the registry. +*/ +bool MEOptions::Save (void) { + + registry.SetFloat("materialTreeWidth", materialTreeWidth); + registry.SetFloat("stageWidth", stageWidth); + registry.SetFloat("previewPropertiesWidth", previewPropertiesWidth); + registry.SetFloat("materialEditHeight", materialEditHeight); + registry.SetFloat("materialPropHeadingWidth", materialPropHeadingWidth); + registry.SetFloat("previewPropHeadingWidth", previewPropHeadingWidth); + + return registry.Save(); +} + +/** +* Loads the material editor options from the registry. +*/ +bool MEOptions::Load (void) { + + if(!registry.Load()) { + return false; + } + + materialTreeWidth = (int)registry.GetFloat("materialTreeWidth"); + stageWidth = (int)registry.GetFloat("stageWidth"); + previewPropertiesWidth = (int)registry.GetFloat("previewPropertiesWidth"); + materialEditHeight = (int)registry.GetFloat("materialEditHeight"); + materialPropHeadingWidth = (int)registry.GetFloat("materialPropHeadingWidth"); + previewPropHeadingWidth = (int)registry.GetFloat("previewPropHeadingWidth"); + + return true; + +} + +} diff --git a/neo/tools/imgui/materialeditor/MEOptions.h b/neo/tools/imgui/materialeditor/MEOptions.h new file mode 100644 index 000000000..b028c9200 --- /dev/null +++ b/neo/tools/imgui/materialeditor/MEOptions.h @@ -0,0 +1,160 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef MEOPTIONS_H_ +#define MEOPTIONS_H_ + +#include "sys/sys_imgui.h" +#include "../util/RegistryOptions.h" + +namespace ImGuiTools { + +/** +* Wrapper class that is responsible for reading and writing Material Editor +* settings to the registry. Settings are written to +* Software\\id Software\\DOOM3\\Tools\\MaterialEditor +*/ +class MEOptions { + +public: + MEOptions(); + ~MEOptions(); + + bool Save (void); + bool Load (void); + + /** + * Sets the flag that determines if the settings need to be saved because + * they where modified. + */ + void SetModified(bool mod = true) { modified = mod; }; + /** + * Get the flag that determines if the settings need to be saved because + * they where modified. + */ + bool GetModified() { return modified; }; + + void SetWindowPlacement ( const char* name, int hwnd ); + bool GetWindowPlacement ( const char* name, int hwnd ); + + void SetMaterialTreeWidth(int width); + int GetMaterialTreeWidth(); + + void SetStageWidth(int width); + int GetStageWidth(); + + void SetPreviewPropertiesWidth(int width); + int GetPreviewPropertiesWidth(); + + void SetMaterialEditHeight(int height); + int GetMaterialEditHeight(); + + void SetMaterialPropHeadingWidth(int width); + int GetMaterialPropHeadingWidth(); + + void SetPreviewPropHeadingWidth(int width); + int GetPreviewPropHeadingWidth(); + +protected: + rvRegistryOptions registry; + + bool modified; + + int materialTreeWidth; + int stageWidth; + int previewPropertiesWidth; + int materialEditHeight; + int materialPropHeadingWidth; + int previewPropHeadingWidth; +}; + + +ID_INLINE void MEOptions::SetWindowPlacement ( const char* name, int hwnd ) { + //registry.SetWindowPlacement ( name, hwnd ); +} + +ID_INLINE bool MEOptions::GetWindowPlacement ( const char* name, int hwnd ) { + return false; //registry.GetWindowPlacement ( name, hwnd ); +} + +ID_INLINE void MEOptions::SetMaterialTreeWidth(int width) { + materialTreeWidth = width; + SetModified(true); +} + +ID_INLINE int MEOptions::GetMaterialTreeWidth() { + return materialTreeWidth; +} + +ID_INLINE void MEOptions::SetStageWidth(int width) { + stageWidth = width; + SetModified(true); +} + +ID_INLINE int MEOptions::GetStageWidth() { + return stageWidth; +} + +ID_INLINE void MEOptions::SetPreviewPropertiesWidth(int width) { + previewPropertiesWidth = width; + SetModified(true); +} + +ID_INLINE int MEOptions::GetPreviewPropertiesWidth() { + return previewPropertiesWidth; +} + +ID_INLINE void MEOptions::SetMaterialEditHeight(int height) { + materialEditHeight = height; + SetModified(true); +} + +ID_INLINE int MEOptions::GetMaterialEditHeight() { + return materialEditHeight; +} + +ID_INLINE void MEOptions::SetMaterialPropHeadingWidth(int width) { + materialPropHeadingWidth = width; + SetModified(true); +} + +ID_INLINE int MEOptions::GetMaterialPropHeadingWidth() { + return materialPropHeadingWidth; +} + +ID_INLINE void MEOptions::SetPreviewPropHeadingWidth(int width) { + previewPropHeadingWidth = width; + SetModified(true); +} + +ID_INLINE int MEOptions::GetPreviewPropHeadingWidth() { + return previewPropHeadingWidth; +} + +} + +#endif /* !MEOPTIONS_h_ */ diff --git a/neo/tools/imgui/materialeditor/MaterialDef.cpp b/neo/tools/imgui/materialeditor/MaterialDef.cpp new file mode 100644 index 000000000..49571f997 --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialDef.cpp @@ -0,0 +1,204 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#include "idlib/containers/HashTable.h" + +#include "framework/FileSystem.h" + +#include "MaterialDef.h" + +namespace ImGuiTools { + + +/** +* Constructor. +*/ +MaterialDef::MaterialDef(void) { + type = 0; + quotes = false; +} + +/** +* Destructor. +*/ +MaterialDef::~MaterialDef(void) { +} + +/** +* Returns view specific data associated with the material definition. +*/ +int MaterialDef::GetViewData(const char* viewName) { + int* value = NULL; + viewData.Get(viewName, &value); + return *value; +} + +/** +* Sets view specific data for the material definition. +*/ +void MaterialDef::SetViewData(const char* viewName, int value) { + viewData.Set(viewName, value); +} + +#define MATERIAL_DEF_FILE "MaterialEditorDefs.med" + +MaterialDefList MaterialDefManager::materialDefs[MaterialDefManager::MATERIAL_DEF_NUM]; + + +/** +* Loads the material definition file instatiates MaterialDef objects for each definition +* and groups the definitions. +*/ +void MaterialDefManager::InitializeMaterialDefLists() { + + char *buffer; + int length = fileSystem->ReadFile( MATERIAL_DEF_FILE, (void **)&buffer); + + if ( length == -1 ) { + common->Error( "Couldn't load material editor definition: %s", MATERIAL_DEF_FILE ); + return; + } + + idLexer src; + if ( !src.LoadMemory( buffer, length, MATERIAL_DEF_FILE ) ) { + common->Error( "Couldn't parse %s", MATERIAL_DEF_FILE ); + fileSystem->FreeFile(buffer); + } + + + InitializeMaterialDefList(&src, "materialprops", &materialDefs[MATERIAL_DEF_MATERIAL]); + InitializeMaterialDefList(&src, "stageprops", &materialDefs[MATERIAL_DEF_STAGE]); + InitializeMaterialDefList(&src, "specialmapstageprops", &materialDefs[MATERIAL_DEF_SPECIAL_STAGE]); + + fileSystem->FreeFile(buffer); +} + +/** +* Loads a single type of material attributes and adds them to the supplied MaterialDefList object. +* @param src The idLexer object that contains the file. +* @param typeName The name of the attribute grouping to search for in the file. +* @param list The MaterialDefList object to append the MaterialDef instances to. +*/ +void MaterialDefManager::InitializeMaterialDefList(idLexer* src, const char* typeName, MaterialDefList* list) { + + idToken token; + + src->Reset(); + src->SkipUntilString(typeName); + src->SkipUntilString("{"); + + while(1) { + if ( !src->ExpectAnyToken( &token ) ) { + //Todo: Add some error checking here + return; + } + + if ( token == "}" ) { + break; + } + + MaterialDef* newProp = new MaterialDef(); + + if(!token.Icmp("TYPE_GROUP")) { + newProp->type = MaterialDef::MATERIAL_DEF_TYPE_GROUP; + } else if(!token.Icmp("TYPE_BOOL")) { + newProp->type = MaterialDef::MATERIAL_DEF_TYPE_BOOL; + } else if(!token.Icmp("TYPE_STRING")) { + newProp->type = MaterialDef::MATERIAL_DEF_TYPE_STRING; + } else if(!token.Icmp("TYPE_FLOAT")) { + newProp->type = MaterialDef::MATERIAL_DEF_TYPE_FLOAT; + } else if(!token.Icmp("TYPE_INT")) { + newProp->type = MaterialDef::MATERIAL_DEF_TYPE_INT; + } + + //Skip the , + src->ReadToken(&token); + + //Read Dict Name + src->ReadToken(&token); + newProp->dictName = token; + + //Skip the , + src->ReadToken(&token); + + //Read Display Name + src->ReadToken(&token); + newProp->displayName = token; + + //Skip the , + src->ReadToken(&token); + + //Read Display Info + src->ReadToken(&token); + newProp->displayInfo = token; + + //Type Specific Data + if(newProp->type == MaterialDef::MATERIAL_DEF_TYPE_STRING) { + + newProp->quotes = false; + + //Skip the , + src->ReadToken(&token); + + //Read validate flag + src->ReadToken(&token); + if(token == "1") { + newProp->quotes = true; + } + } + + src->SkipRestOfLine(); + + list->Append(newProp); + } +} + +/** +* Destroys all MaterialDef instances and clears the material attribute grouping lists. +*/ +void MaterialDefManager::DestroyMaterialDefLists() { + + for(int i = 0; i < MATERIAL_DEF_NUM; i++) { + for(int j = 0; j < materialDefs[i].Num(); j++) { + delete materialDefs[i][j]; + } + materialDefs[i].Clear(); + } +} + +/** +* Returns the MaterialDefList for the specified attribute grouping. +* @param type The attribute grouping for which to retreive the attribute list. +*/ +MaterialDefList* MaterialDefManager::GetMaterialDefs(int type) { + if(type >= 0 && type < MATERIAL_DEF_NUM) { + return &materialDefs[type]; + } + return NULL; +} + +} diff --git a/neo/tools/imgui/materialeditor/MaterialDef.h b/neo/tools/imgui/materialeditor/MaterialDef.h new file mode 100644 index 000000000..3081906a3 --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialDef.h @@ -0,0 +1,111 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef MATERIALDEF_H_ +#define MATERIALDEF_H_ + +#include "idlib/containers/HashTable.h" + +namespace ImGuiTools { + +/** +* Represents a single attribute in a material. Represents material, stage +* and special stage attributes. The MaterialDef manager loads these +* definitions from the material definition file as the editor +* is being initialized. +*/ +class MaterialDef { + +public: + /** + * Defines possible attribute types. + */ + enum { + MATERIAL_DEF_TYPE_GROUP, + MATERIAL_DEF_TYPE_BOOL, + MATERIAL_DEF_TYPE_STRING, + MATERIAL_DEF_TYPE_FLOAT, + MATERIAL_DEF_TYPE_INT + }; + + int type; + idStr dictName; + idStr displayName; + idStr displayInfo; + bool quotes; + idHashTable viewData; + +public: + + MaterialDef(void); + virtual ~MaterialDef(void); + + int GetViewData(const char* viewName); + void SetViewData(const char* viewName, int value); +}; + +/** +* A list of material attributes. Material, stage, and special stage attributes +* are grouped together during the load process for use by the different view and +* MaterialDoc. +*/ +typedef idList MaterialDefList; + +/** +* This class contains static utility functions that view and MaterialDoc use +* to access the MaterialDef and MaterialDefList data that is loaded. This class +* is also responsible for loading and destroying the MaterialDef instances. +*/ +class MaterialDefManager { + +public: + + /** + * Defines the groupings of material attributes. + */ + enum { + MATERIAL_DEF_MATERIAL = 0, + MATERIAL_DEF_STAGE, + MATERIAL_DEF_SPECIAL_STAGE, + MATERIAL_DEF_NUM + }; + + static void InitializeMaterialDefLists(); + static void InitializeMaterialDefList(idLexer* src, const char* typeName, MaterialDefList* list); + + static void DestroyMaterialDefLists(); + + static MaterialDefList* GetMaterialDefs(int type); + + +protected: + static MaterialDefList materialDefs[MATERIAL_DEF_NUM]; +}; + +} + +#endif /* !MATERIALDEF_H_ */ diff --git a/neo/tools/imgui/materialeditor/MaterialDoc.cpp b/neo/tools/imgui/materialeditor/MaterialDoc.cpp new file mode 100644 index 000000000..395cf380b --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialDoc.cpp @@ -0,0 +1,970 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "renderer/Material.h" +#include "framework/FileSystem.h" + +#include "MaterialDoc.h" +#include "MaterialView.h" + +namespace ImGuiTools { + +/** +* Constructor for MaterialDoc. +*/ +MaterialDoc::MaterialDoc(void) { + modified = false; + applyWaiting = false; + sourceModify = false; +} + +/** +* Destructor for MaterialDoc. +*/ +MaterialDoc::~MaterialDoc(void) { + ClearEditMaterial(); +} + +/** +* Initializes the MaterialDoc instance with a specific idMaterial. This method will +* parse the material into the internal dictionary representation and optionally +* allow the idMaterial object to reparse the source. +* @param material The idMaterial instance to use. +* @param parseMaterial Flag to determine if the material should be parsed into the editor representation. +* @param parseRenderMaterial Flag to determine if the idMaterial object should be reparsed. +*/ +void MaterialDoc::SetRenderMaterial(idMaterial* material, bool parseMaterial, bool parseRenderMatierial) { + + renderMaterial = material; + + + if(!parseMaterial || !renderMaterial) + return; + + if(parseRenderMatierial) { + char *declText = (char *) _alloca( material->GetTextLength() + 1 ); + material->GetText( declText ); + + renderMaterial->GetText(declText); + ParseMaterialText(declText); + + } + + ClearEditMaterial(); + + name = material->GetName(); + + idLexer src; + + char *declText = (char *) _alloca( material->GetTextLength() + 1 ); + material->GetText( declText ); + + renderMaterial->GetText(declText); + src.LoadMemory(declText, strlen(declText), "Material"); + + ParseMaterial(&src); +} + +/** +* Returns the number of stages in this material. +*/ +int MaterialDoc::GetStageCount() { + return editMaterial.stages.Num(); +} + +/** +* Returns the index of the stage with the specified type and name or -1 +* if the stage does not exist. +* @param stageType The type of stage to find. +* @param name The name of the stage to find. +*/ +int MaterialDoc::FindStage(int stageType, const char* name) { + + for(int i = 0; i < editMaterial.stages.Num(); i++) { + int type = GetAttributeInt(i, "stagetype"); + idStr localname = GetAttribute(i, "name"); + if(stageType == type && !localname.Icmp(name)) + return i; + } + return -1; +} + +/** +* Returns a copy of the specified stage. +* @param stage The stage to return. +*/ +MEStage_t MaterialDoc::GetStage(int stage) { + assert(stage >= 0 && stage < GetStageCount()); + return *editMaterial.stages[stage]; + +} + +/** +* Specifies the enabled state of a single stage. +* @param stage The stage to change. +* @param enabled The enabled state. +*/ +void MaterialDoc::EnableStage(int stage, bool enabled) { + + assert(stage >= 0 && stage < GetStageCount()); + editMaterial.stages[stage]->enabled = enabled; + + OnMaterialChanged(); +} + +/** +* Sets the enabled state of all stages. +* @param enabled The enabled state. +*/ +void MaterialDoc::EnableAllStages(bool enabled) { + for(int i = 0; i < GetStageCount(); i++) { + editMaterial.stages[i]->enabled = enabled; + } +} + +/** +* Returns the enabled state of a stage. +* @param stage The stage to check. +*/ +bool MaterialDoc::IsStageEnabled(int stage) { + assert(stage >= 0 && stage < GetStageCount()); + return editMaterial.stages[stage]->enabled; +} + +/** +* Returns an attribute string from the material or a stage. +* @param stage The stage or -1 for the material. +* @param attribName The name of the attribute. +* @param defaultString The default value if the attribute is not specified. +*/ +const char* MaterialDoc::GetAttribute(int stage, const char* attribName, const char* defaultString) { + + if(stage == -1) { + return editMaterial.materialData.GetString(attribName, defaultString); + } else { + assert(stage >= 0 && stage < GetStageCount()); + MEStage_t* pStage = editMaterial.stages[stage]; + return pStage->stageData.GetString(attribName, defaultString); + } +} + +/** +* Returns an attribute int from the material or a stage. +* @param stage The stage or -1 for the material. +* @param attribName The name of the attribute. +* @param defaultString The default value if the attribute is not specified. +*/ +int MaterialDoc::GetAttributeInt(int stage, const char* attribName, const char* defaultString) { + if(stage == -1) { + return editMaterial.materialData.GetInt(attribName, defaultString); + } else { + assert(stage >= 0 && stage < GetStageCount()); + MEStage_t* pStage = editMaterial.stages[stage]; + return pStage->stageData.GetInt(attribName, defaultString); + } +} + +/** +* Returns an attribute float from the material or a stage. +* @param stage The stage or -1 for the material. +* @param attribName The name of the attribute. +* @param defaultString The default value if the attribute is not specified. +*/ +float MaterialDoc::GetAttributeFloat(int stage, const char* attribName, const char* defaultString) { + if(stage == -1) { + return editMaterial.materialData.GetFloat(attribName, defaultString); + } else { + assert(stage >= 0 && stage < GetStageCount()); + MEStage_t* pStage = editMaterial.stages[stage]; + return pStage->stageData.GetFloat(attribName, defaultString); + } +} + +/** +* Returns an attribute bool from the material or a stage. +* @param stage The stage or -1 for the material. +* @param attribName The name of the attribute. +* @param defaultString The default value if the attribute is not specified. +*/ +bool MaterialDoc::GetAttributeBool(int stage, const char* attribName, const char* defaultString) { + if(stage == -1) { + return editMaterial.materialData.GetBool(attribName, defaultString); + } else { + assert(stage >= 0 && stage < GetStageCount()); + MEStage_t* pStage = editMaterial.stages[stage]; + return pStage->stageData.GetBool(attribName, defaultString); + } +} + +/** +* Sets an attribute string in the material or a stage. +* @param stage The stage or -1 for the material. +* @param attribName The name of the attribute. +* @param value The value to set. +* @param addUndo Flag that specifies if the system should add an undo operation. +*/ +void MaterialDoc::SetAttribute(int stage, const char* attribName, const char* value, bool addUndo) { + + //Make sure we need to set the attribute + idStr orig = GetAttribute(stage, attribName); + if(orig.Icmp(value)) { + + idDict* dict; + if(stage == -1) { + dict = &editMaterial.materialData; + } else { + assert(stage >= 0 && stage < GetStageCount()); + dict = &editMaterial.stages[stage]->stageData; + } + + if(addUndo) { + //Create a new Modifier for this change so we can undo and redo later + AttributeMaterialModifierString* mod = new AttributeMaterialModifierString(manager, name, stage, attribName, value, orig); + manager->AddMaterialUndoModifier(mod); + } + + dict->Set(attribName, value); + + manager->AttributeChanged(this, stage, attribName); + OnMaterialChanged(); + } +} + +/** +* Sets an attribute int in the material or a stage. +* @param stage The stage or -1 for the material. +* @param attribName The name of the attribute. +* @param value The value to set. +* @param addUndo Flag that specifies if the system should add an undo operation. +*/ +void MaterialDoc::SetAttributeInt(int stage, const char* attribName, int value, bool addUndo) { + //Make sure we need to set the attribute + int orig = GetAttributeInt(stage, attribName); + if(orig != value) { + + idDict* dict; + if(stage == -1) { + dict = &editMaterial.materialData; + } else { + assert(stage >= 0 && stage < GetStageCount()); + dict = &editMaterial.stages[stage]->stageData; + } + + dict->SetInt(attribName, value); + + manager->AttributeChanged(this, stage, attribName); + OnMaterialChanged(); + } +} + +/** +* Sets an attribute float in the material or a stage. +* @param stage The stage or -1 for the material. +* @param attribName The name of the attribute. +* @param value The value to set. +* @param addUndo Flag that specifies if the system should add an undo operation. +*/ +void MaterialDoc::SetAttributeFloat(int stage, const char* attribName, float value, bool addUndo) { + //Make sure we need to set the attribute + float orig = GetAttributeFloat(stage, attribName); + if(orig != value) { + + idDict* dict; + if(stage == -1) { + dict = &editMaterial.materialData; + } else { + assert(stage >= 0 && stage < GetStageCount()); + dict = &editMaterial.stages[stage]->stageData; + } + + dict->SetFloat(attribName, value); + + manager->AttributeChanged(this, stage, attribName); + OnMaterialChanged(); + } +} + +/** +* Sets an attribute bool in the material or a stage. +* @param stage The stage or -1 for the material. +* @param attribName The name of the attribute. +* @param value The value to set. +* @param addUndo Flag that specifies if the system should add an undo operation. +*/ +void MaterialDoc::SetAttributeBool(int stage, const char* attribName, bool value, bool addUndo) { + //Make sure we need to set the attribute + bool orig = GetAttributeBool(stage, attribName); + if(orig != value) { + + idDict* dict; + if(stage == -1) { + dict = &editMaterial.materialData; + } else { + assert(stage >= 0 && stage < GetStageCount()); + dict = &editMaterial.stages[stage]->stageData; + } + + if(addUndo) { + //Create a new Modifier for this change so we can undo and redo later + AttributeMaterialModifierBool* mod = new AttributeMaterialModifierBool(manager, name, stage, attribName, value, orig); + manager->AddMaterialUndoModifier(mod); + } + + dict->SetBool(attribName, value); + + manager->AttributeChanged(this, stage, attribName); + OnMaterialChanged(); + } +} + +/** +* Sets the material name. +* @param materialName The new name of the material. +* @param addUndo Flag that specifies if the system should add an undo operation. +*/ +void MaterialDoc::SetMaterialName(const char* materialName, bool addUndo) { + idStr oldName = name; + + declManager->RenameDecl(DECL_MATERIAL, oldName, materialName); + name = renderMaterial->GetName(); + + if(addUndo) { + RenameMaterialModifier* mod = new RenameMaterialModifier(manager, name, oldName); + manager->AddMaterialUndoModifier(mod); + } + + manager->MaterialNameChanged(oldName, this); + + OnMaterialChanged(); + + //Need to do an instant apply for material name changes + ApplyMaterialChanges(); +} + +/** +* Sets the entire dictionary for a material or stage +* @param stage The stage or -1 for the material. +* @param data The dictionary to copy. +*/ +void MaterialDoc::SetData(int stage, idDict* data) { + idDict* dict; + if(stage == -1) { + dict = &editMaterial.materialData; + } else { + assert(stage >= 0 && stage < GetStageCount()); + dict = &editMaterial.stages[stage]->stageData; + } + dict->Clear(); + dict->Copy(*data); +} + +/** +* Called when the editor modifies the source of the material. +* @param text The new source text. +*/ +void MaterialDoc::SourceModify(SourceModifyOwner* owner) { + + sourceModifyOwner = owner; + sourceModify = true; + OnMaterialChanged(); +} + +/** +* Returns true if the source text of this material has been edited. +*/ +bool MaterialDoc::IsSourceModified() { + return sourceModify; +} + +/** +* Applies any source changes to the edit representation of the material. +*/ +void MaterialDoc::ApplySourceModify(idStr& text) { + + if(sourceModify) { + + //Changes in the source need to clear any undo redo buffer because we have no idea what has changed + manager->ClearUndo(); + manager->ClearRedo(); + + ClearEditMaterial(); + + idLexer src; + src.LoadMemory(text, text.Length(), "Material"); + + src.SetFlags( + LEXFL_NOSTRINGCONCAT | // multiple strings seperated by whitespaces are not concatenated + LEXFL_NOSTRINGESCAPECHARS | // no escape characters inside strings + LEXFL_ALLOWPATHNAMES | // allow path seperators in names + LEXFL_ALLOWMULTICHARLITERALS | // allow multi character literals + LEXFL_ALLOWBACKSLASHSTRINGCONCAT | // allow multiple strings seperated by '\' to be concatenated + LEXFL_NOFATALERRORS // just set a flag instead of fatal erroring + ); + + idToken token; + if(!src.ReadToken(&token)) { + src.Warning( "Missing decl name" ); + return; + } + + ParseMaterial(&src); + sourceModify = false; + + //Check to see if the name has changed + if(token.Icmp(name)) { + SetMaterialName(token, false); + } + } +} + +/** +* Returns the appropriate source for the editing +*/ +const char* MaterialDoc::GetEditSourceText() { + + return GenerateSourceText(); +} + +/** +* Adds a stage to the material. +* @param stageType The type of the stage: normal or special. +* @param stageName The name of the stage. +* @param addUndo Flag that specifies if the system should add an undo operation. +*/ +void MaterialDoc::AddStage(int stageType, const char* stageName, bool addUndo) { + MEStage_t* newStage = new MEStage_t(); + + int index = editMaterial.stages.Append(newStage); + newStage->stageData.Set("name", stageName); + newStage->stageData.SetInt("stagetype", stageType); + newStage->enabled = true; + + if(addUndo) { + StageInsertModifier* mod = new StageInsertModifier(manager, name, index, stageType, stageName); + manager->AddMaterialUndoModifier(mod); + } + + manager->StageAdded(this, index); + + OnMaterialChanged(); +} + +/** +* Inserts a new stage to the material at a specified location. +* @param stage The location to insert the stage. +* @param stageType The type of the stage: normal or special. +* @param stageName The name of the stage. +* @param addUndo Flag that specifies if the system should add an undo operation. +*/ +void MaterialDoc::InsertStage(int stage, int stageType, const char* stageName, bool addUndo) { + MEStage_t* newStage = new MEStage_t(); + + editMaterial.stages.Insert(newStage, stage); + newStage->stageData.Set("name", stageName); + newStage->stageData.SetInt("stagetype", stageType); + newStage->enabled = true; + + if(addUndo) { + StageInsertModifier* mod = new StageInsertModifier(manager, name, stage, stageType, stageName); + manager->AddMaterialUndoModifier(mod); + } + + manager->StageAdded(this, stage); + + OnMaterialChanged(); +} + +/** +* Removes a stage from the material. +* @param stage The stage to remove. +* @param addUndo Flag that specifies if the system should add an undo operation. +*/ +void MaterialDoc::RemoveStage(int stage, bool addUndo) { + assert(stage >= 0 && stage < GetStageCount()); + + if(addUndo) { + //Add modifier to undo this operation + StageDeleteModifier* mod = new StageDeleteModifier(manager, name, stage, editMaterial.stages[stage]->stageData); + manager->AddMaterialUndoModifier(mod); + } + + //delete the stage and remove it from the list + delete editMaterial.stages[stage]; + editMaterial.stages.RemoveIndex(stage); + + manager->StageDeleted(this, stage); + + OnMaterialChanged(); +} + +/** +* Removes all stages from the material. +*/ +void MaterialDoc::ClearStages() { + + //Delete each stage and clear the list + for(int i = GetStageCount() - 1; i >= 0; i--) { + RemoveStage(i); + } +} + +/** +* Moves a stage from one location to another. +* @param from The original location of the stage. +* @param to The new location of the stage. +* @param addUndo Flag that specifies if the system should add an undo operation. +*/ +void MaterialDoc::MoveStage(int from, int to, bool addUndo) { + assert(from >= 0 && from < GetStageCount()); + assert(to >= 0 && to < GetStageCount()); + + int origFrom = from; + int origTo = to; + + if(from < to) + to++; + + MEStage_t* pMove = editMaterial.stages[from]; + editMaterial.stages.Insert(pMove, to); + + if(from > to) + from++; + + editMaterial.stages.RemoveIndex(from); + + manager->StageMoved(this, origFrom, origTo); + + if(addUndo) { + StageMoveModifier *mod = new StageMoveModifier(manager, name, origFrom, origTo); + manager->AddMaterialUndoModifier(mod); + } + + OnMaterialChanged(); +} + +/** +* Applies any changes to the material +* @param force If true then the material will be applied regardless of the number of changes. +*/ +void MaterialDoc::ApplyMaterialChanges(bool force) { + + if(force || applyWaiting) { + + if(sourceModify && sourceModifyOwner) { + idStr text = sourceModifyOwner->GetSourceText(); + ApplySourceModify(text); + } + + ReplaceSourceText(); + + char *declText = (char *) _alloca( renderMaterial->GetTextLength() + 1 ); + renderMaterial->GetText( declText ); + + renderMaterial->GetText(declText); + + ParseMaterialText(declText); + + applyWaiting = false; + + assert(manager); + manager->MaterialApplied(this); + } +} + +/** +* Saves the material. +*/ +void MaterialDoc::Save() { + + EnableAllStages(true); + + //Apply the material so that the renderMaterial has the source text + if(!deleted) { + ApplyMaterialChanges(true); + } else { + //Replace the text with nothing + renderMaterial->SetText(" "); + } + + if(renderMaterial->Save()) { + + modified = false; + + //Notify the world + assert(manager); + manager->MaterialSaved(this); + } else { + // FIXME: finish this + //MessageBox(GetMaterialEditorWindow(), va("Unable to save '%s'. It may be read-only", name.c_str()), "Save Error", MB_OK | MB_ICONERROR); + } +} + +/** +* Deletes the material. +*/ +void MaterialDoc::Delete() { + deleted = true; + + OnMaterialChanged(); +} + +/** +* Sets the proper internal states and notifies the MaterialDocManager once a material has been changed. +*/ +void MaterialDoc::OnMaterialChanged() { + + modified = true; + applyWaiting = true; + + assert(manager); + manager->MaterialChanged(this); +} + +/** +* Passes text to a render material for parsing. +* @param source The text that sould be applied to the idMaterial. +*/ +void MaterialDoc::ParseMaterialText(const char* source) { + + /*idLexer src; + src.LoadMemory(source, strlen(source), "material"); + src.SetFlags( + LEXFL_NOSTRINGCONCAT | // multiple strings seperated by whitespaces are not concatenated + LEXFL_NOSTRINGESCAPECHARS | // no escape characters inside strings + LEXFL_ALLOWPATHNAMES | // allow path seperators in names + LEXFL_ALLOWMULTICHARLITERALS | // allow multi character literals + LEXFL_ALLOWBACKSLASHSTRINGCONCAT | // allow multiple strings seperated by '\' to be concatenated + LEXFL_NOFATALERRORS // just set a flag instead of fatal erroring + ); + + //Skip the name becuase the material parsing code expects it + src.SkipUntilString("{");*/ + + //Now let the material parse the text + renderMaterial->Parse(source, strlen(source)); +} + +/** +* Parses the source text from an idMaterial and initializes the editor dictionary representation +* of the material. +* @param src The idLexer object that contains the material text. +*/ +void MaterialDoc::ParseMaterial(idLexer* src) { + + idToken token; + + //Parse past the name + src->SkipUntilString("{"); + + while ( 1 ) { + if ( !src->ExpectAnyToken( &token ) ) { + //Todo: Add some error checking here + return; + } + + if ( token == "}" ) { + break; + } + + if(ParseMaterialDef(&token, src, MaterialDefManager::MATERIAL_DEF_MATERIAL, &editMaterial.materialData)) { + continue; + } + + if ( !token.Icmp( "diffusemap" ) ) { + //Added as a special stage + idStr str; + src->ReadRestOfLine( str ); + AddSpecialMapStage("diffusemap", str); + } + else if ( !token.Icmp( "specularmap" ) ) { + idStr str; + src->ReadRestOfLine( str ); + AddSpecialMapStage("specularmap", str); + } + else if ( !token.Icmp( "bumpmap" ) ) { + idStr str; + src->ReadRestOfLine( str ); + AddSpecialMapStage("bumpmap", str); + } + else if( token == "{" ) { + ParseStage(src); + } + } +} + +/** +* Parses a single stage from the source text from an idMaterial and initializes the editor dictionary +* representation of the material. +* @param src The idLexer object that contains the material text. +*/ +void MaterialDoc::ParseStage(idLexer* src) { + + MEStage_t* newStage = new MEStage_t(); + int index = editMaterial.stages.Append(newStage); + + newStage->stageData.SetInt("stagetype", STAGE_TYPE_NORMAL); + newStage->enabled = true; + + idToken token; + + while ( 1 ) { + + if ( !src->ExpectAnyToken( &token ) ) { + //Todo: Add some error checking here + return; + } + + if ( token == "}" ) { + break; + } + + if(ParseMaterialDef(&token, src, MaterialDefManager::MATERIAL_DEF_STAGE, &newStage->stageData)) { + continue; + } + + if(!token.Icmp("name")) { + + idStr str; + src->ReadRestOfLine( str ); + str.StripTrailing('\"'); + str.StripLeading('\"'); + newStage->stageData.Set("name", str); + continue; + } + } + + idStr name; + newStage->stageData.GetString("name", "", name); + if(name.Length() <= 0) + newStage->stageData.Set("name", va("Stage %d", index+1)); + +} + +/** +* Adds a special stage to the material. +* @param stageName The name of the special stage bumpmap, diffusemap or specularmap +* @param map The map for the special stage. +*/ +void MaterialDoc::AddSpecialMapStage(const char* stageName, const char* map) { + MEStage_t* newStage = new MEStage_t(); + newStage->stageData.Set("name", stageName); + newStage->stageData.Set("map", map); + newStage->stageData.SetInt("stagetype", STAGE_TYPE_SPECIALMAP); + newStage->enabled = true; +} + +/** +* Finds the appropriate material definition for the supplied token and initializes the +* internal dictionary data. +* @param token The token to lookup +* @param src The idLexer that contains the material source text. +* @param type The type of attribute grouping to use material, stage or special stage. +* @param dict The dictionary to initialize. +*/ +bool MaterialDoc::ParseMaterialDef(idToken* token, idLexer* src, int type, idDict* dict) { + + MaterialDefList* defs = MaterialDefManager::GetMaterialDefs(type); + + for(int i = 0; i < defs->Num(); i++) { + if(!token->Icmp((*defs)[i]->dictName)) { + + switch((*defs)[i]->type) { + case MaterialDef::MATERIAL_DEF_TYPE_STRING: + { + idStr str; + src->ReadRestOfLine( str ); + if((*defs)[i]->quotes) { + str.StripTrailing('\"'); + str.StripLeading('\"'); + } + dict->Set((*defs)[i]->dictName, str); + } + break; + case MaterialDef::MATERIAL_DEF_TYPE_BOOL: + { + src->SkipRestOfLine(); + dict->SetBool((*defs)[i]->dictName, true); + } + break; + case MaterialDef::MATERIAL_DEF_TYPE_FLOAT: + { + idStr str; + src->ReadRestOfLine( str ); + dict->Set((*defs)[i]->dictName, str); + } + break; + case MaterialDef::MATERIAL_DEF_TYPE_INT: + { + idStr str; + src->ReadRestOfLine( str ); + dict->Set((*defs)[i]->dictName, str); + } + break; + } + return true; + } + } + return false; +} + +/** +* Cleans up the edit material by deleting the stage data structures. +*/ +void MaterialDoc::ClearEditMaterial() { + + for(int i = 0; i < GetStageCount(); i++) { + delete editMaterial.stages[i]; + } + editMaterial.stages.Clear(); + editMaterial.materialData.Clear(); +} + +/** +* Writes the internal dictionary data to the standard format. +*/ +const char* MaterialDoc::GenerateSourceText() { + + idFile_Memory f; + + f.WriteFloatString("\n\n/*\n" + "\tGenerated by the Material Editor.\n" + "\tType 'materialeditor' at the console to launch the material editor.\n" + "*/\n" ); + + f.WriteFloatString("%s\n", name.c_str()); + f.WriteFloatString( "{\n" ); + WriteMaterialDef(-1, &f, MaterialDefManager::MATERIAL_DEF_MATERIAL, 1); + + for(int i = 0; i < editMaterial.stages.Num(); i++) { + if(editMaterial.stages[i]->enabled) { + WriteStage(i, &f); + } + } + + f.WriteFloatString( "}\n" ); + + return f.GetDataPtr(); + +} + +/** +* Writes the internal dictionary data to the standard format and replaces the +* idMaterial source text with the newly generated text. +*/ +void MaterialDoc::ReplaceSourceText() { + renderMaterial->SetText(GenerateSourceText()); +} + +/** +* Writes a single stage. +* @param stage The stage to write. +* @param file The file where the stage should be wirtten +*/ +void MaterialDoc::WriteStage(int stage, idFile_Memory* file) { + + //idStr stageName = GetAttribute(stage, "name"); + int type = GetAttributeInt(stage, "stagetype"); + //if(!stageName.Icmp("diffusemap") || !stageName.Icmp("specularmap") || !stageName.Icmp("bumpmap")) { + if(type == STAGE_TYPE_SPECIALMAP) { + WriteSpecialMapStage(stage, file); + return; + } + + file->WriteFloatString( "\t{\n" ); + idStr name = GetAttribute(stage, "name"); + if(name.Length() > 0) { + file->WriteFloatString("\t\tname\t\"%s\"\n", name.c_str()); + } + WriteMaterialDef(stage, file, MaterialDefManager::MATERIAL_DEF_STAGE, 2); + file->WriteFloatString( "\t}\n" ); + +} + +/** +* Writes a single special stage. +* @param stage The stage to write. +* @param file The file where the stage should be wirtten +*/ +void MaterialDoc::WriteSpecialMapStage(int stage, idFile_Memory* file) { + idStr stageName = GetAttribute(stage, "name"); + idStr map = GetAttribute(stage, "map"); + + file->WriteFloatString( "\t%s\t%s\n", stageName.c_str(), map.c_str() ); +} + +/** +* Writes a set of material attributes to a file. +* @param stage The stage to write or -1 for the material. +* @param file The file where the stage should be wirtten. +* @param type The attribute grouping to use. +* @param indent The number of tabs to indent the text. +*/ +void MaterialDoc::WriteMaterialDef(int stage, idFile_Memory* file, int type, int indent) { + + idStr prefix = ""; + for(int i = 0; i < indent; i++) { + prefix += "\t"; + } + + MaterialDefList* defs = MaterialDefManager::GetMaterialDefs(type); + for(int i = 0; i < defs->Num(); i++) { + switch((*defs)[i]->type) { + case MaterialDef::MATERIAL_DEF_TYPE_STRING: + { + idStr attrib = GetAttribute(stage, (*defs)[i]->dictName); + if(attrib.Length() > 0) { + if((*defs)[i]->quotes) + file->WriteFloatString("%s%s\t\"%s\"\n", prefix.c_str(), (*defs)[i]->dictName.c_str(), attrib.c_str()); + else + file->WriteFloatString("%s%s\t%s\n", prefix.c_str(), (*defs)[i]->dictName.c_str(), attrib.c_str()); + } + } + break; + case MaterialDef::MATERIAL_DEF_TYPE_BOOL: + { + if(GetAttributeBool(stage, (*defs)[i]->dictName)) + file->WriteFloatString("%s%s\t\n",prefix.c_str(), (*defs)[i]->dictName.c_str()); + } + break; + case MaterialDef::MATERIAL_DEF_TYPE_FLOAT: + { + float val = GetAttributeFloat(stage, (*defs)[i]->dictName); + file->WriteFloatString("%s%s\t%f\n", prefix.c_str(), (*defs)[i]->dictName.c_str(), val); + } + break; + case MaterialDef::MATERIAL_DEF_TYPE_INT: + { + int val = GetAttributeInt(stage, (*defs)[i]->dictName); + file->WriteFloatString("%s%s\t%d\n", prefix.c_str(), (*defs)[i]->dictName.c_str(), val); + } + break; + } + } +} + +} diff --git a/neo/tools/imgui/materialeditor/MaterialDoc.h b/neo/tools/imgui/materialeditor/MaterialDoc.h new file mode 100644 index 000000000..c3088f66b --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialDoc.h @@ -0,0 +1,165 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef MATERIALDOC_H__ +#define MATERIALDOC_H__ + +#include "MaterialEditor.h" +#include "MaterialModifier.h" +#include "MaterialDef.h" + +class idMaterial; +class idFile_Memory; + +namespace ImGuiTools { + +/** +* Dictionary representation of a Material Stage. +*/ +typedef struct { + idDict stageData; + bool enabled; +} MEStage_t; + +/** +* Dictionary representation of a material. +*/ +typedef struct { + idDict materialData; + idList stages; +} MEMaterial_t; + +/** +* Implemented by the edit window that is responsible for modifying the material source text. +*/ +class SourceModifyOwner { + +public: + SourceModifyOwner() {}; + virtual ~SourceModifyOwner() {}; + + virtual idStr GetSourceText() { return ""; }; +}; + +class MaterialDocManager; + +/** +* Responsible for managing a single material that is being viewed and/or edited. +*/ +class MaterialDoc { + +public: + MaterialDocManager* manager; + idStr name; + idMaterial* renderMaterial; + MEMaterial_t editMaterial; + + bool modified; + bool applyWaiting; + bool deleted; + + bool sourceModify; + SourceModifyOwner* sourceModifyOwner; + +public: + MaterialDoc(void); + ~MaterialDoc(void); + + /** + * Define the types of stages in a material. + */ + enum { + STAGE_TYPE_NORMAL, + STAGE_TYPE_SPECIALMAP + }; + + //Initialization Methods + void SetRenderMaterial(idMaterial* material, bool parseMaterial = true, bool parseRenderMatierial = false); + + //Stage Info Methods + int GetStageCount(); + int FindStage(int stageType, const char* name); + MEStage_t GetStage(int stage); + void EnableStage(int stage, bool enabled); + void EnableAllStages(bool enabled); + bool IsStageEnabled(int stage); + + //Get Attributes + const char* GetAttribute(int stage, const char* attribName, const char* defaultString = ""); + int GetAttributeInt(int stage, const char* attribName, const char* defaultString = "0"); + float GetAttributeFloat(int stage, const char* attribName, const char* defaultString = "0"); + bool GetAttributeBool(int stage, const char* attribName, const char* defaultString = "0"); + + //Set Attribute Methods + void SetAttribute(int stage, const char* attribName, const char* value, bool addUndo = true); + void SetAttributeInt(int stage, const char* attribName, int value, bool addUndo = true); + void SetAttributeFloat(int stage, const char* attribName, float value, bool addUndo = true); + void SetAttributeBool(int stage, const char* attribName, bool value, bool addUndo = true); + void SetMaterialName(const char* materialName, bool addUndo = true); + void SetData(int stage, idDict* data); + + //Source Editing Methods + void SourceModify(SourceModifyOwner* owner); + bool IsSourceModified(); + void ApplySourceModify(idStr& text); + const char* GetEditSourceText(); + + //Stage Modification Methods + void AddStage(int stageType, const char* stageName, bool addUndo = true); + void InsertStage(int stage, int stageType, const char* stageName, bool addUndo = true); + void RemoveStage(int stage, bool addUndo = true); + void ClearStages(); + void MoveStage(int from, int to, bool addUndo = true); + + void ApplyMaterialChanges(bool force = false); + void Save(); + void Delete(); + +protected: + + //Internal Notifications + void OnMaterialChanged(); + + //Load Material Methods + void ParseMaterialText(const char* source); + void ParseMaterial(idLexer* src); + void ParseStage(idLexer* src); + void AddSpecialMapStage(const char* stageName, const char* map); + bool ParseMaterialDef(idToken* token, idLexer* src, int type, idDict* dict); + void ClearEditMaterial(); + + //Save/Apply Material Methods + const char* GenerateSourceText(); + void ReplaceSourceText(); + void WriteStage(int stage, idFile_Memory* file); + void WriteSpecialMapStage(int stage, idFile_Memory* file); + void WriteMaterialDef(int stage, idFile_Memory* file, int type, int indent); +}; + +} + +#endif /* !MATERIALDOC_H__ */ diff --git a/neo/tools/imgui/materialeditor/MaterialDocManager.cpp b/neo/tools/imgui/materialeditor/MaterialDocManager.cpp new file mode 100644 index 000000000..495a7d3ff --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialDocManager.cpp @@ -0,0 +1,897 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "renderer/Material.h" + + +#include "MaterialDocManager.h" +#include "MaterialView.h" + +namespace ImGuiTools { + + +/** +* Constructor for MaterialDocManager. +*/ +MaterialDocManager::MaterialDocManager(void) { + currentMaterial = NULL; + cutMaterial = false; +} + +/** +* Destructor for MaterialDocManager. +*/ +MaterialDocManager::~MaterialDocManager(void) { + UnRegisterAllMaterialViews(); + + ClearUndo(); + ClearRedo(); +} + +/** +* Registers an object to receive notifications about changes made to materials. +* @param view The object that would like to receive material notifications. +*/ +void MaterialDocManager::RegisterMaterialView(MaterialView* view) { + assert(view); + UnRegisterMaterialView(view); + materialViews.Append(view); + + //Notify the view of myself + view->SetMaterialDocManager(this); +} + +/** +* Tells the MaterialDocManager to stop sending notifications to a view. +* @param view The view that no longer wants notifications. +*/ +void MaterialDocManager::UnRegisterMaterialView(MaterialView* view) { + assert(view); + materialViews.Remove(view); + + //Remove the reference to myself + view->SetMaterialDocManager(NULL); +} + +/** +* Unregisters all of the views that are registered to get material change +* notifications. +*/ +void MaterialDocManager::UnRegisterAllMaterialViews() { + + //Remove the reference to myself + int c = materialViews.Num(); + for(int i = 0; i < c; i++) { + materialViews[i]->SetMaterialDocManager(NULL); + } + materialViews.Clear(); +} + +/** +* Tells the MaterialDocManager which material has been selected for editing. +* @param material The material that has been selected. +*/ +void MaterialDocManager::SetSelectedMaterial(idMaterial* material) { + + bool change = false; + + //Do we need to change the material + if(material) { + if(currentMaterial) { + if(strcmp(material->GetName(), currentMaterial->renderMaterial->GetName())) { + change = true; + } + } else { + change = true; + } + } else { + if(currentMaterial) { + change = true; + } + } + + //Now make the change + if(change) { + if(currentMaterial) { + + //Delete the material unless it has been changed + if(!inProgressMaterials.Get(currentMaterial->name.c_str())) { + delete currentMaterial; + currentMaterial = NULL; + } + } + + MaterialDoc** tempDoc; + if(material && inProgressMaterials.Get(material->GetName(), &tempDoc)) { + currentMaterial = *tempDoc; + + } else { + currentMaterial = CreateMaterialDoc(material); + } + + NotifyViews(currentMaterial, SELECTION_CHANGE); + } +} + +/** +* Returns true if the specified file needs to be applied and false otherwise. +*/ +bool MaterialDocManager::DoesFileNeedApply(const char* filename) { + for(int i = 0; i < inProgressMaterials.Num(); i++) { + MaterialDoc** pDoc = inProgressMaterials.GetIndex(i); + if(!strcmp((*pDoc)->renderMaterial->GetFileName(), filename) && (*pDoc)->applyWaiting) + return true; + } + return false; +} + +/** +* Returns true if any material needs to be applied. +*/ +bool MaterialDocManager::DoesAnyNeedApply() { + for(int i = 0; i < inProgressMaterials.Num(); i++) { + MaterialDoc** pDoc = inProgressMaterials.GetIndex(i); + if((*pDoc)->applyWaiting) + return true; + } + return false; +} + +/** +* Returns true if the specified file has been modified. +*/ +bool MaterialDocManager::IsFileModified(const char* filename) { + for(int i = 0; i < inProgressMaterials.Num(); i++) { + MaterialDoc** pDoc = inProgressMaterials.GetIndex(i); + if(!strcmp((*pDoc)->renderMaterial->GetFileName(), filename)) + return true; + } + return false; +} + +/** +* Returns true if any material has been modified. +*/ +bool MaterialDocManager::IsAnyModified() { + return (inProgressMaterials.Num() > 0); +} + +/** +* Adds a material. +* @param name The name of the material. +* @param filename The file to place the material in. +* @param sourceText The initial material definition. +* @param addUndo Can this operation be undone. +*/ +void MaterialDocManager::AddMaterial(const char* name, const char* filename, const char* sourceText, bool addUndo) { + + if(addUndo) { + AddMaterialModifier* mod = new AddMaterialModifier(this, name, filename); + AddMaterialUndoModifier(mod); + } + + MaterialDoc* newDoc = new MaterialDoc(); + newDoc->manager = this; + newDoc->modified = true; + + idMaterial* rendMat = (idMaterial*)declManager->CreateNewDecl(DECL_MATERIAL, name, filename); + + if(sourceText) { + rendMat->SetText(sourceText); + } + + newDoc->SetRenderMaterial(rendMat, true, sourceText ? true : false); + + inProgressMaterials.Set(newDoc->name.c_str(), newDoc); + + NotifyViews(newDoc, MATERIAL_ADD); + + //Force an apply so the text will be generated to match the new file + newDoc->applyWaiting = true; + newDoc->ApplyMaterialChanges(); +} + +/** +* Used to redo an add material and undo a delete material. +* The undo for adding a material deletes the material. Instead of adding a completely +* new material RedoAddMaterial finds the one that was just deleted and uses that. +* @param name The name of the material that was added/deleted. +* @param clearData Should the material definition be reset to the default definition. +*/ +void MaterialDocManager::RedoAddMaterial(const char* name, bool clearData) { + + MaterialDoc* newDoc = new MaterialDoc(); + newDoc->manager = this; + newDoc->modified = true; + + idMaterial* rendMat = const_cast(declManager->FindMaterial(name, false)); + + if(clearData) { + rendMat->SetText(rendMat->DefaultDefinition()); + } + + newDoc->SetRenderMaterial(rendMat, true, true); + + inProgressMaterials.Set(newDoc->name.c_str(), newDoc); + + NotifyViews(newDoc, MATERIAL_ADD); + + //Force an apply so the text will be generated to match the new file + newDoc->applyWaiting = true; + newDoc->ApplyMaterialChanges(); +} + +/** +* Deletes a material. +* @param material The material to be deleted. +* @param addUndo Can this operation be undone. +*/ +void MaterialDocManager::DeleteMaterial(MaterialDoc* material, bool addUndo) { + + assert(material); + + //This will just flag for delete. The actual delete will happen during the save + material->Delete(); + + if(addUndo) { + DeleteMaterialModifier* mod = new DeleteMaterialModifier(this, material->name); + AddMaterialUndoModifier(mod); + } + + NotifyViews(material, MATERIAL_DELETE); +} + +/** +* Applys changes to a material. +* @param materialDoc The material to be applied. +*/ +void MaterialDocManager::ApplyMaterial(MaterialDoc* materialDoc) { + assert(materialDoc); + materialDoc->ApplyMaterialChanges(); +} + +/** +* Applies all materials in the specified filename. +* @param filename The file to apply. +*/ +void MaterialDocManager::ApplyFile(const char* filename) { + + for(int i = 0; i < inProgressMaterials.Num(); i++) { + MaterialDoc** pDoc = inProgressMaterials.GetIndex(i); + if(!strcmp((*pDoc)->renderMaterial->GetFileName(), filename)) + (*pDoc)->ApplyMaterialChanges(); + } +} + +/** +* Applies all materials that have been changed. +*/ +void MaterialDocManager::ApplyAll() { + for(int i = 0; i < inProgressMaterials.Num(); i++) { + MaterialDoc** pDoc = inProgressMaterials.GetIndex(i); + (*pDoc)->ApplyMaterialChanges(); + } +} + +/** +* Saves a single material. +* @param material The material to save. +*/ +void MaterialDocManager::SaveMaterial(MaterialDoc* material) { + assert(material); + material->Save(); +} + +/** +* Saves all materials in the specified file. +* @param filename The file to save. +*/ +void MaterialDocManager::SaveFile(const char* filename) { + + for(int i = inProgressMaterials.Num()-1; i >= 0; i--) { + MaterialDoc** pDoc = inProgressMaterials.GetIndex(i); + if(!strcmp((*pDoc)->renderMaterial->GetFileName(), filename)) + (*pDoc)->Save(); + } + + //Notify everyone + NotifyViews(NULL, MATERIAL_SAVE_FILE, filename); +} + +/** +* Saves all materials that have been changed. +*/ +void MaterialDocManager::SaveAllMaterials() { + for(int i = inProgressMaterials.Num()-1; i >= 0; i--) { + MaterialDoc** pDoc = inProgressMaterials.GetIndex(i); + (*pDoc)->Save(); + } +} + +/** +* Reloads a specified file. +* @param filename The file to reload. +*/ +void MaterialDocManager::ReloadFile(const char *filename) { + + declManager->ReloadFile(filename, true); + + //purge the changes of any in progress materials + for(int j = inProgressMaterials.Num()-1; j >= 0; j--) { + MaterialDoc** pDoc = inProgressMaterials.GetIndex(j); + if(!strcmp((*pDoc)->renderMaterial->GetFileName(), filename)) { + (*pDoc)->SetRenderMaterial((*pDoc)->renderMaterial); + inProgressMaterials.Remove((*pDoc)->name); + } + } + + //Reparse the current material + if(currentMaterial) { + currentMaterial->SetRenderMaterial(currentMaterial->renderMaterial); + + //Trigger all the views to refresh + NotifyViews(currentMaterial, SELECTION_CHANGE); + } + + NotifyViews(NULL, FILE_RELOAD, filename); +} + +/** +* Creates a MaterialDoc object for the specified material name. If a MaterialDoc +* object already exists then it is used. +* @param materialName The name of the material for which to create a MaterialDoc object. +*/ +MaterialDoc* MaterialDocManager::CreateMaterialDoc(const char* materialName) { + + const idMaterial* material = declManager->FindMaterial(materialName); + return CreateMaterialDoc(const_cast(material)); +} + +/** +* Creates a MaterialDoc object for the specified material. If a MaterialDoc +* object already exists then it is used. +* @param material The material for which to create a MaterialDoc object. +*/ +MaterialDoc* MaterialDocManager::CreateMaterialDoc(idMaterial* material) { + + MaterialDoc* existingDoc = GetInProgressDoc(material); + if(existingDoc) { + return existingDoc; + } + + if(currentMaterial && material && !currentMaterial->name.Icmp(material->GetName())) { + return currentMaterial; + } + + if(material) { + MaterialDoc* newDoc = new MaterialDoc(); + newDoc->manager = this; + newDoc->SetRenderMaterial(material); + + return newDoc; + } + + return NULL; +} + +/** +* Checks the current list of in progress MaterialDoc objects to see if +* a MaterialDoc object already exists. +* @param material The material to check for. +*/ +MaterialDoc* MaterialDocManager::GetInProgressDoc(idMaterial* material) { + + if(material) { + for(int i = 0; i < inProgressMaterials.Num(); i++) { + MaterialDoc** pDoc = inProgressMaterials.GetIndex(i); + + if(!(*pDoc)->name.Icmp(material->GetName())) + return *pDoc; + } + } + return NULL; +} + +/** +* Prepares a material for a copy/cut and paste operations. +* @param materialDoc The material to copy. +* @param cut Is this a cut operation. +*/ +void MaterialDocManager::CopyMaterial(MaterialDoc* materialDoc, bool cut) { + + cutMaterial = cut; + + if(materialDoc) + copyMaterial = materialDoc->name; + else + ClearCopy(); +} + +/** +* Clears the copy buffer for a material. +*/ +void MaterialDocManager::ClearCopy() { + copyMaterial.Empty(); +} + +/** +* Returns true if there is a material in the copy buffer. +*/ +bool MaterialDocManager::IsCopyMaterial() { + return (copyMaterial.Length() ) ? true : false; +} + +/** +* Returns the name of the material in the copy buffer. +*/ +idStr MaterialDocManager::GetCopyMaterialName() { + return copyMaterial; +} + +/** +* Performs a material paste operation for a material in the copy buffer. +* @param name The new name for the material that is being copied. +* @param filename The file to paste the material in. +*/ +void MaterialDocManager::PasteMaterial(const char* name, const char* filename) { + + if(!IsCopyMaterial()) { + return; + } + + //Apply the material if there are some changes + MaterialDoc* copyMat = CreateMaterialDoc(copyMaterial); + if(copyMat->applyWaiting) { + copyMat->ApplyMaterialChanges(); + } + //Paste the material + idMaterial* material = copyMat->renderMaterial; + + //Add a material with the existing source text + char *declText = (char *) _alloca( material->GetTextLength() + 1 ); + material->GetText( declText ); + + AddMaterial(name, filename, declText, !cutMaterial); + + //If this is a cut then remove the original + if(cutMaterial) { + MaterialDoc* cutMaterial = CreateMaterialDoc(material); + DeleteMaterial(cutMaterial, false); + + MoveMaterialModifier* mod = new MoveMaterialModifier(this, name, filename, copyMaterial); + AddMaterialUndoModifier(mod); + + ClearCopy(); + } + +} + +/** +* Prepares a material stage for a copy/paste operation. +* @param materialDoc The materialDoc that contains the stage to be copied. +* @param stageNum the stage to copy. +*/ +void MaterialDocManager::CopyStage(MaterialDoc* materialDoc, int stageNum) { + + assert(materialDoc); + + copyStageMaterial = materialDoc->name; + copyStage = materialDoc->GetStage(stageNum); + + idStr stageName = copyStage.stageData.GetString("name"); +} + +/** +* Clears the copy buffer for copied stages. +*/ +void MaterialDocManager::ClearCopyStage() { + copyStageMaterial.Empty(); + copyStage.stageData.Clear(); +} + +/** +* Returns true if there is a stage in the copy buffer. +*/ +bool MaterialDocManager::IsCopyStage() { + return (copyStageMaterial.Length() ) ? true : false; +} + +/** +* Performs a paste operation of the stage in the copy buffer. +* @param materialDoc The materialDoc to paste the stage in. +*/ +void MaterialDocManager::PasteStage(MaterialDoc* materialDoc) { + + assert(materialDoc); + + int stageType = copyStage.stageData.GetInt("stagetype"); + + //Create a new stage and copy the data + materialDoc->AddStage(stageType, copyStage.stageData.GetString("name")); + materialDoc->SetData(materialDoc->GetStageCount()-1, ©Stage.stageData); +} + +/** +* Returns information about the stage in the copy buffer. +* @param type Holds the type of the stage in the copy buffer. +* @param name Hold the name of the stage in the copy buffer. +*/ +void MaterialDocManager::GetCopyStageInfo(int& type, idStr& name) { + if(IsCopyStage()) { + type = copyStage.stageData.GetInt("stagetype"); + name = copyStage.stageData.GetString("name"); + } +} + +/** +* Performs the first available undo operation. +*/ +void MaterialDocManager::Undo() { + + if(IsUndoAvailable()) { + MaterialModifier* mod = undoModifiers[undoModifiers.Num()-1]; + undoModifiers.RemoveIndex(undoModifiers.Num()-1); + + mod->Undo(); + + //Add this modifier to the redo list + AddMaterialRedoModifier(mod); + } +} + +/** +* Returns true if an undo operation is available. +*/ +bool MaterialDocManager::IsUndoAvailable() { + return (undoModifiers.Num() > 0); +} + +/** +* Clears the entire undo buffer. +*/ +void MaterialDocManager::ClearUndo() { + + int c = undoModifiers.Num(); + for(int i = 0; i < c; i++) { + delete undoModifiers[i]; + } + undoModifiers.Clear(); +} + +/** +* Performs the first available redo operation. +*/ +void MaterialDocManager::Redo() { + + if(IsRedoAvailable()) { + MaterialModifier* mod = redoModifiers[redoModifiers.Num()-1]; + redoModifiers.RemoveIndex(redoModifiers.Num()-1); + + mod->Redo(); + + //Done with the mod because the redo process will set + //attributes and create the appropriate redo modifier + AddMaterialUndoModifier(mod, false); + } +} + +/** +* Returns true if a redo operation is available. +*/ +bool MaterialDocManager::IsRedoAvailable() { + return (redoModifiers.Num() > 0); +} + +/** +* Clears the redo buffer. +*/ +void MaterialDocManager::ClearRedo() { + + int c = redoModifiers.Num(); + for(int i = 0; i < c; i++) { + delete redoModifiers[i]; + } + redoModifiers.Clear(); +} + +/** +* Adds an undo operation to the undo buffer. +* @param mod The MaterialModifier object that contains the undo data. +* @param clearRedo Should we clear the redo buffer. +*/ +void MaterialDocManager::AddMaterialUndoModifier(MaterialModifier* mod, bool clearRedo) { + undoModifiers.Append(mod); + + while(undoModifiers.Num() > MAX_UNDOREDO) { + undoModifiers.RemoveIndex(0); + } + + if(clearRedo) { + ClearRedo(); + } +} + +/** +* Adds a redo operation to the redo buffer. +* @param mod The MaterialModifier object that contains the redo data. +*/ +void MaterialDocManager::AddMaterialRedoModifier(MaterialModifier* mod) { + redoModifiers.Append(mod); + + while(redoModifiers.Num() > MAX_UNDOREDO) { + redoModifiers.RemoveIndex(0); + } +} + +/** +* Searches for a material that matches the specified search data. +* @param name The name of the material to search. +* @param searchData The search parameters. +* @param checkName If true then the name of the material will be checked along with the material text. +*/ +bool MaterialDocManager::FindMaterial(const char* name, MaterialSearchData_t* searchData, bool checkName) { + + //Fast way of finding the material without parsing + const idMaterial* material = static_cast(declManager->FindDeclWithoutParsing(DECL_MATERIAL, name, false)); + + if(material) { + + int findPos; + + if(checkName) { + //Check the name + idStr name = material->GetName(); + + findPos = name.Find(searchData->searchText, false); + if(findPos != -1) { + return true; + } + } + + //Skip to the open braket so the name is not checked + char *declText = (char *) _alloca( material->GetTextLength() + 1 ); + material->GetText( declText ); + + idStr text = declText; + int start = text.Find("{"); + if(start != -1) { + text = text.Right(text.Length()-start); + } + + findPos = text.Find(searchData->searchText, false); + if(findPos != -1) { + //Todo: Include match whole word + return true; + } + } + return false; +} + +/** +* Returns a unique material name given a base name. This is used to resolve materials with the same name. +* @param name The base name of the material. +*/ +idStr MaterialDocManager::GetUniqueMaterialName(idStr name) { + int num = 0; + while(1) { + idStr testName; + if(num == 0) + testName = name; + else + testName = va("%s%d", name.c_str(), num); + + const idMaterial* mat = declManager->FindMaterial(testName.c_str(), false); + if(!mat) { + return testName; + } else { + + //We can reuse delete material names + if(mat->GetTextLength() < 1) + return testName; + } + num++; + } +} + +/** +* Notifies all registered views of a material event. +* @param materialDoc The material that has been affected. +* @param notifyType The type of event that has occured. +* @param ... Notification specific data. See MaterialView. +*/ +void MaterialDocManager::NotifyViews(MaterialDoc* materialDoc, int notifyType, ... ) { + + va_list argptr; + + int c = materialViews.Num(); + for(int i = 0; i < c; i++) { + va_start( argptr, notifyType ); + switch(notifyType) { + case SELECTION_CHANGE: + materialViews[i]->MV_OnMaterialSelectionChange(materialDoc); + break; + case MATERIAL_CHANGE: + materialViews[i]->MV_OnMaterialChange(materialDoc); + break; + case MATERIAL_APPLY: + materialViews[i]->MV_OnMaterialApply(materialDoc); + break; + case MATERIAL_SAVE: + materialViews[i]->MV_OnMaterialSaved(materialDoc); + break; + case MATERIAL_SAVE_FILE: + materialViews[i]->MV_OnMaterialSaveFile(va_arg(argptr, const char*)); + break; + case MATERIAL_ADD: + materialViews[i]->MV_OnMaterialAdd(materialDoc); + break; + case MATERIAL_DELETE: + materialViews[i]->MV_OnMaterialDelete(materialDoc); + break; + case MATERIAL_ADD_STAGE: + materialViews[i]->MV_OnMaterialStageAdd(materialDoc, va_arg(argptr, int)); + break; + case MATERIAL_DELETE_STAGE: + materialViews[i]->MV_OnMaterialStageDelete(materialDoc, va_arg(argptr, int)); + break; + case MATERIAL_MOVE_STAGE: + { + int from = va_arg(argptr, int); + int to = va_arg(argptr, int); + materialViews[i]->MV_OnMaterialStageMove(materialDoc, from, to); + } + break; + case MATERIAL_ATTRIBUTE_CHANGE: + { + int stage = va_arg(argptr, int); + const char* attribName = va_arg(argptr, const char*); + materialViews[i]->MV_OnMaterialAttributeChanged(materialDoc, stage, attribName); + } + break; + case MATERIAL_NAME_CHANGE: + { + const char* oldName = va_arg(argptr, const char*); + materialViews[i]->MV_OnMaterialNameChanged(materialDoc, oldName); + } + break; + case FILE_RELOAD: + { + const char* filename = va_arg(argptr, const char*); + materialViews[i]->MV_OnFileReload(filename); + } + break; + } + va_end( argptr ); + } +} + +/** +* Called when a material has been edited and notifies all views of the change. +* @param materialDoc The material that has changed. +*/ +void MaterialDocManager::MaterialChanged(MaterialDoc* materialDoc) { + + //Make sure this material is in our list of changed materials + if(!inProgressMaterials.Get(materialDoc->name.c_str())) { + inProgressMaterials.Set(materialDoc->name.c_str(), materialDoc); + } + + //Notify everyone + NotifyViews(materialDoc, MATERIAL_CHANGE); +} + +/** +* Called when a material has been applied and notifies all views of the apply. +* @param materialDoc The material that has been applied. +*/ +void MaterialDocManager::MaterialApplied(MaterialDoc* materialDoc) { + + //Notify everyone + NotifyViews(materialDoc, MATERIAL_APPLY); +} + +/** +* Called when a material has been saved and notifies all views of the save. +* @param materialDoc The material that has been saved. +*/ +void MaterialDocManager::MaterialSaved(MaterialDoc* materialDoc) { + + MaterialDoc** tempDoc; + if(inProgressMaterials.Get(materialDoc->name.c_str(), &tempDoc)) { + + idStr name = materialDoc->name.c_str(); + + //Remove this file from our in progress list + inProgressMaterials.Remove(name.c_str()); + + //Notify everyone + NotifyViews(materialDoc, MATERIAL_SAVE); + + if(materialDoc != currentMaterial) + delete materialDoc; + } +} + +/** +* Called when a material name has been changed and notifies all views of the change. +* @param materialDoc The material that has changed. +*/ +void MaterialDocManager::MaterialNameChanged(const char* oldName, MaterialDoc* materialDoc) { + + MaterialDoc** tempDoc; + if(inProgressMaterials.Get(oldName, &tempDoc)) { + inProgressMaterials.Set(materialDoc->name, *tempDoc); + inProgressMaterials.Remove(oldName); + } + + NotifyViews(materialDoc, MATERIAL_NAME_CHANGE, oldName); +} + +/** +* Called when a stage is added and notifies all views of the addition. +* @param materialDoc The material that has changed. +* @param stageNum The stage that was added. +*/ +void MaterialDocManager::StageAdded(MaterialDoc* materialDoc, int stageNum) { + //Notify everyone + NotifyViews(materialDoc, MATERIAL_ADD_STAGE, stageNum); +} + +/** +* Called when a stage has been deleted and notifies all views of the change. +* @param materialDoc The material that has changed. +* @param stageNum The stage that was deleted. +*/ +void MaterialDocManager::StageDeleted(MaterialDoc* materialDoc, int stageNum) { + //Notify everyone + NotifyViews(materialDoc, MATERIAL_DELETE_STAGE, stageNum); +} + +/** +* Called when a stage has been movied and notifies all views of the change. +* @param materialDoc The material that has changed. +* @param from The original position of the stage. +* @param to The new position of the stage. +*/ +void MaterialDocManager::StageMoved(MaterialDoc* materialDoc, int from, int to) { + //Notify everyone + NotifyViews(materialDoc, MATERIAL_MOVE_STAGE, from, to); +} + +/** +* Called when a material attribute has been edited and notifies all views of the change. +* @param materialDoc The material that has changed. +* @param stage The stage that contains the changed attribute. +* @param attribName The name of the attribute that changed. +*/ +void MaterialDocManager::AttributeChanged(MaterialDoc* materialDoc, int stage, const char* attribName) { + //Notify everyone + NotifyViews(materialDoc, MATERIAL_ATTRIBUTE_CHANGE, stage, attribName); +} + +} diff --git a/neo/tools/imgui/materialeditor/MaterialDocManager.h b/neo/tools/imgui/materialeditor/MaterialDocManager.h new file mode 100644 index 000000000..bad3470f0 --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialDocManager.h @@ -0,0 +1,173 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef MATERIALDOCMANAGER_H_ +#define MATERIALDOCMANAGER_H_ + +#include "MaterialEditor.h" +#include "MaterialModifier.h" +#include "MaterialDoc.h" + +class idMaterial; + +namespace ImGuiTools { + +class MaterialView; + +#define MAX_UNDOREDO 32 + +/** +* Responsible for managing the materials that are being viewed and/or edited. +*/ +class MaterialDocManager { + +public: + MaterialDocManager(void); + ~MaterialDocManager(void); + + //Reg/UnReg Material Views + void RegisterMaterialView(MaterialView* view); + void UnRegisterMaterialView(MaterialView* view); + void UnRegisterAllMaterialViews(); + + //Material Selection + void SetSelectedMaterial(idMaterial* material); + MaterialDoc* GetCurrentMaterialDoc() { return currentMaterial; }; + + //State Checking Methods + bool DoesFileNeedApply(const char* filename); + bool DoesAnyNeedApply(); + bool IsFileModified(const char* filename); + bool IsAnyModified(); + + //Adding or deleting a material + void AddMaterial(const char* name, const char* filename, const char* sourceText = NULL, bool addUndo = true); + void RedoAddMaterial(const char* name, bool clearData = true); + void DeleteMaterial(MaterialDoc* material, bool addUndo = true); + + //Applying + void ApplyMaterial(MaterialDoc* materialDoc); + void ApplyFile(const char* filename); + void ApplyAll(); + + //Saving + void SaveMaterial(MaterialDoc* material); + void SaveFile(const char* filename); + void SaveAllMaterials(); + + //File Reloading + void ReloadFile(const char *filename); + + + //Used to get and/or create a MaterialDoc object for editing + MaterialDoc* CreateMaterialDoc(const char* materialName); + MaterialDoc* CreateMaterialDoc(idMaterial* material); + MaterialDoc* GetInProgressDoc(idMaterial* material); + + //Copy Paste + void CopyMaterial(MaterialDoc* materialDoc = NULL, bool cut = false); + void ClearCopy(); + bool IsCopyMaterial(); + idStr GetCopyMaterialName(); + void PasteMaterial(const char* name, const char* filename); + + void CopyStage(MaterialDoc* materialDoc, int stageNum); + void ClearCopyStage(); + bool IsCopyStage(); + void PasteStage(MaterialDoc* materialDoc); + void GetCopyStageInfo(int& type, idStr& name); + + //Undo/Redo + void Undo(); + bool IsUndoAvailable(); + void ClearUndo(); + void Redo(); + bool IsRedoAvailable(); + void ClearRedo(); + void AddMaterialUndoModifier(MaterialModifier* mod, bool clearRedo = true); + void AddMaterialRedoModifier(MaterialModifier* mod); + + //Searching + bool FindMaterial(const char* name, MaterialSearchData_t* searchData, bool checkName); + + //Misc + idStr GetUniqueMaterialName(idStr name); + +protected: + + /** + * View notification types + */ + enum { + SELECTION_CHANGE, + MATERIAL_CHANGE, + MATERIAL_APPLY, + MATERIAL_SAVE, + MATERIAL_SAVE_FILE, + MATERIAL_ADD, + MATERIAL_DELETE, + MATERIAL_ADD_STAGE, + MATERIAL_DELETE_STAGE, + MATERIAL_MOVE_STAGE, + MATERIAL_ATTRIBUTE_CHANGE, + MATERIAL_NAME_CHANGE, + FILE_RELOAD + }; + void NotifyViews(MaterialDoc* materialDoc, int notifyType, ... ); + + //Doc Notification members + friend MaterialDoc; + void MaterialChanged(MaterialDoc* materialDoc); + void MaterialApplied(MaterialDoc* materialDoc); + void MaterialSaved(MaterialDoc* materialDoc); + void MaterialNameChanged(const char* oldName, MaterialDoc* materialDoc); + void StageAdded(MaterialDoc* materialDoc, int stageNum); + void StageDeleted(MaterialDoc* materialDoc, int stageNum); + void StageMoved(MaterialDoc* materialDoc, int from, int to); + void AttributeChanged(MaterialDoc* materialDoc, int stage, const char* attribName); + +protected: + idList materialViews; + MaterialDoc* currentMaterial; + idHashTable inProgressMaterials; + + idList undoModifiers; + idList redoModifiers; + + //Copy/Paste + bool cutMaterial; + idStr copyMaterial; + + //Copy/Paste Stage + idStr copyStageMaterial; + MEStage_t copyStage; +}; + +} + +#endif /* !MATERIALDOCMANAGER_H_ */ + diff --git a/neo/tools/imgui/materialeditor/MaterialEditView.cpp b/neo/tools/imgui/materialeditor/MaterialEditView.cpp new file mode 100644 index 000000000..69af03ddf --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialEditView.cpp @@ -0,0 +1,301 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "MaterialEditView.h" +#include "tools/imgui/materialeditor/MaterialEditor.h" + +#define EDIT_HEIGHT 25 + +#define EDIT_TAB_CONTROL 0x2006 +#define NAME_CONTROL 0x2007 + +namespace ImGuiTools { + +/** +* Constructor for MaterialEditView. +*/ +MaterialEditView::MaterialEditView() { + sourceInit = false; + sourceChanged = false; + propsTabSelected = true; + textTabSelected = false; + tabSel = 0; +} + +/** +* Destructor for MaterialEditView. +*/ +MaterialEditView::~MaterialEditView() { +} + +/** +* Called when the selected material has changed. +* @param pMaterial The newly selected material. +*/ +void MaterialEditView::MV_OnMaterialSelectionChange(MaterialDoc* pMaterial) { + + //Apply any text changes that have been made + ApplyMaterialSource(); + + if ( pMaterial ) { + m_nameEdit = pMaterial->name; + m_textView.SetReadOnly( false ); + + //If the edit tab is selected then get the source + if ( textTabSelected ) { + GetMaterialSource(); + } + + currentMaterialName = pMaterial->name; + } else { + m_nameEdit.Clear(); + + GetMaterialSource(); + m_textView.SetReadOnly( true ); + + currentMaterialName.Clear(); + } +} + +void MaterialEditView::MV_OnMaterialNameChanged(MaterialDoc* pMaterial, const char* oldName) { + if ( !currentMaterialName.Icmp( oldName ) ) { + currentMaterialName = pMaterial->name; + } +} + +/** +* Returns the current source text in the source edit control. +*/ +idStr MaterialEditView::GetSourceText() { + idStr text; + m_textView.GetText( text ); + + return text; +} + +bool MaterialEditView::CanCopy() { + return m_textView.CanCopy(); +} + +void MaterialEditView::OnCopy() { + m_textView.Copy(); +} + +bool MaterialEditView::CanCut() { + return m_textView.CanCut(); +} + +void MaterialEditView::OnCut() { + m_textView.Cut(); +} + +bool MaterialEditView::CanPaste() { + return m_textView.CanCopy(); +} + +void MaterialEditView::OnPaste() { + m_textView.Paste(); +} + +bool MaterialEditView::CanDelete() { + return !m_textView.GetReadOnly(); +} + +void MaterialEditView::OnDelete() { + m_textView.Delete(); +} + + +/** +* Gets the source of the current document and populates the +* source edit control. +*/ +void MaterialEditView::GetMaterialSource() { + + //Clear it + sourceInit = true; + m_textView.SetText( "" ); + sourceInit = false; + + if ( materialDocManager ) { + MaterialDoc* material = materialDocManager->GetCurrentMaterialDoc(); + if ( material ) { + idStr text = material->GetEditSourceText(); + + sourceInit = true; + m_textView.SetText(text); + sourceInit = false; + } + } +} + +/** +* Takes the source out of the edit control and applies it +* to the material. +*/ +void MaterialEditView::ApplyMaterialSource() { + + if ( !sourceChanged ) { + return; + } + + MaterialDoc* material = materialDocManager->CreateMaterialDoc(currentMaterialName); + + if(material) { + idStr text = GetSourceText(); + material->ApplySourceModify(text); + } + + sourceChanged = false; +} + +/** +* Called by the MFC framework when the view is being created. +*/ +void MaterialEditView::OnCreate() +{ + m_textView.Init(); + m_textView.LoadKeyWordsFromFile( "editors/material.def" ); + + m_textView.SetText(""); + m_textView.SetReadOnly( true ); + + m_editSplitterPos = 200; + m_editSplitterWidth = 700; + m_editSplitterHeight = 200; + + m_stageView = new StageView(); + m_stageView->OnCreate(); + m_materialPropertyView = new MaterialPropTreeView(); +} + +bool MaterialEditView::Draw( const ImVec2 &size ) { + if ( ImGui::BeginChild( "MaterialEditView", size, ImGuiChildFlags_Borders ) ) { + + // two rows + + if ( ImGui::BeginTabBar( "MaterialEditViewTabBar" ) ) { + + if ( ImGui::BeginTabItem( "Properties" ) ) { + OnTcnSelChange( 0 ); + + if ( ImGui::BeginChild( "materialEditSplitter", size ) ) { + float splitterButtonWidthOrHeight = 8.0f; + + m_editSplitterHeight = size.y; + + ImGui::PushStyleVar( ImGuiStyleVar_ItemSpacing, ImVec2( 0, 0 ) ); + + if ( m_stageView->Draw( ImVec2( m_editSplitterPos, m_editSplitterHeight ) ) ) { + + } + + ImGui::SameLine(); + ImGui::InvisibleButton( "editSplitter", ImVec2( splitterButtonWidthOrHeight, m_editSplitterHeight) ); + if ( ImGui::IsItemActive() ) { + m_editSplitterPos += ImGui::GetIO().MouseDelta.x; + } + ImGui::SameLine(); + + if ( m_materialPropertyView->Draw( ImVec2( 0, m_editSplitterHeight) ) ) { + + } + + ImGui::PopStyleVar(); + } + ImGui::EndChild(); + + ImGui::EndTabItem(); + } + if ( ImGui::BeginTabItem( "Text" ) ) { + OnTcnSelChange( 1 ); + + m_textView.Draw(); + if ( ImGui::IsWindowFocused() ) { + MaterialEditorSetActiveWindow( ME_WINDOW_TEXT_EDIT ); + } + OnEnChangeEdit(); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + } + } + + ImGui::EndChild(); + return false; +} + +/** +* Called when the user changes the properties/text tab selection. This methods shows and hides +* the appropriate windows. +*/ +void MaterialEditView::OnTcnSelChange(int sel) { + if ( tabSel == sel ) { + return; + } + + switch(sel) { + case 0: + propsTabSelected = true; + textTabSelected = false; + + ApplyMaterialSource(); + + m_stageView->RefreshStageList(); + + break; + case 1: + propsTabSelected = false; + textTabSelected = true; + + GetMaterialSource(); + m_textView.SetReadOnly( false ); + + break; + } + tabSel = sel; +} + +/** +* Called when the user changes text in the edit control +*/ +void MaterialEditView::OnEnChangeEdit() { + if ( !m_textView.IsEdited() ) { + return; + } + + if ( materialDocManager && !sourceInit ) { + MaterialDoc* material = materialDocManager->GetCurrentMaterialDoc(); + if ( material && !material->IsSourceModified() ) { + sourceChanged = true; + material->SourceModify(this); + } + } +} + +} diff --git a/neo/tools/imgui/materialeditor/MaterialEditView.h b/neo/tools/imgui/materialeditor/MaterialEditView.h new file mode 100644 index 000000000..6f6495239 --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialEditView.h @@ -0,0 +1,107 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef MATERIALEDITVIEW_H_ +#define MATERIALEDITVIEW_H_ + +#include "../util/ImGui_IdWidgets.h" +#include "sys/sys_imgui.h" + +#include "MaterialEditor.h" +#include "MaterialView.h" +#include "MaterialPropTreeView.h" +#include "StageView.h" + +#include "../util/SyntaxRichEditCtrl.h" + +namespace ImGuiTools { + +/** +* View that contains the material edit controls. These controls include +* the stage view, the properties view and the source view. +*/ +class MaterialEditView : public MaterialView, SourceModifyOwner { + +public: + idStr m_nameEdit; + float m_editSplitterPos; + float m_editSplitterWidth; + float m_editSplitterHeight; + + StageView* m_stageView; + MaterialPropTreeView* m_materialPropertyView; + SyntaxRichEditCtrl m_textView; + +public: + MaterialEditView(); + virtual ~MaterialEditView(); + + bool Draw( const ImVec2 &size ); + + //MaterialView Interface + virtual void MV_OnMaterialSelectionChange( MaterialDoc* pMaterial ); + virtual void MV_OnMaterialNameChanged( MaterialDoc* pMaterial, const char* oldName ); + + //SourceModifyOwner Interface + virtual idStr GetSourceText(); + + bool CanCopy(); + void OnCopy(); + bool CanCut(); + void OnCut(); + bool CanPaste(); + void OnPaste(); + bool CanDelete(); + void OnDelete(); + +protected: + void GetMaterialSource(); + void ApplyMaterialSource(); + +public: + //Message Handlers + void OnCreate(); + +protected: + //afx_msg void OnSize(UINT nType, int cx, int cy); + void OnTcnSelChange(int sel); + void OnEnChangeEdit(); + +protected: + bool initHack; + bool sourceInit; + + bool sourceChanged; + idStr currentMaterialName; + bool propsTabSelected; + bool textTabSelected; + int tabSel; +}; + +} + +#endif /* !MATERIALEDITVIEW_H_ */ diff --git a/neo/tools/imgui/materialeditor/MaterialEditor.cpp b/neo/tools/imgui/materialeditor/MaterialEditor.cpp new file mode 100644 index 000000000..07cbc7589 --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialEditor.cpp @@ -0,0 +1,112 @@ + +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "sys/sys_imgui.h" +#include "../util/RegistryOptions.h" +#include "MEMainFrame.h" + +namespace ImGuiTools { + +MEMainFrame* meMainFrame = NULL; + +/** +* Initializes the material editor tool. +*/ +void MaterialEditorInit( void ) { + com_editors = EDITOR_MATERIAL; + + // To create the main window, this code creates a new frame window + // object and then sets it as the application's main window object + meMainFrame = new MEMainFrame; + + meMainFrame->OnCreateClient(); + + impl::SetReleaseToolMouse( true ); + + D3::ImGuiHooks::OpenWindow(D3::ImGuiHooks::D3_ImGuiWin_MaterialEditor); +} + +void MaterialEditorSetActiveWindow( MaterialEditorWindow_t window ) { + if ( !meMainFrame ) { + return; + } + + meMainFrame->SetActiveWindow( window ); +} + +/** +* Called every frame by the doom engine to allow the material editor to process messages. +*/ +void MaterialEditorDraw( void ) { + bool showTool; + + if ( !meMainFrame ) { + return; + } + + showTool = meMainFrame != NULL; + + if ( ImGui::Begin( "Material Editor", &showTool, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_MenuBar ) ) { + impl::SetReleaseToolMouse( true ); + + meMainFrame->Draw(); + } + ImGui::End(); + + if ( meMainFrame && !showTool ) + { + MaterialEditorShutdown(); + impl::SetReleaseToolMouse( false ); + D3::ImGuiHooks::CloseWindow( D3::ImGuiHooks::D3_ImGuiWin_MaterialEditor ); + } +} + +/** +* Called by the doom engine when the material editor needs to be destroyed. +*/ +void MaterialEditorShutdown( void ) { + + meMainFrame->OnDestroy(); + + delete meMainFrame; + + meMainFrame = NULL; + com_editors = 0; +} + +/** +* Allows the doom engine to reflect console output to the material editors console. +*/ +void MaterialEditorPrintConsole( const char *msg ) { + // meMainFrame can be null when starting immediately from commandline. + if(meMainFrame && com_editors & EDITOR_MATERIAL) + meMainFrame->PrintConsoleMessage(msg); +} + +} diff --git a/neo/tools/imgui/materialeditor/MaterialEditor.h b/neo/tools/imgui/materialeditor/MaterialEditor.h new file mode 100644 index 000000000..b09521f69 --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialEditor.h @@ -0,0 +1,60 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef MATERIALEDITOR_H_ +#define MATERIALEDITOR_H_ + +#include "../../edit_public.h" + +namespace ImGuiTools { + +/** +* Structure used to store the user defined search parameters. +*/ +typedef struct { + bool searched; + idStr searchText; + int nameOnly; + int searchScope; +} MaterialSearchData_t; + +typedef enum { + ME_WINDOW_NONE = 0, + ME_WINDOW_TREE, + ME_WINDOW_TEXT_EDIT, + ME_WINDOW_PROP, + ME_WINDOW_PREVIEW, + ME_WINDOW_PREVIEW_PROP, + ME_WINDOW_STAGE, + ME_WINDOW_CONSOLE, +} MaterialEditorWindow_t; + +void MaterialEditorSetActiveWindow( MaterialEditorWindow_t window ); + +} + +#endif /* !MATERIALEDITOR_H_ */ diff --git a/neo/tools/imgui/materialeditor/MaterialModifier.cpp b/neo/tools/imgui/materialeditor/MaterialModifier.cpp new file mode 100644 index 000000000..e47036040 --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialModifier.cpp @@ -0,0 +1,431 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#include "../../edit_public.h" + + +#include "MaterialModifier.h" +#include "MaterialDocManager.h" +#include "MaterialTreeView.h" + +namespace ImGuiTools { + +////////////////////////////////////////////////////////////////////////////// +//MaterialModifier +////////////////////////////////////////////////////////////////////////////// + +/** +* Constructor for MaterialModifier +*/ +MaterialModifier::MaterialModifier(MaterialDocManager* manager, const char* materialName) { + this->manager = manager; + this->materialName = materialName; +} + +////////////////////////////////////////////////////////////////////////////// +//AttributeMaterialModifier +////////////////////////////////////////////////////////////////////////////// + +/** +* Constructor for AttributeMaterialModifier +*/ +AttributeMaterialModifier::AttributeMaterialModifier(MaterialDocManager* manager, const char* materialName, int stage, const char* key) +: MaterialModifier(manager, materialName) { + + this->stage = stage; + this->key = key; + +} + +////////////////////////////////////////////////////////////////////////////// +//AttributeMaterialModifierString +////////////////////////////////////////////////////////////////////////////// + +/** +* Constructor for AttributeMaterialModifierString +*/ +AttributeMaterialModifierString::AttributeMaterialModifierString(MaterialDocManager* manager, const char* materialName, int stage, const char* key, const char* value, const char* oldValue) +: AttributeMaterialModifier(manager, materialName, stage, key) { + + this->value = value; + this->oldValue = oldValue; +} + +/** +* Performs an undo operation of a string attribute change. +*/ +void AttributeMaterialModifierString::Undo() { + MaterialDoc* material = manager->CreateMaterialDoc(materialName); + material->SetAttribute(stage, key, oldValue, false); +} + +/** +* Performs a redo operation of a string attribute change. +*/ +void AttributeMaterialModifierString::Redo() { + MaterialDoc* material = manager->CreateMaterialDoc(materialName); + material->SetAttribute(stage, key, value, false); +} + +////////////////////////////////////////////////////////////////////////////// +//AttributeMaterialModifierBool +////////////////////////////////////////////////////////////////////////////// + +/** +* Constructor for AttributeMaterialModifierBool +*/ +AttributeMaterialModifierBool::AttributeMaterialModifierBool(MaterialDocManager* manager, const char* materialName, int stage, const char* key, bool value, bool oldValue) +: AttributeMaterialModifier(manager, materialName, stage, key) { + + this->value = value; + this->oldValue = oldValue; +} + +/** +* Performs an undo operation of a boolean attribute change. +*/ +void AttributeMaterialModifierBool::Undo() { + MaterialDoc* material = manager->CreateMaterialDoc(materialName); + material->SetAttributeBool(stage, key, oldValue, false); +} + +/** +* Performs a redo operation of a boolean attribute change. +*/ +void AttributeMaterialModifierBool::Redo() { + MaterialDoc* material = manager->CreateMaterialDoc(materialName); + material->SetAttributeBool(stage, key, value, false); +} + +////////////////////////////////////////////////////////////////////////////// +//StageMoveModifier +////////////////////////////////////////////////////////////////////////////// + +/** +* Constructor for StageMoveModifier +*/ +StageMoveModifier::StageMoveModifier(MaterialDocManager* manager, const char* materialName, int from, int to) +: MaterialModifier(manager, materialName) { + this->from = from; + this->to = to; +} + +/** +* Performs an undo operation of a stage move. +*/ +void StageMoveModifier::Undo() { + MaterialDoc* material = manager->CreateMaterialDoc(materialName); + material->MoveStage(to, from, false); +} + +/** +* Performs a redo operation of a moved stage. +*/ +void StageMoveModifier::Redo() { + MaterialDoc* material = manager->CreateMaterialDoc(materialName); + material->MoveStage(from, to, false); +} + +////////////////////////////////////////////////////////////////////////////// +//StageDeleteModifier +////////////////////////////////////////////////////////////////////////////// + +/** +* Constructor for StageDeleteModifier +*/ +StageDeleteModifier::StageDeleteModifier(MaterialDocManager* manager, const char* materialName, int stageNum, idDict stageData) +: MaterialModifier(manager, materialName) { + this->stageNum = stageNum; + this->stageData = stageData; +} + +/** +* Performs an undo operation of a deleted stage. +*/ +void StageDeleteModifier::Undo() { + + MaterialDoc* material = manager->CreateMaterialDoc(materialName); + material->InsertStage(stageNum, stageData.GetInt("stagetype"), stageData.GetString("name"), false); + material->SetData(stageNum, &stageData); + +} + +/** +* Performs a redo operation of a deleted stage. +*/ +void StageDeleteModifier::Redo() { + MaterialDoc* material = manager->CreateMaterialDoc(materialName); + material->RemoveStage(stageNum, false); +} + +////////////////////////////////////////////////////////////////////////////// +//StageInsertModifier +////////////////////////////////////////////////////////////////////////////// + +/** +* Constructor for StageInsertModifier +*/ +StageInsertModifier::StageInsertModifier(MaterialDocManager* manager, const char* materialName, int stageNum, int stageType, const char* stageName) +: MaterialModifier(manager, materialName) { + this->stageNum = stageNum; + this->stageType = stageType; + this->stageName = stageName; +} + +/** +* Performs an undo operation of an inserted stage. +*/ +void StageInsertModifier::Undo() { + + MaterialDoc* material = manager->CreateMaterialDoc(materialName); + material->RemoveStage(stageNum, false); +} + +/** +* Performs a redo operation of an inserted stage. +*/ +void StageInsertModifier::Redo() { + MaterialDoc* material = manager->CreateMaterialDoc(materialName); + material->InsertStage(stageNum, stageType, stageName, false); +} + +////////////////////////////////////////////////////////////////////////////// +//AddMaterialModifier +////////////////////////////////////////////////////////////////////////////// + +/** +* Constructor for AddMaterialModifier +*/ +AddMaterialModifier::AddMaterialModifier(MaterialDocManager* manager, const char* materialName, const char* materialFile) +: MaterialModifier(manager, materialName) { + this->materialFile = materialFile; +} + +/** +* Performs an undo operation of an added material. +*/ +void AddMaterialModifier::Undo() { + MaterialDoc* material = manager->CreateMaterialDoc(materialName); + manager->DeleteMaterial(material, false); +} + +/** +* Performs a redo operation of an added material. +*/ +void AddMaterialModifier::Redo() { + manager->RedoAddMaterial(materialName); +} + +////////////////////////////////////////////////////////////////////////////// +//DeleteMaterialModifier +////////////////////////////////////////////////////////////////////////////// + +/** +* Constructor for DeleteMaterialModifier +*/ +DeleteMaterialModifier::DeleteMaterialModifier(MaterialDocManager* manager, const char* materialName) +: MaterialModifier(manager, materialName) { +} + +/** +* Performs an undo operation of a deleted material. +*/ +void DeleteMaterialModifier::Undo() { + + manager->RedoAddMaterial(materialName, false); +} + +/** +* Performs a redo operation of a deleted material. +*/ +void DeleteMaterialModifier::Redo() { + + MaterialDoc* material = manager->CreateMaterialDoc(materialName); + manager->DeleteMaterial(material, false); +} + + +////////////////////////////////////////////////////////////////////////////// +//MoveMaterialModifier +////////////////////////////////////////////////////////////////////////////// + +/** +* Constructor for MoveMaterialModifier +*/ +MoveMaterialModifier::MoveMaterialModifier(MaterialDocManager* manager, const char* materialName, const char* materialFile, const char* copyMaterial) +: MaterialModifier(manager, materialName) { + this->materialFile = materialFile; + this->copyMaterial = copyMaterial; +} + +/** +* Performs an undo operation of a moved material +*/ +void MoveMaterialModifier::Undo() { + + //Delete New Material + MaterialDoc* material = manager->CreateMaterialDoc(materialName); + manager->DeleteMaterial(material, false); + + //RedoAdd Old Material + manager->RedoAddMaterial(copyMaterial, false); +} + +/** +* Performs a redo operation of a moved material. +*/ +void MoveMaterialModifier::Redo() { + //Delete Old Material + MaterialDoc* material = manager->CreateMaterialDoc(copyMaterial); + manager->DeleteMaterial(material, false); + + //Redo Add New Material + manager->RedoAddMaterial(materialName, false); +} + +////////////////////////////////////////////////////////////////////////////// +//RenameMaterialModifier +////////////////////////////////////////////////////////////////////////////// +/** +* Constructor for RenameMaterialModifier +*/ +RenameMaterialModifier::RenameMaterialModifier(MaterialDocManager* manager, const char* materialName, const char* oldName) +: MaterialModifier(manager, materialName) { + this->oldName = oldName; +} + +/** +* Performs an undo operation of a renamed material +*/ +void RenameMaterialModifier::Undo() { + + MaterialDoc* material = manager->CreateMaterialDoc(materialName); + material->SetMaterialName(oldName, false); +} + +/** +* Performs a redo operation of a renamed material. +*/ +void RenameMaterialModifier::Redo() { + + MaterialDoc* material = manager->CreateMaterialDoc(oldName); + material->SetMaterialName(materialName, false); +} + +////////////////////////////////////////////////////////////////////////////// +//AddMaterialFolderModifier +////////////////////////////////////////////////////////////////////////////// +/** +* Constructor for AddMaterialFolderModifier +*/ +AddMaterialFolderModifier::AddMaterialFolderModifier(MaterialDocManager* manager, const char* materialName, MaterialTreeView* view, TreeNode* item, TreeNode* parent) +: MaterialModifier(manager, materialName) { + this->view = view; + this->item = item; + this->parent = parent; +} + +/** +* Performs an undo operation of an added material folder. +*/ +void AddMaterialFolderModifier::Undo() { + view->DeleteFolder(item, false); +} + +/** +* Performs a redo operation of an added material folder. +*/ +void AddMaterialFolderModifier::Redo() { + view->AddFolder(materialName, parent); +} + +////////////////////////////////////////////////////////////////////////////// +//RenameMaterialFolderModifier +////////////////////////////////////////////////////////////////////////////// + +/** +* Constructor for RenameMaterialFolderModifier +*/ +RenameMaterialFolderModifier::RenameMaterialFolderModifier(MaterialDocManager* manager, const char* materialName, MaterialTreeView* view, TreeNode* item, const char* oldName) +: MaterialModifier(manager, materialName) { + this->view = view; + this->item = item; + this->oldName = oldName; +} + +/** +* Performs an undo operation of a renamed material folder. +*/ +void RenameMaterialFolderModifier::Undo() { + view->RenameFolder(item, oldName); +} + +/** +* Performs a redo operation of a renamed material folder. +*/ +void RenameMaterialFolderModifier::Redo() { + view->RenameFolder(item, materialName); +} + + +////////////////////////////////////////////////////////////////////////////// +//DeleteMaterialFolderModifier +////////////////////////////////////////////////////////////////////////////// + +/** +* Constructor for DeleteMaterialFolderModifier +*/ +DeleteMaterialFolderModifier::DeleteMaterialFolderModifier(MaterialDocManager* manager, const char* materialName, MaterialTreeView* view, TreeNode* parent, idStrList* affectedMaterials) +: MaterialModifier(manager, materialName) { + this->view = view; + this->parent = parent; + this->affectedMaterials = *affectedMaterials; +} + +/** +* Performs an undo operation of a deleted material folder. +*/ +void DeleteMaterialFolderModifier::Undo() { + + //Add the folder back and save the folder position for the redo + item = view->AddFolder(materialName, parent); + + //Add each of the children back + for(int i = 0; i < affectedMaterials.Num(); i++) { + manager->RedoAddMaterial(affectedMaterials[i], false); + } +} + +/** +* Performs a redo operation of a deleted material folder. +*/ +void DeleteMaterialFolderModifier::Redo() { + + view->DeleteFolder(item, false); +} + +} diff --git a/neo/tools/imgui/materialeditor/MaterialModifier.h b/neo/tools/imgui/materialeditor/MaterialModifier.h new file mode 100644 index 000000000..8fc4483ac --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialModifier.h @@ -0,0 +1,276 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef MATERIALMODIFIER_H_ +#define MATERIALMODIFIER_H_ + +#include "MaterialEditor.h" +#include "idlib/containers/StrList.h" +#include "../util/TreeCtrl.h" + +namespace ImGuiTools { + +class MaterialDocManager; +class MaterialTreeView; + +/** +* Base class for modifications that can be made to a material that can be undone and redone. +*/ +class MaterialModifier { + +public: + MaterialModifier(MaterialDocManager* manager, const char* materialName); + virtual ~MaterialModifier() {}; + + virtual void Undo() = 0; + virtual void Redo() = 0; + +protected: + MaterialDocManager* manager; + idStr materialName; +}; + +/** +* Base class for Undo/Redo operations for attribute changes +*/ +class AttributeMaterialModifier : public MaterialModifier { + +public: + AttributeMaterialModifier(MaterialDocManager* manager, const char* materialName, int stage, const char* key); + virtual ~AttributeMaterialModifier() {}; + + virtual void Undo() = 0; + virtual void Redo() = 0; + +protected: + int stage; + idStr key; +}; + +/** +* Undo/Redo operation for string attribute changes +*/ +class AttributeMaterialModifierString : public AttributeMaterialModifier { + +public: + AttributeMaterialModifierString(MaterialDocManager* manager, const char* materialName, int stage, const char* key, const char* value, const char* oldValue); + virtual ~AttributeMaterialModifierString() {}; + + virtual void Undo(); + virtual void Redo(); + +protected: + idStr value; + idStr oldValue; + +}; + +/** +* Undo/Redo operation for boolean attribute changes +*/ +class AttributeMaterialModifierBool : public AttributeMaterialModifier { + +public: + AttributeMaterialModifierBool(MaterialDocManager* manager, const char* materialName, int stage, const char* key, bool value, bool oldValue); + virtual ~AttributeMaterialModifierBool() {}; + + virtual void Undo(); + virtual void Redo(); + +protected: + bool value; + bool oldValue; + +}; + +/** +* Undo/Redo operation for stage moves +*/ +class StageMoveModifier : public MaterialModifier { + +public: + StageMoveModifier(MaterialDocManager* manager, const char* materialName, int from, int to); + virtual ~StageMoveModifier() {}; + + virtual void Undo(); + virtual void Redo(); + +protected: + int from; + int to; +}; + +/** +* Undo/Redo operation for stage deletes +*/ +class StageDeleteModifier : public MaterialModifier { +public: + StageDeleteModifier(MaterialDocManager* manager, const char* materialName, int stageNum, idDict stageData); + virtual ~StageDeleteModifier() {}; + + virtual void Undo(); + virtual void Redo(); + +protected: + int stageNum; + idDict stageData; +}; + +/** +* Undo/Redo operation for stage inserts +*/ +class StageInsertModifier : public MaterialModifier { +public: + StageInsertModifier(MaterialDocManager* manager, const char* materialName, int stageNum, int stageType, const char* stageName); + virtual ~StageInsertModifier() {}; + + virtual void Undo(); + virtual void Redo(); + +protected: + int stageNum; + int stageType; + idStr stageName; +}; + +/** +* Undo/Redo operation for adding materials +*/ +class AddMaterialModifier : public MaterialModifier { +public: + AddMaterialModifier(MaterialDocManager* manager, const char* materialName, const char* materialFile); + virtual ~AddMaterialModifier() {}; + + virtual void Undo(); + virtual void Redo(); + +protected: + idStr materialFile; +}; + +/** +* Undo/Redo operation for deleting materials +*/ +class DeleteMaterialModifier : public MaterialModifier { +public: + DeleteMaterialModifier(MaterialDocManager* manager, const char* materialName); + virtual ~DeleteMaterialModifier() {}; + + virtual void Undo(); + virtual void Redo(); + +protected: + +}; + +/** +* Undo/Redo operation for moving materials +*/ +class MoveMaterialModifier : public MaterialModifier { +public: + MoveMaterialModifier(MaterialDocManager* manager, const char* materialName, const char* materialFile, const char* copyMaterial); + virtual ~MoveMaterialModifier() {}; + + virtual void Undo(); + virtual void Redo(); + +protected: + idStr materialFile; + idStr copyMaterial; +}; + +/** +* Undo/Redo operation for renaming materials +*/ +class RenameMaterialModifier : public MaterialModifier { +public: + RenameMaterialModifier(MaterialDocManager* manager, const char* materialName, const char* oldName); + virtual ~RenameMaterialModifier() {}; + + virtual void Undo(); + virtual void Redo(); + +protected: + idStr oldName; +}; + +/** +* Undo/Redo operation for adding material folders +*/ +class AddMaterialFolderModifier : public MaterialModifier { +public: + AddMaterialFolderModifier(MaterialDocManager* manager, const char* materialName, MaterialTreeView* view, TreeNode* item, TreeNode* parent); + virtual ~AddMaterialFolderModifier() {}; + + virtual void Undo(); + virtual void Redo(); + +protected: + MaterialTreeView* view; + TreeNode* item; + TreeNode* parent; +}; + +/** +* Undo/Redo operation for renaming a material folder +*/ +class RenameMaterialFolderModifier : public MaterialModifier { +public: + RenameMaterialFolderModifier(MaterialDocManager* manager, const char* materialName, MaterialTreeView* view, TreeNode* item, const char* oldName); + virtual ~RenameMaterialFolderModifier() {}; + + virtual void Undo(); + virtual void Redo(); + +protected: + MaterialTreeView* view; + TreeNode* item; + idStr oldName; +}; + +/** +* Undo/Redo operation for deleting a material folder +*/ +class DeleteMaterialFolderModifier : public MaterialModifier { +public: + DeleteMaterialFolderModifier(MaterialDocManager* manager, const char* materialName, MaterialTreeView* view, TreeNode* parent, idStrList* affectedMaterials); + virtual ~DeleteMaterialFolderModifier() {}; + + virtual void Undo(); + virtual void Redo(); + +protected: + MaterialTreeView* view; + idStrList affectedMaterials; + + TreeNode* item; + TreeNode* parent; +}; + +} + +#endif /* !MATERIALMODIFIER_H_ */ \ No newline at end of file diff --git a/neo/tools/imgui/materialeditor/MaterialPreviewPropView.cpp b/neo/tools/imgui/materialeditor/MaterialPreviewPropView.cpp new file mode 100644 index 000000000..82b06b6b9 --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialPreviewPropView.cpp @@ -0,0 +1,307 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + + + +#include "MaterialPreviewPropView.h" + +namespace ImGuiTools { + +static const char *MaterialPreviewPropViewModelTypes[] = { + "Cube", + "Box - 2:1", + "Box - 4:1", + "Box - 1:2", + "Box - 1:4", + "Cylinder - V", + "Cylinder - H", + "Sphere" +}; + +// MaterialPropTreeView + +MaterialPreviewPropView::MaterialPreviewPropView() + : customModel("") + , lights() + , lightMaterials() + , defaultPointLightIndex(-1) + , customModelSelectDlg( DECL_MODELDEF, "Select Custom Model" ) +{ + materialPreview = NULL; + modelType = 0; + showLights = true; + + for ( int i = 0; i < MAX_ENTITY_SHADER_PARMS; i++) { + if ( i < 4 ) { + localParms[i] = 1; + } else { + localParms[i] = 0; + } + } + + for (int i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++) { + if ( i < 4 ) { + globalParms[i] = 1; + } + else { + globalParms[i] = 0; + } + } +} + +MaterialPreviewPropView::~MaterialPreviewPropView() { + customModel.Clear(); + lights.Clear(); + lightMaterials.Clear(); +} + +bool MaterialPreviewPropView::Draw( const ImVec2 &size ) { + ImGui::BeginChild( "MaterialPreviewPropView", size, ImGuiChildFlags_Borders ); + + if ( ImGui::TreeNodeEx( static_cast( "#PP" ), ImGuiTreeNodeFlags_DefaultOpen, "Preview Properties")) { + const char* modelTypePreview = MaterialPreviewPropViewModelTypes[modelType]; + + if ( ImGui::BeginCombo( "Model Type", modelTypePreview, ImGuiComboFlags_None ) ) { + for ( int n = 0; n < IM_ARRAYSIZE( MaterialPreviewPropViewModelTypes ); n++ ) { + const bool selected = (modelType == n); + if ( ImGui::Selectable( MaterialPreviewPropViewModelTypes[n], selected ) ) { + materialPreview->OnModelChange( n ); + modelType = n; + } + + if ( selected ) { + ImGui::SetItemDefaultFocus(); + } + } + + ImGui::EndCombo(); + } + ImGui::SetItemTooltip( "Select the type of model on which to preview the material." ); + + // Custom model entry + ImGui::Text( "Custom Model" ); + ImGui::SameLine(); + if ( ImGui::InputTextStr( "Custom Model", &customModel ) ) { + materialPreview->OnCustomModelChange( customModel.c_str() ); + } + if ( ImGui::Button( "...###CustomModel" ) ) { + customModelSelectDlg.Start( customModel.c_str() ); + } else { + ImGui::SetItemTooltip( "Specify any model to display the current material." ); + } + if ( customModelSelectDlg.Draw() ) { + idDecl *decl = customModelSelectDlg.GetDecl(); + if ( decl ) { + customModel = decl->GetFileName(); + materialPreview->OnCustomModelChange( customModel.c_str() ); + } + } + + // Checkbox for showing debug light spheres + if ( ImGui::Checkbox( "Show lights", &showLights ) ) { + materialPreview->OnShowLightsChange( showLights ); + } else { + ImGui::SetItemTooltip( "Show the light origin sphere and number in the preview." ); + } + + ImGui::TreePop(); + } + + if ( ImGui::IsItemHovered() ) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted( "Properties for the preview window." ); + ImGui::EndTooltip(); + } + + // Local and Global shader parms + if ( ImGui::TreeNodeEx( static_cast( "#LP" ), 0, "Local Parms")) { + for ( int i = 0; i < MAX_ENTITY_SHADER_PARMS; i++ ) { + if ( ImGui::InputFloat( va( "parm%d", i ), &localParms[i] ) ) { + materialPreview->OnLocalParmChange( i, localParms[i] ); + } + ImGui::SetItemTooltip( "Set the local shaderparm for the model" ); + } + + ImGui::TreePop(); + } + if ( ImGui::IsItemHovered() ) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted( "Local shaderparms for the model being displayed." ); + ImGui::EndTooltip(); + } + + if ( ImGui::TreeNodeEx( static_cast( "#GP" ), 0, "Global Parms")) { + for ( int i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) { + if ( ImGui::InputFloat( va( "global%d", i ), &globalParms[i] ) ) { + materialPreview->OnGlobalParmChange( i, globalParms[i] ); + } + ImGui::SetItemTooltip( "Set the global shaderparm for the renderworld" ); + } + + ImGui::TreePop(); + } + if ( ImGui::IsItemHovered() ) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted( "Global shaderparms for the renderworld being displayed." ); + ImGui::EndTooltip(); + } + + // Lights + + if ( ImGui::TreeNodeEx( static_cast( "#L" ), 0, "Lights" ) ) { + ImGui::TextUnformatted( "Preview Lights" ); + ImGui::SameLine(); + if ( ImGui::Button( "Add Light" ) ) { + AddLight(); + } + ImGui::SetItemTooltip( "Test the button" ); + + int lightToDelete = -1; + + for ( int i = 0; i < lights.Num(); i++ ) { + bool lightDeletion = false; + LightData *light = &lights[i]; + + ImGui::PushID( i ); + + if ( ImGui::Button( "Remove" ) ) { + lightToDelete = i; + } + + const char* shader = light->materialNum == -1 ? NULL : lightMaterials[light->materialNum].c_str(); + + if ( ImGui::BeginCombo( "Shader", shader, ImGuiComboFlags_None ) ) { + for ( int n = 0; n < lightMaterials.Num(); n++ ) { + const bool selected = ( light->materialNum == n ); + if ( ImGui::Selectable( lightMaterials[n], selected ) ) { + light->materialNum = n; + materialPreview->OnLightShaderChange( i, lightMaterials[n] ); + } + + if ( selected ) { + ImGui::SetItemDefaultFocus(); + } + } + + ImGui::EndCombo(); + } + ImGui::SetItemTooltip( "Set the light shader." ); + + if ( ImGui::ColorEdit3( "Color", light->color.ToFloatPtr() ) ) { + materialPreview->OnLightColorChange( i, light->color ); + } + ImGui::SetItemTooltip( "Color of the light." ); + + float r = light->radius; + if ( ImGui::DragFloat( "Radius", &r, 1.0f, 0.0f, 10000.0f, "%.1f" ) ) { + light->radius = r; + materialPreview->OnLightRadiusChange( i, r ); + } + ImGui::SetItemTooltip( "Radius of the light." ); + + bool moveable = light->moveable; + if ( ImGui::Checkbox( "Move light", &moveable ) ) { + light->moveable = moveable; + materialPreview->OnLightAllowMoveChange( i, moveable ); + } + ImGui::SetItemTooltip( "When checked, allow light to move." ); + + ImGui::PopID(); + } + + if ( lightToDelete != -1 ) { + LightData *light = &lights[lightToDelete]; + int lightNum = light->id; + + materialPreview->OnDeleteLight( lightNum ); + + lights.RemoveIndex( lightToDelete ); + + for ( int j = lightToDelete; j < lights.Num(); j++) { + lights[j].id = lightNum++; + } + } + + ImGui::TreePop(); + + if ( ImGui::IsWindowFocused() ) { + MaterialEditorSetActiveWindow( ME_WINDOW_PREVIEW_PROP ); + } + } + + ImGui::EndChild(); + return false; +} + +void MaterialPreviewPropView::AddLight( void ) { + if ( !lightMaterials.Num() ) { + const idMaterial* mat; + + // Add all light shaders to the combo box + int count = declManager->GetNumDecls( DECL_MATERIAL ); + for ( int i = 0; i < count; i++ ) { + mat = declManager->MaterialByIndex( i, false ); + + idStr materialName = mat->GetName(); + materialName.ToLower(); + + if ( materialName.Left(7) == "lights/" || materialName.Left(5) == "fogs/" ) { + int index = lightMaterials.Append( materialName ); + + if ( materialName == "lights/defaultpointlight" ) { + defaultPointLightIndex = index; + } + } + } + } + + LightData light; + + light.id = lights.Num(); + light.materialNum = defaultPointLightIndex; + light.radius = 300.0f; + light.color.Set( 1.0f, 1.0f, 1.0f ); + light.moveable = true; + + lights.Append( light ); + + if ( materialPreview ) { + materialPreview->OnAddLight(); + } +} + +//Create sample data for the preview properties +void MaterialPreviewPropView::InitializePropTree( void ) { + AddLight(); +} + +void MaterialPreviewPropView::RegisterPreviewView( MaterialPreviewView *view ) { + materialPreview = view; +} + +} diff --git a/neo/tools/imgui/materialeditor/MaterialPreviewPropView.h b/neo/tools/imgui/materialeditor/MaterialPreviewPropView.h new file mode 100644 index 000000000..a3b861201 --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialPreviewPropView.h @@ -0,0 +1,87 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef MATERIALPREVIEWPROPVIEW_H_ +#define MATERIALPREVIEWPROPVIEW_H_ + +#include "MaterialPreviewView.h" + +namespace ImGuiTools { + +// MaterialPreviewPropView view + +class MaterialPreviewPropView +{ +public: + MaterialPreviewPropView(); + + virtual ~MaterialPreviewPropView(); + + bool Draw( const ImVec2 &size ); + +public: + void AddLight( void ); + void InitializePropTree( void ); + + void RegisterPreviewView( MaterialPreviewView *view ); + +protected: + class LightData + { + public: + int id; + int materialNum; + idVec3 color; + float radius; + bool moveable; + + LightData() + : id(0) + , materialNum(-1) + , color(vec3_zero) + , radius(1.0f) + , moveable(false) + { + } + }; + + idStr customModel; + idList lights; + idStrList lightMaterials; + int defaultPointLightIndex; + int modelType; + bool showLights; + float localParms[MAX_ENTITY_SHADER_PARMS]; + float globalParms[MAX_GLOBAL_SHADER_PARMS]; + DeclSelect customModelSelectDlg; // TODO: replace by file dialog? + + MaterialPreviewView *materialPreview; +}; + +} + +#endif /* !MATERIALPREVIEWPROPVIEW_H_ */ diff --git a/neo/tools/imgui/materialeditor/MaterialPreviewView.cpp b/neo/tools/imgui/materialeditor/MaterialPreviewView.cpp new file mode 100644 index 000000000..dd1bf3c4b --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialPreviewView.cpp @@ -0,0 +1,615 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + + +#include "framework/EventLoop.h" +#include "framework/Session.h" +#include "imgui.h" +#include "imgui_internal.h" +#include "renderer/Image.h" +#include "renderer/ModelManager.h" +#include "../../renderer/tr_local.h" + + +//#include "../radiant/QE3.H" +#include "MaterialDoc.h" +#include "renderer/RenderSystem.h" +#include "MaterialPreviewView.h" + +namespace ImGuiTools { + +// MaterialPreviewView + +MaterialPreviewView::MaterialPreviewView() { + // Initialize the rendered material + renderedView.setMedia( "_default" ); +} + +MaterialPreviewView::~MaterialPreviewView() { +} + +// MaterialPreviewView drawing + +bool MaterialPreviewView::Draw( const ImVec2 &size ) { + if ( ImGui::BeginChild( "MaterialPreviewView", size, ImGuiChildFlags_Borders ) ) { + ImGui::SetNextItemAllowOverlap(); + ImGui::InvisibleButton("###MaterialPreviewRenderedView", size, ImGuiButtonFlags_AllowOverlap | ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonMiddle | ImGuiButtonFlags_MouseButtonRight ); + ImVec2 pos = ImGui::GetCursorPos(); + if ( ImGui::IsItemActive() ) + { + renderedView.input(); + } + renderedView.draw( pos.x, pos.y, size.x, size.y ); + + if ( ImGui::IsWindowFocused() ) { + MaterialEditorSetActiveWindow( ME_WINDOW_PREVIEW ); + } + } + ImGui::EndChild(); + + return false; +} + +// MaterialPreviewView message handlers + +void MaterialPreviewView::MV_OnMaterialSelectionChange( MaterialDoc *pMaterial ) +{ + if ( pMaterial && pMaterial->renderMaterial ) { + currentMaterial = pMaterial->renderMaterial->GetName(); + renderedView.setMedia( currentMaterial ); + } +} + +void MaterialPreviewView::OnLocalParmChange( int parmNum, float value ) { + + renderedView.setLocalParm( parmNum, value ); +} + +void MaterialPreviewView::OnGlobalParmChange( int parmNum, float value ) { + + renderedView.setGlobalParm( parmNum, value ); +} + +void MaterialPreviewView::OnLightShaderChange( int lightId, idStr shaderName ) { + + renderedView.setLightShader( lightId, shaderName ); +} + +void MaterialPreviewView::OnLightColorChange( int lightId, idVec3 &color ) { + + renderedView.setLightColor( lightId, color ); +} + +void MaterialPreviewView::OnLightRadiusChange( int lightId, float radius ) { + + renderedView.setLightRadius( lightId, radius ); +} + +void MaterialPreviewView::OnLightAllowMoveChange( int lightId, bool move ) { + + renderedView.setLightAllowMove( lightId, move ); +} + +void MaterialPreviewView::OnAddLight( void ) { + + renderedView.addLight(); +} + +void MaterialPreviewView::OnDeleteLight( int lightId ) { + + renderedView.deleteLight( lightId ); +} + +void MaterialPreviewView::OnModelChange( int modelId ) { + + renderedView.setObject( modelId ); +} + +void MaterialPreviewView::OnCustomModelChange( idStr modelName ) { + + renderedView.setCustomModel( modelName ); +} + +void MaterialPreviewView::OnShowLightsChange( bool showLights ) { + + renderedView.setShowLights( showLights ); +} + +/* + ============================================================================= + ============================================================================= + ============================================================================= + */ + +idGLDrawable::idGLDrawable() { + scale = 1.0; + xOffset = 0.0; + yOffset = 0.0; + realTime = 0; +} + +void idGLDrawable::draw(int x, int y, int w, int h) { + GL_State( GLS_DEFAULT ); + qglViewport( x, y, w, h ); + qglScissor( x, y, w, h ); + qglMatrixMode( GL_PROJECTION ); + qglClearColor( 0.1f, 0.1f, 0.1f, 0.0f ); + qglClear( GL_COLOR_BUFFER_BIT ); + qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + qglLineWidth( 0.5 ); + qglColor3f( 1, 1, 1 ); + globalImages->BindNull(); + qglBegin(GL_LINE_LOOP); + qglColor3f(1, 0, 0); + qglVertex2f(x + 3, y + 3); + qglColor3f(0, 1, 0); + qglVertex2f(x + 3, h - 3); + qglColor3f(0, 0, 1); + qglVertex2f(w - 3, h - 3); + qglColor3f(1, 1, 1); + qglVertex2f(w - 3, y + 3); + qglEnd(); +} + +idGLDrawableView::idGLDrawableView() { + material = NULL; + modelDefHandle = -1; + + objectId = 0; + showLights = true; + realTime = 16; + + viewOrigin.Set( 0.f, 0.f, 0.f ); + viewRotation.Set( 0.f, 0.f, 0.f ); + viewDistance = -196.f; + + world = NULL; + worldModel = NULL; + + ResetView(); +} + +idGLDrawableView::~idGLDrawableView() { + delete world; + delete worldModel; +} + +void idGLDrawableView::ResetView( void ) { + idDict spawnArgs; + + InitWorld(); + + memset( &worldEntity, 0, sizeof( worldEntity ) ); + spawnArgs.Clear(); + spawnArgs.Set("classname", "func_static"); + spawnArgs.Set("name", spawnArgs.GetString("model")); + spawnArgs.Set("origin", "0 0 0"); + + gameEdit->ParseSpawnArgsToRenderEntity(&spawnArgs, &worldEntity); + + // load a model and set the current material as its customshader + worldModel = renderModelManager->FindModel( "models/materialeditor/cube128.ase" ); + worldEntity.hModel = worldModel; + + // current material + worldEntity.customShader = material; + + // current rotation + worldEntity.axis = mat3_identity; + + // set global shader parms + memset( globalParms, 0, sizeof( globalParms ) ); + globalParms[0] = globalParms[1] = globalParms[2] = globalParms[3] = 1.f; + + worldEntity.shaderParms[0] = 1.f; + worldEntity.shaderParms[1] = 1.f; + worldEntity.shaderParms[2] = 1.f; + worldEntity.shaderParms[3] = 1.f; + modelDefHandle = world->AddEntityDef( &worldEntity ); +} + +void idGLDrawableView::InitWorld() { + + if ( world == NULL ) { + world = renderSystem->AllocRenderWorld(); + } + if ( worldModel == NULL ) { + worldModel = renderModelManager->AllocModel(); + } + + world->InitFromMap( NULL ); + worldModel->InitEmpty( "GLWorldModel" ); + + viewLights.Clear(); +} + +void idGLDrawableView::addLight( void ) { + int lightId; + idStr str; + lightInfo_t viewLight; + idDict spawnArgs; + + lightId = viewLights.Num(); + + spawnArgs.Set( "classname", "light" ); + spawnArgs.Set( "name", va( "light_%d", lightId ) ); + spawnArgs.Set( "origin", va( "-128 0 %d", (lightId * 16) ) ); + spawnArgs.Set( "light", "300" ); + spawnArgs.Set( "texture", "lights/defaultPointLight" ); + sprintf( str, "%f %f %f", 1.f, 1.f, 1.f ); + spawnArgs.Set( "_color", str ); + gameEdit->ParseSpawnArgsToRenderLight( &spawnArgs, &viewLight.renderLight ); + + viewLight.lightDefHandle = world->AddLightDef( &viewLight.renderLight ); + viewLight.origin = viewLight.renderLight.origin; + viewLight.shader = declManager->FindMaterial( "lights/defaultPointLight", false ); + viewLight.color.x = viewLight.renderLight.shaderParms[ SHADERPARM_RED ]; + viewLight.color.y = viewLight.renderLight.shaderParms[ SHADERPARM_GREEN ]; + viewLight.color.z = viewLight.renderLight.shaderParms[ SHADERPARM_BLUE ]; + viewLight.radius = 300.f; + viewLight.allowMove = true; + + // Add light to the list + viewLights.Append( viewLight ); +} + +void idGLDrawableView::deleteLight( const int lightId ) { + if ( lightId < viewLights.Num() ) { + world->FreeLightDef( viewLights[lightId].lightDefHandle ); + + viewLights.RemoveIndex( lightId ); + } +} + +void idGLDrawableView::UpdateCamera( renderView_t *refdef ) { + idVec3 pos, dir; + idAngles angs; + + // Set the camera origin + pos = viewRotation.ToForward(); + pos *= viewDistance; + refdef->vieworg = pos; + + // Set the view to point back at the origin + dir = vec3_origin - pos; + angs = dir.ToAngles(); + refdef->viewaxis = angs.ToMat3(); +} + + +void idGLDrawableView::UpdateModel( void ) { + + switch( objectId ) { + case 0: + worldModel = renderModelManager->FindModel( "models/materialeditor/cube128.ase" ); + break; + case 1: + worldModel = renderModelManager->FindModel( "models/materialeditor/box128x64.ase" ); + break; + case 2: + worldModel = renderModelManager->FindModel( "models/materialeditor/box128x32.ase" ); + break; + case 3: + worldModel = renderModelManager->FindModel( "models/materialeditor/box64x128.ase" ); + break; + case 4: + worldModel = renderModelManager->FindModel( "models/materialeditor/box32x128.ase" ); + break; + case 5: + worldModel = renderModelManager->FindModel( "models/materialeditor/cylinder_v.ase" ); + break; + case 6: + worldModel = renderModelManager->FindModel( "models/materialeditor/cylinder_h.ase" ); + break; + case 7: + worldModel = renderModelManager->FindModel( "models/materialeditor/sphere64.ase" ); + break; + case -1: + worldModel = renderModelManager->FindModel( customModelName.c_str() ); + break; + default: + worldModel = renderModelManager->FindModel( "models/materialeditor/cube128.ase" ); + break; + }; + + worldEntity.hModel = worldModel; + + // current material + worldEntity.customShader = material; + // current rotation + worldEntity.origin = viewOrigin; + + worldEntity.axis = mat3_identity; + + world->UpdateEntityDef( modelDefHandle, &worldEntity ); +} + +void idGLDrawableView::UpdateLights( void ) { + int i; + + for ( i = 0; i < viewLights.Num(); i++ ) { + lightInfo_t *vLight = &viewLights[i]; + + vLight->renderLight.shader = vLight->shader; + + vLight->renderLight.shaderParms[ SHADERPARM_RED ] = vLight->color.x; + vLight->renderLight.shaderParms[ SHADERPARM_GREEN ] = vLight->color.y; + vLight->renderLight.shaderParms[ SHADERPARM_BLUE ] = vLight->color.z; + + vLight->renderLight.lightRadius[0] = vLight->renderLight.lightRadius[1] = + vLight->renderLight.lightRadius[2] = vLight->radius; + + vLight->renderLight.origin = vLight->origin; + + world->UpdateLightDef( vLight->lightDefHandle, &vLight->renderLight ); + } +} + +void idGLDrawableView::drawLights( renderView_t *refdef ) { + int i; + + for ( i=0; i < viewLights.Num(); i++ ) { + lightInfo_t *vLight = &viewLights[i]; + + idVec4 lColor; + lColor.x = vLight->color.x; + lColor.y = vLight->color.y; + lColor.z = vLight->color.z; + lColor.w = 1.f; + + idSphere sphere(vLight->renderLight.origin, 4); + session->rw->DebugSphere( lColor, sphere, 0, true ); + session->rw->DrawText( va( "%d", i + 1 ), vLight->renderLight.origin + idVec3( 0, 0, 5 ), 0.25f, idVec4( 1, 1, 0, 1 ), refdef->viewaxis, 1, 0, true ); + } +} + +void idGLDrawableView::input() { + // mouse move + bool doZoom; + float sensitivity = 0.5f; + + if ( ImGui::IsMouseDragging( ImGuiMouseButton_Left ) || ImGui::IsMouseDragging( ImGuiMouseButton_Middle ) || ImGui::IsMouseDragging( ImGuiMouseButton_Right ) ) { + ImVec2 delta = ImGui::GetIO().MouseDelta; + + // Left mouse button rotates and zooms the view + if ( ImGui::IsMouseDragging( ImGuiMouseButton_Left ) ) { + doZoom = ImGui::IsKeyDown( ImGuiKey_LeftAlt ) || ImGui::IsKeyDown( ImGuiKey_RightAlt ); + if (doZoom) { + viewDistance -= delta.y; + } else { + viewRotation.yaw += -(delta.x * sensitivity); + viewRotation.pitch += (delta.y * sensitivity); + + viewRotation.pitch = idMath::ClampFloat( -89.9f, 89.9f, viewRotation.pitch ); + } + + // Right mouse button moves lights in the view plane + } else if ( ImGui::IsMouseDragging( ImGuiMouseButton_Right ) ) { + int i; + float lightMovement = 0; + idVec3 lightForward, lightRight, lightUp; + idVec3 lightMove; + + lightMove.Zero(); + viewRotation.ToVectors( &lightForward, &lightRight, &lightUp ); + + doZoom = ImGui::IsKeyDown( ImGuiKey_LeftAlt ) || ImGui::IsKeyDown( ImGuiKey_RightAlt ); + if (doZoom) { + if ( delta.y != 0 ) { + lightMovement = -delta.y * sensitivity; + + lightMovement = idMath::ClampFloat( -32.f, 32.f, lightMovement ); + lightMove = lightForward * lightMovement; + } + } else { + if (delta.x != 0) { + lightMovement = delta.x * sensitivity; + + lightMovement = idMath::ClampFloat( -32.f, 32.f, lightMovement ); + lightMove = lightRight * lightMovement; + } + if (delta.y != 0) { + lightMovement = -delta.y * sensitivity; + + lightMovement = idMath::ClampFloat( -32.f, 32.f, lightMovement ); + lightMove += lightUp * lightMovement; + } + } + + // Go through the lights and move the ones that are set to allow movement + for ( i = 0; i < viewLights.Num(); i++ ) { + lightInfo_t *vLight = &viewLights[i]; + + if ( vLight->allowMove ) { + vLight->origin += lightMove; + } + } + + // Middle mouse button moves object up and down + } else if ( ImGui::IsMouseDragging( ImGuiMouseButton_Middle ) ) { + viewOrigin.z -= delta.y; + + UpdateModel(); + } + } +} + +void idGLDrawableView::draw( int x, int y, int w, int h ) { + int i; + renderView_t refdef; + const idMaterial *mat = material; + + if ( mat ) { + UpdateLights(); + + // render it + memset( &refdef, 0, sizeof( refdef ) ); + + UpdateCamera( &refdef ); + + // Copy global shaderparms to view + for( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) { + refdef.shaderParms[ i ] = globalParms[ i ]; + } + + refdef.width = SCREEN_WIDTH; + refdef.height = SCREEN_HEIGHT; + refdef.fov_x = 90; + refdef.fov_y = 2 * atan((float)h / w) * idMath::M_RAD2DEG; + + refdef.time = eventLoop->Milliseconds(); + + renderSystem->SetColor( idVec4( 0.1f, 0.1f, 0.1f, 1.0f ) ); + renderSystem->DrawStretchPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0, 0, white ); + + world->RenderScene( &refdef ); + + if ( showLights ) { + drawLights( &refdef ); + } + + renderSystem->CropRenderSize( SCREEN_WIDTH, SCREEN_HEIGHT, true ); + renderSystem->CaptureRenderToImage( "_currentRender" ); + renderSystem->UnCrop(); + + const ImVec2 p0 = ImGui::GetItemRectMin(); + const ImVec2 p1 = ImGui::GetItemRectMax(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + ImGui::PushClipRect(p0, p1, true); + draw_list->AddImage(currentRender->texnum, p0, p1, ImVec2( 0.0f, 1.0f ), ImVec2( 1.0f, 0.0f ) ); + ImGui::PopClipRect(); + } +} + +// ============================ +// Interface to PropTree Window +// ============================ + +void idGLDrawableView::setMedia( const char *name ) { + float ratio = 1; + + currentRender = globalImages->GetImage( "_currentRender" ); + white = declManager->FindMaterial( "guis/assets/white.tga" ); + + if ( name && *name ) { + material = declManager->FindMaterial( name ); + } else { + material = NULL; + } + + if ( material->GetNumStages() == 0 ) { + material = declManager->FindMaterial( "_default" ); + } + + if ( material->GetStage(0)->texture.image ) { + ratio = (float)((float)material->GetImageWidth() / (float)material->GetImageHeight()); + } + + if ( objectId == -1 ) { + // Don't change a custom model + } else if ( ratio == 1 ) { + objectId = 0; + } else if ( ratio == 2 ) { + objectId = 1; + } else if ( ratio == 4 ) { + objectId = 2; + } else if ( ratio == 0.5 ) { + objectId = 3; + } else if ( ratio == 0.25 ) { + objectId = 4; + } + + UpdateModel(); +} + +void idGLDrawableView::setLocalParm( int parmNum, float value ) { + if ( parmNum < 0 || parmNum >= MAX_ENTITY_SHADER_PARMS ) { + return; + } + + worldEntity.shaderParms[ parmNum ] = value; + + UpdateModel(); +} + +void idGLDrawableView::setGlobalParm( int parmNum, float value ) { + if ( parmNum < 0 || parmNum >= MAX_GLOBAL_SHADER_PARMS ) { + return; + } + + globalParms[ parmNum ] = value; +} + +void idGLDrawableView::setLightShader( const int lightId, const idStr shaderName ) { + if ( lightId < viewLights.Num() ) { + viewLights[ lightId ].shader = declManager->FindMaterial( shaderName, false ); + } +} + +void idGLDrawableView::setLightColor( const int lightId, const idVec3 &value ) { + if ( lightId < viewLights.Num() ) { + // Update this lights color + viewLights[ lightId ].color = value; + } +} + +void idGLDrawableView::setLightRadius( const int lightId, const float radius ) { + if ( lightId < viewLights.Num() ) { + viewLights[ lightId ].radius = radius; + } +} + +void idGLDrawableView::setLightAllowMove( const int lightId, const bool move ) { + if ( lightId < viewLights.Num() ) { + viewLights[ lightId ].allowMove = move; + } +} + +void idGLDrawableView::setObject( int Id ) { + objectId = Id; + + UpdateModel(); +} + +void idGLDrawableView::setCustomModel( const idStr modelName ) { + if ( modelName.Length() ) { + objectId = -1; + } else { + objectId = 0; + } + + customModelName = modelName; + + UpdateModel(); +} + +void idGLDrawableView::setShowLights( bool _showLights ) { + showLights = _showLights; +} + +} diff --git a/neo/tools/imgui/materialeditor/MaterialPreviewView.h b/neo/tools/imgui/materialeditor/MaterialPreviewView.h new file mode 100644 index 000000000..29be8942e --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialPreviewView.h @@ -0,0 +1,182 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef MATERIALPREVIEWVIEW_H_ +#define MATERIALPREVIEWVIEW_H_ + +#include "../util/ImGui_IdWidgets.h" +#include "renderer/Material.h" +#include "sys/sys_imgui.h" + +#include "MaterialDoc.h" +#include "MaterialView.h" +//#include "../radiant/GLWidget.h" + +namespace ImGuiTools { + +class idGLDrawable { +public: + idGLDrawable(); + ~idGLDrawable() {}; + virtual void input() {} + virtual void draw(int x, int y, int w, int h); + virtual void setMedia(const char* name) {} + virtual int getRealTime() { return realTime; }; + virtual bool ScreenCoords() { + return true; + } + void SetRealTime(int i) { + realTime = i; + } + virtual void Update() {}; + float getScale() { + return scale; + } + void setScale(float f) { + scale = f; + } +protected: + float scale; + float xOffset; + float yOffset; + float zOffset; + int realTime; +}; + +class idGLDrawableView : public idGLDrawable { + +public: + idGLDrawableView(); + ~idGLDrawableView(); + + virtual void setMedia(const char *name); + virtual void input(); + virtual void draw(int x, int y, int w, int h); + virtual void Update() {}; + + void UpdateCamera( renderView_t *refdef ); + void UpdateModel( void ); + void UpdateLights( void ); + + void addLight( void ); + void deleteLight( const int lightId ); + void drawLights( renderView_t *refdef ); + + void InitWorld(); + void ResetView( void ); + + void setLightShader( const int lightId, const idStr shaderName ); + void setLightColor( const int lightId, const idVec3 &value ); + void setLightRadius( const int lightId, const float radius ); + void setLightAllowMove( const int lightId, const bool move ); + void setObject( int Id ); + void setCustomModel( const idStr modelName ); + void setShowLights( bool _showLights ); + void setLocalParm( int parmNum, float value ); + void setGlobalParm( int parmNum, float value ); + +protected: + idRenderWorld *world; + idRenderModel *worldModel; + const idImage *currentRender; + const idMaterial *white; + const idMaterial *material; + + bool showLights; + + idVec3 viewOrigin; + idAngles viewRotation; + float viewDistance; + + renderEntity_t worldEntity; + qhandle_t modelDefHandle; + + int objectId; + idStr customModelName; + + float globalParms[MAX_GLOBAL_SHADER_PARMS]; + + typedef struct { + renderLight_t renderLight; + qhandle_t lightDefHandle; + idVec3 origin; + const idMaterial *shader; + float radius; + idVec3 color; + bool allowMove; + } lightInfo_t; + + idList viewLights; +}; + +// ================================================================== +// ================================================================== + +class MaterialPreviewView : public MaterialView +{ + //DECLARE_DYNCREATE(MaterialPreviewView) + +public: + MaterialPreviewView(); + virtual ~MaterialPreviewView(); + + bool Draw( const ImVec2 &size ); + +public: + //virtual void OnDraw(CDC* pDC); // overridden to draw this view + + void MV_OnMaterialSelectionChange(MaterialDoc* pMaterial); + + void OnModelChange( int modelId ); + void OnCustomModelChange( idStr modelName ); + void OnShowLightsChange( bool showLights ); + + void OnLocalParmChange( int parmNum, float value ); + void OnGlobalParmChange( int parmNum, float value ); + + void OnLightShaderChange( int lightId, idStr shaderName ); + void OnLightRadiusChange( int lightId, float radius ); + void OnLightColorChange( int lightId, idVec3 &color ); + void OnLightAllowMoveChange( int lightId, bool move ); + + void OnAddLight( void ); + void OnDeleteLight( int lightId ); + +protected: + //idGLWidget renderWindow; + idGLDrawableView renderedView; + + idStr currentMaterial; + +public: + //afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + //afx_msg void OnSize(UINT nType, int cx, int cy); +}; + +} + +#endif /* !MATERIALPREVIEWVIEW_H_ */ diff --git a/neo/tools/imgui/materialeditor/MaterialPropTreeView.cpp b/neo/tools/imgui/materialeditor/MaterialPropTreeView.cpp new file mode 100644 index 000000000..a66ea1cae --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialPropTreeView.cpp @@ -0,0 +1,224 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../util/ImGui_IdWidgets.h" +#include "../util/RegistryOptions.h" +#include "tools/imgui/materialeditor/MaterialEditor.h" + +#include "MaterialPropTreeView.h" + +#define PROP_TREE_VIEW "PropTreeView" + +namespace ImGuiTools { + +/** +* Constructor for MaterialPropTreeView. +*/ +MaterialPropTreeView::MaterialPropTreeView() + : currentMaterial(NULL) + , currentListType(0) + , currentStage(-1) + , currentPropDefs(NULL) +{ + registry.Init("MaterialEditor_PropertySettings"); + internalChange = false; +} + +/** +* Destructor for MaterialPropTreeView. +*/ +MaterialPropTreeView::~MaterialPropTreeView() { +} + +bool MaterialPropTreeView::Draw( const ImVec2 &size ) { + if ( ImGui::BeginChild( "###MaterialPropTreeView", size, ImGuiChildFlags_Borders ) ) { + + MaterialDoc *materialDoc = materialDocManager->GetCurrentMaterialDoc(); + + if ( currentPropDefs ) + { + for ( int i = 0; i < currentPropDefs->Num(); ) { + MaterialDef *propItem = (*currentPropDefs)[i]; + ImGuiTreeNodeFlags flags = 0; + int j; + + switch ( (*currentPropDefs)[i]->type ) { + case MaterialDef::MATERIAL_DEF_TYPE_GROUP: + { + ImGui::SetNextItemOpen( registry.GetBool(va("Expand%d%s", currentListType, propItem->displayName.c_str())) ); + + if ( ImGui::TreeNodeEx( static_cast(propItem), flags, "%s", propItem->displayName.c_str() ) ) { + if ( ImGui::IsItemClicked() ) { + registry.SetBool(va("Expand%d%s", currentListType, propItem->displayName.c_str()), ImGui::IsItemToggledOpen()); + registry.Save(); + } + + j = DrawGroup( i + 1 ); + + ImGui::TreePop(); + } else { + // skip until next group + j = i + 1; + while ( j < currentPropDefs->Num() && (*currentPropDefs)[j]->type != MaterialDef::MATERIAL_DEF_TYPE_GROUP ) { + j++; + } + } + + if ( ImGui::IsItemHovered() && !propItem->displayInfo.IsEmpty() ) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted( propItem->displayInfo.c_str() ); + ImGui::EndTooltip(); + } + } + break; + default: + j = DrawGroup( i ); + break; + } + + i = j; + } + } + + if ( ImGui::IsWindowFocused() ) { + MaterialEditorSetActiveWindow( ME_WINDOW_PROP ); + } + } + + ImGui::EndChild(); + + return false; +} + +int MaterialPropTreeView::DrawGroup( int startGroupNum ) { + if ( !currentPropDefs ) { + return startGroupNum + 1; + } + + MaterialDoc* materialDoc = materialDocManager->GetCurrentMaterialDoc(); + + int i = startGroupNum; + + for ( ; i < currentPropDefs->Num(); i++ ) { + MaterialDef* propItem = (*currentPropDefs)[i]; + + switch ( propItem->type ) { + case MaterialDef::MATERIAL_DEF_TYPE_GROUP: + return i; + case MaterialDef::MATERIAL_DEF_TYPE_BOOL: + { + bool val = materialDoc->GetAttributeBool( currentStage, propItem->dictName ); + + if ( ImGui::Checkbox( propItem->displayName.c_str(), &val ) ) { + internalChange = true; + materialDoc->SetAttributeBool(currentStage, propItem->dictName, val ? true : false); + internalChange = false; + } + } + break; + case MaterialDef::MATERIAL_DEF_TYPE_STRING: + { + idStr val = materialDoc->GetAttribute( currentStage, propItem->dictName ); + + if ( ImGui::InputTextStr( propItem->displayName, &val ) ) { + internalChange = true; + materialDoc->SetAttribute( currentStage, propItem->dictName, val ); + internalChange = false; + } + } + break; + case MaterialDef::MATERIAL_DEF_TYPE_FLOAT: + { + float val = materialDoc->GetAttributeFloat( currentStage, propItem->dictName ); + + if ( ImGui::InputFloat( propItem->displayName, &val ) ) { + internalChange = true; + materialDoc->SetAttributeFloat( currentStage, propItem->dictName, val ); + internalChange = false; + } + } + case MaterialDef::MATERIAL_DEF_TYPE_INT: + { + int val = materialDoc->GetAttributeInt( currentStage, propItem->dictName ); + + if ( ImGui::InputInt( propItem->displayName, &val ) ) { + internalChange = true; + materialDoc->SetAttributeInt( currentStage, propItem->dictName, val ); + internalChange = false; + } + } + break; + } + + if ( ImGui::IsItemHovered() && !propItem->displayInfo.IsEmpty() ) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted( propItem->displayInfo.c_str() ); + ImGui::EndTooltip(); + } + } + + return i; +} + +/** +* Initializes the list of properties based on the type (material, stage, special stage). +* @param listType The type of list (material, stage, special stage) +* @param stageNum The stage from which to get the attributes. +*/ +void MaterialPropTreeView::SetPropertyListType(int listType, int stageNum) { + + currentListType = listType; + currentStage = stageNum; + + MaterialDefList* propList = MaterialDefManager::GetMaterialDefs(currentListType); + currentPropDefs = propList; +} + +/** +* Loads the property view settings from the registry. +*/ +void MaterialPropTreeView::LoadSettings() { + registry.Load(); +} + +/** +* Saves the property view settings to the registry. +*/ +void MaterialPropTreeView::SaveSettings() { + registry.Save(); +} + +/** +* Called when the material has changed but not applied. +* @param pMaterial The selected material. +*/ +void MaterialPropTreeView::MV_OnMaterialChange(MaterialDoc* pMaterial) { + +} + +} diff --git a/neo/tools/imgui/materialeditor/MaterialPropTreeView.h b/neo/tools/imgui/materialeditor/MaterialPropTreeView.h new file mode 100644 index 000000000..1d505c960 --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialPropTreeView.h @@ -0,0 +1,71 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef MATERIALPROPTREEVIEW_H_ +#define MATERIALPROPTREEVIEW_H_ + +#include "sys/sys_imgui.h" +#include "MaterialView.h" +#include "MaterialDef.h" +#include "../util/RegistryOptions.h" + +namespace ImGuiTools { + + +/** +* View that displays material and stage properties and allows the user to edit the properties. +*/ +class MaterialPropTreeView : public MaterialView { + +public: + MaterialPropTreeView(); + virtual ~MaterialPropTreeView(); + + bool Draw( const ImVec2 &size ); + + void SetPropertyListType( int listType, int stageNum = -1 ); + + void LoadSettings(); + void SaveSettings(); + + //Material Interface + virtual void MV_OnMaterialChange( MaterialDoc *pMaterial ); + +private: + int DrawGroup( int startGroupNum ); + + MaterialDoc * currentMaterial; + int currentListType; + int currentStage; + MaterialDefList * currentPropDefs; + rvRegistryOptions registry; + bool internalChange; +}; + +} + +#endif /* !MATERIALPROPTREEVIEW_H_ */ diff --git a/neo/tools/imgui/materialeditor/MaterialTreeView.cpp b/neo/tools/imgui/materialeditor/MaterialTreeView.cpp new file mode 100644 index 000000000..241e67142 --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialTreeView.cpp @@ -0,0 +1,1940 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#include "../util/ImGui_IdWidgets.h" + +#include "MaterialTreeView.h" + +#define IMAGE_FOLDER 0 +#define IMAGE_FILE 1 +#define IMAGE_MATERIAL 2 +#define IMAGE_MATERIAL_FOLDER 3 +#define IMAGE_FILE_MOD 4 +#define IMAGE_MATERIAL_MOD 5 +#define IMAGE_MATERIAL_MOD_APPLY 6 + +#define HOVER_EXPAND_DELAY 500 + +#define MSG_RENAME_FOLDER_COMPLETE (WM_USER + 1000) +#define MSG_RENAME_MATERIAL_COMPLETE (WM_USER + 1001) + +namespace ImGuiTools { + +bool MaterialTreeViewOnToolTipNotify(void* data, TreeNode* item, idStr& tooltipText) { + return false; +} +void MaterialTreeViewOnTreeSelChanged(void* data, bool doubleClicked) { + reinterpret_cast(data)->OnTvnSelchanged(doubleClicked); +} +void MaterialTreeViewOnContextMenu( void *data, TreeNode *item ) { + reinterpret_cast(data)->OnNMRclick( item ); +} +void MaterialTreeViewOnBeginDrag( void *data, TreeNode *item ) { + reinterpret_cast(data)->OnTvnBegindrag( item ); +} +void MaterialTreeViewOnEndDrag( void *data, TreeNode *source, TreeNode *destination ) { + reinterpret_cast(data)->OnTvnEndDrag( source, destination ); +} +void MaterialTreeViewOnInput( void *data, bool prepare, TreeNode *item ) { + reinterpret_cast(data)->OnInput( prepare, item ); +} + +/** +* Constructor for MaterialTreeView +*/ +MaterialTreeView::MaterialTreeView() { + treeWithFile = false; + bDragging = false; + hoverItem = NULL; + internalChange = false; + messageBox = MSG_BOX_CLOSED; + newName.Clear(); +} + +/** +* Destructor for MaterialTreeView +*/ +MaterialTreeView::~MaterialTreeView() { + newName.FreeData(); +} + +/** +* Clears the tree and rebuilds it. +* @param includeFile Should the list include the filename +* @param filename The file to load or NULL to load all files. +*/ +void MaterialTreeView::InitializeMaterialList(bool includeFile, const char* filename) { + + treeWithFile = includeFile; + + tree.DeleteAllItems(); + quickTree.Clear(); + materialToTree.Clear(); + fileToTree.Clear(); + + BuildMaterialList(includeFile, filename); +} + +/** +* Builds the tree of materials. +* @param includeFile Should the list include the filename +* @param filename The file to load or NULL to load all files. +*/ +void MaterialTreeView::BuildMaterialList(bool includeFile, const char* filename) { + + idStrList list(1024); + + int count = declManager->GetNumDecls( DECL_MATERIAL ); + if (count > 0) { + for (int i = 0; i < count; i++) { + const idMaterial *mat = declManager->MaterialByIndex(i, false); + + if(filename && strcmp(filename, mat->GetFileName())) { + continue; + } + + idStr temp; + + //Do Not Include Implicit File Definitions + idStr filename = mat->GetFileName(); + if(!filename.Icmp("")) { + continue; + } + + if(includeFile) { + filename.StripPath(); + temp = idStr(mat->GetFileName()) + "/" + idStr(mat->GetName()) + "|" + filename; + } else { + temp = mat->GetName(); + } + + list.Append(temp); + } + AddStrList(NULL, &list, includeFile); + } +} + +bool MaterialTreeView::Draw( const ImVec2 &size ) { + + const char *messageBoxTitle = NULL; + const char *messageBoxText = NULL; + bool messageBoxYesNo = true; + switch ( messageBox ) { + case MSG_BOX_UNABLE_TO_RENAME_MATERIAL: + messageBoxTitle = "Error"; + messageBoxText = "Unable to rename material because it conflicts with another material"; + messageBoxYesNo = false; + break; + case MSG_BOX_RENAME_MATERIAL: + messageBoxTitle = "Please provide a new name"; + messageBoxText = "Name"; + break; + case MSG_BOX_DELETE_MATERIAL: + messageBoxTitle = "Are you sure?"; + messageBoxText = "Are you sure you want to delete this material?"; + break; + case MSG_BOX_DELETE_FOLDER: + messageBoxTitle = "Are you sure?"; + messageBoxText = "Are you sure you want to delete this folder?"; + break; + case MSG_BOX_RELOAD_MODIFIED_FILE: + messageBoxTitle = "Are you sure?"; + messageBoxText = "The file has been modified. Are you sure you want to reload this file?"; + break; + default: + case MSG_BOX_CLOSED: + break; + } + + if ( messageBox != MSG_BOX_CLOSED ) { + ImGui::OpenPopup( "###MaterialTreeViewPopup" ); + } + + if ( ImGui::BeginPopupModal( "###MaterialTreeViewPopup", NULL, ImGuiWindowFlags_AlwaysAutoResize ) ) { + bool accepted = false; + + ImGui::Text( "%s", messageBoxTitle ); + if ( messageBox == MSG_BOX_RENAME_MATERIAL ) { + if ( ImGui::InputTextStr( messageBoxText, &newName ) ) { + } + } else { + ImGui::Text( "%s", messageBoxText ); + } + + if ( messageBoxYesNo ) { + + if ( ImGui::Button( "Yes" ) ) { + ImGui::CloseCurrentPopup(); + accepted = true; + } + if ( ImGui::Button( "No" ) ) { + ImGui::CloseCurrentPopup(); + messageBox = MSG_BOX_CLOSED; + } + } else { + if ( ImGui::Button( "OK" ) ) { + ImGui::CloseCurrentPopup(); + messageBox = MSG_BOX_CLOSED; + } + } + + if ( accepted ) { + switch ( messageBox ) { + case MSG_BOX_DELETE_MATERIAL: + case MSG_BOX_DELETE_FOLDER: + messageBox = MSG_BOX_CLOSED; + OnDeleteMaterialAccepted(); + break; + case MSG_BOX_RELOAD_MODIFIED_FILE: + messageBox = MSG_BOX_CLOSED; + OnReloadFileAccepted(); + break; + case MSG_BOX_RENAME_MATERIAL: + messageBox = MSG_BOX_CLOSED; + OnTvnEndlabeledit( tree.GetSelectedItem(), newName ); + break; + default: + case MSG_BOX_CLOSED: + break; + } + } + + ImGui::EndPopup(); + } + + if ( ImGui::BeginChild( "###MaterialTreeView", size, ImGuiChildFlags_Borders ) ) { + + tree.Draw( MaterialTreeViewOnToolTipNotify, MaterialTreeViewOnTreeSelChanged, MaterialTreeViewOnContextMenu, MaterialTreeViewOnBeginDrag, MaterialTreeViewOnEndDrag, MaterialTreeViewOnInput, this ); + + if ( ImGui::IsWindowFocused() ) { + MaterialEditorSetActiveWindow( ME_WINDOW_TREE ); + } + } + + ImGui::EndChild(); + + return false; +} + +/** +* Called when the material has changed but not applied. +* @param pMaterial The selected material. +*/ +void MaterialTreeView::MV_OnMaterialChange(MaterialDoc* pMaterial) { + TreeNode** materialItemptrptr; + + materialItemptrptr = NULL; + + //When a material changes place an asterik next to the material and the file + TreeNode* materialItem = NULL; + if (materialToTree.Get(pMaterial->name, &materialItemptrptr)) { + materialItem = *materialItemptrptr; + } + + if(!materialItem) + return; + + // TODO: implement + //tree.SetItemImage(*materialItem, IMAGE_MATERIAL_MOD_APPLY, IMAGE_MATERIAL_MOD_APPLY); + + + if(treeWithFile) { + TreeNode** fileItemptrptr = NULL; + TreeNode* fileItem = NULL; + idStr file = pMaterial->renderMaterial->GetFileName(); + + common->Printf("Filename = %s\n", file.c_str()); + + if(fileToTree.Get(file, &fileItemptrptr)){ + fileItem = *fileItemptrptr; + common->Printf("Found: %d\n", fileItem->GetID()); + // TODO: implement + //tree.SetItemImage(*fileItem, IMAGE_FILE_MOD, IMAGE_FILE_MOD); + } + } +} + +/** +* Called when the material changes have been applied. +* @param pMaterial The selected material. +*/ +void MaterialTreeView::MV_OnMaterialApply(MaterialDoc* pMaterial) { + //When a material is applied then just change the image to material modified + TreeNode** materialItemptrptr = NULL; + TreeNode* materialItem = NULL; + if (materialToTree.Get(pMaterial->name, &materialItemptrptr)) { + materialItem = *materialItemptrptr; + } + + if(!materialItem) + return; + + // TODO: implement + //tree.SetItemImage(*materialItem, IMAGE_MATERIAL_MOD, IMAGE_MATERIAL_MOD); +} + +/** +* Called when the material changes have been saved. +* @param pMaterial The saved material. +*/ +void MaterialTreeView::MV_OnMaterialSaved(MaterialDoc* pMaterial) { + //Remove the asterik + TreeNode** materialItemptrptr = NULL; + TreeNode* materialItem = NULL; + + if (materialToTree.Get(pMaterial->name, &materialItemptrptr)) { + materialItem = *materialItemptrptr; + } + + //We will get this message for a delete file so the material will not be in the tree + if(materialItem) { + // TODO: implement + //tree.SetItemImage(*materialItem, IMAGE_MATERIAL, IMAGE_MATERIAL); + } + + //Check if the file is completely saved + if(treeWithFile) { + + if(!materialDocManager->IsFileModified(pMaterial->renderMaterial->GetFileName())) { + + TreeNode** fileItemptrptr = NULL; + TreeNode* fileItem = NULL; + idStr file = pMaterial->renderMaterial->GetFileName(); + + if(fileToTree.Get(file, &fileItemptrptr)) { + fileItem = *fileItemptrptr; + // TODO: implement + //tree.SetItemImage(*fileItem, IMAGE_FILE, IMAGE_FILE); + } + } + } +} + +/** +* Called when a material is added +* @param pMaterial The material that was added. +*/ +void MaterialTreeView::MV_OnMaterialAdd(MaterialDoc* pMaterial) { + + idStrList list(1024); + + idMaterial *mat = pMaterial->renderMaterial; + idStr temp; + + if(treeWithFile) { + idStr filename = mat->GetFileName(); + filename.StripPath(); + temp = idStr(mat->GetFileName()) + "/" + idStr(mat->GetName()) + "|" + filename; + } else { + temp = mat->GetName(); + } + + list.Append(temp); + AddStrList(NULL, &list, treeWithFile); + + //Keep the items sorted + TreeNode** itemptrptr = NULL; + TreeNode* item = NULL; + if (materialToTree.Get(pMaterial->name, &itemptrptr)) { + item = *itemptrptr; + } + if(item) { + TreeNode *parent = tree.GetParentItem(item); + tree.SortChildren(parent); + } + + MV_OnMaterialChange(pMaterial); +} + +/** +* Called when a material is deleted +* @param pMaterial The material that was deleted. +*/ +void MaterialTreeView::MV_OnMaterialDelete(MaterialDoc* pMaterial) { + + //Our doc told us a material has been deleted. Lets find and remove the item from our tree + TreeNode** materialItemptrptr = NULL; + TreeNode* materialItem = NULL; + + if (materialToTree.Get(pMaterial->name, &materialItemptrptr)) { + materialItem = *materialItemptrptr; + } + + tree.DeleteItem(materialItem); + + //Remove our old quick lookup value + materialToTree.Remove(pMaterial->name.c_str()); +} + +/** +* Called when the material name has changed +* @param pMaterial The material that was deleted. +* @param oldName The old name of the material. +*/ +void MaterialTreeView::MV_OnMaterialNameChanged(MaterialDoc* pMaterial, const char* oldName) { + + if(!internalChange) { + + //Delete the old tree item + TreeNode** itemptrptr; + TreeNode* item = NULL; + if (materialToTree.Get(oldName, &itemptrptr)) { + item = *itemptrptr; + } + TreeNode* tempItem = item; + CleanLookupTrees(tempItem); + tree.DeleteItem(tempItem); + + + //Now add it back + idStrList list(1024); + idMaterial *mat = pMaterial->renderMaterial; + idStr temp; + + if(treeWithFile) { + idStr filename = mat->GetFileName(); + filename.StripPath(); + temp = idStr(mat->GetFileName()) + "/" + idStr(mat->GetName()) + "|" + filename; + } else { + temp = mat->GetName(); + } + + list.Append(temp); + AddStrList(NULL, &list, treeWithFile); + + //Keep the items sorted + //item = NULL; + if (materialToTree.Get(pMaterial->name.c_str(), &itemptrptr)) { + item = *itemptrptr; + } + if(item) { + TreeNode *parent = tree.GetParentItem(item); + tree.SortChildren(parent); + } + + MV_OnMaterialChange(pMaterial); + + } +} + +/** +* Called when a file has been reloaded +* @param filename The file that was reloaded. +*/ +void MaterialTreeView::MV_OnFileReload(const char* filename) { + + TreeNode** fileItemptrptr = NULL; + TreeNode* fileItem = NULL; + if (fileToTree.Get(filename, &fileItemptrptr)) { + fileItem = *fileItemptrptr; + } + + TreeNode *item = fileItem; + + CleanLookupTrees(item); + tree.DeleteItem(item); + + BuildMaterialList(treeWithFile, filename); + + //Resort the parent to make sure the file is back where it was + TreeNode** newItemptrptr = NULL; + TreeNode* newItem = NULL; + if (fileToTree.Get(filename, &newItemptrptr)) { + newItem = *newItemptrptr; + } + if(newItem) { + TreeNode *parent = tree.GetParentItem(newItem); + tree.SortChildren(parent); + } +} + +/** +* Returns true if the user can copy the selected item. +*/ +bool MaterialTreeView::CanCopy() { + + TreeNode *item = tree.GetSelectedItem(); + if (!item) { + return false; + } + int itemType = tree.GetItemData(item); + + if(item && itemType == TYPE_MATERIAL) { + return true; + } else { + return false; + } +} + +/** +* Returns true if the user can paste an item in the copy buffer. +*/ +bool MaterialTreeView::CanPaste() { + return materialDocManager->IsCopyMaterial(); +} + +/** +* Returns true if the user can cut the selected item. +*/ +bool MaterialTreeView::CanCut() { + + TreeNode *item = tree.GetSelectedItem(); + if (!item) { + return false; + } + int itemType = tree.GetItemData(item); + + if(item && itemType == TYPE_MATERIAL) { + return true; + } else { + return false; + } +} + +/** +* Returns true if the user can delete the selected item. +*/ +bool MaterialTreeView::CanDelete() { + + TreeNode *item = tree.GetSelectedItem(); + if (!item) { + return false; + } + int itemType = tree.GetItemData(item); + + if(itemType == TYPE_MATERIAL_FOLDER || itemType == TYPE_MATERIAL) { + return true; + } + + return false; +} + +/** +* Returns true if the user can rename the selected item. +*/ +bool MaterialTreeView::CanRename() { + + TreeNode *item = tree.GetSelectedItem(); + if (!item) { + return false; + } + int itemType = tree.GetItemData(item); + + if(itemType == TYPE_MATERIAL_FOLDER || itemType == TYPE_MATERIAL) { + return true; + } + return false; +} + +/** +* Returns true if the currently selected file needs to be saved. +*/ +bool MaterialTreeView::CanSaveFile() { + + TreeNode *item = tree.GetSelectedItem(); + if (!item) { + return false; + } + + idStr filename; + if(item && GetFileName(item, filename)) { + if(materialDocManager->IsFileModified(filename.c_str())) + return true; + else + return false; + } else { + return false; + } +} + +/** +* Returns the filename of currently selected file. +*/ +idStr MaterialTreeView::GetSaveFilename() { + + TreeNode *item = tree.GetSelectedItem(); + + idStr filename = ""; + if(item) { + if(!GetFileName(item, filename)) { + filename = ""; + } + } + + return filename; +} + +/** +* Searches for a material given the supplied search parameters. +* @param searchData The parameters to use for the search. +*/ +bool MaterialTreeView::FindNextMaterial(MaterialSearchData_t* searchData) { + + TreeNode *selected = tree.GetSelectedItem(); + if(!selected) { + selected = tree.GetRootItem(); + if(!selected) { + return false; + } + } + + //Make sure we are in a file + if(searchData->searchScope == 0) { + int type = tree.GetItemData(selected); + if(type == TYPE_FOLDER || type == TYPE_ROOT) + return false; + } + + TreeNode *search = selected; + + while((search = GetNextSeachItem(search, (searchData->searchScope == 0))) != NULL) { + TreeNode *found = FindNextMaterial(search, searchData); + if(found) { + tree.SelectItem(found); + return true; + } + } + return false; +} + +/** +* Searches for a material given the supplied search parameters. Returns the tree item where +* the item was found or NULL if no material was found. +* @param item The tree item from where to start the search. +* @param searchData The parameters to use for the search. +*/ +TreeNode *MaterialTreeView::FindNextMaterial(TreeNode* item, MaterialSearchData_t* searchData) { + + int type = tree.GetItemData(item); + + if(type == TYPE_MATERIAL) { + //check the tree name first + idStr itemName = tree.GetItemText(item); + int findPos = itemName.Find(searchData->searchText, false); + if(findPos != -1) { + //Todo: Include match whole word + return item; + } + + if(!searchData->nameOnly) { + //Check the material + idStr materialName = GetMediaPath(item, TYPE_MATERIAL); + if(materialDocManager->FindMaterial(materialName, searchData, false)) { + return item; + } + } + } else { + //Just check the tree name + idStr itemName = tree.GetItemText(item); + + int findPos = itemName.Find(searchData->searchText, false); + if(findPos != -1) { + //Todo: Include match whole word + return item; + } + } + return NULL; +} + +/** +* Returns the next item to search or NULL if there is nothing else to search. +* @param item The last item searched. +* @param stayInFile True if the search should stay in the current file. +*/ +TreeNode *MaterialTreeView::GetNextSeachItem(TreeNode *item, bool stayInFile) { + TreeNode *nextItem = NULL; + + //Check our children + if(tree.GetChildItem(item)) { + nextItem = tree.GetChildItem(item); + return nextItem; + } + + //Check our siblings + nextItem = tree.GetNextSiblingItem(item); + if(nextItem) { + return nextItem; + } + + //Check our parents next sibiling + TreeNode *parent = item; + while((parent = tree.GetParentItem(parent)) != NULL) { + int parType = tree.GetItemData(parent); + if(stayInFile && parType == TYPE_FILE) + break; + + TreeNode *sib = tree.GetNextSiblingItem(parent); + if(sib) { + nextItem = sib; + break; + } + } + return nextItem; +} + +/** +* Deletes a given folder. +* @param item The folder to delete. +* @param addUndo True if this operation can be undone. +*/ +void MaterialTreeView::DeleteFolder(TreeNode *item, bool addUndo) { + + idList materialsToDelete; + + //Get the complete list of materials to delete + GetMaterialPaths(item, &materialsToDelete); + + idStrList affectedMaterials; + + //Now delete the materials + for(int i = 0; i < materialsToDelete.Num(); i++) { + + affectedMaterials.Append(materialsToDelete[i].materialName); + + const idMaterial* material = declManager->FindMaterial(materialsToDelete[i].materialName); + + MaterialDoc* pMaterial = NULL; + pMaterial = materialDocManager->CreateMaterialDoc(const_cast(material)); + materialDocManager->DeleteMaterial(pMaterial, false); + } + + //Make our undo modifier + if(addUndo) { + DeleteMaterialFolderModifier* mod = new DeleteMaterialFolderModifier(materialDocManager, tree.GetItemText(item), this, tree.GetParentItem(item), &affectedMaterials); + materialDocManager->AddMaterialUndoModifier(mod); + } + + + //Now clean up the folders and quicktree + CleanLookupTrees(item); + + //Remove any folders that were there + tree.DeleteItem(item); +} + +/** +* Adds a new material folder. +* @param name The name of the folder. +* @param parent The parent item of the folder. +*/ +TreeNode *MaterialTreeView::AddFolder(const char* name, TreeNode* parent) { + + TreeNode *newItem = tree.InsertItem(name, parent); + // TODO: implement + //tree.SetItemImage(newItem, IMAGE_MATERIAL_FOLDER, IMAGE_MATERIAL_FOLDER); + tree.SetItemData(newItem, TYPE_MATERIAL_FOLDER); + // TODO: implement + //tree.Expand(newItem, TVE_EXPAND); + + //Make sure the tree is still sorted + tree.SortChildren(parent); + + //Build the entire path to this item for the quicktree + idStr qt = GetQuicktreePath(newItem); + quickTree.Set(qt, newItem); + + return newItem; +} + +/** +* Renames a material folder. +* @param item The folder tree item. +* @param name The new name of the material folder. +*/ +void MaterialTreeView::RenameFolder(TreeNode *item, const char* name) { + + //Clean up the quicktree with the current tree before we allow the edit to commit + CleanLookupTrees(item); + + //Store some data so the we can make the appropriate changes after the commit + renamedFolder = item; + + affectedMaterials.Clear(); + GetMaterialPaths(renamedFolder, &affectedMaterials); + + tree.SetItemText(item, name); + + OnRenameFolderComplete(); +} + +/** +* Called by the MFC framework as the view is being created. +*/ +int MaterialTreeView::OnCreate() { + + //m_image.Create(IDB_ME_TREEBITMAP, 16, 1, RGB(255, 255, 255)); + //tree.SetImageList(&m_image, TVSIL_NORMAL); + + return 0; +} + +/** +* Changes the selected material when the select tree item changes. +*/ +void MaterialTreeView::OnTvnSelchanged(bool doubleClicked) { + TreeNode* item = tree.GetSelectedItem(); + + if(item) { + int type = tree.GetItemData(item); + if(type == TYPE_MATERIAL) { + idStr mediaName = GetMediaPath(item, type); + const idMaterial* material = declManager->FindMaterial(mediaName); + + materialDocManager->SetSelectedMaterial(const_cast(material)); + + } else { + + materialDocManager->SetSelectedMaterial(NULL); + } + + } else { + + materialDocManager->SetSelectedMaterial(NULL); + } +} + +/** +* Determines if a tree item's label can be edited. +*/ +bool MaterialTreeView::OnTvnBeginlabeledit(TreeNode *item) { + + int type = tree.GetItemData(item); + + //Only allow renaming of materials and material folders + return ( type == TYPE_MATERIAL || type == TYPE_MATERIAL_FOLDER ); +} + +/** +* Makes sure that a rename operation can be performed after a label edit is complete and +* performs the folder or material rename. +*/ +bool MaterialTreeView::OnTvnEndlabeledit(TreeNode *item, idStr &text) { + + bool result = false; + + if( text.Length() ) { + + //Convert any edited text to lower case to keep the name canonical + idStr newLabel = text; + newLabel.ToLower(); + text = newLabel; + + int type = tree.GetItemData( item ); + + if(type == TYPE_MATERIAL) { + + MaterialDoc* pMaterial = materialDocManager->GetCurrentMaterialDoc(); + + //Remove our old quick lookup value + materialToTree.Remove(pMaterial->name.c_str()); + + //Generate the new name + idStr material; + TreeNode *parent = tree.GetParentItem(item); + int parentType = tree.GetItemData(parent); + if(parentType == TYPE_MATERIAL_FOLDER) { + //Need to include the material folder + material = GetMediaPath(parent, TYPE_MATERIAL_FOLDER); + material += "/"; + } + + material += text; + + if(declManager->FindMaterial(material, false)) { + //Can't rename because it conflicts with an existing file + OpenMessageBox( MSG_BOX_UNABLE_TO_RENAME_MATERIAL ); + } else { + //Add it to our quick lookup + materialToTree.Set(material, item); + + // change the tree node label + item->SetLabel( newLabel.c_str() ); + + //Finally make the change + internalChange = true; + pMaterial->SetMaterialName(material); + internalChange = false; + + renamedFolder = item; + OnRenameMaterialComplete(); + + result = true; + } + + } else if (type == TYPE_MATERIAL_FOLDER) { + + //Clean up the quicktree with the current tree before we allow the edit to commit + CleanLookupTrees(item); + + //Store some data so the we can make the appropriate changes after the commit + renamedFolder = item; + + // change the tree node label + item->SetLabel( newLabel.c_str() ); + + affectedMaterials.Clear(); + GetMaterialPaths(renamedFolder, &affectedMaterials); + + OnRenameFolderComplete(); + + RenameMaterialFolderModifier* mod = new RenameMaterialFolderModifier(materialDocManager, text, this, item, tree.GetItemText(item)); + materialDocManager->AddMaterialUndoModifier(mod); + + result = true; + } + } + + return result; +} + +/** +* Displays the popup menu. +*/ +void MaterialTreeView::OnContextMenu(TreeNode *item) +{ + PopupMenu(item); +} + +/** +* Displays the popup menu. +*/ +bool MaterialTreeView::OnNMRclick(TreeNode *item) +{ + // Select the item + OnContextMenu(item); + return true; +} + +/** +* Handles keyboard shortcut for cut, copy and paste +*/ +void MaterialTreeView::OnInput(bool prepare, TreeNode *item) +{ + if ( prepare ) { + ImGui::SetItemKeyOwner( ImGuiKey_F2 ); + ImGui::SetItemKeyOwner( ImGuiKey_LeftCtrl ); + ImGui::SetItemKeyOwner( ImGuiKey_RightCtrl ); + ImGui::SetItemKeyOwner( ImGuiKey_LeftShift ); + ImGui::SetItemKeyOwner( ImGuiKey_RightShift ); + ImGui::SetItemKeyOwner( ImGuiKey_Z ); + ImGui::SetItemKeyOwner( ImGuiKey_C ); + ImGui::SetItemKeyOwner( ImGuiKey_X ); + ImGui::SetItemKeyOwner( ImGuiKey_V ); + ImGui::SetItemKeyOwner( ImGuiKey_Delete ); + return; + } + + if( ImGui::IsKeyChordPressed( ImGuiMod_Ctrl | ImGuiKey_C ) ) { + if (CanCopy()) { + OnCopy(); + } + } + + if( ImGui::IsKeyChordPressed( ImGuiMod_Ctrl | ImGuiKey_V ) ) { + if (CanPaste()) { + OnPaste(); + } + } + + if( ImGui::IsKeyChordPressed( ImGuiMod_Ctrl | ImGuiKey_X ) ) { + if (CanCut()) { + OnCut(); + } + } + + if ( ImGui::IsKeyPressed( ImGuiKey_F2 ) ) { + if (CanRename()) { + OnRenameMaterial(); + } + } + + if ( ImGui::IsKeyPressed( ImGuiKey_Delete ) ) { + OnDeleteMaterial(); + } +} + +/** +* Begins the process of a drag cut/copy. +*/ +void MaterialTreeView::OnTvnBegindrag(TreeNode *item) +{ + TreeNode *selecteditem = tree.GetSelectedItem(); + + if(item != selecteditem) { + tree.SelectItem(item); + } + + int itemType = tree.GetItemData(item); + + if(itemType == TYPE_MATERIAL) { + + //Create the drag image + dragImage = item->GetImageID(); + + //Drag is in progress + bDragging = true; + + dragItem = item; + + //Capture the messages + //SetCapture(); + } +} + +/** +* Handles the end of a drag copy/move when the user releases the left mouse button. +*/ +void MaterialTreeView::OnTvnEndDrag( TreeNode *source, TreeNode *item ) { + if (bDragging) { + //Release mouse capture + //ReleaseCapture(); + + //Delete the drag image + //dragImage->DragLeave(GetDesktopWindow()); + //dragImage->EndDrag(); + + bDragging = false; + + //delete dragImage; + + if(item) { + + int itemType = tree.GetItemData(item); + + if(itemType == TYPE_MATERIAL) //Backup one if a file is selected + item = tree.GetParentItem(item); + + //Make sure we aren't dragging to the same place + TreeNode *dragItemParent = tree.GetParentItem(dragItem); + if(dragItemParent != item) { + + idStr dragFile; + GetFileName(dragItem, dragFile); + + idStr filename; + GetFileName(item, filename); + + //Move within a file copy across files + if(!dragFile.Icmp(filename)) { + materialDocManager->CopyMaterial(materialDocManager->GetCurrentMaterialDoc(), true); + } else { + materialDocManager->CopyMaterial(materialDocManager->GetCurrentMaterialDoc(), false); + } + + //Generate the name + + idStr materialName = GetMediaPath(item, itemType); + + idStr copyName = materialDocManager->GetCopyMaterialName(); + idStr copyMaterialName; + copyName.ExtractFileName(copyMaterialName); + materialName += "/" + copyMaterialName; + + //If the material name already exists add numbers until we don't find it + materialName = materialDocManager->GetUniqueMaterialName(materialName); + + //Paste + materialDocManager->PasteMaterial(materialName, filename); + } + } + } +} + +/** +* Applies the current material. +*/ +void MaterialTreeView::OnApplyMaterial() { + materialDocManager->ApplyMaterial(materialDocManager->GetCurrentMaterialDoc()); +} + +/** +* Applies all materials in the currently selected file. +*/ +void MaterialTreeView::OnApplyFile() { + idStr filename; + TreeNode *item = tree.GetSelectedItem(); + if(GetFileName(item, filename)) { + materialDocManager->ApplyFile(filename.c_str()); + } +} + +/** +* Applies all materials that need to be applied. +*/ +void MaterialTreeView::OnApplyAll() { + materialDocManager->ApplyAll(); +} + +/** +* Saves the selected material. +*/ +void MaterialTreeView::OnSaveMaterial() { + materialDocManager->SaveMaterial(materialDocManager->GetCurrentMaterialDoc()); +} + +/** +* Saves all materials in the selected file. +*/ +void MaterialTreeView::OnSaveFile() { + idStr filename; + TreeNode *item = tree.GetSelectedItem(); + if(GetFileName(item, filename)) { + materialDocManager->SaveFile(filename.c_str()); + } +} + +/** +* Save all materials that have been changed. +*/ +void MaterialTreeView::OnSaveAll() { + materialDocManager->SaveAllMaterials(); +} + +/** +* Begins a label edit to rename a material or material folder. +*/ +void MaterialTreeView::OnRenameMaterial() { + + TreeNode *node = tree.GetSelectedItem(); + if ( node && OnTvnBeginlabeledit( node ) ) { + newName = node->GetLabel(); + OpenMessageBox( MSG_BOX_RENAME_MATERIAL ); + } +} + +/** +* Adds a new material. +*/ +void MaterialTreeView::OnAddMaterial() { + + TreeNode *item = tree.GetSelectedItem(); + int itemType = tree.GetItemData(item); + + //Determine the file + TreeNode *parent = NULL; + if(itemType != TYPE_FILE) { + + parent = tree.GetParentItem(item); + while(1) { + if(tree.GetItemData(parent) == TYPE_FILE) + break; + parent = tree.GetParentItem(parent); + } + } else { + parent = item; + } + idStr filename = GetMediaPath(parent, TYPE_FILE); + + + //Determine the material folder + idStr materialFolder = ""; + switch(itemType) { + case TYPE_MATERIAL: + { + TreeNode *parentFolderItem = tree.GetParentItem(item); + if(tree.GetItemData(parentFolderItem) == TYPE_MATERIAL_FOLDER) + materialFolder = GetMediaPath(parentFolderItem, TYPE_MATERIAL_FOLDER); + } + break; + case TYPE_MATERIAL_FOLDER: + materialFolder = GetMediaPath(item, TYPE_MATERIAL_FOLDER); + break; + case TYPE_FILE: + //There is no material folder + break; + } + + idStr name; + int num = 1; + while(1) { + if(materialFolder.Length() > 0) { + name = va("%s/newmaterial%d", materialFolder.c_str(), num); + } else { + name = va("newmaterial%d", num); + } + if(!declManager->FindMaterial(name, false)) + break; + num++; + } + + materialDocManager->AddMaterial(name.c_str(), filename.c_str()); + +} + +/** +* Adds a new folder +*/ +void MaterialTreeView::OnAddFolder() { + + TreeNode *item = tree.GetSelectedItem(); + int itemType = tree.GetItemData(item); + + + //Backup if the selected item is a material + if(itemType == TYPE_MATERIAL) { + item = tree.GetParentItem(item); + } + + //Pick a unique material name + idStr newFolder; + int num = 1; + while(1) { + newFolder = va("newfolder%d", num); + if(tree.GetChildItem(item)) { + TreeNode *hChildItem = tree.GetChildItem(item); + bool found = false; + while (hChildItem != NULL) + { + if(!newFolder.Icmp(tree.GetItemText(hChildItem))) { + found = true; + break; + } + hChildItem = tree.GetNextSiblingItem(hChildItem); + } + if(!found) + break; + } else { + break; + } + num++; + } + + TreeNode *newItem = AddFolder(newFolder, item); + + AddMaterialFolderModifier* mod = new AddMaterialFolderModifier(materialDocManager, newFolder, this, newItem, item); + materialDocManager->AddMaterialUndoModifier(mod); +} + +/** +* Deletes a material or material folder. +*/ +void MaterialTreeView::OnDeleteMaterial() { + + TreeNode *item = tree.GetSelectedItem(); + int itemType = tree.GetItemData(item); + + if(itemType == TYPE_MATERIAL_FOLDER) { + OpenMessageBox( MSG_BOX_DELETE_FOLDER ); + } else if (itemType == TYPE_MATERIAL) { + OpenMessageBox( MSG_BOX_DELETE_MATERIAL ); + } +} + +void MaterialTreeView::OnDeleteMaterialAccepted() { + TreeNode *item = tree.GetSelectedItem(); + int itemType = tree.GetItemData(item); + + if(itemType == TYPE_MATERIAL_FOLDER) { + DeleteFolder(item); + } else if (itemType == TYPE_MATERIAL) { + materialDocManager->DeleteMaterial(materialDocManager->GetCurrentMaterialDoc()); + } +} + +/** +* Reloads the selected file. +*/ +void MaterialTreeView::OnReloadFile() { + + TreeNode *item = tree.GetSelectedItem(); + int itemType = tree.GetItemData(item); + + if(itemType == TYPE_MATERIAL || itemType == TYPE_FILE || itemType == TYPE_MATERIAL_FOLDER) { + idStr filename; + GetFileName(item, filename); + + if(materialDocManager->IsFileModified(filename)) { + OpenMessageBox( MSG_BOX_RELOAD_MODIFIED_FILE ); + } else { + materialDocManager->ReloadFile(filename); + } + } +} + +void MaterialTreeView::OnReloadFileAccepted() { + TreeNode *item = tree.GetSelectedItem(); + int itemType = tree.GetItemData(item); + + if(itemType == TYPE_MATERIAL || itemType == TYPE_FILE || itemType == TYPE_MATERIAL_FOLDER) { + idStr filename; + GetFileName(item, filename); + + materialDocManager->ReloadFile(filename); + } +} + +/** +* Performs a cut operation. +*/ +void MaterialTreeView::OnCut() { + TreeNode *item = tree.GetSelectedItem(); + int itemType = tree.GetItemData(item); + + if(item && itemType == TYPE_MATERIAL) { + materialDocManager->CopyMaterial(materialDocManager->GetCurrentMaterialDoc(), true); + } else if (itemType == TYPE_MATERIAL_FOLDER) { + } +} + +/** +* Performs a copy operation. +*/ +void MaterialTreeView::OnCopy() { + + TreeNode *item = tree.GetSelectedItem(); + int itemType = tree.GetItemData(item); + + if(itemType == TYPE_MATERIAL) { + materialDocManager->CopyMaterial(materialDocManager->GetCurrentMaterialDoc(), false); + } else if (itemType == TYPE_MATERIAL_FOLDER) { + } +} + +/** +* Performs a paste operation. +*/ +void MaterialTreeView::OnPaste() { + + TreeNode *item = tree.GetSelectedItem(); + int itemType = tree.GetItemData(item); + + //Paste a material + if(item && materialDocManager->IsCopyMaterial() && itemType >= TYPE_FILE) { + + //Generate the name + if(itemType == TYPE_MATERIAL) {//Backup one if a file is selected + item = tree.GetParentItem(item); + itemType = tree.GetItemData(item); + } + + idStr materialName = ""; + if(itemType != TYPE_FILE) { + materialName = GetMediaPath(item, itemType) + "/"; + } + + idStr copyName = materialDocManager->GetCopyMaterialName(); + idStr copyMaterialName; + copyName.ExtractFileName(copyMaterialName); + materialName += copyMaterialName; + + idStr filename; + GetFileName(item, filename); + + //If the material name already exists add numbers until we don't find it + materialName = materialDocManager->GetUniqueMaterialName(materialName); + + //Paste + materialDocManager->PasteMaterial(materialName, filename); + + } +} + +/** +* This message is sent after the label edit is complete to actually perform the rename +* operation. +*/ +void MaterialTreeView::OnRenameFolderComplete() { + + //Generate new quick tree info for all material folders + BuildLookupTrees(renamedFolder); + + //Go through the list of affected materials and rename them + for(int i = 0; i < affectedMaterials.Num(); i++) { + RenameMaterial(affectedMaterials[i].treeItem, affectedMaterials[i].materialName); + } + + //Make sure the tree stays sorted + TreeNode *parent = tree.GetParentItem(renamedFolder); + tree.SortChildren(parent); +} + +/** +* This message is sent after the label edit is complete to ensure that the sorting stays consistent. +*/ +void MaterialTreeView::OnRenameMaterialComplete() { + + //Make sure the tree stays sorted + TreeNode *parent = tree.GetParentItem(renamedFolder); + tree.SortChildren(parent); +} + +/** +* Handles all of the little problems associated with renaming a folder. +*/ +void MaterialTreeView::RenameMaterial(TreeNode *item, const char* originalName) { + + const idMaterial* material = declManager->FindMaterial(originalName); + + MaterialDoc* pMaterial; + //pMaterial = materialDocManager->GetInProgressDoc(material); + + //if(!pMaterial) { + pMaterial = materialDocManager->CreateMaterialDoc(const_cast(material)); + //} + + //Remove our old quick lookup value + materialToTree.Remove(originalName); + + //Generate the new name + idStr materialName; + TreeNode *parent = tree.GetParentItem(item); + int parentType = tree.GetItemData(parent); + if(parentType == TYPE_MATERIAL_FOLDER) { + //Need to include the material folder + materialName = GetMediaPath(parent, TYPE_MATERIAL_FOLDER); + materialName += "/"; + } + materialName += tree.GetItemText(item); + + + //Add it to our quick lookup + materialToTree.Set(materialName, item); + + //Finally make the change + internalChange = true; + pMaterial->SetMaterialName(materialName, false); + internalChange = false; +} + +/** +* Returns the filename of the provided item. +* @param item The item for which to generate the filename +* @param out The location the filename will be placed. +*/ +bool MaterialTreeView::GetFileName(TreeNode *item, idStr& out) { + + out = ""; + + int type = tree.GetItemData(item); + + if(type != TYPE_MATERIAL && type != TYPE_MATERIAL_FOLDER && type != TYPE_FILE) + return false; + + if(type == TYPE_FILE) { + out = GetMediaPath(item, TYPE_FILE); + return true; + } + + TreeNode *parent = tree.GetParentItem( item ); + while ( parent != NULL ) { + int parentType = tree.GetItemData(parent); + if(parentType == TYPE_FILE) { + out = GetMediaPath(parent, TYPE_FILE); + return true; + } + parent = tree.GetParentItem( parent ); + } + + return false; +} + +/** +* Returns the Doom III name for the provided item +* @param item The item for which to generate the name +* @param type The type of the selected item +*/ +idStr MaterialTreeView::GetMediaPath(TreeNode *item, int type) { + + //Determine when to stop building the path + int stopType = TYPE_ROOT; + switch(type) { + case TYPE_MATERIAL: + stopType = TYPE_FILE; + break; + case TYPE_MATERIAL_FOLDER: + stopType = TYPE_FILE; + break; + case TYPE_FILE: + stopType = TYPE_ROOT; + break; + }; + + idStr mediaName = tree.GetItemText( item ); + + // have to build the name back up + TreeNode *parent = tree.GetParentItem( item ); + while ( parent != NULL ) { + + //stop the iteration once we have found a specific type + int parentType = tree.GetItemData(parent); + if(parentType == stopType) { + break; + } + + idStr strParent = tree.GetItemText( parent ); + strParent += "/"; + strParent += mediaName; + mediaName = strParent; + parent = tree.GetParentItem( parent ); + + } + + return mediaName; +} + +/** +* Creates a list of material paths for all materials under the provided item. +* @param item The base item for which to generate the list +* @param list The list in which the paths will be stored. +*/ +void MaterialTreeView::GetMaterialPaths(TreeNode *item, idList* list) { + + if(tree.GetChildItem(item)) { + + TreeNode *childItem = tree.GetChildItem(item); + while(childItem != NULL) { + + int childType = tree.GetItemData(childItem); + if (childType == TYPE_MATERIAL) { + MaterialTreeItem_t mat; + mat.materialName = GetMediaPath(childItem, TYPE_MATERIAL); + mat.treeItem = childItem; + list->Append(mat); + } else if (childType == TYPE_MATERIAL_FOLDER) { + GetMaterialPaths(childItem, list); + } + childItem = tree.GetNextSiblingItem(childItem); + } + } +} + +/** +* Adds a string list of materials to the tree creating the proper hierarchy. +* @param root The name of the root item or NULL for no root item. +* @param list The list of materials. +* @param includeFile If true the materials will be sorted by file. +*/ +void MaterialTreeView::AddStrList(const char *root, idStrList *list, bool includeFile) { + + idStr out, path; + TreeNode* base = NULL; + + if(root) { + base = tree.GetRootItem(); + if (base) { + out = tree.GetItemText(base); + if (stricmp(root, out)) { + base = NULL; + } + } + + if (base == NULL) { + base = tree.InsertItem(root); + tree.SetItemData(base, TYPE_ROOT); + } + } + + TreeNode* item = base; + TreeNode* add; + + list->Sort(); + int count = list->Num(); + + idStr last, qt; + for (int i = 0; i < count; i++) { + idStr *strItem = &(*list)[i]; + + + idStr name = strItem->c_str(); + + idStr filename; + bool afterFile = true; + if(includeFile) { + int index = name.Find("|"); + if(index >= 0) { + afterFile = false; + filename = name.Right(name.Length() - index - 1); + name = name.Left(index); + } + } + + // now break the name down convert to slashes + name.BackSlashesToSlashes(); + name.Strip(' '); + + int index; + int len = last.Length(); + if (len == 0) { + index = name.Last('/'); + if (index >= 0) { + name.Left(index, last); + } + } + else if (idStr::Icmpn(last, name, len) == 0 && name.Last('/') <= len) { + name.Right(name.Length() - len - 1, out); + add = tree.InsertItem(out, item); + qt = root; + qt += "/"; + qt += name; + quickTree.Set(qt, add); + // TODO: fix this + //tree.SetItemImage(add, IMAGE_MATERIAL, IMAGE_MATERIAL); + tree.SetItemData(add, TYPE_MATERIAL); + + //Add the item to a quick lookup table + idStr material = GetMediaPath(add, TYPE_MATERIAL); + materialToTree.Set(material, add); + + continue; + } + else { + last.Empty(); + } + + index = 0; + item = base; + path = ""; + while (index >= 0) { + index = name.Find('/'); + if (index >= 0) { + TreeNode* newItem = NULL; + TreeNode** checkptrptr = NULL; + TreeNode *check = NULL; + name.Left(index, out); + path += out; + qt = root; + qt += "/"; + qt += path; + if (quickTree.Get(qt, &checkptrptr)) { + check = *checkptrptr; + newItem = check; + } + + bool thisisfile = false; + if(out == filename) { + thisisfile = true; + afterFile = true; + + } + + if (newItem == NULL) { + newItem = tree.InsertItem(out, item); + qt = root; + qt += "/"; + qt += path; + quickTree.Set(qt, newItem); + + + if(!afterFile || thisisfile) { + if(thisisfile) { + afterFile = true; + // TODO: fix this + //tree.SetItemImage(newItem, IMAGE_FILE, IMAGE_FILE); + tree.SetItemData(newItem, TYPE_FILE); + + //Add the item to a quick lookup table + idStr file = GetMediaPath(newItem, TYPE_FILE); + //common->Printf("Adding fileToTree: %s - %d\n", file.c_str(), newItem); + fileToTree.Set(file, newItem); + + } else { + //treeMedia.SetItemImage(newItem, IMAGE_FOLDER, IMAGE_FOLDER); + tree.SetItemData(newItem, TYPE_FOLDER); + } + } else { + //treeMedia.SetItemImage(newItem, IMAGE_MATERIAL_FOLDER, IMAGE_MATERIAL_FOLDER); + tree.SetItemData(newItem, TYPE_MATERIAL_FOLDER); + + } + } + + + item = newItem; + name.Right(name.Length() - index - 1, out); + name = out; + path += "/"; + } + else { + add = tree.InsertItem(name, item); + qt = root; + qt += "/"; + qt += path; + qt += name; + quickTree.Set(qt, add); + //tree.SetItemImage(add, IMAGE_MATERIAL, IMAGE_MATERIAL); + tree.SetItemData(add, TYPE_MATERIAL); + path = ""; + + //Add the item to a quick lookup table + idStr material = GetMediaPath(add, TYPE_MATERIAL); + materialToTree.Set(material, add); + } + } + } +} + +/** +* Displays the popup menu with all of the appropriate menu items enabled. +* @param pt The location where the menu should be displayed. +*/ +void MaterialTreeView::PopupMenu(TreeNode *item) { + + //Determine the type of object clicked on + int itemType = tree.GetItemData(item); + + //Enable/Disable based on the state + MaterialDoc* pDoc = materialDocManager->GetCurrentMaterialDoc(); + + if (ImGui::BeginPopupContextItem( NULL ) ) { + tree.SelectItem( item ); + + //Apply Changes + ImGui::BeginDisabled( !(pDoc && pDoc->applyWaiting) ); + if ( ImGui::Button( "Apply Material" ) ) { + OnApplyMaterial(); + } + ImGui::EndDisabled(); + + //Apply File + idStr filename; + bool applyFileEnabled = false; + if(GetFileName(item, filename)) { + applyFileEnabled = materialDocManager->DoesFileNeedApply(filename.c_str()); + } + ImGui::BeginDisabled( !applyFileEnabled ); + if ( ImGui::Button( "Apply File" ) ) { + OnApplyFile(); + } + ImGui::EndDisabled(); + + //Apply All + ImGui::BeginDisabled( !materialDocManager->DoesAnyNeedApply() ); + if ( ImGui::Button( "Apply All" ) ) { + OnApplyAll(); + } + ImGui::EndDisabled(); + + ImGui::Separator(); + + //Save Material + ImGui::BeginDisabled( !(pDoc && pDoc->modified) ); + if ( ImGui::Button( "Save Material" ) ) { + OnSaveMaterial(); + } + ImGui::EndDisabled(); + + //Save File + bool saveFileEnabled = false; + if(GetFileName(item, filename)) { + saveFileEnabled = (materialDocManager->IsFileModified(filename.c_str())); + } + ImGui::BeginDisabled( !saveFileEnabled ); + if ( ImGui::Button( "Save File" ) ) { + OnSaveFile(); + } + ImGui::EndDisabled(); + + //Save All + ImGui::BeginDisabled( !materialDocManager->IsAnyModified() ); + if ( ImGui::Button( "Save All" ) ) { + OnSaveMaterial(); + } + ImGui::EndDisabled(); + + ImGui::Separator(); + + bool cutEnabled = (itemType == TYPE_MATERIAL); + ImGui::BeginDisabled( !cutEnabled ); + ImGui::SetNextItemShortcut( ImGuiMod_Ctrl | ImGuiKey_X ); + if ( ImGui::Button( "Cut" ) ) { + OnCut(); + } + ImGui::EndDisabled(); + bool copyEnabled = (itemType == TYPE_MATERIAL); + ImGui::BeginDisabled( !copyEnabled ); + ImGui::SetNextItemShortcut( ImGuiMod_Ctrl | ImGuiKey_C ); + if ( ImGui::Button( "Copy" ) ) { + OnCopy(); + } + ImGui::EndDisabled(); + bool pasteEnabled = ((itemType == TYPE_MATERIAL || itemType == TYPE_FILE || itemType == TYPE_MATERIAL_FOLDER) && materialDocManager->IsCopyMaterial()); + ImGui::BeginDisabled( !pasteEnabled ); + ImGui::SetNextItemShortcut( ImGuiMod_Ctrl | ImGuiKey_V ); + if ( ImGui::Button( "Paste" ) ) { + OnPaste(); + } + ImGui::EndDisabled(); + bool deleteMaterialEnabled = (itemType == TYPE_MATERIAL || itemType == TYPE_MATERIAL_FOLDER); + ImGui::BeginDisabled( !deleteMaterialEnabled ); + ImGui::SetNextItemShortcut( ImGuiKey_Delete ); + if ( ImGui::Button( "Delete" ) ) { + OnDeleteMaterial(); + } + ImGui::EndDisabled(); + + ImGui::Separator(); + + bool addMaterialEnabled = (itemType == TYPE_FILE || itemType == TYPE_MATERIAL_FOLDER || itemType == TYPE_MATERIAL); + ImGui::BeginDisabled( !addMaterialEnabled ); + if ( ImGui::Button( "Add Material" ) ) { + OnAddMaterial(); + } + ImGui::EndDisabled(); + bool addFolderEnabled = (itemType == TYPE_FILE || itemType == TYPE_MATERIAL_FOLDER || itemType == TYPE_MATERIAL); + ImGui::BeginDisabled( !addFolderEnabled ); + if ( ImGui::Button( "Add Folder" ) ) { + OnAddFolder(); + } + ImGui::EndDisabled(); + bool renameMaterialEnabled = (itemType == TYPE_MATERIAL || itemType == TYPE_MATERIAL_FOLDER); + ImGui::BeginDisabled( !renameMaterialEnabled ); + if ( ImGui::Button( "Rename" ) ) { + OnRenameMaterial(); + } + ImGui::EndDisabled(); + + ImGui::Separator(); + + bool reloadFileEnabled = (itemType == TYPE_MATERIAL || itemType == TYPE_FILE || itemType == TYPE_MATERIAL_FOLDER); + ImGui::BeginDisabled( !reloadFileEnabled ); + if ( ImGui::Button( "Reload" ) ) { + OnReloadFile(); + } + ImGui::EndDisabled(); + + ImGui::Separator(); + + ImGui::EndPopup(); + } +} + +/** +* Sets the appropriate item image based on the state of the item. +* @param item The item to set. +* @param mod Is the item modified +* @param apply Does the item need an apply +* @param children Should this method recurse through the items children and set their icons. +*/ +void MaterialTreeView::SetItemImage(TreeNode *item, bool mod, bool apply, bool children) { + + int image = 0; + + int itemType = tree.GetItemData(item); + switch(itemType) { + case TYPE_FILE: + if(mod) + image = IMAGE_FILE_MOD; + else + image = IMAGE_FILE; + break; + case TYPE_MATERIAL_FOLDER: + image = IMAGE_MATERIAL_FOLDER; + break; + case TYPE_MATERIAL: + if(mod && apply) + image = IMAGE_MATERIAL_MOD_APPLY; + else if(mod) + image = IMAGE_MATERIAL_MOD; + else + image = IMAGE_MATERIAL; + break; + } + + //tree.SetItemImage(item, image, image); + + if(children) { + if(tree.GetChildItem(item)) { + TreeNode *hChildItem = tree.GetChildItem(item); + while (hChildItem != NULL) { + SetItemImage(hChildItem, mod, apply, children); + hChildItem = tree.GetNextSiblingItem(hChildItem); + } + } + } +} + +/** +* Cleans the lookup tables for the provided item and all children. +* @param item The item to start from +*/ +void MaterialTreeView::CleanLookupTrees(TreeNode *item) { + + idStr qt = GetQuicktreePath(item); + quickTree.Remove(qt); + + //Clean special lookup tables + int type = tree.GetItemData(item); + if(type == TYPE_FILE) { + idStr file = GetMediaPath(item, TYPE_FILE); + fileToTree.Remove(file); + } else if(type == TYPE_MATERIAL) { + idStr name = GetMediaPath(item, TYPE_MATERIAL); + materialToTree.Remove(name); + } + + //Clean all my children + if(tree.GetChildItem(item)) { + TreeNode *childItem = tree.GetChildItem(item); + while(childItem != NULL) { + CleanLookupTrees(childItem); + childItem = tree.GetNextSiblingItem(childItem); + } + } +} + +/** +* Build the lookup tree for a given item and all of its children. +* @param item The item to start from +*/ +void MaterialTreeView::BuildLookupTrees(TreeNode *item) { + + //Add my quicktree item + idStr qt = GetQuicktreePath(item); + quickTree.Set(qt, item); + + if(tree.GetChildItem(item)) { + TreeNode *childItem = tree.GetChildItem(item); + while(childItem != NULL) { + int childType = tree.GetItemData(childItem); + if(childType == TYPE_MATERIAL_FOLDER) { + //Recursively call this method for all my child folders + BuildLookupTrees(childItem); + } + childItem = tree.GetNextSiblingItem(childItem); + } + } +} + +/** +* Returns the quicktree path for a given item. +* @param item The item for which to generate the quicktree path +*/ +idStr MaterialTreeView::GetQuicktreePath(TreeNode *item) { + idStr qt = ""; + TreeNode *pathItem = item; + while(pathItem != NULL) { + qt = "/" + idStr(tree.GetItemText(pathItem)) + qt; + pathItem = tree.GetParentItem(pathItem); + } + return qt; +} + +void MaterialTreeView::OpenMessageBox( int message ) { + messageBox = message; +} + +} diff --git a/neo/tools/imgui/materialeditor/MaterialTreeView.h b/neo/tools/imgui/materialeditor/MaterialTreeView.h new file mode 100644 index 000000000..f047cda0d --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialTreeView.h @@ -0,0 +1,196 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef MATERIALTREEVIEW_H_ +#define MATERIALTREEVIEW_H_ + +#include "../util/ImGui_IdWidgets.h" + +#include "idlib/Str.h" +#include "idlib/containers/HashTable.h" +#include "../util/TreeCtrl.h" +#include "MaterialEditor.h" +#include "MaterialView.h" + +namespace ImGuiTools { + +/** +* Structure used associate a material name with a tree item. +*/ +typedef struct { + idStr materialName; + TreeNode* treeItem; +} MaterialTreeItem_t; + +/** +* A tree view of all the materials that have been defined. +*/ +class MaterialTreeView : /*public CTreeView,*/ public MaterialView { + +public: + MaterialTreeView(); + virtual ~MaterialTreeView(); + + void InitializeMaterialList(bool includeFile = true, const char* filename = NULL); + void BuildMaterialList(bool includeFile = true, const char* filename = NULL); + + bool Draw( const ImVec2 & size ); + + //Material Interface + virtual void MV_OnMaterialChange(MaterialDoc* pMaterial); + virtual void MV_OnMaterialApply(MaterialDoc* pMaterial); + virtual void MV_OnMaterialSaved(MaterialDoc* pMaterial); + virtual void MV_OnMaterialAdd(MaterialDoc* pMaterial); + virtual void MV_OnMaterialDelete(MaterialDoc* pMaterial); + virtual void MV_OnMaterialNameChanged(MaterialDoc* pMaterial, const char* oldName); + virtual void MV_OnFileReload(const char* filename); + + bool CanCopy(); + void OnCopy(); + bool CanPaste(); + void OnPaste(); + bool CanCut(); + void OnCut(); + bool CanDelete(); + void OnDeleteMaterial(); + bool CanRename(); + void OnRenameMaterial(); + bool CanSaveFile(); + idStr GetSaveFilename(); + + bool FindNextMaterial(MaterialSearchData_t* searchData); + TreeNode* FindNextMaterial(TreeNode *item, MaterialSearchData_t* searchData); + TreeNode* GetNextSeachItem(TreeNode *item, bool stayInFile); + + void DeleteFolder(TreeNode *item, bool addUndo = true); + TreeNode* AddFolder(const char* name, TreeNode *parent); + void RenameFolder(TreeNode *item, const char* name); + + int OnCreate(); + +private: + //DECLARE_DYNCREATE(MaterialTreeView) + + /** + * List of tree item types + */ + enum { + TYPE_ROOT = 0, + TYPE_FOLDER, + TYPE_FILE, + TYPE_MATERIAL_FOLDER, + TYPE_MATERIAL + }; + + //Window Messages +public: + void OnTvnSelchanged( bool doubleClicked ); + bool OnNMRclick( TreeNode *item ); + void OnTvnBegindrag( TreeNode *item ); + void OnTvnEndDrag( TreeNode *source, TreeNode *destination ); + void OnInput( bool prepare, TreeNode *item ); +private: + bool OnTvnBeginlabeledit( TreeNode *item ); + bool OnTvnEndlabeledit( TreeNode *item, idStr &text ); + void OnContextMenu( TreeNode *item ); + + //Menu Commands + void OnApplyMaterial(); + void OnApplyFile(); + void OnApplyAll(); + void OnSaveMaterial(); + void OnSaveFile(); + void OnSaveAll(); + void OnAddMaterial(); + void OnAddFolder(); + void OnDeleteMaterialAccepted(); + void OnReloadFile(); + void OnReloadFileAccepted(); + + //Internal Messages + void OnRenameFolderComplete(); + void OnRenameMaterialComplete(); + + //Utility methods + void RenameMaterial(TreeNode *item, const char* originalName); + bool GetFileName(TreeNode *item, idStr& out); + idStr GetMediaPath(TreeNode *item, int type); + void GetMaterialPaths(TreeNode *item, idList* list); + void AddStrList(const char *root, idStrList *list, bool includeFile); + void PopupMenu(TreeNode *item); + void SetItemImage(TreeNode *item, bool mod, bool apply, bool children); + + void OpenMessageBox(int message); + + //Methods for working with the quicktree + void CleanLookupTrees(TreeNode *item); + void BuildLookupTrees(TreeNode *item); + idStr GetQuicktreePath(TreeNode *item); + + +private: + //CImageList m_image; + bool treeWithFile; + + TreeCtrl tree; + + //Hashtables for quick lookups + idHashTable quickTree; + idHashTable materialToTree; + idHashTable fileToTree; + + + //Member variables for renaming folders + TreeNode* renamedFolder; + idList affectedMaterials; + idStr newName; + + int dragImage; + bool bDragging; + //ImVec2 dropPoint; + TreeNode* dragItem; + + //Hover Expand + TreeNode* hoverItem; + int hoverStartTime; + + bool internalChange; + + enum { + MSG_BOX_CLOSED = 0, + MSG_BOX_UNABLE_TO_RENAME_MATERIAL, + MSG_BOX_DELETE_FOLDER, + MSG_BOX_DELETE_MATERIAL, + MSG_BOX_RELOAD_MODIFIED_FILE, + MSG_BOX_RENAME_MATERIAL, + }; + int messageBox; +}; + +} + +#endif /* !MATERIALTREEVIEW_H_ */ diff --git a/neo/tools/imgui/materialeditor/MaterialView.cpp b/neo/tools/imgui/materialeditor/MaterialView.cpp new file mode 100644 index 000000000..f6f15c348 --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialView.cpp @@ -0,0 +1,30 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + + +#include "MaterialView.h" diff --git a/neo/tools/imgui/materialeditor/MaterialView.h b/neo/tools/imgui/materialeditor/MaterialView.h new file mode 100644 index 000000000..3f8572774 --- /dev/null +++ b/neo/tools/imgui/materialeditor/MaterialView.h @@ -0,0 +1,155 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef MATERIALVIEW_H_ +#define MATERIALVIEW_H_ + +#include "MaterialDocManager.h" + +namespace ImGuiTools { + +/** +* MaterialView Interface. Interface to be implemented by classes that want +* notifications of material changes. Classes that implement this interface +* must register themself with the MaterialDocManager class with the +* RegisterView method. +*/ +class MaterialView { +public: + /** + * Constructor. + */ + MaterialView(void) { materialDocManager = NULL; }; + /** + * Destructor. + */ + virtual ~MaterialView(void) {}; + + ////////////////////////////////////////////////////////////////////////// + //Public Interface to be implemented by subclasses + ////////////////////////////////////////////////////////////////////////// + + /** + * Sets the material document manager for this view instance. + * @param docManager The material document manager for this view instance. + */ + virtual void SetMaterialDocManager(MaterialDocManager* docManager) { materialDocManager = docManager; }; + + /** + * Called when the selected material has changed. + * @param pMaterial The newly selected material. + */ + virtual void MV_OnMaterialSelectionChange(MaterialDoc* pMaterial) {}; + + /** + * Called when the material has changed but not applied. + * @param pMaterial The selected material. + */ + virtual void MV_OnMaterialChange(MaterialDoc* pMaterial) {}; + + /** + * Called when the material changes have been applied. + * @param pMaterial The selected material. + */ + virtual void MV_OnMaterialApply(MaterialDoc* pMaterial) {}; + + /** + * Called when the material changes have been saved. + * @param pMaterial The saved material. + */ + virtual void MV_OnMaterialSaved(MaterialDoc* pMaterial) {}; + + /** + * Called when a material file has been saved + * @param filename path of the file that was saved. + */ + virtual void MV_OnMaterialSaveFile(const char* filename) {}; + + /** + * Called when a material is added + * @param pMaterial The material that was added. + */ + virtual void MV_OnMaterialAdd(MaterialDoc* pMaterial) {}; + + /** + * Called when a material is deleted + * @param pMaterial The material that was deleted. + */ + virtual void MV_OnMaterialDelete(MaterialDoc* pMaterial) {}; + + /** + * Called when a stage is added + * @param pMaterial The material that was affected. + * @param stageNum The index of the stage that was added + */ + virtual void MV_OnMaterialStageAdd(MaterialDoc* pMaterial, int stageNum) {}; + + /** + * Called when a stage is deleted + * @param pMaterial The material that was affected. + * @param stageNum The index of the stage that was deleted + */ + virtual void MV_OnMaterialStageDelete(MaterialDoc* pMaterial, int stageNum) {}; + + /** + * Called when a stage is moved + * @param pMaterial The material that was deleted. + * @param from The from index + * @param to The to index + */ + virtual void MV_OnMaterialStageMove(MaterialDoc* pMaterial, int from, int to) {}; + + /** + * Called when an attribute is changed + * @param pMaterial The material that was deleted. + * @param stage The stage that contains the change. + * @param attribName The attribute that has changed. + */ + virtual void MV_OnMaterialAttributeChanged(MaterialDoc* pMaterial, int stage, const char* attribName) {}; + + + /** + * Called when the material name has changed + * @param pMaterial The material that was deleted. + * @param oldName The old name of the material. + */ + virtual void MV_OnMaterialNameChanged(MaterialDoc* pMaterial, const char* oldName) {}; + + /** + * Called when a file has been reloaded + * @param filename The file that was reloaded. + */ + virtual void MV_OnFileReload(const char* filename) {}; + + +protected: + MaterialDocManager* materialDocManager; +}; + +} + +#endif /* !MATERIALVIEW_H_ */ diff --git a/neo/tools/imgui/materialeditor/StageView.cpp b/neo/tools/imgui/materialeditor/StageView.cpp new file mode 100644 index 000000000..4eed426e7 --- /dev/null +++ b/neo/tools/imgui/materialeditor/StageView.cpp @@ -0,0 +1,966 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "sys/sys_imgui.h" +#include "../util/ImGui_IdWidgets.h" +#include "../util/RegistryOptions.h" + +#include "StageView.h" + +namespace ImGuiTools { + +/** +* Constructor for StageView. +*/ +StageView::StageView() + : m_propView(NULL) + , currentMaterial(NULL) + , messageBox(MSG_BOX_CLOSED) + , labelEdit() + , labelIndex(-1) + , replaceStageName() + , items() + , itemCurSel(-1) + , lastKeyDownTime(0) + , bDragging(false) + , internalChange(false) +{ +} + +/** +* Destructor for StageView. +*/ +StageView::~StageView() { + items.Clear(); + itemCurSel = -1; +} + +/** +* Called when the selected material has changed. +* @param pMaterial The newly selected material. +*/ +void StageView::MV_OnMaterialSelectionChange( MaterialDoc *pMaterial ) { + + currentMaterial = pMaterial; + + RefreshStageList(); +} + +/** +* Called when the material changes have been saved. +* @param pMaterial The saved material. +*/ +void StageView::MV_OnMaterialSaved( MaterialDoc *pMaterial ) { + // Saving a material reenables all of the stages + if( pMaterial == currentMaterial ) { + for ( int i = 1; i < GetItemCount(); i++ ) { + SetToggleState( i, TOGGLE_STATE_ON ); + } + } +} + +/** +* Called when a stage is added +* @param pMaterial The material that was affected. +* @param stageNum The index of the stage that was added +*/ +void StageView::MV_OnMaterialStageAdd( MaterialDoc *pMaterial, int stageNum ) { + idStr name = pMaterial->GetAttribute( stageNum, "name" ); + + int index = InsertItem( stageNum + 1, name.c_str() ); + SetToggleState( index, TOGGLE_STATE_ON ); +} + +/** +* Called when a stage is deleted +* @param pMaterial The material that was affected. +* @param stageNum The index of the stage that was deleted +*/ +void StageView::MV_OnMaterialStageDelete(MaterialDoc* pMaterial, int stageNum) { + DeleteItem(stageNum+1); +} + +/** +* Called when a stage is moved +* @param pMaterial The material that was deleted. +* @param from The from index +* @param to The to index +*/ +void StageView::MV_OnMaterialStageMove(MaterialDoc* pMaterial, int from, int to) { + + if ( !internalChange ) { + from++; + to++; + + idStr szLabel; + GetItem( from, szLabel ); + + //Delete the original item + DeleteItem( from ); + + //Insert the item + InsertItem( to, szLabel.c_str() ); + + int type = -1; + + int stageType = currentMaterial->GetAttributeInt( to - 1, "stagetype" ); + switch ( stageType ) { + case MaterialDoc::STAGE_TYPE_NORMAL: + type = MaterialDefManager::MATERIAL_DEF_STAGE; + break; + case MaterialDoc::STAGE_TYPE_SPECIALMAP: + type = MaterialDefManager::MATERIAL_DEF_SPECIAL_STAGE; + break; + } + + m_propView->SetPropertyListType(type, to-1); + } +} + +/** +* Called when an attribute is changed +* @param pMaterial The material that was deleted. +* @param stage The stage that contains the change. +* @param attribName The attribute that has changed. +*/ +void StageView::MV_OnMaterialAttributeChanged(MaterialDoc* pMaterial, int stage, const char* attribName) { + + //Refresh this stage list if a material name has changed + if( !internalChange && currentMaterial == pMaterial && stage >= 0 && attribName && !strcmp( attribName, "name" ) ) { + SetItemText(stage+1, currentMaterial->GetAttribute(stage, attribName)); + } +} + +/** +* Returns true if the current state of the stage view will allow a copy operation +*/ +bool StageView::CanCopy() { + int nItem = GetSelectedItemIndex(); + + if(nItem > 0) { + return true; + } else { + return false; + } +} + +/** +* Returns true if the current state of the stage view will allow a paste operation +*/ +bool StageView::CanPaste() { + return materialDocManager->IsCopyStage(); +} + +/** +* Cut is not supported for stages. +*/ +bool StageView::CanCut() { + //No cut for stages + return false; +} + +/** +* Returns true if the current state of the stage view will allow a delete operation +*/ +bool StageView::CanDelete() { + int nItem = GetSelectedItemIndex(); + + if(nItem > 0) + return true; + + return false; +} + +/** +* Returns true if the current state of the stage view will allow a rename operation +*/ +bool StageView::CanRename() { + int nItem = GetSelectedItemIndex(); + + if ( nItem > 0 ) { + MaterialDoc *material = materialDocManager->GetCurrentMaterialDoc(); + if( nItem > 0 && material->GetAttributeInt(nItem-1, "stagetype") == MaterialDoc::STAGE_TYPE_NORMAL) { + return true; + } + } + + return false; +} + +/** +* Rebuilds the list of stages based on the currently selected material +*/ +void StageView::RefreshStageList() { + + int selectedItem = GetSelectedItemIndex(); + + DeleteAllItems(); + + if ( currentMaterial ) { + + // Always add the material item for the main material properties + InsertItem( 0, "Material" ); + SetToggleState( 0, TOGGLE_STATE_DISABLED ); + + // Get the stage info + int stageCount = currentMaterial->GetStageCount(); + for ( int i = 0; i < stageCount; i++ ) { + const char *name = currentMaterial->GetAttribute( i, "name" ); + + int itemNum = InsertItem( GetItemCount(), name ); + + if ( currentMaterial->IsStageEnabled( i ) ) { + SetToggleState( itemNum, TOGGLE_STATE_ON ); + } else { + SetToggleState( itemNum, TOGGLE_STATE_OFF ); + } + } + + if ( selectedItem < 0 ) { + //Select the material + SetItemState( 0 ); + } else { + SetItemState( selectedItem ); + } + } +} + +/** +* Called by the MFC framework when the view is being created. +*/ +int StageView::OnCreate() { + /*if (ToggleListView::OnCreate(lpCreateStruct) == -1) + return -1; + + SetToggleIcons(MAKEINTRESOURCE(IDI_ME_DISABLED_ICON), MAKEINTRESOURCE(IDI_ME_ON_ICON), MAKEINTRESOURCE(IDI_ME_OFF_ICON)); + */ + return 0; +} + +bool StageView::Draw( const ImVec2 &size ) { + if ( ImGui::BeginChild( "###StageView", size, ImGuiChildFlags_Borders ) ) { + + if ( messageBox != MSG_BOX_CLOSED ) { + ImGui::OpenPopup( "###StageViewPopup" ); + } + + if ( ImGui::BeginPopupModal( "###StageViewPopup", NULL, ImGuiWindowFlags_AlwaysAutoResize ) ) { + bool accepted = false; + + switch ( messageBox ) { + case MSG_BOX_RENAME_STAGE: + ImGui::Text( "%s", "Please enter the new name of the stage" ); + if ( ImGui::InputTextStr( "Edit Label", &labelEdit ) ) { + } + break; + case MSG_BOX_DELETE_STAGE: + ImGui::Text( "%s", "Are you sure you want to delete this stage?" ); + break; + case MSG_BOX_DELETE_ALL_STAGES: + ImGui::Text( "%s", "Are you sure you want to delete all stages?" ); + break; + case MSG_BOX_REPLACE_STAGE: + ImGui::Text( "Do you want to replace '%s' stage?", replaceStageName.c_str() ); + break; + } + + if ( ImGui::Button( "Yes" ) ) { + ImGui::CloseCurrentPopup(); + accepted = true; + } + if ( ImGui::Button( "No" ) ) { + ImGui::CloseCurrentPopup(); + messageBox = MSG_BOX_CLOSED; + } + + + if ( accepted ) { + switch ( messageBox ) { + case MSG_BOX_RENAME_STAGE: + OnLvnEndlabeledit( labelIndex, labelEdit.c_str() ); + messageBox = MSG_BOX_CLOSED; + break; + case MSG_BOX_DELETE_STAGE: + OnDeleteStageAccepted(); + messageBox = MSG_BOX_CLOSED; + break; + case MSG_BOX_DELETE_ALL_STAGES: + OnDeleteAllStagesAccepted(); + messageBox = MSG_BOX_CLOSED; + break; + case MSG_BOX_REPLACE_STAGE: + OnPasteAccepted(); + messageBox = MSG_BOX_CLOSED; + break; + default: + messageBox = MSG_BOX_CLOSED; + break; + } + } + + ImGui::EndPopup(); + } + + + if ( ImGui::BeginListBox( "##StageViewItems", size ) ) { + int num = items.Num(); + for ( int i = 0; i < num; i++ ) { + ImGui::PushID( i ); + + bool selected = ( i == itemCurSel ); + bool enabled = items[i].GetData() == TOGGLE_STATE_ON; + bool disabled = items[i].GetData() == TOGGLE_STATE_DISABLED; + + ImGuiSelectableFlags flags = ImGuiSelectableFlags_AllowDoubleClick; + if ( disabled ) { + flags |= ImGuiSelectableFlags_Disabled; + } + + ImGui::SetNextItemAllowOverlap(); + + OnChar( true, i ); + + if ( selected ) { + ImGui::SetItemDefaultFocus(); + } + + bool interacted = ImGui::Selectable( items[i].GetLabel().c_str(), selected ); + if ( interacted ) { + SetItemState( i ); + MaterialEditorSetActiveWindow( ME_WINDOW_STAGE ); + } + OnChar( false, i ); + PopupMenu(); + if ( ImGui::BeginDragDropSource( ImGuiDragDropFlags_None ) ) + { + ImGui::SetDragDropPayload( "StageViewItemsCell", &i, sizeof( int ) ); + + // Display preview + ImGui::Text( "%s", items[i].GetLabel().c_str() ); + ImGui::EndDragDropSource(); + OnLvnBegindrag( i ); + MaterialEditorSetActiveWindow( ME_WINDOW_STAGE ); + } + if ( ImGui::BeginDragDropTarget() ) + { + if ( const ImGuiPayload* payload = ImGui::AcceptDragDropPayload( "StageViewItemsCell" ) ) + { + IM_ASSERT( payload->DataSize == sizeof( int ) ); + int dropSource = *( const int * )payload->Data; + OnLvnEnddrag( i ); + MaterialEditorSetActiveWindow( ME_WINDOW_STAGE ); + } + ImGui::EndDragDropTarget(); + } + ImGui::SameLine(); + ImGui::BeginDisabled( disabled ); + if ( ImGui::Checkbox("###cb", &enabled ) ) { + OnNMClick( i ); + MaterialEditorSetActiveWindow( ME_WINDOW_STAGE ); + } + ImGui::EndDisabled(); + + ImGui::PopID(); + } + ImGui::EndListBox(); + } + } + ImGui::EndChild(); + + return false; +} + +void StageView::DeleteAllItems() { + items.Clear(); + itemCurSel = -1; + OnLvnItemchanged(); + OnLvnDeleteallitems(); +} + +void StageView::SetItemState( int index ) { + if ( index >= items.Num() ) { + return; + } + itemCurSel = index; + OnLvnItemchanged(); +} + +int StageView::GetItemCount() const { + return items.Num(); +} + +void StageView::DeleteItem( int index ) { + if ( index >= 0 && index < items.Num() ) { + items.RemoveIndex( index ); + } +} + +int StageView::InsertItem( int index, const char* label ) { + if ( index < 0 || index > items.Num() + 1 ) { + return -1; + } + + StageViewItem item; + + item.SetLabel( label ); + + if ( index < items.Num() ) { + return items.Insert( item, index ); + } + + return items.Append( item ); +} + +void StageView::GetItem(int index, idStr &label) { + if ( index >= 0 && index < items.Num() ) { + label = items[index].GetLabel(); + } +} + +int StageView::GetSelectedItemIndex() { + return itemCurSel; +} + +void StageView::SetItemText( int index, const char* label ) { + if ( index >= 0 && index < items.Num() ) { + items[index].SetLabel( label ); + } +} + +void StageView::SetItemData( int index, int data ) { + if ( index >= 0 && index < items.Num() ) { + items[index].SetData( data ); + } +} + +int StageView::GetItemData( int index ) { + if ( index >= 0 && index < items.Num() ) { + return items[index].GetData(); + } + return 0; +} + +/** +* Sets the state of an item in the list. +* @param index Index of the item whose state should be changed. +* @param toggleState The state to set +* @param notify Determines if the notification method OnStateChanged should +* be called. OnStateChanged will also not be called if the state has not changed. +*/ +void StageView::SetToggleState( int index, int toggleState, bool notify ) { + assert(index >= 0 && index < GetItemCount()); + + int oldState = GetToggleState(index); + SetItemData( index, toggleState ); + + if ( notify && oldState != toggleState ) { + OnStateChanged( index, toggleState ); + } +} + +/** +* Gets the state of an item in the list +* @param index Index of the item of which to retreive the state. +*/ +int StageView::GetToggleState(int index) { + assert(index >= 0 && index < GetItemCount()); + + int data = GetItemData( index ); + return data; +} + +/** +* Called when the user changes the selection in the list box. This method will notify the +* property view of the change so that it can display the appropriate properties. +*/ +void StageView::OnLvnItemchanged() { + + if (!bDragging) { + + //The state has changed and changed to selected + if(itemCurSel >= 0) { + + int type = -1; + + if(itemCurSel >= 0) { + if(itemCurSel == 0) + type = MaterialDefManager::MATERIAL_DEF_MATERIAL; + else { + int stageType = currentMaterial->GetAttributeInt(itemCurSel-1, "stagetype"); + switch(stageType) { + case MaterialDoc::STAGE_TYPE_NORMAL: + type = MaterialDefManager::MATERIAL_DEF_STAGE; + break; + case MaterialDoc::STAGE_TYPE_SPECIALMAP: + type = MaterialDefManager::MATERIAL_DEF_SPECIAL_STAGE; + break; + } + } + } + + m_propView->SetPropertyListType(type, itemCurSel-1); + } + + if (itemCurSel == -1) { + //This item was deselected. + //If there is no item selected then clear the prop list + m_propView->SetPropertyListType(-1); + } + } +} + +/** +* Notifies the property view that all stages have been removed. +*/ +void StageView::OnLvnDeleteallitems() { + //The list has been cleared so clear the prop view + m_propView->SetPropertyListType(-1); +} + +/** +* Starts the stage drag operation. +*/ +void StageView::OnLvnBegindrag( int index ) { + MaterialEditorSetActiveWindow( ME_WINDOW_STAGE ); + + //Start a drag if the item isn't the material + if(index > 0) { + + dragIndex = index; + + //Drag is in progress + bDragging = true; + dropIndex = -1; + } +} + +/** +* Finishes a stage drag operation of the user was dragging a stage. +*/ +void StageView::OnLvnEnddrag( int index ) { + if( bDragging ) { + MaterialEditorSetActiveWindow( ME_WINDOW_STAGE ); + DropItemOnList( index ); + + bDragging = false; + } +} + +/** +* Displays the popup menu when the user performs a right mouse click. +*/ +void StageView::OnNMRclick() { + if(materialDocManager->GetCurrentMaterialDoc()) { + PopupMenu(); + } +} + +/** +* Toggles the state of an item when the user clicks in the window. +*/ +void StageView::OnNMClick( int index ) { + if ( index != -1 ) { + MaterialEditorSetActiveWindow( ME_WINDOW_STAGE ); + int toggleState = GetToggleState( index ); + if ( toggleState != TOGGLE_STATE_DISABLED ) { + + if (toggleState == TOGGLE_STATE_ON) { + SetToggleState( index, TOGGLE_STATE_OFF, true ); + } else { + SetToggleState( index, TOGGLE_STATE_ON, true ); + } + } + } +} + +/** +* Begins a label edit when the user selects the rename menu option. +*/ +void StageView::OnRenameStage() { + MaterialEditorSetActiveWindow( ME_WINDOW_STAGE ); + + int nItem = GetSelectedItemIndex(); + if ( nItem ) { + if ( OnLvnBeginlabeledit( nItem ) ) { + labelEdit = items[nItem].GetLabel(); + labelIndex = nItem; + messageBox = MSG_BOX_RENAME_STAGE; + } + } +} + +/** +* Deletes the selected stage when the user selects the delete menu option. +*/ +void StageView::OnDeleteStage() { + MaterialEditorSetActiveWindow( ME_WINDOW_STAGE ); + + int nItem = GetSelectedItemIndex(); + if(nItem > 0) { + messageBox = MSG_BOX_DELETE_STAGE; + } +} + +void StageView::OnDeleteStageAccepted() { + int nItem = GetSelectedItemIndex(); + if(nItem > 0) { + MaterialDoc* material = materialDocManager->GetCurrentMaterialDoc(); + material->RemoveStage(nItem-1); + itemCurSel = 0; + } +} + +/** +* Conforms the user wants to delete all stages and then performs the operation. +*/ +void StageView::OnDeleteAllStages() { + MaterialEditorSetActiveWindow( ME_WINDOW_STAGE ); + messageBox = MSG_BOX_DELETE_ALL_STAGES; +} + +void StageView::OnDeleteAllStagesAccepted() { + MaterialDoc* material = materialDocManager->GetCurrentMaterialDoc(); + material->ClearStages(); + itemCurSel = -1; +} + +/** +* Adds a new stage when the user selects the menu option. +*/ +void StageView::OnAddStage() { + MaterialEditorSetActiveWindow( ME_WINDOW_STAGE ); + + MaterialDoc *material = materialDocManager->GetCurrentMaterialDoc(); + + idStr name = va( "Stage %d", material->GetStageCount() + 1 ); + material->AddStage( MaterialDoc::STAGE_TYPE_NORMAL, name.c_str() ); +} + +/** +* Adds a new bumpmap stage when the user selects the menu option. +*/ +void StageView::OnAddBumpmapStage() { + MaterialDoc *material = materialDocManager->GetCurrentMaterialDoc(); + material->AddStage( MaterialDoc::STAGE_TYPE_SPECIALMAP, "bumpmap" ); +} + +/** +* Adds a new diffusemap stage when the user selects the menu option. +*/ +void StageView::OnAddDiffuseStage() { + MaterialEditorSetActiveWindow( ME_WINDOW_STAGE ); + MaterialDoc* material = materialDocManager->GetCurrentMaterialDoc(); + material->AddStage( MaterialDoc::STAGE_TYPE_SPECIALMAP, "diffusemap" ); +} + +/** +* Adds a new specularmap stage when the user selects the menu option. +*/ +void StageView::OnAddSpecualarStage() { + MaterialEditorSetActiveWindow( ME_WINDOW_STAGE ); + MaterialDoc* material = materialDocManager->GetCurrentMaterialDoc(); + material->AddStage( MaterialDoc::STAGE_TYPE_SPECIALMAP, "specularmap" ); +} + +/** +* Performs a copy operation when the user selects the menu option. +*/ +void StageView::OnCopy() { + + MaterialEditorSetActiveWindow( ME_WINDOW_STAGE ); + MaterialDoc* material = materialDocManager->GetCurrentMaterialDoc(); + + int nItem = GetSelectedItemIndex(); + + if ( nItem > 0 ) { + materialDocManager->CopyStage( material, nItem - 1 ); + } +} + +/** +* Performs a paste operation when the user selects the menu option. +*/ +void StageView::OnPaste() { + MaterialEditorSetActiveWindow( ME_WINDOW_STAGE ); + if ( materialDocManager->IsCopyStage() ) { + + MaterialDoc* material = materialDocManager->GetCurrentMaterialDoc(); + + int type; + idStr name; + + materialDocManager->GetCopyStageInfo(type, name); + + int existingIndex = material->FindStage(type, name); + + if ( type != MaterialDoc::STAGE_TYPE_SPECIALMAP || existingIndex == -1 ) { + materialDocManager->PasteStage( material ); + } else { + replaceStageName = name; + messageBox = MSG_BOX_REPLACE_STAGE; + } + } +} + +void StageView::OnPasteAccepted() { + if ( materialDocManager->IsCopyStage() ) { + + MaterialDoc* material = materialDocManager->GetCurrentMaterialDoc(); + + int type; + idStr name; + + materialDocManager->GetCopyStageInfo(type, name); + + int existingIndex = material->FindStage(type, name); + + if ( type != MaterialDoc::STAGE_TYPE_SPECIALMAP || existingIndex == -1 ) { + + } else { + material->RemoveStage( existingIndex ); + materialDocManager->PasteStage( material ); + } + } +} + +/** +* Determines is a label edit can be performed on the selected stage. +*/ +bool StageView::OnLvnBeginlabeledit( int index ) { + //if this is a special stage then don't allow edit + + MaterialDoc* material = materialDocManager->GetCurrentMaterialDoc(); + if ( index <= 0 || material->GetAttributeInt( index-1, "stagetype" ) != MaterialDoc::STAGE_TYPE_NORMAL ) + { + return true; + } + + return false; +} + +/** +* Performs the stage name change after the label edit is done. +*/ +void StageView::OnLvnEndlabeledit( int index, const char *newLabel ) { + if ( newLabel ) { + MaterialDoc* material = materialDocManager->GetCurrentMaterialDoc(); + internalChange = true; + material->SetAttribute( index - 1, "name", newLabel ); + internalChange = false; + } +} + +/** +* Handles keyboard shortcuts for copy and paste operations. +*/ +void StageView::OnChar( bool prepare, int index ) { + if ( prepare ) { + ImGui::SetItemKeyOwner( ImGuiKey_C ); + ImGui::SetItemKeyOwner( ImGuiKey_V ); + ImGui::SetItemKeyOwner( ImGuiKey_F2 ); + ImGui::SetItemKeyOwner( ImGuiKey_Delete ); + } + + int timeEnd = Sys_Milliseconds(); + int elapsed = timeEnd - lastKeyDownTime; + int keydownTime = 200; + + if ( ImGui::IsKeyChordPressed( ImGuiMod_Ctrl | ImGuiKey_C ) ) { + if ( elapsed > keydownTime ) { + OnCopy(); + lastKeyDownTime = timeEnd; + } + } + if ( ImGui::IsKeyChordPressed( ImGuiMod_Ctrl | ImGuiKey_V ) ) { + if ( elapsed > keydownTime ) { + OnPaste(); + lastKeyDownTime = timeEnd; + } + } + if ( ImGui::IsMouseDoubleClicked( ImGuiMouseButton_Left ) ) { + if ( OnLvnBeginlabeledit( index ) ) { + labelEdit = items[index].GetLabel(); + labelIndex = index; + } + } + if ( ImGui::IsKeyPressed( ImGuiKey_F2 ) ) { + if ( elapsed > keydownTime ) { + if ( OnLvnBeginlabeledit( itemCurSel ) ) { + labelEdit = items[itemCurSel].GetLabel(); + labelIndex = itemCurSel; + } + lastKeyDownTime = timeEnd; + } + } + if ( ImGui::IsKeyPressed( ImGuiKey_Delete ) ) { + if ( elapsed > keydownTime ) { + OnDeleteStage(); + lastKeyDownTime = timeEnd; + } + } + + if ( ImGui::IsMouseClicked( ImGuiMouseButton_Right ) ) { + OnNMRclick(); + } +} + +/** +* Called by the ToggleListView when the toggle state has changed. +*/ +void StageView::OnStateChanged(int index, int toggleState) { + MaterialDoc* material = materialDocManager->GetCurrentMaterialDoc(); + if ( material && index > 0 ) { + if ( toggleState == TOGGLE_STATE_ON ) { + material->EnableStage( index - 1, true ); + } else if ( toggleState == TOGGLE_STATE_OFF ) { + material->EnableStage( index - 1, false ); + } + } +} + +/** +* Displays the popup menu with the appropriate menu items enabled. +*/ +void StageView::PopupMenu() { + + //Determine the type of object clicked on + int nItem = GetSelectedItemIndex(); + + if (ImGui::BeginPopupContextItem( NULL ) ) { + + MaterialDoc* material = materialDocManager->GetCurrentMaterialDoc(); + int stageType = nItem > 0 ? material->GetAttributeInt(nItem-1, "stagetype") : -1; + + ImGui::BeginDisabled( nItem <= 0 || (nItem > 0 && stageType != MaterialDoc::STAGE_TYPE_NORMAL) ); + if ( ImGui::Button( "Rename" ) ) { + OnRenameStage(); + } + ImGui::EndDisabled(); + + if ( ImGui::Button( "Add" ) ) { + OnAddStage(); + } + ImGui::BeginDisabled( material->FindStage(MaterialDoc::STAGE_TYPE_SPECIALMAP, "bumpmap") >= 0 ); + if ( ImGui::Button( "Add Bumpmap Stage" ) ) { + OnAddBumpmapStage(); + } + ImGui::EndDisabled(); + ImGui::BeginDisabled( material->FindStage(MaterialDoc::STAGE_TYPE_SPECIALMAP, "diffusemap") >= 0 ); + if ( ImGui::Button( "Add Diffuse Stage" ) ) { + OnAddDiffuseStage(); + } + ImGui::EndDisabled(); + ImGui::BeginDisabled( material->FindStage(MaterialDoc::STAGE_TYPE_SPECIALMAP, "specularmap") >= 0 ); + if ( ImGui::Button( "Add Specular Stage" ) ) { + OnAddSpecualarStage(); + } + ImGui::EndDisabled(); + + ImGui::Separator(); + + ImGui::BeginDisabled( nItem <= 0 ); + if ( ImGui::Button( "Copy" ) ) { + OnCopy(); + } + ImGui::EndDisabled(); + ImGui::BeginDisabled( !materialDocManager->IsCopyStage() ); + if ( ImGui::Button( "Paste" ) ) { + OnPaste(); + } + ImGui::EndDisabled(); + ImGui::BeginDisabled( nItem <= 0 ); + if ( ImGui::Button( "Delete" ) ) { + OnDeleteStage(); + } + ImGui::EndDisabled(); + if ( ImGui::Button( "Delete All Stages" ) ) { + OnDeleteAllStages(); + } + + ImGui::EndPopup(); + } +} + +/** +* Performs the stage move when the user has dragged and dropped a stage. +*/ +void StageView::DropItemOnList( int dropIndex ) { + int toStage; + + //Get and adjust the drop index based on the direction of the move + if ( dropIndex < 0 ) { + dropIndex = GetItemCount() - 1; + } + + //Ignore the drop if the index is the same or they are droping on the material + if ( dropIndex == dragIndex || dropIndex == 0 ) { + return; + } + + //Move the stage data + MaterialDoc* material = materialDocManager->GetCurrentMaterialDoc(); + + internalChange = true; + toStage = dropIndex - 1; + material->MoveStage( dragIndex - 1, dropIndex - 1 ); + internalChange = false; + + if ( dragIndex < dropIndex ) { + dropIndex++; + } + + //Get the item + idStr szLabel; + + GetItem( dragIndex, szLabel ); + + //Insert the item + InsertItem( dropIndex, szLabel.c_str() ); + + //Adjust the drag index if the move was up in the list + if ( dragIndex > dropIndex ) { + dragIndex++; + } + + //Delete the original item + DeleteItem( dragIndex ); + + int type = -1; + int stageType = currentMaterial->GetAttributeInt(toStage, "stagetype"); + switch(stageType) { + case MaterialDoc::STAGE_TYPE_NORMAL: + type = MaterialDefManager::MATERIAL_DEF_STAGE; + break; + case MaterialDoc::STAGE_TYPE_SPECIALMAP: + type = MaterialDefManager::MATERIAL_DEF_SPECIAL_STAGE; + break; + } + m_propView->SetPropertyListType(type, toStage); +} + +} diff --git a/neo/tools/imgui/materialeditor/StageView.h b/neo/tools/imgui/materialeditor/StageView.h new file mode 100644 index 000000000..b2da8e33f --- /dev/null +++ b/neo/tools/imgui/materialeditor/StageView.h @@ -0,0 +1,208 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ +#ifndef STAGEVIEW_H_ +#define STAGEVIEW_H_ + +#include "MaterialEditor.h" +#include "MaterialView.h" +//#include "ToggleListView.h" +#include "MaterialView.h" +#include "MaterialPropTreeView.h" + +namespace ImGuiTools { + +class StageViewItem { +public: + StageViewItem() : label(), data(0) { + } + virtual ~StageViewItem() { + label.Clear(); + } + + ID_INLINE void SetLabel(const char *_label) { + label = _label; + } + ID_INLINE idStr &GetLabel() { + return label; + } + + ID_INLINE int GetData() const { + return data; + } + ID_INLINE void SetData(int _data) { + data = _data; + } + +private: + idStr label; + int data; +}; + +/** +* View that handles managing the material stages. +*/ +class StageView : public MaterialView +{ + +public: + StageView(); + virtual ~StageView(); + + /** + * Defines the type of stages + */ + enum { + STAGE_TYPE_MATERIAL, + STAGE_TYPE_STAGE, + STAGE_TYPE_SPECIAL_MAP_STAGE + }; + + /** + * Enumeration that defines the possible states of the toggle button. + */ + enum { + TOGGLE_STATE_DISABLED = 0, + TOGGLE_STATE_ON, + TOGGLE_STATE_OFF + }; + + //Associates a property view with this stage view + void SetMaterialPropertyView(MaterialPropTreeView* propView) { m_propView = propView; }; + + //MaterialView Interface + virtual void MV_OnMaterialSelectionChange(MaterialDoc* pMaterial); + virtual void MV_OnMaterialStageAdd(MaterialDoc* pMaterial, int stageNum); + virtual void MV_OnMaterialStageDelete(MaterialDoc* pMaterial, int stageNum); + virtual void MV_OnMaterialStageMove(MaterialDoc* pMaterial, int from, int to); + virtual void MV_OnMaterialAttributeChanged(MaterialDoc* pMaterial, int stage, const char* attribName); + virtual void MV_OnMaterialSaved(MaterialDoc* pMaterial); + + //Edit Operation Tests + bool CanCopy(); + void OnCopy(); + bool CanPaste(); + void OnPaste(); + bool CanCut(); + bool CanDelete(); + void OnDeleteStage(); + bool CanRename(); + void OnRenameStage(); + + //Refresh the stage list + void RefreshStageList(); + +public: + int OnCreate(); + bool Draw( const ImVec2 &size ); + +private: + void DeleteAllItems(); + void SetItemState(int index); + int GetItemCount() const; + void DeleteItem( int index ); + int InsertItem( int index, const char *label ); + void GetItem( int index, idStr &label ); + int GetSelectedItemIndex(); + void SetItemText( int index, const char *label ); + void SetItemData( int index, int data ); + int GetItemData( int index ); + + //void SetToggleIcons(LPCSTR disabled = NULL, LPCSTR on = NULL, LPCSTR off = NULL); + void SetToggleState(int index, int toggleState, bool notify = false); + int GetToggleState(int index); + +protected: + void OnLvnItemchanged(); + void OnLvnDeleteallitems(); + + void OnLvnBegindrag( int index ); + void OnLvnEnddrag( int index ); + void OnNMRclick(); + void OnNMClick( int index ); + + void OnRenameStageAccepted(); + void OnDeleteStageAccepted(); + void OnDeleteAllStages(); + void OnDeleteAllStagesAccepted(); + void OnAddStage(); + void OnAddBumpmapStage(); + void OnAddDiffuseStage(); + void OnAddSpecualarStage(); + + void OnPasteAccepted(); + + bool OnLvnBeginlabeledit( int index ); + void OnLvnEndlabeledit( int index, const char *newLabel ); + + void OnChar( bool prepare, int index ); + + //Toggle List View Interface + void OnStateChanged(int index, int toggleState); + + void PopupMenu(); + + void DropItemOnList( int dropIndex ); + +protected: + + MaterialPropTreeView* m_propView; + MaterialDoc* currentMaterial; + + enum { + MSG_BOX_CLOSED = 0, + MSG_BOX_RENAME_STAGE, + MSG_BOX_DELETE_STAGE, + MSG_BOX_DELETE_ALL_STAGES, + MSG_BOX_REPLACE_STAGE, + }; + int messageBox; + + idStr labelEdit; + int labelIndex; + idStr replaceStageName; + + idList items; + int itemCurSel; + + int lastKeyDownTime; + + //Manual handing of the row dragging + /* + CImageList* dragImage; + */ + bool bDragging; + + int dragIndex; + int dropIndex; + + bool internalChange; +}; + +} + +#endif /* !STAGEVIEW_H_ */ diff --git a/neo/tools/imgui/particleeditor/ParticleEditor.cpp b/neo/tools/imgui/particleeditor/ParticleEditor.cpp new file mode 100644 index 000000000..86ac389a0 --- /dev/null +++ b/neo/tools/imgui/particleeditor/ParticleEditor.cpp @@ -0,0 +1,1511 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../util/ImGui_IdWidgets.h" + +#include "ParticleEditor.h" + +#include "sys/sys_imgui.h" + +#include "idlib/math/Vector.h" +#include "idlib/math/Quat.h" + +#include "framework/FileSystem.h" +#include "framework/Game.h" + +// ParticleEditor dialog + +namespace ImGuiTools +{ + +static const char *customPathValues[] = { + "standard", + "helix", + "flies", + "spherical", + "drip", + NULL +}; + +bool RangeSlider::Draw( const char *label, float sliderWidth ) { + float v = GetValue(); + bool changed = false; + idStr realLabel = label; + realLabel += "##Slider"; + ImGui::SetNextItemWidth( sliderWidth ); + if ( ImGui::SliderFloat( realLabel.c_str(), &v, GetValueLow(), GetValueHigh() ) ) { + SetValuePos( v ); + changed = true; + } + + return changed; +} + +ParticleEditor& ParticleEditor::Instance() +{ + static ParticleEditor instance; + return instance; +} + +ParticleEditor::ParticleEditor() + : particleNewDlg( DECL_PARTICLE, "particles/", ".prt", "New Particle System" ) + , particleSelectDlg( DECL_PARTICLE, "Select Particle System" ) + , materialSelectDlg( DECL_MATERIAL, "Select Material" ) + , colorDlg( "Color" ) + , fadeColorDlg( "Fade Color" ) + , entityColorDlg( "Entity Color" ) + , particleDropDlg() +{ + isShown = false; +} + +void ParticleEditor::Draw() +{ + int i, num; + bool showTool; + bool clickedNew = false, clickedSelect = false; + + showTool = isShown; + + if ( ImGui::Begin( "Particle Editor", &showTool, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_MenuBar ) ) { + impl::SetReleaseToolMouse( true ); + + if( ImGui::BeginMenuBar() ) + { + if( ImGui::BeginMenu( "File" ) ) + { + if( ImGui::MenuItem( "New", "Ctrl+N" ) ) + { + clickedNew = true; + } + + if( ImGui::MenuItem( "Open..", "Ctrl+O" ) ) + { + clickedSelect = true; + } + + if( ImGui::MenuItem( "Save", "Ctrl+S" ) ) + { + OnBnClickedButtonSave(); + } + + if( ImGui::MenuItem( "Close", "Ctrl+W" ) ) + { + showTool = false; + } + + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + + if ( clickedNew ) { + particleNewDlg.Start(); + } + + if ( particleNewDlg.Draw() ) { + idDeclParticle *dp = static_cast( particleNewDlg.GetDecl() ); + + SetCurParticle( dp ); + } + + if ( clickedSelect ) { + particleSelectDlg.Start( NULL ); + } + + if ( particleSelectDlg.Draw() ) { + idDeclParticle *dp = static_cast( particleSelectDlg.GetDecl() ); + + SetCurParticle( dp ); + } + + ImGui::TextUnformatted( inFileText.IsEmpty() ? "(no file opened)" : inFileText.c_str() ); + ImGui::SameLine(); + + ImGui::SetNextItemWidth(70); + if ( ImGui::InputTextStr( "Depth hack", &depthHack ) ) { + CurStageToDlgVars(); + DlgVarsToCurStage(); + } + + ImGui::Text( "Stages" ); + if ( ImGui::BeginTable( "stagesTable", 2, ImGuiTableFlags_SizingFixedFit ) ) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + + bool addStagePressed = ImGui::Button( "+" ); + ImGui::SetItemTooltip( "Add a new generic stage" ); + if ( addStagePressed ) { + AddStage( false ); + } + ImGui::SameLine(); + bool cloneStagePressed = ImGui::Button( "C" ); + ImGui::SetItemTooltip( "Clone the selected stage" ); + if ( cloneStagePressed ) { + AddStage( true ); + } + ImGui::SameLine(); + bool removeStagePressed = ImGui::Button( "-" ); + ImGui::SetItemTooltip( "Remove the selected stage" ); + if ( removeStagePressed ) { + RemoveStage(); + } + RemoveStageThink(); + ImGui::SameLine(); + bool hideStagePressed = ImGui::Button( "H" ); + ImGui::SetItemTooltip( "Hide the selected stage" ); + if ( hideStagePressed ) { + HideStage(); + } + ImGui::SameLine(); + bool showStagePressed = ImGui::Button( "S" ); + ImGui::SetItemTooltip( "Show the selected stage" ); + if ( showStagePressed ) { + ShowStage(); + } + // some additional space after [S], before the right column + ImGui::SameLine( 0, 30 ); + ImGui::NewLine(); + + if ( ImGui::BeginListBox( "##ListStages", ImVec2( 100, 150 ) ) ) { + num = listStages.Num(); + for ( i = 0; i < num; i++ ) { + ImGui::PushID(i); + + bool selected = ( i == listStagesSel ); + + if ( ImGui::Selectable( listStages[i].c_str(), selected ) ) { + listStagesSel = i; + OnLbnSelchangeListStages(); + } + + if ( selected ) { + ImGui::SetItemDefaultFocus(); + } + + ImGui::PopID(); + } + ImGui::EndListBox(); + } + + ImGui::BeginDisabled( !stageControlsEnabled ); + + ImGui::TableSetColumnIndex(1); + ImGui::SetNextItemWidth(200); + if ( ImGui::InputTextStr( "Material", &matName ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + ImGui::SameLine(); + if ( ImGui::Button( "...###BrowseMaterial" ) ) { + materialSelectDlg.Start( matName.c_str() ); + } + if ( materialSelectDlg.Draw() ) { + idMaterial *material = static_cast( materialSelectDlg.GetDecl() ); + if ( material ) { + matName = material->GetName(); + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + } + ImGui::SetNextItemWidth(70); + if ( ImGui::InputInt( "Anim Frames", &animFrames, 0 ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(70); + if ( ImGui::InputFloat( "Anim Rate", &animRate ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + + ButtonColor(); + + ButtonFadeColor(); + + if ( ImGui::Checkbox( "Entity Color", &entityColor ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + + if ( sliderFadeIn.Draw( "Fade In %", 150 ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + if ( sliderFadeOut.Draw( "Fade Out %", 150 ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + if ( sliderFadeFraction.Draw( "Fade Frac", 150 ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + if ( sliderBunching.Draw( "Bunching", 150 ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + + ImGui::EndTable(); + } + + bool countPressed = sliderCount.Draw( "Count", 300 ); + ImGui::SetItemTooltip( "How many particles there are (at any moment)" ); + if ( countPressed ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + bool timePressed = sliderTime.Draw( "Time", 300 ); + ImGui::SetItemTooltip( "How long one cycle of the effect will last in seconds" ); + if ( timePressed ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + ImGui::SetNextItemWidth(50); + bool cyclesPressed = ImGui::InputFloat( "Cycles", &cycles ); + ImGui::SetItemTooltip( "Determines how many times the particle effect will repeat (0 = infinite, 1 = single time)" ); + if ( cyclesPressed ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(50); + if ( ImGui::InputFloat( "Time offset", &timeOffset ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(50); + if ( ImGui::InputFloat( "Dead time", &deadTime ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + + if ( ImGui::Checkbox( "World gravity", &worldGravity ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + ImGui::SameLine(); + if ( sliderGravity.Draw( "Gravity", 300 ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + + if ( ImGui::BeginTable( "distributionAndOrientationTable", 2, ImGuiTableFlags_SizingStretchSame ) ) { + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex( 0 ); + ImGui::SeparatorText( "Distribution" ); + if ( ImGui::BeginTable("distributionTable", 2, ImGuiTableFlags_SizingStretchSame ) ) { + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex( 0 ); + ImGui::SetNextItemWidth( 70 ); + if ( ImGui::RadioButton( "Rect", distribution == 0 ) ) { + distribution = 0; + DlgVarsToCurStage(); + CurStageToDlgVars(); + UpdateControlInfo(); + } + ImGui::SetNextItemWidth( 70 ); + if ( ImGui::RadioButton( "Cylinder", distribution == 1 ) ) { + distribution = 1; + DlgVarsToCurStage(); + CurStageToDlgVars(); + UpdateControlInfo(); + } + ImGui::SetNextItemWidth( 70 ); + if ( ImGui::RadioButton( "Sphere", distribution == 2 ) ) { + distribution = 2; + DlgVarsToCurStage(); + CurStageToDlgVars(); + UpdateControlInfo(); + } + ImGui::TextUnformatted( "Offset" ); + ImGui::SetNextItemWidth( 100 ); + if ( ImGui::InputFloat3( "###Offset", offset ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + + ImGui::TableSetColumnIndex( 1 ); + ImGui::SetNextItemWidth( 70 ); + if ( ImGui::InputFloat( "XSize", &xSize ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + ImGui::SetNextItemWidth( 70 ); + if ( ImGui::InputFloat( "YSize", &ySize ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + ImGui::SetNextItemWidth( 70 ); + if ( ImGui::InputFloat( "ZSize", &zSize ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + ImGui::SetNextItemWidth( 70 ); + ImGui::BeginDisabled( !editRingOffsetEnabled ); + if ( ImGui::InputFloat( "Ring", &ringOffset ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + ImGui::EndDisabled(); + + ImGui::EndTable(); + } + if ( ImGui::Checkbox( "Random Distribution", &randomDistribution ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + + ImGui::TableSetColumnIndex( 1 ); + ImGui::SeparatorText( "Direction / Orientation" ); + if ( ImGui::BeginTable( "orientationTable", 3, ImGuiTableFlags_SizingFixedFit ) ) { // ImGuiTableFlags_SizingStretchSame + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex( 0 ); + ImGui::SetNextItemWidth( 70 ); + if ( ImGui::RadioButton( "Cone", direction == 0 ) ) { + direction = 0; + DlgVarsToCurStage(); + CurStageToDlgVars(); + UpdateControlInfo(); + } + ImGui::SetNextItemWidth( 70 ); + if ( ImGui::RadioButton( "Outward", direction == 1 ) ) { + direction = 1; + DlgVarsToCurStage(); + CurStageToDlgVars(); + UpdateControlInfo(); + } + + ImGui::TableSetColumnIndex( 1 ); + ImGui::SetNextItemWidth( 70 ); + bool radioViewPressed = ImGui::RadioButton( "View", orientation == 0 ); + ImGui::SetItemTooltip( "Render particle aligned to the viewer" ); + if ( radioViewPressed ) { + orientation = 0; + DlgVarsToCurStage(); + CurStageToDlgVars(); + UpdateControlInfo(); + } + ImGui::SetNextItemWidth( 70 ); + bool radioAimedPressed = ImGui::RadioButton( "Aimed", orientation == 1 ); + ImGui::SetItemTooltip( "Stretch every particle along its movement direction, drawing a trail behind particle" ); + if ( radioAimedPressed ) { + orientation = 1; + DlgVarsToCurStage(); + CurStageToDlgVars(); + UpdateControlInfo(); + } + + ImGui::TableSetColumnIndex( 2 ); + // empty cell + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex( 0 ); + ImGui::TextUnformatted( "Static" ); + if ( ImGui::InputFloat( "###Static", &directionParm ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + + ImGui::TableSetColumnIndex( 1 ); + ImGui::SetNextItemWidth( 70 ); + if ( ImGui::RadioButton( "X", orientation == 2 ) ) { + orientation = 2; + DlgVarsToCurStage(); + CurStageToDlgVars(); + UpdateControlInfo(); + } + ImGui::SetNextItemWidth( 70 ); + if ( ImGui::RadioButton( "Y", orientation == 3 ) ) { + orientation = 3; + DlgVarsToCurStage(); + CurStageToDlgVars(); + UpdateControlInfo(); + } + ImGui::SetNextItemWidth( 70 ); + if ( ImGui::RadioButton( "Z", orientation == 4 ) ) { + orientation = 4; + DlgVarsToCurStage(); + CurStageToDlgVars(); + UpdateControlInfo(); + } + + ImGui::TableSetColumnIndex( 2 ); + ImGui::BeginDisabled( !editOrientationParm1Enabled ); + bool trailsPressed = ImGui::InputFloat( "Trails", &trails ); + ImGui::SetItemTooltip( "Set to 0 for straight trail. Set to > 0 for curved trails." ); + if ( trailsPressed ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + ImGui::EndDisabled(); + ImGui::BeginDisabled( !editOrientationParm2Enabled ); + bool trailTimePressed = ImGui::InputFloat( "Time", &trailTime ); + ImGui::SetItemTooltip( "The duration in seconds of the trail" ); + if ( trailTimePressed ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + ImGui::EndDisabled(); + ImGui::EndTable(); + } + + ImGui::SetNextItemWidth( 70 ); + if ( ImGui::InputFloat( "Initial Angle", &initialAngle ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + + ImGui::EndTable(); + } + + if ( ImGui::BeginTable( "speedRotationSizeAspectTable", 2, ImGuiTableFlags_SizingStretchSame ) ) { + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex( 0 ); + if ( sliderSpeedFrom.Draw( "Speed From", 150 ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + if ( sliderSpeedTo.Draw( "Speed To", 150 ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + + ImGui::TableSetColumnIndex( 1 ); + if ( sliderRotationFrom.Draw( "Rotation From", 150 ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + if ( sliderRotationTo.Draw( "Rotation To", 150 ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex( 0 ); + if ( sliderSizeFrom.Draw( "Size From", 150 ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + if ( sliderSizeTo.Draw( "Size To", 150 ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + + ImGui::TableSetColumnIndex(1); + if ( sliderAspectFrom.Draw( "Aspect From", 150 ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + if ( sliderAspectTo.Draw( "Aspect To", 150 ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex( 0 ); + ImGui::SetNextItemWidth( 75 ); + if ( ImGui::InputFloat( "Bounds Expansion", &boundsExpansion ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + + ImGui::TableSetColumnIndex( 1 ); + ImGui::Text( "Desc:" ); + ImGui::SameLine(); + ImGui::TextUnformatted( customDesc.c_str() ); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex( 0 ); + if ( ImGui::BeginCombo( "Custom Path", customPath.c_str() ) ) { + for ( i = 0; customPathValues[i]; i++ ) { + ImGui::PushID(0); + bool selected = ( customPath == customPathValues[i]); + if ( ImGui::Selectable( customPathValues[i], selected)) { + customPath = customPathValues[i]; + OnCbnSelchangeComboPath(); + } + if ( selected ) { + ImGui::SetItemDefaultFocus(); + } + ImGui::PopID(); + } + ImGui::EndCombo(); + } + + ImGui::TableSetColumnIndex( 1 ); + if ( ImGui::InputTextStr( "Parms", &customParms ) ) { + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + ImGui::SameLine(); + if ( ImGui::Button( "Apply" ) ) { + OnBnClickedButtonUpdate(); + } + + ImGui::EndTable(); + } + ImGui::EndDisabled(); + + if ( ImGui::BeginTable( "doomStuffAndEntityEditing", 2, ImGuiTableFlags_SizingStretchSame ) ) { + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex( 0 ); + ImGui::SeparatorText( "Doom Stuff" ); + + bool testModel = ImGui::Button( "T" ); + ImGui::SetItemTooltip( "Show the selected particle as a testmodel" ); + if ( testModel ) { + visualization = TESTMODEL; + SetParticleView(); + } + ImGui::SameLine(); + bool impact = ImGui::Button( "I" ); + ImGui::SetItemTooltip( "Show the selected particle on projectile impact" ); + if ( impact ) { + visualization = IMPACT; + SetParticleView(); + } + ImGui::SameLine(); + bool muzzle = ImGui::Button( "M" ); + ImGui::SetItemTooltip( "Show the selected particle as muzzle smoke" ); + if ( muzzle ) { + visualization = MUZZLE; + SetParticleView(); + } + ImGui::SameLine(); + bool flight = ImGui::Button( "F"); + ImGui::SetItemTooltip( "Show the selected particle as projectile flight smoke" ); + if ( flight ) { + visualization = FLIGHT; + SetParticleView(); + } + ImGui::SameLine(); + bool selectedEntity = ImGui::Button( "S" ); + ImGui::SetItemTooltip( "Show the selected particle on the selected entity" ); + if ( selectedEntity ) { + visualization = SELECTED; + SetParticleView(); + } + ImGui::Spacing(); + bool switchToDoom = ImGui::Button( "Switch to DOOM" ); + ImGui::SetItemTooltip( "Force focus to DOOM" ); + if ( switchToDoom ) { + OnBnClickedDoom(); + } + + ImGui::TableSetColumnIndex( 1 ); + ImGui::SeparatorText( "Entity Editing" ); + if ( ImGui::BeginTable( "entityEditingTable", 3, ImGuiTableFlags_SizingFixedSame ) ) { + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex( 0 ); + if ( ImGui::Checkbox( "Edit Mode", &particleMode ) ) { + OnBnClickedParticleMode(); + } + ImGui::BeginDisabled( !editControlsEnabled ); + if ( ImGui::Button( "Drop" ) ) { + idDeclParticle *idp = GetCurParticle(); + particleDropDlg.Start( idp ); + } + particleDropDlg.Draw(); + + ImGui::TableSetColumnIndex( 1 ); + if ( ImGui::BeginTable( "entityPosTableXY", 3, ImGuiTableFlags_SizingFixedSame ) ) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex( 0 ); + ImGui::TableSetColumnIndex( 1 ); + if ( ImGui::Button( "Y+" ) ) { + OnBtnYup(); + } + ImGui::TableSetColumnIndex( 2 ); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex( 0 ); + if ( ImGui::Button( "X-" ) ) { + OnBtnXdn(); + } + ImGui::TableSetColumnIndex( 1 ); + ImGui::TableSetColumnIndex( 2 ); + if ( ImGui::Button( "X+" ) ) { + OnBtnXup(); + } + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex( 0 ); + ImGui::TableSetColumnIndex( 1 ); + if ( ImGui::Button( "Y-" ) ) { + OnBtnYdn(); + } + ImGui::TableSetColumnIndex( 2 ); + + ImGui::EndTable(); + } + ImGui::TableSetColumnIndex( 2 ); + if ( ImGui::BeginTable( "entityPosTableZ", 1 ) ) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex( 0 ); + if ( ImGui::Button( "Z+" ) ) { + OnBtnZup(); + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex( 0 ); + if ( ImGui::Button( "Z-" ) ) { + OnBtnZdn(); + } + + ImGui::EndTable(); + } + + ImGui::EndDisabled(); + + ImGui::EndTable(); + } + + ImGui::Spacing(); + if ( ImGui::Button( "Vector" ) ) { // TODO: what is this for? + } + + ImGui::SameLine(); + + ButtonEntityColor(); + + ImGui::SameLine(0, 60); + + bool saveMap = ImGui::Button( "Save .MAP" ); + ImGui::SetItemTooltip( "Save the current map with any updated particle emitter entities" ); + if ( saveMap ) { + OnBnClickedButtonSaveParticles(); + } + + ImGui::EndTable(); + } + } + ImGui::End(); + + if ( isShown && !showTool ) + { + isShown = showTool; + impl::SetReleaseToolMouse( false ); + D3::ImGuiHooks::CloseWindow( D3::ImGuiHooks::D3_ImGuiWin_ParticleEditor ); + } +} + +// ParticleEditor message handlers + +void ParticleEditor::OnBnClickedParticleMode() { + cvarSystem->SetCVarInteger( "g_editEntityMode", ( particleMode ) ? 4 : 0 ); + EnableEditControls(); +} + +void ParticleEditor::OnBnClickedButtonSaveParticles() { + cmdSystem->BufferCommandText( CMD_EXEC_NOW, "saveParticles" ); + buttonSaveParticleEntitiesEnabled = false; +} + +void ParticleEditor::ButtonColor() { + colorDlg.Button( color ); + ImGui::SetItemTooltip( "Color that the material will be multiplied by" ); + ImGui::SameLine(); + ImGui::TextUnformatted( "Color" ); + + if ( colorDlg.Draw() ) { + idParticleStage *ps = GetCurStage(); + if ( ps ) { + color = colorDlg.GetColor(); + color.w = 1.0f; + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + } +} + +void ParticleEditor::ButtonEntityColor() { + idList list; + idDict dict2; + idStr str; + list.SetNum( 128 ); + int count = gameEdit->GetSelectedEntities( list.Ptr(), list.Num() ); + list.SetNum( count ); + idVec3 selectedEntityColor = vec3_origin; + + if ( count ) { + const idDict *dict = gameEdit->EntityGetSpawnArgs( list[0] ); + if ( dict ) { + selectedEntityColor = dict->GetVector( "_color", "1 1 1" ); + } + } + + entityColorDlg.Button( idVec4( selectedEntityColor.x, selectedEntityColor.y, selectedEntityColor.z, 1.0f ) ); + + if ( entityColorDlg.Draw() ) { + if ( count ) { + idVec3 clr = entityColorDlg.GetColor().ToVec3(); + + for ( int i = 0; i < count; i++ ) { + const idDict *dict = gameEdit->EntityGetSpawnArgs( list[i] ); + const char *name = dict->GetString( "name" ); + idEntity *ent = gameEdit->FindEntity( name ); + if ( ent ) { + gameEdit->EntitySetColor( ent, clr ); + str = va( "%f %f %f", clr.x, clr.y, clr.z ); + dict2.Clear(); + dict2.Set( "_color", str ); + gameEdit->EntityChangeSpawnArgs( ent, &dict2 ); + gameEdit->MapSetEntityKeyVal( name, "_color", str ); + } + } + buttonSaveParticleEntitiesEnabled = true; + } + } +} + +void ParticleEditor::ButtonFadeColor() { + fadeColorDlg.Button( fadeColor ); + ImGui::SetItemTooltip( "Material will be multiplied by the color of a fully faded out particle" ); + ImGui::SameLine(); + ImGui::TextUnformatted( "Fade Color" ); + + if ( fadeColorDlg.Draw() ) { + idParticleStage *ps = GetCurStage(); + if ( ps ) { + fadeColor = fadeColorDlg.GetColor(); + fadeColor.w = 1.0f; + DlgVarsToCurStage(); + CurStageToDlgVars(); + } + } +} + +void ParticleEditor::OnBnClickedButtonUpdate() { + DlgVarsToCurStage(); + CurStageToDlgVars(); +} + +void ParticleEditor::SelectParticle( const char *name ) { + idDeclParticle *decl = static_cast( const_cast( declManager->FindType( DECL_PARTICLE, name, false ) ) ); + + SetCurParticle( decl ); +} + +void ParticleEditor::SetCurParticle( idDeclParticle *dp ) { + curParticle = dp; + UpdateParticleData(); +} + +idDeclParticle *ParticleEditor::GetCurParticle() { + return curParticle; +} + +void ParticleEditor::UpdateParticleData() { + + listStages.Clear(); + listStagesItemData.Clear(); + listStagesSel = -1; + idDeclParticle *idp = GetCurParticle(); + if ( idp == NULL ) { + return; + } + listStages.Resize( idp->stages.Num() ); + for ( int i = 0; i < idp->stages.Num(); i++ ) { + int index = listStages.Append( idStr::Format( "stage %i", i ) ); + if ( index >= 0 ) { + listStagesItemData.Add( index, i ); + } + } + listStagesSel = 0; + OnLbnSelchangeListStages(); + inFileText = idStr::Format( "%s (%s)", idp->GetName(), idp->GetFileName() ); + + SetParticleView(); +} + +void ParticleEditor::OnCbnSelchangeComboPath() { + DlgVarsToCurStage(); + CurStageToDlgVars(); + UpdateControlInfo(); +} + +void ParticleEditor::UpdateControlInfo() { + editRingOffsetEnabled = ( distribution == 2 ); + dirParmText = (direction == 0 ) ? "Angle" : "Upward Bias"; + editOrientationParm1Enabled = ( orientation == 1 ); + editOrientationParm2Enabled = ( orientation == 1 ); + + idParticleStage *ps = GetCurStage(); + if ( ps == NULL ) { + return; + } + sliderBunching.SetValuePos( ps->spawnBunching ); + sliderFadeIn.SetValuePos( ps->fadeInFraction ); + sliderFadeOut.SetValuePos( ps->fadeOutFraction ); + sliderFadeFraction.SetValuePos( ps->fadeIndexFraction ); + sliderCount.SetValuePos( ps->totalParticles ); + sliderTime.SetValuePos( ps->particleLife ); + sliderGravity.SetValuePos( ps->gravity ); + sliderSpeedFrom.SetValuePos( ps->speed.from ); + sliderSpeedTo.SetValuePos( ps->speed.to ); + sliderRotationFrom.SetValuePos( ps->rotationSpeed.from ); + sliderRotationTo.SetValuePos( ps->rotationSpeed.to ); + sliderSizeFrom.SetValuePos( ps->size.from ); + sliderSizeTo.SetValuePos( ps->size.to ); + sliderAspectFrom.SetValuePos( ps->aspect.from ); + sliderAspectTo.SetValuePos( ps->aspect.to ); +} + +void ParticleEditor::OnBnClickedDoom() { + //::SetFocus(win32.hWnd); +} + +void ParticleEditor::SetParticleVisualization( int i ) { + visualization = i; + SetParticleView(); +} + +void ParticleEditor::SetParticleView() { + idDeclParticle *idp = GetCurParticle(); + if ( idp == NULL ) { + return; + } + cmdSystem->BufferCommandText( CMD_EXEC_NOW, "testmodel" ); + idStr str; + switch ( visualization ) { + case TESTMODEL : + str = idp->GetName(); + str.SetFileExtension( ".prt" ); + cmdSystem->BufferCommandText( CMD_EXEC_NOW, va("testmodel %s\n", str.c_str() ) ); + break; + case IMPACT : + str = idp->GetName(); + str.SetFileExtension( ".prt" ); + cvarSystem->SetCVarInteger( "g_testParticle", TEST_PARTICLE_IMPACT ); + cvarSystem->SetCVarString( "g_testParticleName", str ); + break; + case MUZZLE : + str = idp->GetName(); + str.SetFileExtension( ".prt" ); + cvarSystem->SetCVarInteger( "g_testParticle", TEST_PARTICLE_MUZZLE ); + cvarSystem->SetCVarString( "g_testParticleName", str ); + break; + case FLIGHT : + str = idp->GetName(); + str.SetFileExtension( ".prt" ); + cvarSystem->SetCVarInteger( "g_testParticle", TEST_PARTICLE_FLIGHT ); + cvarSystem->SetCVarString( "g_testParticleName", str ); + break; + case SELECTED : + str = idp->GetName(); + str.SetFileExtension( ".prt" ); + cvarSystem->SetCVarInteger( "g_testParticle", TEST_PARTICLE_FLIGHT ); + SetSelectedModel( str ); + break; + } +} + +void ParticleEditor::SetSelectedModel( const char *val ) { + idList list; + idMat3 axis; + + list.SetNum( 128 ); + int count = gameEdit->GetSelectedEntities( list.Ptr(), list.Num() ); + list.SetNum( count ); + + if ( count ) { + for ( int i = 0; i < count; i++ ) { + const idDict *dict = gameEdit->EntityGetSpawnArgs( list[i] ); + if ( dict == NULL ) { + continue; + } + const char *name = dict->GetString( "name" ); + gameEdit->EntitySetModel( list[i], val ); + gameEdit->EntityUpdateVisuals( list[i] ); + gameEdit->EntityGetAxis( list[i], axis ); + //vectorControl.SetidAxis( axis ); + gameEdit->MapSetEntityKeyVal( name, "model", val ); + } + buttonSaveParticleEntitiesEnabled = true; + } +} + +void ParticleEditor::AddStage( bool clone ) { + + idDeclParticle *idp = GetCurParticle(); + if ( idp == NULL ) { + return; + } + + idParticleStage *stage = new idParticleStage; + + if ( clone ) { + idParticleStage *source = GetCurStage(); + if ( source == NULL ) { + delete stage; + return; + } + *stage = *source; + } else { + stage->Default(); + } + int newIndex = idp->stages.Append( stage ); + int index = listStages.Append( idStr::Format( "stage %i", newIndex ) ); + listStagesSel = index; + listStagesItemData.Add( index, newIndex ); + ShowCurrentStage(); + EnableStageControls(); +} + +void ParticleEditor::RemoveStage() { + idDeclParticle *idp = GetCurParticle(); + if ( idp == NULL ) { + return; + } + + ImGui::OpenPopup( "Remove stage?" ); +} + +void ParticleEditor::RemoveStageThink() +{ + bool accepted = false; + bool canceled = false; + + if ( ImGui::BeginPopupModal( "Remove stage?", nullptr, ImGuiWindowFlags_AlwaysAutoResize ) ) { + ImGui::TextColored( ImVec4( 1, 0, 0, 1 ), "Are you sure you want to remove this stage?" ); + + if ( ImGui::Button( "OK" ) ) { + accepted = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if ( ImGui::Button( "Cancel" ) ) { + canceled = true; + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + + if ( canceled ) { + return; + } + if ( !accepted ) { + return; + } + + idDeclParticle *idp = GetCurParticle(); + if ( idp == NULL ) { + return; + } + + int index = listStagesSel; + if ( index >= 0 ) { + int newIndex = listStagesItemData.First( index ); + if ( newIndex >= 0 && newIndex < idp->stages.Num() ) { + listStages.RemoveIndex( index ); + listStagesItemData.Remove( index, newIndex ); + listStagesSel = index; + idp->stages.RemoveIndex( newIndex ); + UpdateParticleData(); + ShowCurrentStage(); + } + } + EnableStageControls(); +} + +void ParticleEditor::ShowStage() { + idParticleStage *ps = GetCurStage(); + if ( ps == NULL ) { + return; + } + ps->hidden = false; + int index = listStagesSel; + int newIndex = listStagesItemData.First( index ); + listStages.RemoveIndex( index ); + listStagesItemData.Remove( index, newIndex ); + listStages.Insert( idStr::Format("stage %i", index ), index ); + listStagesItemData.Add( index, newIndex ); + listStagesSel = index; + EnableStageControls(); +} + +void ParticleEditor::HideStage() { + idParticleStage *ps = GetCurStage(); + if ( ps == NULL ) { + return; + } + ps->hidden = true; + int index = listStagesSel; + int newIndex = listStagesItemData.First( index ); + listStages.RemoveIndex( index ); + listStagesItemData.Remove( index, newIndex ); + listStages.Insert( idStr::Format("stage %i (H) ", index ), index ); + listStagesItemData.Add( index, newIndex ); + listStagesSel = index; + EnableStageControls(); +} + +idParticleStage *ParticleEditor::GetCurStage() { + idDeclParticle *idp = GetCurParticle(); + int sel = listStagesSel; + int index = listStagesItemData.First( sel ); + if ( idp == NULL || index < 0 || index >= idp->stages.Num() ) { + return NULL; + } + return idp->stages[index]; +} + +void ParticleEditor::ClearDlgVars() { + matName.Clear(); + color = idVec4(0, 0, 0, 1); + fadeColor = idVec4(0, 0, 0, 1); + offset[0] = 0; + offset[1] = 0; + offset[2] = 0; + direction = 1; + orientation = 1; + distribution = 1; + customPath.Clear(); + customParms.Clear(); + worldGravity = false; + entityColor = false; + randomDistribution = true; + customDesc.Clear(); +} + +void ParticleEditor::CurStageToDlgVars() { + + // go ahead and get the two system vars too + idDeclParticle *idp = GetCurParticle(); + if ( idp == NULL ) { + return; + } + + depthHack = va( "%.3f", idp->depthHack ); + + idParticleStage *ps = GetCurStage(); + if ( ps == NULL ) { + return; + } + matName = ps->material->GetName(); + animFrames = ps->animationFrames; + animRate = ps->animationRate; + color.x = ps->color.x; + color.y = ps->color.y; + color.z = ps->color.z; + color.w = ps->color.w; + fadeColor.x = ps->fadeColor.x; + fadeColor.y = ps->fadeColor.y; + fadeColor.z = ps->fadeColor.z; + fadeColor.w = ps->fadeColor.w; + sliderFadeIn.SetValuePos( ps->fadeInFraction ); + sliderFadeOut.SetValuePos( ps->fadeOutFraction ); + sliderFadeFraction.SetValuePos( ps->fadeIndexFraction ); + sliderCount.SetValuePos( ps->totalParticles ); + sliderTime.SetValuePos( ps->particleLife ); + timeOffset = ps->timeOffset; + deadTime = ps->deadTime; + sliderGravity.SetValuePos( ps->gravity ); + sliderBunching.SetValuePos( ps->spawnBunching ); + offset[0] = ps->offset.x; + offset[1] = ps->offset.y; + offset[2] = ps->offset.z; + xSize = ps->distributionParms[0]; + ySize = ps->distributionParms[1]; + zSize = ps->distributionParms[2]; + ringOffset = ps->distributionParms[3]; + directionParm = ps->directionParms[0]; + direction = ps->directionType; + orientation = ps->orientation; + distribution = ps->distributionType; + sliderSpeedFrom.SetValuePos( ps->speed.from ); + sliderSpeedTo.SetValuePos( ps->speed.to ); + sliderRotationFrom.SetValuePos( ps->rotationSpeed.from ); + sliderRotationTo.SetValuePos( ps->rotationSpeed.to ); + sliderSizeFrom.SetValuePos( ps->size.from ); + sliderSizeTo.SetValuePos( ps->size.to ); + sliderAspectFrom.SetValuePos( ps->aspect.from ); + sliderAspectTo.SetValuePos( ps->aspect.to ); + trails = ps->orientationParms[0]; + trailTime = ps->orientationParms[1]; + cycles = ps->cycles; + customPath = ps->GetCustomPathName(); + customDesc = ps->GetCustomPathDesc(); + customParms.Clear(); + if ( ps->customPathType != PPATH_STANDARD ) { + for ( int i = 0; i < ps->NumCustomPathParms(); i++ ) { + customParms += va( "%.1f ", ps->customPathParms[i] ); + } + } + worldGravity = ps->worldGravity; + initialAngle = ps->initialAngle; + boundsExpansion = ps->boundsExpansion; + randomDistribution = ps->randomDistribution; + entityColor = ps->entityColor; +} + +void ParticleEditor::DlgVarsToCurStage() { + + // go ahead and set the two system vars too + idDeclParticle *idp = GetCurParticle(); + if ( idp == NULL ) { + return; + } + idp->depthHack = atof( depthHack ); + + idParticleStage *ps = GetCurStage(); + if ( ps == NULL ) { + return; + } + ps->material = declManager->FindMaterial( matName ); + ps->animationFrames = animFrames; + ps->animationRate = animRate; + ps->color.x = color.x; + ps->color.y = color.y; + ps->color.z = color.z; + ps->color.w = color.w; + ps->fadeColor.x = fadeColor.x; + ps->fadeColor.y = fadeColor.y; + ps->fadeColor.z = fadeColor.z; + ps->fadeColor.w = fadeColor.w; + ps->fadeInFraction = sliderFadeIn.GetValue(); + ps->fadeOutFraction = sliderFadeOut.GetValue(); + ps->fadeIndexFraction = sliderFadeFraction.GetValue(); + ps->totalParticles = sliderCount.GetValue(); + ps->particleLife = sliderTime.GetValue(); + ps->timeOffset = timeOffset; + ps->deadTime = deadTime; + ps->gravity = sliderGravity.GetValue(); + ps->spawnBunching = sliderBunching.GetValue(); + ps->offset.x = offset[0]; + ps->offset.y = offset[1]; + ps->offset.z = offset[2]; + ps->distributionParms[0] = xSize; + ps->distributionParms[1] = ySize; + ps->distributionParms[2] = zSize; + ps->distributionParms[3] = ringOffset; + ps->directionParms[0] = directionParm; + ps->directionType = static_cast( direction ); + ps->orientation = static_cast( orientation ); + ps->distributionType = static_cast( distribution ); + ps->speed.from = sliderSpeedFrom.GetValue(); + ps->speed.to = sliderSpeedTo.GetValue(); + ps->rotationSpeed.from = sliderRotationFrom.GetValue(); + ps->rotationSpeed.to = sliderRotationTo.GetValue(); + ps->size.from = sliderSizeFrom.GetValue(); + ps->size.to = sliderSizeTo.GetValue(); + ps->aspect.from = sliderAspectFrom.GetValue(); + ps->aspect.to = sliderAspectTo.GetValue(); + ps->orientationParms[0] = trails; + ps->orientationParms[1] = trailTime; + ps->worldGravity = worldGravity; + ps->cycles = cycles; + ps->cycleMsec = ( ps->particleLife + ps->deadTime ) * 1000; + + sscanf( customParms, "%f %f %f %f %f %f %f %f", &ps->customPathParms[0], &ps->customPathParms[1], &ps->customPathParms[2], + &ps->customPathParms[3], &ps->customPathParms[4], &ps->customPathParms[5], + &ps->customPathParms[6], &ps->customPathParms[7] ); + + ps->SetCustomPathType( customPath ); + + ps->initialAngle = initialAngle; + ps->boundsExpansion = boundsExpansion; + ps->randomDistribution = randomDistribution; + ps->entityColor = entityColor; + +} + +void ParticleEditor::ShowCurrentStage() { + ClearDlgVars(); + idParticleStage *ps = GetCurStage(); + if ( ps == NULL ) { + return; + } + CurStageToDlgVars(); + UpdateControlInfo(); +} + +void ParticleEditor::OnLbnSelchangeListStages() { + ShowCurrentStage(); + EnableStageControls(); +} + +void ParticleEditor::OnBnClickedButtonSave() { + idDeclParticle *idp = GetCurParticle(); + if ( idp == NULL ) { + return; + } + + idp->Save(); +} + +/*void VectorCallBack(idQuat rotation) { + ParticleEditor::Instance().SetVectorControlUpdate( rotation ); +}*/ + +void ParticleEditor::SetVectorControlUpdate( idQuat rotation ) { + if ( particleMode ) { + idList list; + + list.SetNum( 128 ); + int count = gameEdit->GetSelectedEntities( list.Ptr(), list.Num() ); + list.SetNum( count ); + + if ( count ) { + for ( int i = 0; i < count; i++ ) { + const idDict *dict = gameEdit->EntityGetSpawnArgs( list[i] ); + if ( dict == NULL ) { + continue; + } + const char *name = dict->GetString( "name" ); + gameEdit->EntitySetAxis( list[i], rotation.ToMat3() ); + gameEdit->EntityUpdateVisuals( list[i] ); + gameEdit->MapSetEntityKeyVal( name, "rotation", rotation.ToMat3().ToString() ); + } + buttonSaveParticleEntitiesEnabled = true; + } + } +} + +void ParticleEditor::Reset() +{ + particleMode = ( cvarSystem->GetCVarInteger( "g_editEntityMode" ) == 4 ); + mapModified = false; + + sliderBunching.SetRange( 0, 20 ); + sliderBunching.SetValueRange( 0.0f, 1.0f ); + sliderFadeIn.SetRange( 0, 20 ); + sliderFadeIn.SetValueRange( 0.0f, 1.0f ); + sliderFadeOut.SetRange( 0, 20 ); + sliderFadeOut.SetValueRange( 0.0f, 1.0f ); + sliderCount.SetRange( 0, 1024 ); + sliderCount.SetValueRange( 0, 4096 ); + sliderTime.SetRange( 0, 200 ); + sliderTime.SetValueRange( 0.0f, 10.0f ); + sliderGravity.SetRange( 0, 600 ); + sliderGravity.SetValueRange( -300.0f, 300.0f ); + sliderSpeedFrom.SetRange( 0, 600 ); + sliderSpeedFrom.SetValueRange( -300.0f, 300.0f ); + sliderSpeedTo.SetRange( 0, 600 ); + sliderSpeedTo.SetValueRange( -300.0f, 300.0f ); + sliderRotationFrom.SetRange( 0, 100 ); + sliderRotationFrom.SetValueRange( 0.0f, 100.0f ); + sliderRotationTo.SetRange( 0, 100 ); + sliderRotationTo.SetValueRange( 0.0f, 100.0f ); + sliderSizeFrom.SetRange( 0, 256 ); + sliderSizeFrom.SetValueRange( 0.0f, 128.0f ); + sliderSizeTo.SetRange( 0, 256 ); + sliderSizeTo.SetValueRange( 0.0f, 128.0f ); + sliderAspectFrom.SetRange( 0, 256 ); + sliderAspectFrom.SetValueRange( 0.0f, 128.0f ); + sliderAspectTo.SetRange( 0, 256 ); + sliderAspectTo.SetValueRange( 0.0f, 128.0f ); + sliderFadeFraction.SetRange( 0, 20 ); + sliderFadeFraction.SetValueRange( 0.0f, 1.0f ); + + UpdateParticleData(); + SetParticleView(); + + buttonSaveParticleEntitiesEnabled = false; + EnableEditControls(); + + //vectorControl.SetVectorChangingCallback( VectorCallBack ); +} + +void ParticleEditor::EnableStageControls() { + idParticleStage *stage = GetCurStage(); + bool b = ( stage && stage->hidden ) ? false : true; + + stageControlsEnabled = b; +} + +void ParticleEditor::EnableEditControls() { + editControlsEnabled = particleMode; +} + +void ParticleEditor::UpdateSelectedOrigin( float x, float y, float z ) { + idList list; + idVec3 origin; + idVec3 vec(x, y, z); + + list.SetNum( 128 ); + int count = gameEdit->GetSelectedEntities( list.Ptr(), list.Num() ); + list.SetNum( count ); + + if ( count ) { + for ( int i = 0; i < count; i++ ) { + const idDict *dict = gameEdit->EntityGetSpawnArgs( list[i] ); + if ( dict == NULL ) { + continue; + } + const char *name = dict->GetString( "name" ); + gameEdit->EntityTranslate( list[i], vec ); + gameEdit->EntityUpdateVisuals( list[i] ); + gameEdit->MapEntityTranslate( name, vec ); + } + buttonSaveParticleEntitiesEnabled = true; + } +} + +void ParticleEditor::OnBtnYup() +{ + UpdateSelectedOrigin(0, 8, 0); +} + +void ParticleEditor::OnBtnYdn() +{ + UpdateSelectedOrigin(0, -8, 0); +} + +void ParticleEditor::OnBtnXdn() +{ + UpdateSelectedOrigin(-8, 0, 0); +} + +void ParticleEditor::OnBtnXup() +{ + UpdateSelectedOrigin(8, 0, 0); +} + +void ParticleEditor::OnBtnZup() +{ + UpdateSelectedOrigin(0, 0, 8); +} + +void ParticleEditor::OnBtnZdn() +{ + UpdateSelectedOrigin(0, 0, -8); +} + +ParticleDrop::ParticleDrop() + : classname( "" ) + , org( 0.0f, 0.0f, 0.0f ) + , args() + , viewAngles( 0.0f, 0.0f, 0.0f ) + , errorText( "" ) + , name( "" ) + , state( DONE ) +{ + +} + +void ParticleDrop::Start( idDeclParticle *idp ) +{ + if ( !idp ) { + return; + } + + if ( !gameEdit->PlayerIsValid() ) { + return; + } + + gameEdit->PlayerGetViewAngles( viewAngles ); + gameEdit->PlayerGetEyePosition( org ); + + org += idAngles( 0, viewAngles.yaw, 0 ).ToForward() * 80 + idVec3( 0, 0, 1 ); + args.Clear(); + args.Set( "origin", org.ToString() ); + args.Set( "classname", "func_emitter"); + args.Set( "angle", va( "%f", viewAngles.yaw + 180 ) ); + + if ( idp == NULL ) { + return; + } + + idStr str = idp->GetName(); + str.SetFileExtension( ".prt" ); + + args.Set( "model", str ); + + name = gameEdit->GetUniqueEntityName( "func_emitter" ); + state = NAME; + errorText.Clear(); + + ImGui::OpenPopup( "Name particle" ); +} + +void ParticleDrop::Draw() +{ + if ( state == DONE ) { + return; + } + + bool accepted = false; + bool canceled = false; + + if ( ImGui::BeginPopupModal( "Name particle", nullptr, ImGuiWindowFlags_AlwaysAutoResize ) ) { + ImGui::TextColored( ImVec4( 1, 0, 0, 1 ), "%s", errorText.c_str() ); + + if ( ImGui::InputTextStr( "Name", &name ) ) { + // nop + } + + if ( ImGui::Button( "OK" ) ) { + accepted = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if ( ImGui::Button( "Cancel" ) ) { + canceled = true; + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + + if ( canceled ) { + state = DONE; + return; + } + if ( !accepted ) { + return; + } + + idEntity *gameEnt = gameEdit->FindEntity( name ); + if ( gameEnt ) { + errorText = "Please choose another name"; + return; + } + + args.Set( "name", name.c_str() ); + + idEntity *ent = NULL; + gameEdit->SpawnEntityDef( args, &ent ); + if ( ent ) { + gameEdit->EntityUpdateChangeableSpawnArgs( ent, NULL ); + gameEdit->ClearEntitySelection(); + gameEdit->AddSelectedEntity( ent ); + } + + gameEdit->MapAddEntity( &args ); + state = DONE; +} + +} diff --git a/neo/tools/imgui/particleeditor/ParticleEditor.h b/neo/tools/imgui/particleeditor/ParticleEditor.h new file mode 100644 index 000000000..6023a5dca --- /dev/null +++ b/neo/tools/imgui/particleeditor/ParticleEditor.h @@ -0,0 +1,265 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __PARTICLEEDITOR_H__ +#define __PARTICLEEDITOR_H__ + +#include "../../edit_public.h" + +class idDeclParticle; +class idParticleStage; + +namespace ImGuiTools +{ + +class RangeSlider { +public: + bool Draw( const char *label, float sliderWidth ); + + void SetRange( int _min, int _max ) { + min = _min; + max = _max; + } + int GetRangeMin() { + return min; + } + int GetRangeMax() { + return max; + } + + void SetValueRange(float _low, float _high) { + low = _low; + high = _high; + } + + float GetValueLow() { + return low; + } + + float GetValueHigh() { + return high; + } + + void SetValuePos( float val ) { + SetPos( GetRangeMin() + ( GetRangeMax() - GetRangeMin() ) * ( val - low ) / ( high - low ) ); + } + + float GetValue() { + return low + ( high - low ) * ( float )( GetPos() - GetRangeMin() ) / ( GetRangeMax() - GetRangeMin() ); + } + + void SetPos( int _pos ) { + pos = _pos; + } + + int GetPos() { + return pos; + } + +private: + float low, high; + int pos; + int min, max; +}; + +class ParticleDrop { +public: + ParticleDrop(); + + void Start( idDeclParticle *curParticle ); + void Draw(); + +private: + idStr classname; + idVec3 org; + idDict args; + idAngles viewAngles; + + enum state_t { DONE = 0, NAME }; + + idStr errorText; + idStr name; + state_t state; +}; + +class ParticleEditor { + +public: + ParticleEditor(); // standard constructor + + static ParticleEditor& Instance(); + + void SelectParticle( const char *name ); + void SetParticleVisualization( int i ); + void SetVectorControlUpdate( idQuat rotation ); + + enum { TESTMODEL, IMPACT, MUZZLE, FLIGHT, SELECTED }; + + void Reset(); + void Draw(); + + void ShowIt(bool show) { + isShown = show; + } + bool IsShown() { + return isShown; + } + +private: + void OnCbnSelchangeComboPath(); + void OnLbnSelchangeListStages(); + void ButtonColor(); + void ButtonFadeColor(); + void ButtonEntityColor(); + void OnBnClickedButtonSave(); + void OnBnClickedButtonSaveParticles(); + void OnBnClickedTestModel(); + void OnBnClickedImpact(); + void OnBnClickedMuzzle(); + void OnBnClickedFlight(); + void OnBnClickedSelected(); + void OnBnClickedDoom(); + void OnBnClickedButtonUpdate(); + void OnBnClickedParticleMode(); + void OnBtnYup(); + void OnBtnYdn(); + void OnBtnXdn(); + void OnBtnXup(); + void OnBtnZup(); + void OnBtnZdn(); + +private: + bool isShown; + + DeclNewSelect particleNewDlg; + DeclSelect particleSelectDlg; + DeclSelect materialSelectDlg; + + idStr inFileText; + + bool buttonSaveParticleEntitiesEnabled; + + ColorPicker colorDlg; + ColorPicker fadeColorDlg; + ColorPicker entityColorDlg; + ParticleDrop particleDropDlg; + + idDeclParticle * curParticle; + + // edit controls + bool editControlsEnabled; + + // stage controls + bool stageControlsEnabled; + bool editRingOffsetEnabled; + bool editOrientationParm1Enabled; + bool editOrientationParm2Enabled; + + idList listStages; + idHashIndex listStagesItemData; + int listStagesSel; + RangeSlider sliderBunching; + RangeSlider sliderFadeIn; + RangeSlider sliderFadeOut; + RangeSlider sliderFadeFraction; + RangeSlider sliderCount; + RangeSlider sliderTime; + RangeSlider sliderGravity; + RangeSlider sliderSpeedFrom; + RangeSlider sliderSpeedTo; + RangeSlider sliderRotationFrom; + RangeSlider sliderRotationTo; + RangeSlider sliderSizeFrom; + RangeSlider sliderSizeTo; + RangeSlider sliderAspectFrom; + RangeSlider sliderAspectTo; + //VectorCtl vectorControl; + + idStr depthHack; + idStr matName; + int animFrames; + float animRate; + idVec4 color; + idVec4 fadeColor; + float timeOffset; + float deadTime; + float offset[3]; + float xSize; + float ySize; + float zSize; + float ringOffset; + idStr dirParmText; + float directionParm; + int direction; + int orientation; + int distribution; + idStr viewOrigin; + idStr customPath; + idStr customParms; + float trails; + float trailTime; + float cycles; + idStr editRingOffset; + bool worldGravity; + bool entityColor; + bool randomDistribution; + float initialAngle; + float boundsExpansion; + idStr customDesc; + + bool particleMode; + + int visualization; + + bool mapModified; + +private: + void AddStage( bool clone ); + void RemoveStage(); + void RemoveStageThink(); + void ShowStage(); + void HideStage(); + void SetCurParticle( idDeclParticle *dp ); + idDeclParticle * GetCurParticle(); + idParticleStage * GetCurStage(); + void ClearDlgVars(); + void CurStageToDlgVars(); + void DlgVarsToCurStage(); + void ShowCurrentStage(); + void UpdateControlInfo(); + void SetParticleView(); + void UpdateParticleData(); + void SetSelectedModel( const char *val ); + void EnableStageControls(); + void EnableEditControls(); + void UpdateSelectedOrigin( float x, float y, float z ); +}; + +} + +#endif /* !__PARTICLEEDITOR_H__ */ diff --git a/neo/tools/imgui/pdaeditor/PDAEditor.cpp b/neo/tools/imgui/pdaeditor/PDAEditor.cpp new file mode 100644 index 000000000..773614004 --- /dev/null +++ b/neo/tools/imgui/pdaeditor/PDAEditor.cpp @@ -0,0 +1,1150 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "PDAEditor.h" + +#include "sys/sys_imgui.h" +#include "../util/ImGui_IdWidgets.h" + +#include "idlib/math/Vector.h" + +#include "framework/FileSystem.h" +#include "framework/Game.h" +#include "framework/DeclPDA.h" + +namespace ImGuiTools { + +///////////////////////////////////////////////////////////////////////////// +// PDAEditor + +PDAEditor& PDAEditor::Instance() +{ + static PDAEditor instance; + return instance; +} + +PDAEditor::PDAEditor() +{ + isShown = false; +} + +void PDAEditor::Reset() { + // Indicate the PDA dialog is opened + com_editors |= EDITOR_PDA; + + pdaListSel = -1; + emailListSel = -1; + audioListSel = -1; + videoListSel = -1; + + windowTitle = "PDA Editor"; + + addPDADlg.Reset(); + editEmailDlg.Reset(); + editAudioDlg.Reset(); + editVideoDlg.Reset(); + + PopulatePDAList(); +} + +void PDAEditor::Draw() { + int i, num; + bool selected; + bool showTool; + + showTool = isShown; + + idStr windowName = windowTitle + "###PDA Editor"; + if ( ImGui::Begin( windowName, &showTool, ImGuiWindowFlags_AlwaysAutoResize)) //, ImGuiWindowFlags_ShowBorders ) ) + { + if ( ImGui::BeginTable( "table", 3 ) ) + { + ImGui::PushID( "pda" ); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text( "PDAs:" ); + if ( ImGui::BeginListBox( "##PDAList", ImVec2( 300, 200 ) ) ) { + num = pdaList.Num(); + for ( i = 0; i < num; i++ ) { + ImGui::PushID( i ); + + selected = ( i == pdaListSel ); + + if ( ImGui::Selectable( pdaList[i].c_str(), selected ) ) { + OnSelChangePDA( i ); + } + + if ( selected ) { + ImGui::SetItemDefaultFocus(); + } + + ImGui::PopID(); + } + ImGui::EndListBox(); + } + if ( ImGui::Button( "Add" ) ) { + OnBtnClickedPDAAdd(); + } + /* + * FIXME: decl manager does not support deletion + ImGui::SameLine(); + if ( ImGui::Button( "Del" ) ) { + OnBtnClickedPDADel(); + } + */ + + if ( ImGui::BeginPopupModal( "PDAAdd", nullptr, ImGuiWindowFlags_AlwaysAutoResize ) ) { + bool accepted = addPDADlg.Draw(); + + if ( accepted ) { + addPDADlg.name.ToLower(); + idDecl* decl = declManager->CreateNewDecl( DECL_PDA, addPDADlg.name, "newpdas/" + addPDADlg.name + ".pda" ); + decl->ReplaceSourceFileText(); + decl->Invalidate(); + PopulatePDAList(); + int index = pdaList.FindIndex( addPDADlg.name ); + OnSelChangePDA( index ); + } + + ImGui::EndPopup(); + } + + /* + * FIXME: decl manager does not support deletion + if ( ImGui::BeginPopupModal( "PDADel", nullptr, ImGuiWindowFlags_AlwaysAutoResize ) ) { + int index; + + index = pdaListSel; + assert( index >= 0 ); + + idStr &name = pdaList[ index ]; + + ImGui::Text("Are you sure you want to delete PDA %s?", name.c_str() ); + + if ( ImGui::Button( "OK" ) ) { + ImGui::CloseCurrentPopup(); + + const idDeclPDA* pda = dynamic_cast< const idDeclPDA * >( declManager->DeclByIndex( DECL_PDA, index ) ); + if ( pda ) { + // FIXME: doesn't work, we need to delete the decl object itself somehow + //fileSystem->RemoveFile( pda->GetFileName() ); + //PopulatePDAList(); + } + } + ImGui::SameLine(); + if ( ImGui::Button( "Cancel" ) ) { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + */ + + ImGui::PopID(); + + ImGui::TableSetColumnIndex(1); + ImGui::PushID( "pdaEdit" ); + ImGui::SetNextItemWidth( 300 ); + if ( ImGui::InputTextStr( "Full Name", &fullName ) ) { + + } + ImGui::SetNextItemWidth( 300 ); + if ( ImGui::InputTextStr( "Short Name", &shortName ) ) { + + } + ImGui::SetNextItemWidth( 300 ); + if ( ImGui::InputTextStr( "Title", &title ) ) { + + } + ImGui::SetNextItemWidth( 300 ); + if ( ImGui::InputTextStr( "Post (Location)", &post ) ) { + + } + ImGui::SetNextItemWidth( 300 ); + if ( ImGui::InputTextStr( "Security Level", &security ) ) { + + } + ImGui::SetNextItemWidth( 50 ); + if ( ImGui::Button( "Rand" ) ) { + OnBtnClickedRandom(); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth( 255 ); + if ( ImGui::InputTextStr( "ID number", &idnum ) ) { + + } + ImGui::PopID(); + + ImGui::PushID( "audio" ); + ImGui::Dummy( ImVec2(0, 20) ); + ImGui::Text( "Audio Logs:" ); + if (ImGui::BeginListBox( "##audioList", ImVec2( 300, 200 ) ) ) { + num = audioList.Num(); + for ( i = 0; i < num; i++ ) { + ImGui::PushID( i ); + + selected = ( i == audioListSel ); + if ( ImGui::Selectable( audioList[i].c_str(), selected ) ) { + audioListSel = i; + } + + if ( selected ) { + ImGui::SetItemDefaultFocus(); + } + + ImGui::PopID(); + } + ImGui::EndListBox(); + } + if ( ImGui::Button( "Add" ) ) { + OnBtnClickedAudioAdd(); + } + ImGui::SameLine(); + if ( ImGui::Button( "Edit" ) ) { + OnBtnClickedAudioEdit(); + } + /* + * FIXME: decl manager does not support deletion + ImGui::SameLine(); + if ( ImGui::Button( "Delete" ) ) { + OnBtnClickedAudioDel(); + } + */ + if ( ImGui::BeginPopupModal( "AudioAdd", nullptr, ImGuiWindowFlags_AlwaysAutoResize ) ) { + bool accepted; + + accepted = editAudioDlg.Draw(); + if ( accepted ) { + OnAudioAdd(); + } + + ImGui::EndPopup(); + } + if ( ImGui::BeginPopupModal( "AudioEdit", nullptr, ImGuiWindowFlags_AlwaysAutoResize ) ) { + bool accepted = editAudioDlg.Draw(); + + if ( accepted ) { + OnAudioEdit(); + } + + ImGui::EndPopup(); + } + ImGui::PopID(); + + ImGui::TableSetColumnIndex(2); + ImGui::PushID( "email" ); + ImGui::Text( "Email" ); + if ( ImGui::BeginListBox( "##emailList", ImVec2( 300, 200 ) ) ) { + num = emailList.Num(); + for ( i = 0; i < num; i++ ) { + ImGui::PushID( i ); + + selected = ( i == emailListSel ); + if ( ImGui::Selectable( emailList[i].c_str(), selected ) ) { + emailListSel = i; + } + + if ( selected ) { + ImGui::SetItemDefaultFocus(); + } + + ImGui::PopID(); + } + ImGui::EndListBox(); + } + if ( ImGui::Button( "Add" ) ) { + OnBtnClickedEmailAdd(); + } + ImGui::SameLine(); + if ( ImGui::Button( "Edit" ) ) { + OnBtnClickedEmailEdit(); + } + /* + * FIXME: decl manager does not support deletion + ImGui::SameLine(); + if ( ImGui::Button( "Delete" ) ) { + OnBtnClickedEmailDel(); + } + */ + if ( ImGui::BeginPopupModal( "EmailAdd", nullptr, ImGuiWindowFlags_AlwaysAutoResize ) ) { + bool accepted; + + accepted = editEmailDlg.Draw(); + if ( accepted ) { + OnEmailAdd(); + } + + ImGui::EndPopup(); + } + if ( ImGui::BeginPopupModal( "EmailEdit", nullptr, ImGuiWindowFlags_AlwaysAutoResize ) ) { + bool accepted = editEmailDlg.Draw(); + + if ( accepted ) { + OnEmailEdit(); + } + + ImGui::EndPopup(); + } + ImGui::PopID(); + + ImGui::PushID("video"); + ImGui::Dummy(ImVec2(0, 20)); + ImGui::Text("Videos"); + if ( ImGui::BeginListBox( "##videoList", ImVec2( 300, 200 ) ) ) { + num = videoList.Num(); + for ( i = 0; i < num; i++ ) { + selected = ( i == videoListSel ); + if ( ImGui::Selectable( videoList[i].c_str(), selected ) ) { + videoListSel = i; + } + + if ( selected ) { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndListBox(); + } + if ( ImGui::Button( "Add" ) ) { + OnBtnClickedVideoAdd(); + } + ImGui::SameLine(); + if ( ImGui::Button( "Edit" ) ) { + OnBtnClickedVideoEdit(); + } + /* + * FIXME: decl manager does not support deletion + ImGui::SameLine(); + if ( ImGui::Button( "Delete" ) ) { + OnBtnClickedVideoDel(); + } + */ + + if ( ImGui::BeginPopupModal( "VideoAdd", nullptr, ImGuiWindowFlags_AlwaysAutoResize ) ) { + bool accepted; + + accepted = editVideoDlg.Draw(); + if ( accepted ) { + OnVideoAdd(); + } + + ImGui::EndPopup(); + } + if ( ImGui::BeginPopupModal( "VideoEdit", nullptr, ImGuiWindowFlags_AlwaysAutoResize ) ) { + bool accepted = editVideoDlg.Draw(); + + if ( accepted ) { + OnVideoEdit(); + } + + ImGui::EndPopup(); + } + ImGui::PopID(); + + ImGui::EndTable(); + } + + ImGui::BeginDisabled( !saveButtonEnabled ); + if ( ImGui::Button( "Save" ) ) { + OnBtnClickedSave(); + showTool = false; + } + ImGui::EndDisabled(); + ImGui::SameLine(); + if ( ImGui::Button( "Cancel" ) ) { + showTool = false; + } + } + ImGui::End(); + + if ( isShown && !showTool ) + { + isShown = showTool; + impl::SetReleaseToolMouse( false ); + D3::ImGuiHooks::CloseWindow( D3::ImGuiHooks::D3_ImGuiWin_PDAEditor ); + } +} + +///////////////////////////////////////////////////////////////////////////// +// CDialogPDAEditor message handlers + +void PDAEditor::OnDestroy() { + + //com_editors &= ~EDITOR_PDA; + + isShown = false; +} + +void PDAEditor::PopulatePDAList() +{ + int i; + int num = declManager->GetNumDecls(DECL_PDA); + pdaList.Resize( num ); + pdaList.Clear(); + for ( i=0; i < num; i++ ) { + const idDeclPDA *pda = dynamic_cast( declManager->DeclByIndex(DECL_PDA, i) ); + pdaList.Append( pda->GetName() ); + } + pdaListSel = -1; + + fullName.Clear(); + shortName.Clear(); + post.Clear(); + title.Clear(); + security.Clear(); + idnum.Clear(); +} + +void PDAEditor::OnSelChangePDA( int index ) +{ + int i, num; + + if ( index < 0 ) { + pdaListSel = -1; + emailListSel = -1; + audioListSel = -1; + videoListSel = -1; + windowTitle = "PDA Editor"; + saveButtonEnabled = false; + return; + } + + pdaListSel = index; + + const idDeclPDA *pda = dynamic_cast( declManager->DeclByIndex( DECL_PDA, index ) ); + if ( !pda ) { + pdaListSel = -1; + emailListSel = -1; + audioListSel = -1; + videoListSel = -1; + return; + } + + windowTitle = idStr::Format( "PDA Editor - %s", pda->GetName() ); + + idFile *file = fileSystem->OpenFileAppend( pda->GetFileName() ); + if ( file ) { + fileSystem->CloseFile( file ); + saveButtonEnabled = true; + } else { + windowTitle += " [Read Only]"; + saveButtonEnabled = false; + } + + num = pda->GetNumEmails(); + emailList.Resize( num ); + emailList.Clear(); + emailListSel = -1; + for ( i=0; i < num; i++ ) { + emailList.Append( pda->GetEmailByIndex( i )->GetSubject() ); + } + + num = pda->GetNumAudios(); + audioList.Resize( num ); + audioList.Clear(); + audioListSel = -1; + for ( i=0; i < num; i++ ) { + audioList.Append( pda->GetAudioByIndex( i )->GetAudioName() ); + } + + num = pda->GetNumVideos(); + videoList.Resize( num ); + videoList.Clear(); + videoListSel = -1; + for ( i=0; i < num; i++ ) { + videoList.Append( pda->GetVideoByIndex( i )->GetVideoName() ); + } + + fullName = pda->GetFullName(); + shortName = pda->GetPdaName(); + post = pda->GetPost(); + title = pda->GetTitle(); + security = pda->GetSecurity(); + idnum = pda->GetID(); +} + +void PDAEditor::OnBtnClickedSave() +{ + int index = pdaListSel; + if ( index < 0 ) { + return; + } + + const idDeclPDA *pdaConst = dynamic_cast( declManager->DeclByIndex(DECL_PDA, index) ); + if ( pdaConst ) { + idDeclPDA *pda = const_cast(pdaConst); + + idStr declText = "\n"; + declText += "pda "; + declText += pda->GetName(); + declText += " {\n"; + + declText += "\tname \t\t\"" + shortName + "\"\n"; + declText += "\tfullname\t\t\"" + fullName + "\"\n"; + declText += "\ticon \t\t\"\"\n"; + declText += "\tid \t\t\"" + idnum + "\"\n"; + declText += "\tpost \t\t\"" + post + "\"\n"; + declText += "\ttitle \t\t\"" + title + "\"\n"; + declText += "\tsecurity\t\t\"" + security + "\"\n"; + + for ( int i = 0; i < pda->GetNumEmails(); i++ ) { + declText += "\tpda_email\t\t\""; + declText += pda->GetEmailByIndex(i)->GetName(); + declText += "\"\n"; + } + + for ( int i = 0; i < pda->GetNumAudios(); i++ ) { + declText += "\tpda_audio\t\t\""; + declText += pda->GetAudioByIndex(i)->GetName(); + declText += "\"\n"; + } + + for ( int i = 0; i < pda->GetNumVideos(); i++ ) { + declText += "\tpda_video\t\t\""; + declText += pda->GetVideoByIndex(i)->GetName(); + declText += "\"\n"; + } + + declText += "}"; + + pda->SetText( declText ); + pda->ReplaceSourceFileText(); + pda->Invalidate(); + } +} + +DialogPDAAdd::DialogPDAAdd() { + Reset(); +} + +void DialogPDAAdd::Reset() { + name.Clear(); +} + +bool DialogPDAAdd::Draw() { + ImGui::InputTextStr( "Name", &name ); + + bool isAccepted = false; + if ( ImGui::Button( "OK" ) ) { + isAccepted = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if ( ImGui::Button( "Cancel" ) ) { + isAccepted = false; + ImGui::CloseCurrentPopup(); + } + return isAccepted; +} + + +void PDAEditor::OnBtnClickedRandom() +{ + idnum = idStr::Format("%d-%02X", 1000+(rand()%8999), (rand()%255)); +} + +void PDAEditor::OnBtnClickedPDAAdd() +{ + addPDADlg.Reset(); + ImGui::OpenPopup( "PDAAdd" ); +} + +void PDAEditor::OnBtnClickedPDADel() +{ + ImGui::OpenPopup( "PDADel" ); +} + +void PDAEditor::OnBtnClickedEmailAdd() +{ + int index = pdaListSel; + if ( index < 0 ) { + return; + } + + const idDeclPDA *pda = dynamic_cast( declManager->DeclByIndex(DECL_PDA, index) ); + + if ( pda ) { + idStr name; + + // Search for an unused name + int newIndex = pda->GetNumEmails(); + do { + name = idStr::Format("%s_email_%d", pda->GetName(), newIndex++); + } while ( declManager->FindType(DECL_EMAIL, name, false) != NULL ); + + editEmailDlg.Reset(); + editEmailDlg.SetName( name ); + ImGui::OpenPopup( "EmailAdd" ); + } +} + +void PDAEditor::OnEmailAdd() +{ + int index = pdaListSel; + if ( index < 0 ) { + return; + } + + const idDeclPDA *pda = dynamic_cast( declManager->DeclByIndex( DECL_PDA, index ) ); + if ( !pda ) { + return; + } + + idStr &name = editEmailDlg.GetName(); + idDeclEmail *email = static_cast( declManager->CreateNewDecl( DECL_EMAIL, name, pda->GetFileName() ) ); + email->SetText( editEmailDlg.GetDeclText() ); + email->ReplaceSourceFileText(); + email->Invalidate(); + + pda->AddEmail( name ); + + // Get it again to reparse + const idDeclEmail *emailConst = static_cast( declManager->FindType( DECL_EMAIL, name ) ); + emailList.Append( emailConst->GetSubject() ); + + // Save the pda to include this email in the list + // This has a side-effect of saving any other changes, but I don't really care right now + OnBtnClickedSave(); +} + +void PDAEditor::OnBtnClickedEmailEdit() +{ + int index = pdaListSel; + if ( index < 0 ) { + return; + } + + const idDeclPDA *pda = dynamic_cast( declManager->DeclByIndex( DECL_PDA, index ) ); + + if ( !pda ) { + return; + } + + index = emailListSel; + if ( index < 0 ) { + return; + } + + editEmailDlg.SetEmail( pda->GetEmailByIndex( index ) ); + ImGui::OpenPopup( "EmailEdit" ); +} + +void PDAEditor::OnEmailEdit() +{ + int index = pdaListSel; + if ( index < 0 ) { + return; + } + + const idDeclPDA *pda = dynamic_cast( declManager->DeclByIndex( DECL_PDA, index ) ); + + if ( !pda ) { + return; + } + + index = emailListSel; + if ( index < 0 ) { + return; + } + + idDeclEmail* email = const_cast( pda->GetEmailByIndex( index ) ); + email->SetText( editEmailDlg.GetDeclText() ); + email->ReplaceSourceFileText(); + email->Invalidate(); + + // Get it again to reparse + email = const_cast( pda->GetEmailByIndex( index ) ); + + emailList.RemoveIndex( index ); + emailList.Insert( email->GetSubject(), index ); +} + +void PDAEditor::OnBtnClickedEmailDel() +{ +} + +void PDAEditor::OnBtnClickedAudioAdd() +{ + int index = pdaListSel; + if ( index < 0 ) { + return; + } + + const idDeclPDA *pda = dynamic_cast( declManager->DeclByIndex( DECL_PDA, index ) ); + + if ( pda ) { + idStr name; + + // Search for an unused name + int newIndex = pda->GetNumAudios(); + do { + name = idStr::Format("%s_audio_%d", pda->GetName(), newIndex++); + } while ( declManager->FindType(DECL_AUDIO, name, false) != NULL ); + + editAudioDlg.Reset(); + editAudioDlg.SetName( name ); + ImGui::OpenPopup( "AudioAdd" ); + } +} + +void PDAEditor::OnAudioAdd() +{ + int index = pdaListSel; + if ( index < 0 ) { + return; + } + + const idDeclPDA *pda = dynamic_cast( declManager->DeclByIndex( DECL_PDA, index ) ); + if ( !pda ) { + return; + } + + idStr &name = editAudioDlg.GetName(); + idDeclEmail *email = static_cast( declManager->CreateNewDecl( DECL_AUDIO, name, pda->GetFileName() ) ); + email->SetText( editAudioDlg.GetDeclText() ); + email->ReplaceSourceFileText(); + email->Invalidate(); + + pda->AddAudio( name ); + + // Get it again to reparse + const idDeclAudio *audioConst = static_cast( declManager->FindType( DECL_AUDIO, name ) ); + audioList.Append( audioConst->GetAudioName() ); + + // Save the pda to include this audio in the list + // This has a side-effect of saving any other changes, but I don't really care right now + OnBtnClickedSave(); +} + +void PDAEditor::OnBtnClickedAudioEdit() +{ + int index = pdaListSel; + if ( index < 0 ) { + return; + } + + const idDeclPDA *pda = dynamic_cast( declManager->DeclByIndex( DECL_PDA, index ) ); + + if ( !pda ) { + return; + } + + index = audioListSel; + if ( index < 0 ) { + return; + } + + editAudioDlg.SetAudio( pda->GetAudioByIndex( index ) ); + ImGui::OpenPopup( "AudioEdit" ); +} + +void PDAEditor::OnAudioEdit() { + int index = pdaListSel; + if ( index < 0 ) { + return; + } + + const idDeclPDA *pda = dynamic_cast( declManager->DeclByIndex( DECL_PDA, index ) ); + + if ( !pda ) { + return; + } + + index = audioListSel; + if ( index < 0 ) { + return; + } + + idDeclAudio *audio = const_cast( pda->GetAudioByIndex( index ) ); + audio->SetText( editAudioDlg.GetDeclText() ); + audio->ReplaceSourceFileText(); + audio->Invalidate(); + + // Get it again to reparse + audio = const_cast( pda->GetAudioByIndex( index ) ); + + audioList.RemoveIndex( index ); + audioList.Insert( audio->GetAudioName(), index ); +} + +void PDAEditor::OnBtnClickedAudioDel() +{ +} + +void PDAEditor::OnBtnClickedVideoAdd() +{ + int index = pdaListSel; + if ( index < 0 ) { + return; + } + + const idDeclPDA *pda = dynamic_cast( declManager->DeclByIndex( DECL_PDA, index ) ); + + if ( pda ) { + idStr name; + + // Search for an unused name + int newIndex = pda->GetNumVideos(); + do { + name = idStr::Format("%s_video_%d", pda->GetName(), newIndex++); + } while ( declManager->FindType(DECL_VIDEO, name, false) != NULL ); + + editVideoDlg.Reset(); + editVideoDlg.SetName( name ); + ImGui::OpenPopup( "VideoAdd" ); + } +} + +void PDAEditor::OnVideoAdd() { + int index = pdaListSel; + if ( index < 0 ) { + return; + } + + const idDeclPDA *pda = dynamic_cast( declManager->DeclByIndex( DECL_PDA, index ) ); + if ( !pda ) { + return; + } + + idStr &name = editVideoDlg.GetName(); + idDeclVideo *video = static_cast( declManager->CreateNewDecl( DECL_VIDEO, name, pda->GetFileName() ) ); + video->SetText( editVideoDlg.GetDeclText() ); + video->ReplaceSourceFileText(); + video->Invalidate(); + + pda->AddVideo( name ); + + // Get it again to reparse + const idDeclVideo *videoConst = static_cast( declManager->FindType( DECL_VIDEO, name ) ); + videoList.Append( videoConst->GetVideoName() ); + + // Save the pda to include this video in the list + // This has a side-effect of saving any other changes, but I don't really care right now + OnBtnClickedSave(); +} + +void PDAEditor::OnBtnClickedVideoEdit() +{ + int index = pdaListSel; + if ( index < 0 ) { + return; + } + + const idDeclPDA *pda = dynamic_cast( declManager->DeclByIndex( DECL_PDA, index ) ); + + if ( !pda ) { + return; + } + + index = videoListSel; + if ( index < 0 ) { + return; + } + + editVideoDlg.SetVideo( pda->GetVideoByIndex( index ) ); + ImGui::OpenPopup( "VideoEdit" ); +} + +void PDAEditor::OnVideoEdit() { + int index = pdaListSel; + if ( index < 0 ) { + return; + } + + const idDeclPDA *pda = dynamic_cast( declManager->DeclByIndex( DECL_PDA, index ) ); + + if ( !pda ) { + return; + } + + index = videoListSel; + if ( index < 0 ) { + return; + } + + idDeclVideo *video = const_cast( pda->GetVideoByIndex( index ) ); + video->SetText( editVideoDlg.GetDeclText() ); + video->ReplaceSourceFileText(); + video->Invalidate(); + + // Get it again to reparse + video = const_cast( pda->GetVideoByIndex( index ) ); + + videoList.RemoveIndex( index ); + videoList.Insert( video->GetVideoName(), index ); +} + +void PDAEditor::OnBtnClickedVideoDel() +{ +} + + + + +DialogPDAEditEmail::DialogPDAEditEmail() +{ + Reset(); +} + +bool DialogPDAEditEmail::Draw() +{ + ImGui::InputTextStr( "To", &to ); + ImGui::InputTextStr( "From", &from ); + ImGui::InputTextStr( "Date", &date ); + ImGui::InputTextStr( "Subject", &subject ); + ImGui::InputTextMultilineStr( "Body", &body ); + + bool isAccepted = false; + + if ( ImGui::Button( "OK" ) ) { + isAccepted = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if ( ImGui::Button( "Cancel" ) ) { + isAccepted = false; + ImGui::CloseCurrentPopup(); + } + + return isAccepted; +} + +DialogPDAEditAudio::DialogPDAEditAudio() +{ + Reset(); +} + +bool DialogPDAEditAudio::Draw() +{ + ImGui::InputTextStr("Wave", &wave); + ImGui::InputTextStr("Audio Name", &audioName); + ImGui::InputTextMultilineStr("Info", &info); + ImGui::InputTextStr("Preview", &preview); + + bool isAccepted = false; + + if ( ImGui::Button( "OK" ) ) { + isAccepted = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if ( ImGui::Button( "Cancel" ) ) { + isAccepted = false; + ImGui::CloseCurrentPopup(); + } + + return isAccepted; +} + +DialogPDAEditVideo::DialogPDAEditVideo() +{ + Reset(); +} + +bool DialogPDAEditVideo::Draw() +{ + ImGui::InputTextStr("Video Name", &videoName); + ImGui::InputTextMultilineStr("Info", &info); + ImGui::InputTextStr("Roq", &video); + ImGui::InputTextStr("Wave", &audio); + ImGui::InputTextStr("Preview", &preview); + + bool isAccepted = false; + + if ( ImGui::Button("OK" ) ) { + isAccepted = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if ( ImGui::Button( "Cancel" ) ) { + isAccepted = false; + ImGui::CloseCurrentPopup(); + } + + return isAccepted; +} + +///////////////////////////////////////////////////////////////////////////// +// DialogPDAEditEmail message handlers + +void DialogPDAEditEmail::Reset() +{ + to.Clear(); + from.Clear(); + date.Clear(); + subject.Clear(); + body.Clear(); + + name.Clear(); +} + +void DialogPDAEditEmail::SetName( idStr &_name ) +{ + name = _name; +} + +idStr &DialogPDAEditEmail::GetName() +{ + return name; +} + +void DialogPDAEditEmail::SetEmail( const idDeclEmail *email ) +{ + to = email->GetTo(); + from = email->GetFrom(); + date = email->GetDate(); + subject = email->GetSubject(); + body = email->GetBody(); + + name = email->GetName(); +} + +idStr DialogPDAEditEmail::GetDeclText() +{ + idStr mungedBody = body; + mungedBody.Replace("\n", "\\n\"\n\""); + + idStr declText; + declText += "\n"; + declText += "email " + name + " {\n"; + declText += "\tto \t\t\"" + to + "\"\n"; + declText += "\tfrom \t\t\"" + from + "\"\n"; + declText += "\tdate \t\t\"" + date + "\"\n"; + declText += "\tsubject\t\t\"" + subject + "\"\n"; + declText += "\ttext {\n"; + declText += "\"" + mungedBody + "\"\n"; + declText += "\t}\n"; + declText += "}"; + + return declText; +} + +///////////////////////////////////////////////////////////////////////////// +// DialogPDAEditAudio message handlers + +void DialogPDAEditAudio::Reset() +{ + wave.Clear(); + audioName.Clear(); + info.Clear(); + preview.Clear(); + + name.Clear(); +} + +void DialogPDAEditAudio::SetName(idStr& _name) +{ + name = _name; +} + +idStr &DialogPDAEditAudio::GetName() +{ + return name; +} + +void DialogPDAEditAudio::SetAudio( const idDeclAudio *_audio ) +{ + wave = _audio->GetWave(); + audioName = _audio->GetAudioName(); + info = _audio->GetInfo(); + preview = _audio->GetPreview(); + + name = _audio->GetName(); +} + +idStr DialogPDAEditAudio::GetDeclText() +{ + idStr escapedInfo = info; + escapedInfo.Replace("\n", "\\n"); + + idStr declText; + + declText += "\n"; + declText += "audio " + name + " {\n"; + declText += "\tname \t\t\"" + audioName + "\"\n"; + declText += "\tinfo \t\t\"" + escapedInfo + "\"\n"; + declText += "\tpreview \t\t\"" + preview + "\"\n"; + declText += "\taudio\t\t\"" + wave + "\"\n"; + declText += "}"; + + return declText; +} + +///////////////////////////////////////////////////////////////////////////// +// DialogPDAEditVideo message handlers + +void DialogPDAEditVideo::Reset() +{ + preview.Clear(); + video.Clear(); + videoName.Clear(); + info.Clear(); + audio.Clear(); + + name.Clear(); +} + +void DialogPDAEditVideo::SetName(idStr& _name) +{ + name = _name; +} + +idStr& DialogPDAEditVideo::GetName() +{ + return name; +} + +void DialogPDAEditVideo::SetVideo(const idDeclVideo* _video) +{ + video = _video->GetRoq(); + audio = _video->GetWave(); + videoName = _video->GetVideoName(); + info = _video->GetInfo(); + preview = _video->GetPreview(); + + name = _video->GetName(); +} + +idStr DialogPDAEditVideo::GetDeclText() +{ + idStr escapedInfo = info; + escapedInfo.Replace("\n", "\\n"); + + idStr declText; + declText += "\n"; + declText += "video " + name + " {\n"; + declText += "\tname \t\t\"" + videoName + "\"\n"; + declText += "\tinfo \t\t\"" + escapedInfo + "\"\n"; + declText += "\tvideo \t\t\"" + video + "\"\n"; + declText += "\taudio\t\t\"" + audio + "\"\n"; + declText += "\tpreview\t\t\"" + preview + "\"\n"; + declText += "}"; + + return declText; +} + +} diff --git a/neo/tools/imgui/pdaeditor/PDAEditor.h b/neo/tools/imgui/pdaeditor/PDAEditor.h new file mode 100644 index 000000000..58ec7d168 --- /dev/null +++ b/neo/tools/imgui/pdaeditor/PDAEditor.h @@ -0,0 +1,204 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __PDAEDITOR_H__ +#define __PDAEDITOR_H__ + +#include "idlib/Dict.h" +#include "../../edit_public.h" + +class idDeclEmail; +class idDeclAudio; +class idDeclVideo; + +namespace ImGuiTools +{ + +///////////////////////////////////////////////////////////////////////////// +// CCDialogPDAEditor dialog + +class DialogPDAAdd { +public: + DialogPDAAdd(); + + void Reset(); + bool Draw(); + + idStr name; +}; + +class DialogPDAEditEmail { +public: + DialogPDAEditEmail(); // standard constructor + + void Reset(); + bool Draw(); + + void SetName( idStr& name ); + idStr & GetName(); + void SetEmail( const idDeclEmail* email ); + + idStr GetDeclText(); + +private: + idStr to; + idStr from; + idStr date; + idStr subject; + idStr body; + + idStr name; +}; + +class DialogPDAEditAudio { +public: + DialogPDAEditAudio(); + + void Reset(); + bool Draw(); + + void SetName( idStr &name ); + idStr & GetName(); + void SetAudio( const idDeclAudio *audio ); + + idStr GetDeclText(); + +private: + idStr wave; + idStr audioName; + idStr info; + idStr preview; + + idStr name; +}; + +class DialogPDAEditVideo { +public: + DialogPDAEditVideo(); + + void Reset(); + bool Draw(); + + void SetName( idStr &name ); + idStr & GetName(); + void SetVideo( const idDeclVideo *video ); + + idStr GetDeclText(); + +private: + idStr preview; + idStr video; + idStr videoName; + idStr info; + idStr audio; + + idStr name; +}; + +class PDAEditor { +public: + PDAEditor(); // standard constructor + + static PDAEditor& Instance(); + + void OnInitDialog(); + + void Reset(); + void Draw(); + + ID_INLINE void ShowIt(bool show) { + isShown = show; + } + ID_INLINE bool IsShown() { + return isShown; + } + +protected: + void OnMove( int x, int y ); + void OnDestroy(); + + void OnSelChangePDA( int index ); + + void OnBtnClickedSave(); + void OnBtnClickedRandom(); + + void OnBtnClickedPDAAdd(); + void OnBtnClickedPDADel(); + + void OnBtnClickedEmailAdd(); + void OnEmailAdd(); + void OnBtnClickedEmailEdit(); + void OnEmailEdit(); + void OnBtnClickedEmailDel(); + + void OnBtnClickedAudioAdd(); + void OnAudioAdd(); + void OnBtnClickedAudioEdit(); + void OnAudioEdit(); + void OnBtnClickedAudioDel(); + + void OnBtnClickedVideoAdd(); + void OnVideoAdd(); + void OnBtnClickedVideoEdit(); + void OnVideoEdit(); + void OnBtnClickedVideoDel(); + +private: + bool isShown; + + idStr windowTitle; + bool saveButtonEnabled; + + int pdaListSel; + idList pdaList; + int emailListSel; + idList emailList; + int audioListSel; + idList audioList; + int videoListSel; + idList videoList; + + idStr fullName; + idStr shortName; + idStr post; + idStr title; + idStr security; + idStr idnum; + + DialogPDAAdd addPDADlg; + DialogPDAEditEmail editEmailDlg; + DialogPDAEditAudio editAudioDlg; + DialogPDAEditVideo editVideoDlg; + +private: + void PopulatePDAList(); +}; + +} + +#endif /* !__PDAEDITOR_H__ */ diff --git a/neo/tools/imgui/scripteditor/ScriptEditor.cpp b/neo/tools/imgui/scripteditor/ScriptEditor.cpp new file mode 100644 index 000000000..46d951b4d --- /dev/null +++ b/neo/tools/imgui/scripteditor/ScriptEditor.cpp @@ -0,0 +1,374 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../libs/ImGuiColorTextEdit/TextEditor.h" + +#include "../util/ImGui_IdWidgets.h" + +#include "ScriptEditor.h" + +#include "framework/FileSystem.h" + +#include "sys/sys_imgui.h" + +namespace ImGuiTools { + +typedef struct scriptEventInfo_s { + idStr name; + idStr parms; + idStr help; +} scriptEventInfo_t; + +static idList scriptEvents; + +// ScriptEditor dialog + +ScriptEditor& ScriptEditor::Instance() +{ + static ScriptEditor instance; + return instance; +} + +ScriptEditor::ScriptEditor() + : isShown( false ) + , windowText() + , errorText() + , statusBarText( "Script Editor" ) + , scriptEdit() + , okButtonEnabled( false ) + , cancelButtonEnabled( true ) + , fileName() + , firstLine( 0 ) +{ + scriptEdit.Init(); +} + +void ScriptEditor::Reset() { + windowText = "Script Editor###ScriptEditor"; + errorText.Clear(); + statusBarText.Clear(); + scriptEdit.SetText( "" ); + //scriptEdit.SetTabSize( TAB_SIZE ); + okButtonEnabled = false; + cancelButtonEnabled = true; + fileName.Clear(); + firstLine = 0; + + UpdateStatusBar(); +} + +void ScriptEditor::Draw() +{ + bool showTool; + bool clickedNew = false, clickedSelect = false; + + showTool = isShown; + + if ( ImGui::Begin( windowText.c_str(), &showTool, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_MenuBar ) ) { + impl::SetReleaseToolMouse(true); + + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + if (ImGui::MenuItem("New", "Ctrl+N")) + { + clickedNew = true; + } + + if (ImGui::MenuItem("Open..", "Ctrl+O")) + { + clickedSelect = true; + } + + if (ImGui::MenuItem("Save", "Ctrl+S")) + { + OnBnClickedOk(); + } + + if (ImGui::MenuItem("Close", "Ctrl+W")) + { + showTool = false; + } + + ImGui::EndMenu(); + } + + ImGui::EndMenuBar(); + } + + if (clickedNew) { + } + if (clickedSelect) { + } + + scriptEdit.Draw(); + + okButtonEnabled = scriptEdit.IsEdited(); + + UpdateStatusBar(); + + ImGui::BeginDisabled( !okButtonEnabled ); + if ( ImGui::Button( "OK" ) ) { + OnBnClickedOk(); + } + ImGui::EndDisabled(); + ImGui::SameLine(); + ImGui::BeginDisabled( !cancelButtonEnabled ); + if ( ImGui::Button( "Cancel" ) ) { + showTool = false; + } + ImGui::EndDisabled(); + + ImGui::TextColored( ImVec4( 1, 0, 0, 1 ), "%s", errorText.c_str() ); + ImGui::TextUnformatted( statusBarText.c_str() ); + } + ImGui::End(); + + if ( isShown && !showTool ) + { + isShown = showTool; + impl::SetReleaseToolMouse( false ); + D3::ImGuiHooks::CloseWindow( D3::ImGuiHooks::D3_ImGuiWin_ScriptEditor ); + } +} + +/* +================ +ScriptEditor::UpdateStatusBar +================ +*/ +void ScriptEditor::UpdateStatusBar( void ) { + int line, column, character; + + scriptEdit.GetCursorPos( line, column, character ); + + statusBarText = idStr::Format( "Line: %d, Column: %d", line + 1, column + 1 ); +} + +/* +================ +ScriptEditor::InitScriptEvents +================ +*/ +void ScriptEditor::InitScriptEvents( void ) { + int index; + idParser src; + idToken token; + idStr whiteSpace; + scriptEventInfo_t info; + + if ( !src.LoadFile( "script/doom_events.script" ) ) { + return; + } + + scriptEvents.Clear(); + + while( src.ReadToken( &token ) ) { + if ( token == "scriptEvent" ) { + + src.GetLastWhiteSpace( whiteSpace ); + index = whiteSpace.Find( "//" ); + if ( index != -1 ) { + info.help = whiteSpace.Right( whiteSpace.Length() - index ); + info.help.Replace( "\r", "" ); + info.help.Replace( "\n", "\r\n" ); + } else { + info.help = ""; + } + + src.ExpectTokenType( TT_NAME, 0, &token ); + + info.parms = token; + + src.ExpectTokenType( TT_NAME, 0, &token ); + + info.name = token; + + src.ExpectTokenString( "(" ); + + info.parms += " " + info.name + "("; + while( src.ReadToken( &token ) && token != ";" ) { + info.parms.Append( " " + token ); + } + + scriptEvents.Append( info ); + } + } +} + +/* +================ +GetScriptEvents +================ +*/ +bool GetScriptEvents( const char *objectName, idList &listBox ) { + for ( int i = 0; i < scriptEvents.Num(); i++ ) { + listBox.Append( scriptEvents[i].name ); + } + return true; +} + +/* +================ +GetFunctionParms +================ +*/ +bool GetFunctionParms( const char *funcName, idStr &parmString ) { + for ( int i = 0; i < scriptEvents.Num(); i++ ) { + if ( scriptEvents[i].name.Cmp( funcName ) == 0 ) { + parmString = scriptEvents[i].parms; + return true; + } + } + return false; +} + +/* +================ +GetToolTip +================ +*/ +bool GetToolTip( const char *name, idStr &string ) { + for ( int i = 0; i < scriptEvents.Num(); i++ ) { + if ( scriptEvents[i].name.Cmp( name ) == 0 ) { + string = scriptEvents[i].help + scriptEvents[i].parms; + return true; + } + } + return false; +} + +/* +================ +ScriptEditor::OpenFile +================ +*/ +void ScriptEditor::OpenFile( const char *fileName ) { + int numLines = 0; + int numCharsPerLine = 0; + int maxCharsPerLine = 0; + idStr scriptText, extension; + void *buffer; + + scriptEdit.AllowPathNames( false ); + scriptEdit.SetText( "" ); + + idStr( fileName ).ExtractFileExtension( extension ); + + if ( extension.Icmp( "script" ) == 0 ) { + InitScriptEvents(); + + scriptEdit.SetCaseSensitive( true ); + scriptEdit.LoadKeyWordsFromFile( "editors/script.def" ); + scriptEdit.SetObjectMemberCallback( GetScriptEvents ); + scriptEdit.SetFunctionParmCallback( GetFunctionParms ); + scriptEdit.SetToolTipCallback( GetToolTip ); + + } else if ( extension.Icmp( "gui" ) == 0 ) { + + //scriptEdit.SetStringColor(SRE_COLOR_DARK_CYAN, SRE_COLOR_LIGHT_BROWN); + scriptEdit.SetCaseSensitive( false ); + scriptEdit.LoadKeyWordsFromFile( "editors/gui.def" ); + scriptEdit.SetObjectMemberCallback( NULL ); + scriptEdit.SetFunctionParmCallback( NULL ); + scriptEdit.SetToolTipCallback( NULL ); + } + + if ( fileSystem->ReadFile( fileName, &buffer ) == -1 ) { + errorText = "Unable to read the selected file"; + return; + } + scriptText = (char *) buffer; + fileSystem->FreeFile( buffer ); + + this->fileName = fileName; + + scriptEdit.SetText( scriptText.c_str() ); + + for( const char *ptr = scriptText.c_str(); *ptr; ptr++ ) { + if ( *ptr == '\r' ) { + if ( numCharsPerLine > maxCharsPerLine ) { + maxCharsPerLine = numCharsPerLine; + } + numCharsPerLine = 0; + numLines++; + } else if ( *ptr == '\t' ) { + numCharsPerLine += TAB_SIZE; + } else { + numCharsPerLine++; + } + } + + windowText = va( "Script Editor (%s)###ScriptEditor", fileName ); + + okButtonEnabled = false; + cancelButtonEnabled = true; + + UpdateStatusBar(); + + scriptEdit.SetFocus(); +} + +// ScriptEditor message handlers + +/* +================ +ScriptEditor::OnBnClickedOk +================ +*/ +void ScriptEditor::OnBnClickedOk() { + idStr scriptText; + + common->Printf( "Writing \'%s\'...\n", fileName.c_str() ); + + scriptEdit.GetText( scriptText ); + + if ( fileSystem->WriteFile( fileName, scriptText, scriptText.Length(), "fs_devpath" ) == -1 ) { + errorText = va( "Couldn't save: %s", fileName.c_str() ); + return; + } + + okButtonEnabled = false; +} + +/* +================ +ScriptEditor::OnBnClickedCancel +================ +*/ +void ScriptEditor::OnBnClickedCancel() { + if ( okButtonEnabled ) { + //if ( MessageBox( "Cancel changes?", "Cancel", MB_YESNO | MB_ICONQUESTION ) != IDYES ) { + return; + //} + } + //OnCancel(); +} + +} diff --git a/neo/tools/imgui/scripteditor/ScriptEditor.h b/neo/tools/imgui/scripteditor/ScriptEditor.h new file mode 100644 index 000000000..bf0ff70ba --- /dev/null +++ b/neo/tools/imgui/scripteditor/ScriptEditor.h @@ -0,0 +1,80 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __SCRIPTEDITOR_H__ +#define __SCRIPTEDITOR_H__ + +#include "../../edit_public.h" +#include "../util/SyntaxRichEditCtrl.h" + +namespace ImGuiTools +{ + +class SyntaxRichEditCtrl; + +class ScriptEditor { + +public: + ScriptEditor(); // standard constructor + + static ScriptEditor& Instance(); + + void OpenFile( const char *fileName ); + + void Reset(); + void Draw(); + + void ShowIt(bool show) { + isShown = show; + } + bool IsShown() { + return isShown; + } + +protected: + void OnBnClickedOk(); + void OnBnClickedCancel(); + +private: + bool isShown; + idStr windowText; + idStr errorText; + idStr statusBarText; + SyntaxRichEditCtrl scriptEdit; + bool okButtonEnabled; + bool cancelButtonEnabled; + idStr fileName; + int firstLine; + +private: + void InitScriptEvents( void ); + void UpdateStatusBar( void ); +}; + +} +#endif /* !_SCRIPTEDITOR_H__ */ diff --git a/neo/tools/imgui/util/ImGui_IdWidgets.cpp b/neo/tools/imgui/util/ImGui_IdWidgets.cpp new file mode 100644 index 000000000..ecb16b049 --- /dev/null +++ b/neo/tools/imgui/util/ImGui_IdWidgets.cpp @@ -0,0 +1,907 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "sys/sys_imgui.h" +#include "sys/platform.h" + +#include "idlib/math/Vector.h" + +#include "idlib/Heap.h" +#include "idlib/containers/StrList.h" +#include "ImGui_IdWidgets.h" + +#include "framework/FileSystem.h" + +#include "renderer/Material.h" + +static const char* bodyContentsNames[5] = +{ + "solid", + "body", + "corpse", + "playerclip", + "monsterclip" +}; + +static int contentMappingFlags[5] = +{ + CONTENTS_SOLID, + CONTENTS_BODY, + CONTENTS_CORPSE, + CONTENTS_PLAYERCLIP, + CONTENTS_MONSTERCLIP +}; + +namespace ImGuiTools +{ + +MultiSelectWidget::MultiSelectWidget( const char** aNames, int* contentMapping, int aNumEntries ) + : names( aNames ) + , contentMapping( contentMapping ) + , numEntries( aNumEntries ) + , selectables( nullptr ) +{ + selectables = ( bool* )Mem_Alloc( numEntries * sizeof( bool ) ); + memset( selectables, 0, numEntries * sizeof( bool ) ); +} + +MultiSelectWidget::~MultiSelectWidget() +{ + Mem_Free( selectables ); +} + +void MultiSelectWidget::Update( int index, bool value ) +{ + assert( index < numEntries ); + selectables[index] = value; +} + +void MultiSelectWidget::UpdateWithBitFlags( int bitFlags ) +{ + for( int i = 0; i < numEntries; i++ ) + { + Update( i, bitFlags & contentMapping[i] ); + } +} + +bool DoMultiSelect( MultiSelectWidget* widget, int* contents ) +{ + bool pressed = false; + for( int i = 0; i < 5; i++ ) + { + if( ImGui::Selectable( widget->names[i], &widget->selectables[i] ) ) + { + pressed = true; + if( widget->selectables[i] ) + { + *contents |= widget->contentMapping[i]; + } + else + { + *contents &= ~widget->contentMapping[i]; + } + } + } + + return pressed; +} + +void HelpMarker( const char* desc ) +{ + ImGui::TextDisabled( "(?)" ); + if( ImGui::IsItemHovered() ) + { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos( ImGui::GetFontSize() * 35.0f ); + ImGui::TextUnformatted( desc ); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } +} + +bool StringListItemGetter( void* data, int index, const char** outText ) +{ + idStrList* list = reinterpret_cast( data ); + assert( index < list->Num() ); + + *outText = ( *list )[index]; + return true; +} + +MultiSelectWidget MakePhysicsContentsSelector() +{ + return MultiSelectWidget( bodyContentsNames, contentMappingFlags, 5 ); +} + +ColorPicker::ColorPicker( const char *_label ) { + label = _label; + color.Set( 0, 0, 0, 1.0f ); +} + +bool ColorPicker::Button( const idVec4 &_color ) { + ImVec4 col = ImVec4( _color.x, _color.y, _color.z, _color.w ); + + if ( ImGui::ColorButton( label, col ) ) { + oldColor = _color; + ImGui::OpenPopup( label ); + return true; + } + + return false; +} + +bool ColorPicker::Draw() { + idStr realLabel; + bool isAccepted = false; + + if ( ImGui::BeginPopupModal( label, nullptr, ImGuiWindowFlags_AlwaysAutoResize ) ) { + realLabel = label; + realLabel += "Picker"; + + bool changed = ImGui::ColorPicker4( realLabel.c_str(), color.ToFloatPtr(), ImGuiColorEditFlags_AlphaBar, oldColor.ToFloatPtr()); + + if ( ImGui::Button( "OK" ) ) { + isAccepted = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if ( ImGui::Button( "Cancel" ) ) { + isAccepted = false; + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + + return isAccepted; +} + +DeclNewSelect::DeclNewSelect( declType_t _declType, const char *_directory, const char *_extension, const char *_label ) + : declType(_declType) + , directory(_directory) + , extension(_extension) + , label(_label) + , fileSelection(-1) + , files() + , fileName( "" ) + , name( "" ) + , errorText( "" ) + , dp( NULL ) + , state(DONE) +{ +} + +void DeclNewSelect::Start() { + files.Clear(); + + idFileList* names = fileSystem->ListFiles( directory, extension, true, true ); + for( int i = 0; i < names->GetNumFiles(); i++ ) + { + idStr file = names->GetFile( i ); + + file.StripPath(); + file.StripFileExtension(); + + files.Append( file ); + } + fileSystem->FreeFileList( names ); + + fileSelection = -1; + fileName.Clear(); + name.Clear(); + errorText.Clear(); + dp = NULL; + state = NAME; + + ImGui::OpenPopup( label ); +} + +bool DeclNewSelect::Draw() { + if ( state == DONE ) { + return false; + } + + bool accepted = false; + bool canceled = false; + + if ( ImGui::BeginPopupModal( label, nullptr, ImGuiWindowFlags_AlwaysAutoResize ) ) { + ImGui::TextColored( ImVec4( 1, 0, 0, 1 ), "%s", errorText.c_str() ); + + if ( ImGui::InputTextStr( "File Name", &fileName ) ) { + // nop + } + + if ( ImGui::BeginListBox( "Files##prtFileSelect" ) ) { + for( int i = 0; i < files.Num(); i++ ) + { + if ( fileName.Length() && files[i].Find( fileName.c_str(), false ) == -1 ) { + continue; + } + + bool selected = ( i == fileSelection ); + + ImGui::PushID( i ); + if ( ImGui::Selectable( files[i].c_str(), selected ) ) { + fileSelection = i; + fileName = files[fileSelection]; + } + if ( selected ) { + ImGui::SetItemDefaultFocus(); + } + ImGui::PopID(); + } + + ImGui::EndListBox(); + } + + if ( ImGui::InputTextStr( "Name", &name ) ) { + // nop + } + + if ( ImGui::Button( "OK" ) ) { + errorText.Clear(); + + if ( name.IsEmpty() ) { + errorText += "Please enter a name\n"; + accepted = false; + } + + idDecl *newDecl = const_cast( declManager->FindType( declType, name.c_str(), false ) ); + if( newDecl ) { + errorText += idStr::Format( "Decl %s already exists in %s. Please select a different name\n", name.c_str(), newDecl->GetFileName() ); + accepted = false; + } + + if ( errorText.IsEmpty() ) { + idStr fullName; + + fullName = directory; + fullName += fileName; + fullName += extension; + + // create it + dp = declManager->CreateNewDecl( declType, name.c_str(), fullName.c_str() ); + state = DONE; + + accepted = true; + ImGui::CloseCurrentPopup(); + } + } + ImGui::SameLine(); + if ( ImGui::Button( "Cancel" ) ) { + accepted = false; + state = DONE; + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + + return accepted; +} + +DeclSelect::DeclSelect( declType_t _declType, const char *_label ) + : declType(_declType) + , label(_label) + , listSel(-1) + , list() + , name( "" ) + , errorText( "" ) + , dp( NULL ) + , state(DONE) +{ +} + +void DeclSelect::Start( const char *_name ) { + list.Clear(); + for ( int i = 0; i < declManager->GetNumDecls( declType ); i++ ) { + const idDecl *idp = declManager->DeclByIndex( declType, i, false ); + list.Append( idp->GetName() ); + } + if ( _name ) { + name = _name; + listSel = list.FindIndex( name ); + } else { + name.Clear(); + listSel = -1; + } + + errorText.Clear(); + dp = NULL; + state = NAME; + + ImGui::OpenPopup( label ); +} + +bool DeclSelect::Draw() { + if ( state == DONE ) { + return false; + } + + bool accepted = false; + bool canceled = false; + + if ( ImGui::BeginPopupModal( label, nullptr, ImGuiWindowFlags_AlwaysAutoResize ) ) { + ImGui::TextColored( ImVec4( 1, 0, 0, 1 ), "%s", errorText.c_str() ); + + if ( ImGui::InputTextStr( "Name", &name ) ) { + // nop + } + + if ( ImGui::BeginListBox( "Decls##prtSystemSelect" ) ) { + for( int i = 0; i < list.Num(); i++ ) + { + if ( name.Length() && list[i].Find( name.c_str(), false ) == -1 ) { + continue; + } + + bool selected = ( i == listSel ); + + ImGui::PushID( i ); + if ( ImGui::Selectable( list[i].c_str(), selected ) ) { + listSel = i; + name = list[listSel]; + } + if ( selected ) { + ImGui::SetItemDefaultFocus(); + } + ImGui::PopID(); + } + + ImGui::EndListBox(); + } + + if ( ImGui::Button( "OK" ) ) { + errorText.Clear(); + + if ( name.IsEmpty() ) { + errorText += "Please enter a name or select a decl from the list\n"; + accepted = false; + } + + idDecl *decl = const_cast( declManager->FindType( declType, name.c_str(), false ) ); + if( !decl ) { + errorText += idStr::Format( "Decl %s does not exist. Please select a different decl\n", name.c_str() ); + accepted = false; + } + + if ( errorText.IsEmpty() ) { + dp = decl; + state = DONE; + + accepted = true; + ImGui::CloseCurrentPopup(); + } + } + ImGui::SameLine(); + if ( ImGui::Button( "Cancel" ) ) { + accepted = false; + state = DONE; + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + + return accepted; +} + +GoToLineDialog::GoToLineDialog() + : numberEdit(0) + , firstLine(0) + , lastLine(0) + , waiting(false) + , valid(false) + , focus(false) + , caption() +{ +} + +void GoToLineDialog::Start( int _firstLine, int _lastLine, int _line ) { + firstLine = _firstLine; + lastLine = _lastLine; + numberEdit = _line; + valid = ( idMath::ClampInt( firstLine, lastLine, numberEdit ) == numberEdit ); + waiting = true; + focus = true; + caption = va( "Line number (%d - %d)", firstLine, lastLine ); +} + +bool GoToLineDialog::Draw( const ImVec2 &pos, const ImVec2 &size ) { + bool accepted = false; + + if ( !waiting ) { + return accepted; + } + + ImGuiStyle& style = ImGui::GetStyle(); + float fieldWidth = 250.0f; + + float captionWidth = ImGui::CalcTextSize( caption.c_str() ).x; + + float windowHeight = + style.ChildBorderSize * 2.0f + + style.WindowPadding.y * 2.0f + + ImGui::GetFrameHeight() * 2.0f + + style.ItemSpacing.y; + + float windowWidth = + style.ChildBorderSize * 2.0f + + style.WindowPadding.x * 2.0f + + fieldWidth + style.ItemSpacing.x + + captionWidth; + + ImVec2 oldCursorPos = ImGui::GetCursorPos(); + + // TODO: this seems off, the dialog should be centered + ImGui::SetCursorPos(ImVec2( + pos.x + (size.x - windowWidth)*0.5f, + pos.y + (size.y - windowHeight)*0.5f)); + + if ( ImGui::BeginChild( "Go To Line", ImVec2( windowWidth, windowHeight ), ImGuiChildFlags_Borders ) ) { + ImGui::SetNextItemWidth( fieldWidth ); + if ( ImGui::InputInt( caption.c_str(), &numberEdit, 0, 0 ) ) { + valid = ( idMath::ClampInt( firstLine, lastLine, numberEdit ) == numberEdit ); + } + if ( focus ) { + ImGui::SetKeyboardFocusHere( -1 ); + focus = false; + } + + ImGui::BeginDisabled( !valid ); + if ( ImGui::Button( "OK" ) ) { + waiting = false; + accepted = true; + } + ImGui::EndDisabled(); + ImGui::SameLine(); + if ( ImGui::Button( "Cancel" ) ) { + waiting = false; + accepted = false; + } + } + ImGui::EndChild(); + ImGui::SetCursorPos( oldCursorPos ); + + return accepted; +} + +FindReplaceDialog::FindReplaceDialog() + : replace() + , find() + , matchCase(false) + , matchWhole(false) + , replacement(false) + , valid(false) + , visible(false) + , focus(false) +{ +} + +void FindReplaceDialog::Start( idStr &selection, bool _replacement ) { + if ( selection.Length() ) { + find = selection; + } + replace.Clear(); + replacement = _replacement; + valid = ( find.Length() > 0 ); + visible = true; + focus = true; +} + +FindReplaceDialog::command_t FindReplaceDialog::Draw( const ImVec2 &pos, const ImVec2 &size ) { + command_t command = command_t::NONE; + + if ( !visible ) { + return command; + } + + ImGuiStyle &style = ImGui::GetStyle(); + float fieldWidth = 250.0f; + + float replaceWidth = ImGui::CalcTextSize(" Next ").x + style.FramePadding.x * 2.0f; + float replaceAllWidth = ImGui::CalcTextSize(" All ").x + style.FramePadding.x * 2.0f; + float optionWidth = ImGui::CalcTextSize("Aa").x + style.FramePadding.x * 2.0f; + + float windowHeight = + style.ChildBorderSize * 3.0f + + style.WindowPadding.y * 3.0f + + ImGui::GetFrameHeight() * 3.0f + + style.ItemSpacing.y; + + float windowWidth = + style.ChildBorderSize * 2.0f + + style.WindowPadding.x * 2.0f + + fieldWidth + style.ItemSpacing.x + + replaceWidth + style.ItemSpacing.x + + replaceAllWidth + style.ItemSpacing.x; + + ImVec2 oldCursorPos = ImGui::GetCursorPos(); + + ImGui::SetCursorPos(ImVec2( + pos.x + size.x - windowWidth - style.ScrollbarSize - style.ItemSpacing.x, + pos.y + style.ItemSpacing.y * 2.0f)); + + if ( ImGui::BeginChild( "Find/Replace", ImVec2( windowWidth, windowHeight ), ImGuiChildFlags_Borders ) ) { + + ImGui::SetNextItemWidth( fieldWidth ); + + if ( ImGui::InputTextStr( "###Find", &find ) ) { + valid = ( find.Length() > 0 ); + } + if ( focus ) { + ImGui::SetKeyboardFocusHere( -1 ); + focus = false; + } + ImGui::SetItemTooltip( "Search term" ); + ImGui::SameLine(); + + ImGui::BeginDisabled( !valid ); + if ( ImGui::ArrowButton( "Next", ImGuiDir_Down ) ) { + command = command_t::FIND_NEXT; + } + ImGui::SetItemTooltip( "Find next occurrence" ); + ImGui::SameLine(); + if ( ImGui::ArrowButton( "Prev", ImGuiDir_Up ) ) { + command = command_t::FIND_PREV; + } + ImGui::SetItemTooltip( "Find previous occurrence" ); + ImGui::EndDisabled(); + + ImGui::SameLine(); + + if ( ImGui::ToggleButton( "R", &replacement, ImVec2( optionWidth, 0.0f ) ) ) { + + } + ImGui::SetItemTooltip( "Toggle to switch between find and replace modes" ); + + ImGui::SameLine(); + + if ( ImGui::Button( "x", ImVec2( optionWidth, 0.0f ) ) ) { + visible = false; + command = DONE; + } + + ImGui::SetNextItemWidth( fieldWidth ); + ImGui::BeginDisabled( !replacement ); + if ( ImGui::InputTextStr( "###Replace with", &replace ) ) { + } + ImGui::SetItemTooltip( "Replacement term" ); + ImGui::SameLine(); + if ( ImGui::Button( "Next###ReplaceNext" ) ) { + command = command_t::REPLACE_NEXT; + } + ImGui::SetItemTooltip( "Replace Next" ); + ImGui::SameLine(); + if ( ImGui::Button( "All" ) ) { + command = command_t::REPLACE_ALL; + } + ImGui::SetItemTooltip("Replace All"); + ImGui::EndDisabled(); + + if ( ImGui::ToggleButton( "Aa", &matchCase, ImVec2( optionWidth, 0.0f ) ) ) { + } + ImGui::SetItemTooltip( "Match case" ); + + ImGui::SameLine(); + + if ( ImGui::ToggleButton( "[]", &matchWhole, ImVec2( optionWidth, 0.0f ) ) ) { + } + ImGui::SetItemTooltip( "Match whole word" ); + } + ImGui::EndChild(); + + ImGui::SetCursorPos( oldCursorPos ); + + return command; +} + +MessageBoxDialog::MessageBoxDialog() + : message() + , choice(false) + , error(false) + , visible(false) + , acked(false) + , focus(false) +{ +} + +void MessageBoxDialog::Start( const char *_message, bool _choice, bool _error ) { + message = _message; + choice = _choice; + error = _error; + visible = true; + acked = false; + focus = true; +} + +bool MessageBoxDialog::Draw( const ImVec2 &pos, const ImVec2 &size ) { + if ( !visible ) { + return false; + } + + ImGuiStyle &style = ImGui::GetStyle(); + + ImVec2 textSize = ImGui::CalcTextSize( message.c_str() ); + + float windowHeight = + style.ChildBorderSize * 2.0f + + style.WindowPadding.y * 2.0f + + ImGui::GetFrameHeight() * 2.0f + + textSize.y; + + float windowWidth = + style.ChildBorderSize * 2.0f + + style.WindowPadding.x * 2.0f + + textSize.x; + + ImVec2 oldCursorPos = ImGui::GetCursorPos(); + + bool interacted = false; + + ImGui::SetCursorPos(ImVec2( + pos.x + size.x * 0.5f - windowWidth * 0.5f, + pos.y + size.y * 0.5f - windowHeight * 0.5f)); + + if ( ImGui::BeginChild( "Message", ImVec2( windowWidth, windowHeight ), ImGuiChildFlags_Borders ) ) { + if ( error ) { + ImGui::TextColored( ImVec4(1, 0, 0, 1), "%s", message.c_str() ); + } else { + ImGui::TextUnformatted( message.c_str() ); + } + + if ( focus ) { + ImGui::SetKeyboardFocusHere( -1 ); + focus = false; + } + + if ( choice ) { + if ( ImGui::Button( "Yes" ) ) { + acked = true; + interacted = true; + visible = false; + } + ImGui::SameLine(); + if ( ImGui::Button( "No" ) ) { + acked = false; + interacted = true; + visible = false; + } + } else { + if ( ImGui::Button( "OK" ) ) { + visible = false; + interacted = true; + acked = true; + } + } + } + ImGui::EndChild(); + + ImGui::SetCursorPos( oldCursorPos ); + + return interacted; +} + + +} //namespace ImGuiTools + + + +// our custom ImGui functions + +// like DragFloat3(), but with "X: ", "Y: " or "Z: " prepended to each display_format, for vectors +// if !ignoreLabelWidth, it makes sure the label also fits into the current item width. +// note that this screws up alignment with consecutive "value+label widgets" (like Drag* or ColorEdit*) +bool ImGui::DragVec3( const char* label, idVec3& v, float v_speed, + float v_min, float v_max, const char* display_format, float power, bool ignoreLabelWidth ) +{ + bool value_changed = false; + ImGui::BeginGroup(); + ImGui::PushID( label ); + + ImGuiStyle& style = ImGui::GetStyle(); + float wholeWidth = ImGui::CalcItemWidth() - 2.0f * style.ItemSpacing.x; + float spacing = style.ItemInnerSpacing.x; + float labelWidth = ignoreLabelWidth ? 0.0f : ( ImGui::CalcTextSize( label, NULL, true ).x + spacing ); + float coordWidth = ( wholeWidth - labelWidth - 2.0f * spacing ) * ( 1.0f / 3.0f ); // width of one x/y/z dragfloat + + ImGui::PushItemWidth( coordWidth ); + for( int i = 0; i < 3; i++ ) + { + ImGui::PushID( i ); + char format[64]; + idStr::snPrintf( format, sizeof( format ), "%c: %s", "XYZ"[i], display_format ); + value_changed |= ImGui::DragFloat( "##v", &v[i], v_speed, v_min, v_max, format, power ); + + ImGui::PopID(); + ImGui::SameLine( 0.0f, spacing ); + } + ImGui::PopItemWidth(); + ImGui::PopID(); + + const char* labelEnd = strstr( label, "##" ); + ImGui::TextUnformatted( label, labelEnd ); + + ImGui::EndGroup(); + + return value_changed; +} + +// shortcut for DragXYZ with ignorLabelWidth = false +// very similar, but adjusts width to width of label to make sure it's not cut off +// sometimes useful, but might not align with consecutive "value+label widgets" (like Drag* or ColorEdit*) +bool ImGui::DragVec3fitLabel( const char* label, idVec3& v, float v_speed, + float v_min, float v_max, const char* display_format, float power ) +{ + return ImGui::DragVec3( label, v, v_speed, v_min, v_max, display_format, power, false ); +} + +struct InputTextCallback_UserData +{ + idStr * Str; + ImGuiInputTextCallback ChainCallback; + void * ChainCallbackUserData; +}; + +static int InputTextCallback( ImGuiInputTextCallbackData *data ) +{ + InputTextCallback_UserData *user_data = ( InputTextCallback_UserData * )data->UserData; + + if ( data->EventFlag == ImGuiInputTextFlags_CallbackResize ) + { + // Resize string callback + // If for some reason we refuse the new length (BufTextLen) and/or capacity (BufSize) we need to set them back to what we want. + idStr *str = user_data->Str; + IM_ASSERT( data->Buf == str->c_str() ); + int length = data->BufTextLen > 0 ? data->BufTextLen : 1; + str->ReAllocate( length, true ); + data->Buf = ( char * )str->c_str(); + } + else if ( user_data->ChainCallback ) + { + // Forward to user callback, if any + data->UserData = user_data->ChainCallbackUserData; + return user_data->ChainCallback( data ); + } + return 0; +} + +bool ImGui::InputTextStr( const char *label, idStr *str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data ) { + IM_ASSERT( ( flags & ImGuiInputTextFlags_CallbackResize ) == 0 ); + flags |= ImGuiInputTextFlags_CallbackResize; + + InputTextCallback_UserData cb_user_data; + cb_user_data.Str = str; + cb_user_data.ChainCallback = callback; + cb_user_data.ChainCallbackUserData = user_data; + + bool result = ImGui::InputText( label, ( char * )str->c_str(), str->Length() + 1, flags, InputTextCallback, &cb_user_data ); + + // fix the length (characters get appended directly into the buffer allocated by idStr) + idStr tmp = str->c_str(); + *str = tmp; + + return result; +} + +bool ImGui::InputTextMultilineStr( const char *label, idStr *str, const ImVec2 &size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void *user_data ) { + IM_ASSERT( ( flags & ImGuiInputTextFlags_CallbackResize ) == 0 ); + flags |= ImGuiInputTextFlags_CallbackResize; + + InputTextCallback_UserData cb_user_data; + cb_user_data.Str = str; + cb_user_data.ChainCallback = callback; + cb_user_data.ChainCallbackUserData = user_data; + + bool result = ImGui::InputTextMultiline( label, ( char * )str->c_str(), str->Length() + 1, size, flags, InputTextCallback, &cb_user_data ); + + // fix the length (characters get appended directly into the buffer allocated by idStr) + idStr tmp = str->c_str(); + *str = tmp; + + return result; +} + +bool ImGui::InputDialogName( const char *text, const char *label, idStr *str ) { + bool accepted = false; + + if ( ImGui::BeginPopupModal( label, nullptr, ImGuiWindowFlags_AlwaysAutoResize ) ) { + ImGui::TextUnformatted( text ); + if ( ImGui::InputTextStr( "Name", str ) ) { + // nop + } + + if ( ImGui::Button( "OK" ) ) { + accepted = true; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + accepted = false; + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + + return accepted; +} + +bool ImGui::InputMessageBox( const char *text, const char* label, bool allowCancel ) { + bool accepted = false; + + ImGui::OpenPopup( label ); + + if ( ImGui::BeginPopupModal( label, nullptr, ImGuiWindowFlags_AlwaysAutoResize ) ) { + ImGui::TextUnformatted( text ); + + if ( ImGui::Button( "OK" ) ) { + accepted = true; + ImGui::CloseCurrentPopup(); + } + if ( allowCancel ) { + ImGui::SameLine(); + if ( ImGui::Button( "Cancel" ) ) { + accepted = false; + ImGui::CloseCurrentPopup(); + } + } + + ImGui::EndPopup(); + } + + return accepted; +} + +// NOTE: this code is adapted from https://github.com/goossens/ObjectTalk/blob/62977f72389a2bbdde4d2535faadad46ab2920a1/gfx/framework/OtUi.cpp#L202 +bool ImGui::ToggleButton( const char *label, bool *value, const ImVec2 &size ) { + bool changed = false; + ImVec4 *colors = ImGui::GetStyle().Colors; + + if ( *value ) { + ImGui::PushStyleColor( ImGuiCol_Button, colors[ImGuiCol_ButtonActive] ); + ImGui::PushStyleColor( ImGuiCol_ButtonHovered, colors[ImGuiCol_ButtonActive] ); + ImGui::PushStyleColor( ImGuiCol_ButtonActive, colors[ImGuiCol_TableBorderLight] ); + } else { + ImGui::PushStyleColor( ImGuiCol_Button, colors[ImGuiCol_TableBorderLight] ); + ImGui::PushStyleColor( ImGuiCol_ButtonHovered, colors[ImGuiCol_TableBorderLight] ); + ImGui::PushStyleColor( ImGuiCol_ButtonActive, colors[ImGuiCol_ButtonActive] ); + } + + ImGui::Button( label, size ); + + if ( ImGui::IsItemClicked( ImGuiMouseButton_Left ) ) { + *value = !*value; + changed = true; + } + + ImGui::PopStyleColor( 3 ); + + return changed; +} diff --git a/neo/tools/imgui/util/ImGui_IdWidgets.h b/neo/tools/imgui/util/ImGui_IdWidgets.h new file mode 100644 index 000000000..b217b4855 --- /dev/null +++ b/neo/tools/imgui/util/ImGui_IdWidgets.h @@ -0,0 +1,231 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. +Copyright (C) 2022 Stephen Pridham + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#pragma once + +#include "sys/sys_imgui.h" + +#include "idlib/Str.h" +#include "idlib/containers/List.h" +#include "idlib/math/Vector.h" + +#include "framework/Game.h" + +namespace ImGuiTools +{ + +/** +* Holds a list of booleans that correspond to a list of names. This +* class owns the list of bools, not the list of names. +*/ +class MultiSelectWidget +{ +public: + MultiSelectWidget( const char** aNames, int* contentMapping, int aNumEntries ); + ~MultiSelectWidget(); + + void Update( int index, bool value ); + + void UpdateWithBitFlags( int bitFlags ); + +public: + const char** names; + int* contentMapping; + int numEntries; + bool* selectables; +}; + +bool DoMultiSelect( MultiSelectWidget* widget, int* contents ); + +void HelpMarker( const char* desc ); + +bool StringListItemGetter( void* data, int index, const char** outText ); + +MultiSelectWidget MakePhysicsContentsSelector(); + +class ColorPicker { +public: + ColorPicker( const char *label ); + + bool Button( const idVec4 &_color ); + bool Draw(); + + ID_INLINE const idVec4& GetColor() { return color; } + ID_INLINE void SetColor( idVec4 &c ) { + color = oldColor = c; + } + +private: + const char * label; + idVec4 color; + idVec4 oldColor; +}; + +class DeclNewSelect { +public: + DeclNewSelect( declType_t declType, const char *directory, const char *extension, const char *label ); + + void Start(); + bool Draw(); + + ID_INLINE idDecl* GetDecl() { return dp; } + +private: + enum state_t { DONE = 0, NAME }; + + declType_t declType; + const char * directory; + const char * extension; + const char * label; + int fileSelection; + idList files; + idStr fileName; + idStr name; + idStr errorText; + idDecl * dp; + state_t state; +}; + +class DeclSelect { +public: + DeclSelect( declType_t declType, const char *label ); + + void Start( const char *name ); + bool Draw(); + + ID_INLINE idDecl* GetDecl() { return dp; } + +private: + enum state_t { DONE = 0, NAME }; + + declType_t declType; + const char * label; + int listSel; + idList list; + idStr name; + idStr errorText; + idDecl * dp; + state_t state; +}; + +class GoToLineDialog { +public: + GoToLineDialog(); + + void Start( int firstLine, int lastLine, int line ); + ID_INLINE int GetLine() const { return numberEdit; } + bool Draw( const ImVec2 &pos, const ImVec2 &size ); + +private: + + int numberEdit; + int firstLine; + int lastLine; + bool waiting; + bool valid; + bool focus; + idStr caption; +}; + +class FindReplaceDialog { +public: + FindReplaceDialog(); + + enum command_t { NONE, DONE, FIND_NEXT, FIND_PREV, FIND_ALL, REPLACE_NEXT, REPLACE_ALL }; + + void Start( idStr &selection, bool replacement ); + command_t Draw( const ImVec2 &pos, const ImVec2 &size ); + + ID_INLINE const idStr & GetReplaceString() const { return replace; }; // get replacement string + ID_INLINE const idStr & GetFindString() const { return find; }; // get find string + ID_INLINE bool MatchCase() const { return matchCase; }; // true if matching case + ID_INLINE bool MatchWholeWord() const { return matchWhole; }; // true if matching whole words only + +private: + idStr replace; + idStr find; + bool matchCase; + bool matchWhole; + bool replacement; + bool valid; + bool visible; + bool focus; +}; + +class MessageBoxDialog { +public: + MessageBoxDialog(); + + void Start( const char *message, bool choice, bool error ); + bool Draw( const ImVec2 &pos, const ImVec2 &size ); + + ID_INLINE bool Result() const { return acked; }; + +private: + idStr message; + bool choice; + bool error; + bool visible; + bool acked; + bool focus; +}; + +} //namespace ImGuiTools + + +// ##################################### +// Some ImGui Widgets (functions) that +// we put in the ImGui namespace +// ##################################### + +class idVec3; + +namespace ImGui { + +bool DragVec3( const char* label, idVec3& v, float v_speed = 1.0f, + float v_min = 0.0f, float v_max = 0.0f, + const char* display_format = "%.1f", + float power = 1.0f, bool ignoreLabelWidth = true ); + +bool DragVec3fitLabel( const char* label, idVec3& v, float v_speed = 1.0f, + float v_min = 0.0f, float v_max = 0.0f, + const char* display_format = "%.1f", float power = 1.0f ); + + +bool InputTextStr( const char* label, idStr* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr ); + +bool InputTextMultilineStr( const char* label, idStr* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr ); + +bool InputDialogName( const char *text, const char *label, idStr *str ); + +bool InputMessageBox( const char *text, const char* label, bool allowCancel = false ); + +bool ToggleButton( const char *label, bool *value, const ImVec2 &size ); + +} //namespace ImGui diff --git a/neo/tools/imgui/util/RegistryOptions.cpp b/neo/tools/imgui/util/RegistryOptions.cpp new file mode 100644 index 000000000..1b77ed5ac --- /dev/null +++ b/neo/tools/imgui/util/RegistryOptions.cpp @@ -0,0 +1,372 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "sys/sys_imgui.h" +#include "framework/FileSystem.h" +#include "idlib/Token.h" + +#include "RegistryOptions.h" + +namespace ImGuiTools { + +/* +================ +rvRegistryOptions::rvRegistryOptions + +Constructor +================ +*/ +rvRegistryOptions::rvRegistryOptions( void ) { +} + +/* +================ +rvRegistryOptions::Init +================ +*/ +void rvRegistryOptions::Init( const char *key ) { + mBaseKey = key; +} + +void OutputString( idFile *file, const char *string ) { + char *out; + int i, c; + + while ( 1 ) { + c = *string++; + switch( c ) { + case '\0': return; + case '\\': file->Printf( "\\\\" ); break; + case '\n': file->Printf( "\\n" ); break; + case '\r': file->Printf( "\\r" ); break; + case '\t': file->Printf( "\\t" ); break; + case '\v': file->Printf( "\\v" ); break; + case '"': file->Printf( "\\\"" ); break; + default: file->Printf( "%c", c ); break; + } + } +} + +/* +================ +rvRegistryOptions::Save + +Write the options to the registry +================ +*/ +bool rvRegistryOptions::Save ( void ) +{ + int i; + + idFile *file = fileSystem->OpenFileWrite( mBaseKey ); + if ( !file ) { + return false; + } + + file->Printf( "{\n" ); + + // Write out the values + for ( i = 0; i < mValues.GetNumKeyVals(); i ++ ) + { + const idKeyValue* key = mValues.GetKeyVal ( i ); + assert ( key ); + + file->Printf( "%s ", key->GetKey().c_str() ); + file->Printf( "\"" ); + OutputString( file, key->GetValue().c_str() ); + file->Printf( "\"\n" ); + } + + file->Printf( "}\n" ); + + // Write Recent Files + file->Printf( "{\n" ); + for ( i = 0; i < mRecentFiles.Num(); i ++ ) + { + file->Printf( "mru%d ", i ); + file->Printf( "\"%s\"", mRecentFiles[i].c_str() ); + file->Printf( "\n" ); + } + file->Printf( "}\n" ); + + file->Flush(); + fileSystem->CloseFile( file ); + + return true; +} + +/* +================ +rvRegistryOptions::Load + +Read the options from the registry +================ +*/ +bool rvRegistryOptions::Load ( void ) +{ + const char *buffer = NULL; + + mValues.Clear ( ); + mRecentFiles.Clear ( ); + + idLexer src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT ); + + int len = fileSystem->ReadFile( mBaseKey.c_str(), (void**)&buffer ); + if ( len <= 0 ) { + return false; + } + src.LoadMemory( buffer, strlen( buffer ), mBaseKey.c_str() ); + if ( !src.IsLoaded() ) { + return false; + } + + idToken tok, tok2; + src.ExpectTokenString( "{" ); + while ( src.ReadToken( &tok ) ) { + if ( tok == "}" ) { + break; + } + if ( src.ReadToken( &tok2 ) ) { + if ( tok2 == "}" ) { + break; + } + mValues.Set( tok.c_str(), tok2.c_str() ); + } + } + + if ( tok != "}" ) { + idLib::fileSystem->FreeFile( (void*)buffer ); + return false; + } + + while ( src.ReadToken( &tok ) ) { + if ( tok == "}" ) { + break; + } + if ( src.ReadToken( &tok2 ) ) { + if ( tok2 == "}" ) { + break; + } + AddRecentFile( tok2.c_str() ); + } + } + + idLib::fileSystem->FreeFile( (void*)buffer ); + + return true; +} + +/* +================ +rvRegistryOptions::SetWindowPlacement + +Set a window placement in the options +================ +*/ +void rvRegistryOptions::SetWindowPlacement ( const char* name, int hwnd ) +{ + /*WINDOWPLACEMENT wp; + + wp.length = sizeof(wp); + ::GetWindowPlacement ( hwnd, &wp ); + + idStr out; + + out = va("%d %d %d %d %d %d %d %d %d %d", + wp.flags, + wp.ptMaxPosition.x, + wp.ptMaxPosition.y, + wp.ptMinPosition.x, + wp.ptMinPosition.y, + wp.rcNormalPosition.left, + wp.rcNormalPosition.top, + wp.rcNormalPosition.right, + wp.rcNormalPosition.bottom, + wp.showCmd ); + + mValues.Set ( name, out );*/ +} + +/* +================ +rvRegistryOptions::GetWindowPlacement + +Retrieve a window placement from the options +================ +*/ +bool rvRegistryOptions::GetWindowPlacement ( const char* name, int hwnd ) +{/* + WINDOWPLACEMENT wp; + wp.length = sizeof(wp); + + const idKeyValue* key = mValues.FindKey ( name ); + if ( !key ) + { + return false; + } + + sscanf ( key->GetValue().c_str(), "%d %d %d %d %d %d %d %d %d %d", + &wp.flags, + &wp.ptMaxPosition.x, + &wp.ptMaxPosition.y, + &wp.ptMinPosition.x, + &wp.ptMinPosition.y, + &wp.rcNormalPosition.left, + &wp.rcNormalPosition.top, + &wp.rcNormalPosition.right, + &wp.rcNormalPosition.bottom, + &wp.showCmd ); + + ::SetWindowPlacement ( hwnd, &wp ); + + return true;*/ + return false; +} + +/* +================ +rvRegistryOptions::AddRecentFile + +Adds the given filename to the MRU list +================ +*/ +void rvRegistryOptions::AddRecentFile ( const char* filename ) +{ + int i; + + idStr path = filename; + + // Remove duplicates first + for ( i = mRecentFiles.Num() - 1; i >= 0; i -- ) + { + if ( !mRecentFiles[i].Icmp ( filename ) ) + { + mRecentFiles.RemoveIndex ( i ); + break; + } + } + + // Alwasy trip to the max MRU size + while ( mRecentFiles.Num ( ) >= MAX_MRU_SIZE ) + { + mRecentFiles.RemoveIndex ( 0 ); + } + + mRecentFiles.Append ( path ); +} + +/* +================ +rvRegistryOptions::SetColumnWidths + +Set a group of column widths in the options +================ +*/ +void rvRegistryOptions::SetColumnWidths ( const char* name, int list ) +{ + /*LVCOLUMN col; + int index; + idStr widths; + + col.mask = LVCF_WIDTH; + + for ( index = 0; ListView_GetColumn ( list, index, &col ); index ++ ) + { + widths += va("%d ", col.cx ); + } + + mValues.Set ( name, widths );*/ +} + +/* +================ +rvRegistryOptions::GetColumnWidths + +Retrieve a group of column widths from the options +================ +*/ +void rvRegistryOptions::GetColumnWidths ( const char* name, int list ) +{/* + idStr widths; + const char* parse; + const char* next; + int index; + + widths = mValues.GetString ( name ); + parse = widths; + index = 0; + + while ( NULL != (next = strchr ( parse, ' ' ) ) ) + { + int width; + + sscanf ( parse, "%d", &width ); + parse = next + 1; + + ListView_SetColumnWidth ( list, index++, width ); + }*/ +} + +/* +================ +rvRegistryOptions::SetBinary + +Set binary data for the given key +================ +*/ +void rvRegistryOptions::SetBinary ( const char* name, const unsigned char* data, int size ) +{ + idStr binary; + for ( size --; size >= 0; size --, data++ ) + { + binary += va("%02x", *data ); + } + + mValues.Set ( name, binary ); +} + +/* +================ +rvRegistryOptions::GetBinary + +Get the binary data for a given key +================ +*/ +void rvRegistryOptions::GetBinary ( const char* name, unsigned char* data, int size ) +{ + const char* parse; + parse = mValues.GetString ( name ); + for ( size --; size >= 0 && *parse && *(parse+1); size --, parse += 2, data ++ ) + { + int value; + sscanf ( parse, "%02x", &value ); + *data = (unsigned char)value; + } +} + +} diff --git a/neo/tools/imgui/util/RegistryOptions.h b/neo/tools/imgui/util/RegistryOptions.h new file mode 100644 index 000000000..f2b3bd183 --- /dev/null +++ b/neo/tools/imgui/util/RegistryOptions.h @@ -0,0 +1,151 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef REGISTRYOPTIONS_H_ +#define REGISTRYOPTIONS_H_ + +#include "idlib/Dict.h" +#include "idlib/math/Vector.h" + +namespace ImGuiTools { + +class rvRegistryOptions +{ +public: + + static const int MAX_MRU_SIZE = 4; + + rvRegistryOptions(); + + void Init( const char *key ); + + // Write the options to the registery + bool Save ( void ); + + // Read the options from the registry + bool Load ( void ); + + // Window placement routines + void SetWindowPlacement ( const char* name, int hwnd ); + bool GetWindowPlacement ( const char* name, int hwnd ); + + // List view column sizes + void SetColumnWidths ( const char* name, int list ); + void GetColumnWidths ( const char* name, int list ); + + // Set routines + void SetFloat ( const char* name, float v ); + void SetLong ( const char* name, long v ); + void SetBool ( const char* name, bool v ); + void SetString ( const char* name, const char* v ); + void SetVec4 ( const char* name, idVec4& v ); + void SetBinary ( const char* name, const unsigned char* data, int size ); + + // Get routines + float GetFloat ( const char* name ); + long GetLong ( const char* name ); + bool GetBool ( const char* name ); + const char* GetString ( const char* name ); + idVec4 GetVec4 ( const char* name ); + void GetBinary ( const char* name, unsigned char* data, int size ); + + // MRU related methods + void AddRecentFile ( const char* filename ); + const char* GetRecentFile ( int index ); + int GetRecentFileCount ( void ); + +private: + + idList mRecentFiles; + idDict mValues; + idStr mBaseKey; +}; + +ID_INLINE void rvRegistryOptions::SetFloat ( const char* name, float v ) +{ + mValues.SetFloat ( name, v ); +} + +ID_INLINE void rvRegistryOptions::SetLong ( const char* name, long v ) +{ + mValues.SetInt ( name, v ); +} + +ID_INLINE void rvRegistryOptions::SetBool ( const char* name, bool v ) +{ + mValues.SetBool ( name, v ); +} + +ID_INLINE void rvRegistryOptions::SetString ( const char* name, const char* v ) +{ + mValues.Set ( name, v ); +} + +ID_INLINE void rvRegistryOptions::SetVec4 ( const char* name, idVec4& v ) +{ + mValues.SetVec4 ( name, v ); +} + +ID_INLINE float rvRegistryOptions::GetFloat ( const char* name ) +{ + return mValues.GetFloat ( name ); +} + +ID_INLINE long rvRegistryOptions::GetLong ( const char* name ) +{ + return mValues.GetInt ( name ); +} + +ID_INLINE bool rvRegistryOptions::GetBool ( const char* name ) +{ + return mValues.GetBool ( name ); +} + +ID_INLINE const char* rvRegistryOptions::GetString ( const char* name ) +{ + return mValues.GetString ( name ); +} + +ID_INLINE idVec4 rvRegistryOptions::GetVec4 ( const char* name ) +{ + return mValues.GetVec4 ( name ); +} + +ID_INLINE int rvRegistryOptions::GetRecentFileCount ( void ) +{ + return mRecentFiles.Num ( ); +} + +ID_INLINE const char* rvRegistryOptions::GetRecentFile ( int index ) +{ + return mRecentFiles[index].c_str ( ); +} + +} + +#endif // REGISTRYOPTIONS_H_ diff --git a/neo/tools/imgui/util/SyntaxRichEditCtrl.cpp b/neo/tools/imgui/util/SyntaxRichEditCtrl.cpp new file mode 100644 index 000000000..1d650fb82 --- /dev/null +++ b/neo/tools/imgui/util/SyntaxRichEditCtrl.cpp @@ -0,0 +1,1115 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "../../libs/ImGuiColorTextEdit/TextEditor.h" + +#include "../util/ImGui_IdWidgets.h" + +#include "SyntaxRichEditCtrl.h" + +#include "framework/FileSystem.h" +#include "framework/Game.h" + +#include "sys/sys_imgui.h" + +namespace ImGuiTools { + +const int MOUSEWHEEL_DELTA = 120; + +const int AUTOCOMPLETE_WIDTH = 200; +const int AUTOCOMPLETE_HEIGHT = 180; +const int AUTOCOMPLETE_OFFSET = 16; + +const int FUNCPARMTOOLTIP_WIDTH = 16; +const int FUNCPARMTOOLTIP_HEIGHT = 20; +const int FUNCPARMTOOLTIP_OFFSET = 16; +/* +const COLORREF DEFAULT_BACK_COLOR = SRE_COLOR_WHITE; +const COLORREF INVALID_BACK_COLOR = SRE_COLOR_WHITE - 2; +const COLORREF MULTILINE_COMMENT_BACK_COLOR = SRE_COLOR_WHITE - 1; +*/ +//#define IDC_LISTBOX_AUTOCOMPLETE 700 +//#define IDC_EDITBOX_FUNCPARMS 701 + +void SyntaxRichEditCtrlKeyPress( void* data, bool ctrl, bool shift, bool alt, int chr ) { + reinterpret_cast(data)->OnChar(ctrl, shift, alt, chr); +} +bool SyntaxRichEditCtrlKeyDown( void *data ) { + return reinterpret_cast(data)->OnKeyDown(); +} +bool SyntaxRichEditMouseButtonDown( void *data ) { + return reinterpret_cast(data)->OnMouseButtonDown(); +} +bool SyntaxRichEditToolTipNotify( void *data, const char *ident, char *output, size_t outputLength ) { + return reinterpret_cast(data)->OnToolTipNotify( ident, output, outputLength ); +} + +static keyWord_t defaultKeyWords[] = { + { NULL, vec3_origin, "" } +}; + +/* +================ +SyntaxRichEditCtrl::SyntaxRichEditCtrl +================ +*/ +SyntaxRichEditCtrl::SyntaxRichEditCtrl( void ) + : scriptEdit( NULL ) + , scriptEditPos( 0.0f, 0.0f ) + , scriptEditSize( 400.0f, 400.0f ) + , errorText() + , findDlg() + , gotoDlg() + , msgBoxDlg() + , findStr() + , replaceStr() + , matchCase( false ) + , matchWholeWords( false ) + , searchForward( true ) + , firstLine( 0 ) +{ + //m_TextDoc = NULL; + keyWords = defaultKeyWords; + keyWordColors = NULL; + keyWordLengths = NULL; + caseSensitive = false; + allowPathNames = true; + keyWordAutoCompletion = true; + //updateRange.cpMin = 0; + //updateRange.cpMax = 0; + updateSyntaxHighlighting = true; + stringColorIndex = 0; + stringColorLine = -1; + autoCompleteStart = -1; + autoCompleteListBoxSel = -1; + autoCompleteLastKeyDownTime = 0; + autoCompleteInput.Clear(); + autoCompleteListBoxPos = ImVec2( 0.0f, 0.0f ); + autoCompleteListBoxSize = ImVec2( 0.0f, 0.0f ); + funcParmToolTipStart = -1; + funcParmToolTipPos = ImVec2( 0.0f, 0.0f ); + funcParmToolTipSize = ImVec2( 0.0f, 0.0f ); + GetObjectMembers = NULL; + GetFunctionParms = NULL; + GetToolTip = NULL; +} + +/* +================ +SyntaxRichEditCtrl::~SyntaxRichEditCtrl +================ +*/ +SyntaxRichEditCtrl::~SyntaxRichEditCtrl( void ) { + FreeKeyWordsFromFile(); +} + +/* +================ +SyntaxRichEditCtrl::SetCharType +================ +*/ +void SyntaxRichEditCtrl::SetCharType( int first, int last, int type ) { + for ( int i = first; i <= last; i++ ) { + charType[i] = type; + } +} + +/* +================ +SyntaxRichEditCtrl::InitSyntaxHighlighting +================ +*/ +void SyntaxRichEditCtrl::InitSyntaxHighlighting( void ) { + SetCharType( 0x00, 0xFF, CT_PUNCTUATION ); + SetCharType( '\0', ' ', CT_WHITESPACE ); + SetCharType( '/', '/', CT_COMMENT ); + SetCharType( '\"', '\"', CT_STRING ); + SetCharType( '\'', '\'', CT_LITERAL ); + SetCharType( 'a', 'z', CT_NAME ); + SetCharType( 'A', 'Z', CT_NAME ); + SetCharType( '_', '_', CT_NAME ); + SetCharType( '#', '#', CT_NAME ); + SetCharType( '0', '9', CT_NUMBER ); +} + +/* +================ +SyntaxRichEditCtrl::Init +================ +*/ +void SyntaxRichEditCtrl::Init( void ) { + scriptEdit = new TextEditor(); + scriptEdit->SetHandlers( this, SyntaxRichEditCtrlKeyPress, SyntaxRichEditCtrlKeyDown, SyntaxRichEditMouseButtonDown, SyntaxRichEditToolTipNotify ); + + InitSyntaxHighlighting(); + + SetFocus(); + + // create auto complete list box + autoCompleteListBoxPos = ImVec2( 0.0f, 0.0f ); + autoCompleteListBoxSize = ImVec2( AUTOCOMPLETE_WIDTH, AUTOCOMPLETE_HEIGHT ); + autoCompleteStart = -1; + + // create function parameter tool tip + funcParmToolTipPos = ImVec2( 0.0f, 0.0f ); + funcParmToolTipSize = ImVec2( FUNCPARMTOOLTIP_WIDTH, FUNCPARMTOOLTIP_HEIGHT ); + funcParmToolTipStart = -1; +} + +void SyntaxRichEditCtrl::Draw() +{ + bool clickedNew = false, clickedSelect = false; + + scriptEditPos = ImGui::GetCursorPos(); + scriptEditSize = ImVec2( 800, 600 ); + + scriptEdit->Render( "Text", scriptEditSize, false ); + + if ( ImGui::Button( "Go to" ) ) { + OnEditGoToLine(); + } + ImGui::SameLine(); + if ( ImGui::Button( "Find" ) ) { + idStr selText = scriptEdit->GetSelectedText().c_str(); + + findDlg.Start( selText, false ); + } + ImGui::SameLine(); + if ( ImGui::Button( "Replace" ) ) { + idStr selText = scriptEdit->GetSelectedText().c_str(); + + findDlg.Start( selText, true ); + } + + if ( gotoDlg.Draw( scriptEditPos, scriptEditSize ) ) { + TextEditor::Coordinates coords( gotoDlg.GetLine() - 1 - firstLine, 0 ); + + scriptEdit->SetCursorPosition( coords ); + SetFocus(); + } + FindReplaceDialog::command_t findReplaceResult = findDlg.Draw( scriptEditPos, scriptEditSize ); + OnFindDialogMessage( findReplaceResult ); + + if ( msgBoxDlg.Draw( scriptEditPos, scriptEditSize ) ) { + SetFocus(); + } + + if ( autoCompleteStart >= 0 ) { + ImVec2 oldCursorPos = ImGui::GetCursorPos(); + ImVec2 newPos = ImVec2( scriptEditPos.x + autoCompleteListBoxPos.x, scriptEditPos.y + autoCompleteListBoxPos.y ); + + ImGui::SetCursorPos( newPos ); + + if ( ImGui::BeginChild( "###AutoCompleteListBox", autoCompleteListBoxSize, ImGuiChildFlags_Borders ) ) { + for ( int i = 0; i < autoCompleteListBoxFiltered.Num(); i++ ) { + bool selected = ( i == autoCompleteListBoxSel ); + + ImGui::PushID( i ); + if ( ImGui::Selectable( autoCompleteListBox[autoCompleteListBoxFiltered[i]].c_str(), selected ) ) { + // steal focus back from the auto-complete list box + SetFocus(); + + // insert text + autoCompleteListBoxSel = i; + AutoCompleteInsertText(); + AutoCompleteHide(); + } + ImGui::SetItemKeyOwner( ImGuiKey_MouseWheelY ); + if ( selected ) { + ImGui::SetItemDefaultFocus(); + ImGui::SetScrollHereY(); + } + ImGui::PopID(); + } + } + ImGui::EndChild(); + ImGui::SetCursorPos( oldCursorPos ); + } + if ( funcParmToolTipStart >= 0 ) { + ImVec2 oldCursorPos = ImGui::GetCursorPos(); + ImVec2 newPos = ImVec2( scriptEditPos.x + funcParmToolTipPos.x, scriptEditPos.y + funcParmToolTipPos.y ); + + ImGui::SetCursorPos( newPos ); + + if ( ImGui::BeginChild( "###FuncParmToolTip", funcParmToolTipSize ) ) { + ImGui::TextUnformatted( funcParmToolTip.c_str() ); + } + ImGui::EndChild(); + ImGui::SetCursorPos( oldCursorPos ); + } + + ImGui::TextColored( ImVec4( 1, 0, 0, 1 ), "%s", errorText.c_str() ); +} + + +/* +================ +SyntaxRichEditCtrl::FindKeyWord +================ +*/ +ID_INLINE int SyntaxRichEditCtrl::FindKeyWord( const char *keyWord, int length ) const { + int i, hash; + + if ( caseSensitive ) { + hash = idStr::Hash( keyWord, length ); + } else { + hash = idStr::IHash( keyWord, length ); + } + for ( i = keyWordHash.First( hash ); i != -1; i = keyWordHash.Next( i ) ) { + if ( length != keyWordLengths[i] ) { + continue; + } + if ( caseSensitive ) { + if ( idStr::Cmpn( keyWords[i].keyWord, keyWord, length ) != 0 ) { + continue; + } + } else { + if ( idStr::Icmpn( keyWords[i].keyWord, keyWord, length ) != 0 ) { + continue; + } + } + return i; + } + return -1; +} + +/* +================ +SyntaxRichEditCtrl::SetKeyWords +================ +*/ +void SyntaxRichEditCtrl::SetKeyWords( const keyWord_t kws[] ) { + int i, numKeyWords, hash; + + keyWords = kws; + + for ( numKeyWords = 0; keyWords[numKeyWords].keyWord; numKeyWords++ ) { + } + + delete keyWordColors; + keyWordColors = new idVec3[numKeyWords]; + + for ( i = 0; i < numKeyWords; i++ ) { + keyWordColors[i] = keyWords[i].color; + } + + delete keyWordLengths; + keyWordLengths = new int[numKeyWords]; + + for ( i = 0; i < numKeyWords; i++ ) { + keyWordLengths[i] = idStr::Length( keyWords[i].keyWord ); + } + + keyWordHash.Clear( 1024, 1024 ); + for ( i = 0; i < numKeyWords; i++ ) { + if ( caseSensitive ) { + hash = idStr::Hash( keyWords[i].keyWord, keyWordLengths[i] ); + } else { + hash = idStr::IHash( keyWords[i].keyWord, keyWordLengths[i] ); + } + keyWordHash.Add( hash, i ); + } + + TextEditor::LanguageDefinition langDef; + + for ( i = 0; i < numKeyWords; i++ ) { + if ( keyWords[i].description ) { + TextEditor::Identifier id; + id.mDeclaration = keyWords[i].description; + langDef.mIdentifiers.insert( std::make_pair( std::string( keyWords[i].keyWord ), id ) ); + } else { + langDef.mKeywords.insert( std::string( keyWords[i].keyWord ) ); + } + } + + langDef.mTokenize = TextEditor::LanguageDefinition::CPlusPlus().mTokenize; + + langDef.mCommentStart = "/*"; + langDef.mCommentEnd = "*/"; + langDef.mSingleLineComment = "//"; + + langDef.mCaseSensitive = caseSensitive; + langDef.mAutoIndentation = true; + + langDef.mName = "doomscript"; // TODO: change? + + scriptEdit->SetLanguageDefinition( langDef ); +} + +/* +================ +SyntaxRichEditCtrl::LoadKeyWordsFromFile +================ +*/ +bool SyntaxRichEditCtrl::LoadKeyWordsFromFile( const char *fileName ) { + idParser src; + idToken token, name, description; + byte red, green, blue; + keyWord_t keyword; + + if ( !src.LoadFile( fileName ) ) { + return false; + } + + FreeKeyWordsFromFile(); + + while( src.ReadToken( &token ) ) { + if ( token.Icmp( "keywords" ) == 0 ) { + src.ExpectTokenString( "{" ); + while( src.ReadToken( &token ) ) { + if ( token == "}" ) { + break; + } + if ( token == "{" ) { + + // parse name + src.ExpectTokenType( TT_STRING, 0, &name ); + src.ExpectTokenString( "," ); + + // parse color + src.ExpectTokenString( "(" ); + src.ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ); + red = token.GetIntValue(); + src.ExpectTokenString( "," ); + src.ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ); + green = token.GetIntValue(); + src.ExpectTokenString( "," ); + src.ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ); + blue = token.GetIntValue(); + src.ExpectTokenString( ")" ); + src.ExpectTokenString( "," ); + + // parse description + src.ExpectTokenType( TT_STRING, 0, &description ); + src.ExpectTokenString( "}" ); + + keyword.keyWord = Mem_CopyString( name ); + keyword.color = idVec3( red / 255.0f, green / 255.0f, blue / 255.0f ); + keyword.description = Mem_CopyString( description ); + + keyWordsFromFile.Append( keyword ); + } + } + } else { + src.SkipBracedSection(); + } + } + + keyword.keyWord = NULL; + keyword.color = idVec3( 1.0f, 1.0f, 1.0f ); + keyword.description = NULL; + keyWordsFromFile.Append( keyword ); + + SetKeyWords( keyWordsFromFile.Ptr() ); + + return true; +} + +/* +================ +SyntaxRichEditCtrl::FreeKeyWordsFromFile +================ +*/ +void SyntaxRichEditCtrl::FreeKeyWordsFromFile( void ) { + for ( int i = 0; i < keyWordsFromFile.Num(); i++ ) { + Mem_Free( const_cast( keyWordsFromFile[i].keyWord ) ); + Mem_Free( const_cast( keyWordsFromFile[i].description ) ); + } + keyWordsFromFile.Clear(); +} + +/* +================ +SyntaxRichEditCtrl::SetStringColor +================ +*/ +void SyntaxRichEditCtrl::SetStringColor( const idVec3 &color, const idVec3 &altColor ) { + stringColor[0] = color; + if ( altColor == vec3_origin ) { + stringColor[1] = color; + } else { + stringColor[1] = altColor; + } +} + +/* +================ +SyntaxRichEditCtrl::SetObjectMemberCallback +================ +*/ +void SyntaxRichEditCtrl::SetObjectMemberCallback( objectMemberCallback_t callback ) { + GetObjectMembers = callback; +} + +/* +================ +SyntaxRichEditCtrl::SetFunctionParmCallback +================ +*/ +void SyntaxRichEditCtrl::SetFunctionParmCallback( toolTipCallback_t callback ) { + GetFunctionParms = callback; +} + +/* +================ +SyntaxRichEditCtrl::SetToolTipCallback +================ +*/ +void SyntaxRichEditCtrl::SetToolTipCallback( toolTipCallback_t callback ) { + GetToolTip = callback; +} + +/* +================ +SyntaxRichEditCtrl::SetCaseSensitive +================ +*/ +void SyntaxRichEditCtrl::SetCaseSensitive( bool caseSensitive ) { + this->caseSensitive = caseSensitive; +} + +/* +================ +SyntaxRichEditCtrl::AllowPathNames +================ +*/ +void SyntaxRichEditCtrl::AllowPathNames( bool allow ) { + allowPathNames = allow; +} + +/* +================ +SyntaxRichEditCtrl::EnableKeyWordAutoCompletion +================ +*/ +void SyntaxRichEditCtrl::EnableKeyWordAutoCompletion( bool enable ) { + keyWordAutoCompletion = enable; +} + +/* +================ +SyntaxRichEditCtrl::GetCursorPos +================ +*/ +void SyntaxRichEditCtrl::GetCursorPos( int &line, int &column, int &character ) const { + TextEditor::Coordinates coords = scriptEdit->GetCursorPosition(); + line = coords.mLine; + column = coords.mColumn; + character = 0; +} + +/* +================ +SyntaxRichEditCtrl::GetText +================ +*/ +void SyntaxRichEditCtrl::GetText( idStr &text ) const { + text = scriptEdit->GetText().c_str(); +} + +/* +================ +SyntaxRichEditCtrl::SetText +================ +*/ +void SyntaxRichEditCtrl::SetText( const char *text ) { + scriptEdit->SetText( std::string( text ) ); +} + +/* +================ +SyntaxRichEditCtrl::SetReadOnly +================ +*/ +void SyntaxRichEditCtrl::SetReadOnly( bool readOnly ) { + scriptEdit->SetReadOnly( readOnly ); +} + +/* +================ +SyntaxRichEditCtrl::GetReadOnly +================ +*/ +bool SyntaxRichEditCtrl::GetReadOnly() { + return scriptEdit->IsReadOnly(); +} + +/* +================ +SyntaxRichEditCtrl::IsEdited +================ +*/ +bool SyntaxRichEditCtrl::IsEdited() const { + return scriptEdit->CanUndo(); +} + +bool SyntaxRichEditCtrl::CanCopy() { + return scriptEdit->HasSelection(); +} + +void SyntaxRichEditCtrl::Copy() { + scriptEdit->Copy(); +} + +bool SyntaxRichEditCtrl::CanCut() { + return !scriptEdit->IsReadOnly() && scriptEdit->HasSelection(); +} + +void SyntaxRichEditCtrl::Cut() { + scriptEdit->Cut(); +} + +bool SyntaxRichEditCtrl::CanPaste() { + const char *clipboardText = ImGui::GetClipboardText(); + + return !scriptEdit->IsReadOnly() && clipboardText && *clipboardText; +} + +void SyntaxRichEditCtrl::Paste() { + scriptEdit->Paste(); +} + +bool SyntaxRichEditCtrl::CanUndo() { + return scriptEdit->CanUndo(); +} + +void SyntaxRichEditCtrl::Undo() { + scriptEdit->Undo(); +} + +bool SyntaxRichEditCtrl::CanRedo() { + return scriptEdit->CanRedo(); +} + +void SyntaxRichEditCtrl::Redo() { + scriptEdit->Redo(); +} + +bool SyntaxRichEditCtrl::CanDelete() { + return !scriptEdit->IsReadOnly(); +} + +void SyntaxRichEditCtrl::Delete() { + scriptEdit->Delete(); +} + + + +/* +================ +SyntaxRichEditCtrl::SetFocus +================ +*/ +void SyntaxRichEditCtrl::SetFocus() { + scriptEdit->Focus(); +} + +/* +================ +SyntaxRichEditCtrl::AutoCompleteInsertText +================ +*/ +void SyntaxRichEditCtrl::AutoCompleteInsertText( void ) { + TextEditor::Coordinates sel; + int index; + + index = autoCompleteListBoxSel; + if ( index >= 0 && index < autoCompleteListBoxFiltered.Num() ) { + sel = scriptEdit->GetCursorPosition(); + idStr text = autoCompleteListBox[autoCompleteListBoxFiltered[index]]; + + TextEditor::Coordinates autoCompleteCoords = TextEditor::Coordinates( sel.mLine, autoCompleteStart ); + scriptEdit->SetSelection( autoCompleteCoords, sel ); + scriptEdit->ReplaceSelection( text.c_str() ); + } +} + +/* +================ +SyntaxRichEditCtrl::AutoCompleteUpdate +================ +*/ +void SyntaxRichEditCtrl::AutoCompleteUpdate() { + TextEditor::Coordinates sel, autoCompleteCoords; + int index = -1; + + sel = scriptEdit->GetCursorPosition(); + + autoCompleteCoords = TextEditor::Coordinates( sel.mLine, autoCompleteStart ); + autoCompleteInput = scriptEdit->GetText( autoCompleteCoords, sel ).c_str(); + + int i, num; + + num = autoCompleteListBox.Num(); + autoCompleteListBoxFiltered.Clear(); + for( i = 0; i < num; i++ ) { + if ( !autoCompleteInput.Length() || autoCompleteListBox[ i ].Find( autoCompleteInput.c_str(), false ) >= 0 ) { + autoCompleteListBoxFiltered.Append(i); + if ( index == -1 ) { + index = autoCompleteListBoxFiltered.Num() - 1; + } + } + } + + autoCompleteListBoxSel = index; +} + +/* +================ +SyntaxRichEditCtrl::AutoCompleteShow +================ +*/ +void SyntaxRichEditCtrl::AutoCompleteShow( int columnIndex ) { + ImVec2 point; + float top, left; + + if ( !autoCompleteListBox.Num() ) { + return; + } + + autoCompleteStart = columnIndex; + point = scriptEdit->GetCursorScreenCoordinates(); + + if ( point.y + AUTOCOMPLETE_HEIGHT + AUTOCOMPLETE_OFFSET < scriptEditSize.y ) { + top = point.y + AUTOCOMPLETE_OFFSET; + } else { + top = point.y - AUTOCOMPLETE_HEIGHT - AUTOCOMPLETE_OFFSET; + } + left = point.x; + autoCompleteListBoxPos = ImVec2( left, top ); + autoCompleteListBoxSize = ImVec2( AUTOCOMPLETE_WIDTH, AUTOCOMPLETE_HEIGHT ); + + AutoCompleteUpdate(); +} + +/* +================ +SyntaxRichEditCtrl::AutoCompleteHide +================ +*/ +void SyntaxRichEditCtrl::AutoCompleteHide( void ) { + autoCompleteStart = -1; + autoCompleteInput.Clear(); +} + +/* +================ +SyntaxRichEditCtrl::ToolTipShow +================ +*/ +void SyntaxRichEditCtrl::ToolTipShow( int charIndex, const char *string ) { + ImVec2 point; + float top, left; + + funcParmToolTipStart = charIndex; + funcParmToolTip = string; + // set tooltip size based on text size + float sizeX = ImGui::GetFont()->CalcTextSizeA( ImGui::GetFontSize(), FLT_MAX, -1.0f, funcParmToolTip.c_str(), nullptr, nullptr).x; + float sizeY = Min( (float)FUNCPARMTOOLTIP_HEIGHT, ImGui::GetFont()->CalcTextSizeA(ImGui::GetFontSize(), FLT_MAX, -1.0f, " ", nullptr, nullptr).y ); + point = scriptEdit->GetCursorScreenCoordinates(); + + if ( point.y + sizeY + FUNCPARMTOOLTIP_OFFSET < scriptEditSize.y ) { + top = point.y + FUNCPARMTOOLTIP_OFFSET; + } else { + top = point.y - sizeY - FUNCPARMTOOLTIP_OFFSET; + } + left = point.x; + funcParmToolTipPos = ImVec2( left, top ); + funcParmToolTipSize = ImVec2( sizeX, sizeY ); +} + +/* +================ +SyntaxRichEditCtrl::ToolTipHide +================ +*/ +void SyntaxRichEditCtrl::ToolTipHide( void ) { + funcParmToolTipStart = -1; +} + +/* +================ +SyntaxRichEditCtrl::GetNameBeforeCurrentSelection +================ +*/ +bool SyntaxRichEditCtrl::GetNameBeforeCurrentSelection( idStr &name, int &charIndex ) const { + idStr buffer = scriptEdit->GetWordBeforeCursor().c_str(); + + if ( buffer.Length() > 0 ) { + name = buffer; + charIndex = scriptEdit->GetCursorPosition().mColumn; + return true; + } + + return false; +} + +/* +================ +SyntaxRichEditCtrl::OnToolTipNotify +================ +*/ +bool SyntaxRichEditCtrl::OnToolTipNotify( const char *ident, char *toolTip, size_t toolTipSize ) { + idStr str; + idStr name = ident; + + toolTip[0] = '\0'; + if ( GetToolTip == NULL || !GetToolTip( name.c_str(), str)) { + + int keyWordIndex = FindKeyWord( name.c_str(), name.Length() ); + + if ( keyWordIndex != -1 && keyWords[keyWordIndex].description[0] != '\0' ) { + str = keyWords[keyWordIndex].description; + } else { + str = name.c_str(); + } + } + idStr::Copynz( toolTip, str.c_str(), toolTipSize ); + + return str.Length() > 0; +} + +/* +================ +SyntaxRichEditCtrl::OnEditGoToLine +================ +*/ +void SyntaxRichEditCtrl::OnEditGoToLine() { + TextEditor::Coordinates coords = scriptEdit->GetCursorPosition(); + + gotoDlg.Start( firstLine + 1, firstLine + scriptEdit->GetTotalLines(), coords.mLine + 1 ); +} + +/* +================ +SyntaxRichEditCtrl::OnEditFindNext +================ +*/ +void SyntaxRichEditCtrl::OnEditFindNext() { + if ( scriptEdit->FindNext( findStr.c_str(), matchCase, matchWholeWords, searchForward ) ) { + SetFocus(); + } else { + msgBoxDlg.Start( "The specified text was not found.", false, false ); + } + +} + +/* +================ +SyntaxRichEditCtrl::OnFindDialogMessage +================ +*/ +void SyntaxRichEditCtrl::OnFindDialogMessage( FindReplaceDialog::command_t command ) { + switch ( command ) { + case FindReplaceDialog::command_t::FIND_NEXT: + case FindReplaceDialog::command_t::FIND_PREV: + { + findStr = findDlg.GetFindString(); + matchCase = findDlg.MatchCase(); + matchWholeWords = findDlg.MatchWholeWord(); + searchForward = ( command == FindReplaceDialog::command_t::FIND_NEXT ); + OnEditFindNext(); + break; + } + case FindReplaceDialog::command_t::FIND_ALL: + break; + case FindReplaceDialog::command_t::REPLACE_NEXT: + { + replaceStr = findDlg.GetReplaceString(); + idStr selection = scriptEdit->GetSelectedText().c_str(); + + if ( selection.Length() && selection == findStr ) { + scriptEdit->ReplaceSelection( replaceStr.c_str() ); + } + + findStr = findDlg.GetFindString(); + matchCase = findDlg.MatchCase(); + matchWholeWords = findDlg.MatchWholeWord(); + searchForward = true; + OnEditFindNext(); + break; + } + case FindReplaceDialog::command_t::REPLACE_ALL: + { + replaceStr = findDlg.GetReplaceString(); + findStr = findDlg.GetFindString(); + matchCase = findDlg.MatchCase(); + matchWholeWords = findDlg.MatchWholeWord(); + + int numReplaces = scriptEdit->ReplaceAll( findStr.c_str(), replaceStr.c_str(), matchCase, matchWholeWords ); + if (numReplaces == 0) { + msgBoxDlg.Start( "The specified text was not found.", false, false ); + } else { + msgBoxDlg.Start( va( "Replaced %d occurances.", numReplaces ), false, false ); + } + break; + } + case FindReplaceDialog::command_t::DONE: + SetFocus(); + break; + case FindReplaceDialog::command_t::NONE: + default: + break; + } +} + +/* +================ +SyntaxRichEditCtrl::OnKeyDown +================ +*/ +bool SyntaxRichEditCtrl::OnKeyDown() { + + if ( autoCompleteStart >= 0 ) { + int timeEnd = Sys_Milliseconds(); + int elapsed = timeEnd - autoCompleteLastKeyDownTime; + int sel; + int keydownTime = 200; + + if ( ImGui::IsKeyDown( ImGuiKey_UpArrow ) ) { + if ( elapsed > keydownTime ) { + sel = Max( 0, autoCompleteListBoxSel - 1 ); + autoCompleteListBoxSel = sel; + autoCompleteLastKeyDownTime = timeEnd; + } + return true; + } + if ( ImGui::IsKeyDown( ImGuiKey_DownArrow ) ) { + if ( elapsed > keydownTime ) { + sel = Min( autoCompleteListBoxFiltered.Num() - 1, autoCompleteListBoxSel + 1 ); + autoCompleteListBoxSel = sel; + autoCompleteLastKeyDownTime = timeEnd; + } + return true; + } + if ( ImGui::IsKeyDown( ImGuiKey_PageUp ) ) { + if ( elapsed > keydownTime ) { + sel = Max( 0, autoCompleteListBoxSel - 10 ); + autoCompleteListBoxSel = sel; + autoCompleteLastKeyDownTime = timeEnd; + } + return true; + } + if ( ImGui::IsKeyDown( ImGuiKey_PageDown ) ) { + if ( elapsed > keydownTime ) { + sel = Min( autoCompleteListBoxFiltered.Num() - 1, autoCompleteListBoxSel + 10 ); + autoCompleteListBoxSel = sel; + autoCompleteLastKeyDownTime = timeEnd; + } + return true; + } + if ( ImGui::IsKeyDown( ImGuiKey_Home ) ) { + if ( elapsed > keydownTime ) { + autoCompleteListBoxSel = 0; + autoCompleteLastKeyDownTime = timeEnd; + } + return true; + } + if ( ImGui::IsKeyDown( ImGuiKey_End ) ) { + if ( elapsed > keydownTime ) { + autoCompleteListBoxSel = autoCompleteListBoxFiltered.Num() - 1; + autoCompleteLastKeyDownTime = timeEnd; + } + return true; + } + if ( ImGui::IsKeyDown( ImGuiKey_Enter ) || ImGui::IsKeyDown( ImGuiKey_Tab ) ) { + if ( elapsed > keydownTime ) { + AutoCompleteInsertText(); + AutoCompleteHide(); + autoCompleteLastKeyDownTime = timeEnd; + } + return true; + } + if ( ImGui::IsKeyDown( ImGuiKey_Escape ) ) { + if ( elapsed > keydownTime ) { + AutoCompleteHide(); + autoCompleteLastKeyDownTime = timeEnd; + } + return true; + } + if ( ImGui::IsKeyDown( ImGuiKey_LeftArrow ) + || ImGui::IsKeyDown( ImGuiKey_RightArrow ) + || ImGui::IsKeyDown( ImGuiKey_Insert ) + || ImGui::IsKeyDown( ImGuiKey_Delete ) ) { + if ( elapsed > keydownTime ) { + autoCompleteLastKeyDownTime = timeEnd; + } + return true; + } + } + + if ( ImGui::IsKeyChordPressed( ImGuiKey_ModCtrl | ImGuiKey_G ) ) { + OnEditGoToLine(); + + return true; + } else if ( ImGui::IsKeyChordPressed( ImGuiKey_ModCtrl | ImGuiKey_F ) ) { + idStr selText = scriptEdit->GetSelectedText().c_str(); + + findDlg.Start( selText, false ); + + return true; + } else if ( ImGui::IsKeyChordPressed( ImGuiKey_ModCtrl | ImGuiKey_H ) ) { + idStr selText = scriptEdit->GetSelectedText().c_str(); + + findDlg.Start( selText, true ); + + return true; + } + + return false; +} + +/* +================ +SyntaxRichEditCtrl::OnChar +================ +*/ +void SyntaxRichEditCtrl::OnChar( bool ctrl, bool shift, bool alt, int nChar ) { + // if the auto-complete list box is up + if ( autoCompleteStart >= 0 ) { + TextEditor::Coordinates sel; + + if ( charType[nChar] == CT_NAME ) { + AutoCompleteUpdate(); + return; + } else if ( nChar == 0x08/*backspace*/) { + sel = scriptEdit->GetCursorPosition(); + if ( sel.mColumn > autoCompleteStart ) { + AutoCompleteUpdate(); + } else { + AutoCompleteHide(); + } + return; + } else { + AutoCompleteHide(); + } + } + + // if the function parameter tool tip is up + if ( funcParmToolTipStart >= 0 ) { + TextEditor::Coordinates sel; + + if ( nChar == ')' || nChar == 0x1b/*escape*/ ) { + ToolTipHide(); + } else if ( nChar == 0x08/*backspace*/ ) { + sel = scriptEdit->GetCursorPosition(); + if ( sel.mColumn < funcParmToolTipStart ) { + ToolTipHide(); + } + } + } + + // show keyword auto-completion + if ( keyWordAutoCompletion && charType[nChar] == CT_NAME && funcParmToolTipStart < 0 ) { + TextEditor::Coordinates sel; + int line, column, length, i; + + sel = scriptEdit->GetCursorPosition(); + + line = sel.mLine; + column = sel.mColumn; + length = scriptEdit->GetCurrentLineText().length(); + + if ( column <= 1 || scriptEdit->PeekLeftIsWhiteSpace( 2 ) ) { + if ( column >= length-1 || scriptEdit->PeekLeftIsWhiteSpace( 0 ) ) { + + autoCompleteListBox.Clear(); + for ( i = 0; keyWords[i].keyWord; i++ ) { + autoCompleteListBox.Append( keyWords[i].keyWord ); + } + autoCompleteListBox.Sort(); + AutoCompleteShow( sel.mColumn + 1 ); + } + } + return; + } + + // show object member auto-completion + if ( nChar == '.' && GetObjectMembers && funcParmToolTipStart < 0 ) { + int charIndex; + idStr name; + + if ( GetNameBeforeCurrentSelection( name, charIndex ) ) { + autoCompleteListBox.Clear(); + if ( GetObjectMembers( name, autoCompleteListBox ) ) { + AutoCompleteShow( charIndex ); + } + } + return; + } + + // show function parameter tool tip + if ( nChar == '(' && GetFunctionParms ) { + int charIndex; + idStr name; + + if ( GetNameBeforeCurrentSelection( name, charIndex ) ) { + idStr parmString; + if ( GetFunctionParms( name, parmString ) ) { + ToolTipShow( charIndex, parmString ); + } + } + return; + } +} + +/* +================ +SyntaxRichEditCtrl::OnMouseButtonDown +================ +*/ +bool SyntaxRichEditCtrl::OnMouseButtonDown() { + + if ( ImGui::IsMouseClicked( ImGuiMouseButton_Left ) && autoCompleteStart >= 0 ) { + AutoCompleteHide(); + return true; + } + + return false; +} + +/* +================ +SyntaxRichEditCtrl::OnMouseWheel +================ +*/ +void SyntaxRichEditCtrl::OnMouseWheel( float wheel ) { + if ( autoCompleteStart >= 0 ) { + int sel; + + if ( wheel > 0 ) { + sel = Max( 0, autoCompleteListBoxSel - (int)( wheel / MOUSEWHEEL_DELTA ) ); + } else { + sel = Min( autoCompleteListBoxFiltered.Num() - 1, autoCompleteListBoxSel - (int)( wheel / MOUSEWHEEL_DELTA ) ); + } + autoCompleteListBoxSel = sel; + return; + } +} + +} diff --git a/neo/tools/imgui/util/SyntaxRichEditCtrl.h b/neo/tools/imgui/util/SyntaxRichEditCtrl.h new file mode 100644 index 000000000..385273623 --- /dev/null +++ b/neo/tools/imgui/util/SyntaxRichEditCtrl.h @@ -0,0 +1,232 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __SYNTAXRICHEDITCTR_H__ +#define __SYNTAXRICHEDITCTR_H__ + +/* +=============================================================================== + + Rich Edit Control with: + + - syntax highlighting + - braced section highlighting + - braced section auto-indentation + - multi-line tabs + - keyword auto-completion + - object member auto-completion + - keyword tool tip + - function parameter tool tip + +=============================================================================== +*/ + +#include "../../edit_public.h" + +#include "idlib/containers/StrList.h" + +class TextEditor; + +namespace ImGuiTools { + +static const int TAB_SIZE = 4; + +static const idVec3 SRE_COLOR_BLACK = idVec3(0.0f, 0.0f, 0.0f); +static const idVec3 SRE_COLOR_WHITE = idVec3(1.0f, 1.0f, 1.0f); +static const idVec3 SRE_COLOR_RED = idVec3(1.0f, 0.0f, 0.0f); +static const idVec3 SRE_COLOR_GREEN = idVec3(0.0f, 1.0f, 0.0f); +static const idVec3 SRE_COLOR_BLUE = idVec3(0.0f, 0.0f, 1.0f); +static const idVec3 SRE_COLOR_YELLOW = idVec3(1.0f, 1.0f, 0.0f); +static const idVec3 SRE_COLOR_MAGENTA = idVec3(1.0f, 0.0f, 1.0f); +static const idVec3 SRE_COLOR_CYAN = idVec3(0.0f, 1.0f, 1.0f); +static const idVec3 SRE_COLOR_ORANGE = idVec3(1.0f, 0.5f, 0.0f); +static const idVec3 SRE_COLOR_PURPLE = idVec3(0.59f, 0.0f, 0.59f); +static const idVec3 SRE_COLOR_PINK = idVec3(0.73f, 0.4f, 0.48f); +static const idVec3 SRE_COLOR_GREY = idVec3(0.33f, 0.33f, 0.33f); +static const idVec3 SRE_COLOR_BROWN = idVec3(0.4f, 0.35f, 0.07f); +static const idVec3 SRE_COLOR_LIGHT_GREY = idVec3(0.66f, 0.66f, 0.66f); +static const idVec3 SRE_COLOR_LIGHT_BROWN = idVec3(0.66f, 0.59f, 0.08f); +static const idVec3 SRE_COLOR_DARK_GREEN = idVec3(0.0f, 0.5f, 0.0f); +static const idVec3 SRE_COLOR_DARK_CYAN = idVec3(0.0f, 0.59f, 0.59f); +static const idVec3 SRE_COLOR_DARK_YELLOW = idVec3(0.86f, 0.78f, 0.07f); + +typedef struct { + const char * keyWord; + idVec3 color; + const char * description; +} keyWord_t; + +typedef bool ( *objectMemberCallback_t )( const char* objectName, idStrList &listBox ); +typedef bool ( *toolTipCallback_t )( const char* name, idStr &string ); + +class SyntaxRichEditCtrl { +public: + SyntaxRichEditCtrl( void ); + ~SyntaxRichEditCtrl( void ); + + void Init( void ); + void Draw( void ); + + void SetCaseSensitive( bool caseSensitive ); + void AllowPathNames( bool allow ); + void EnableKeyWordAutoCompletion( bool enable ); + void SetKeyWords( const keyWord_t kws[] ); + bool LoadKeyWordsFromFile( const char *fileName ); + void SetObjectMemberCallback( objectMemberCallback_t callback ); + void SetFunctionParmCallback( toolTipCallback_t callback ); + void SetToolTipCallback( toolTipCallback_t callback ); + + void SetStringColor( const idVec3 &color, const idVec3 &altColor = vec3_origin ); + + void GetCursorPos( int &line, int &column, int &character ) const; + + void GetText( idStr &text ) const; + void SetText( const char *text ); + + void SetReadOnly( bool readOnly ); + bool GetReadOnly(); + + bool CanCopy(); + void Copy(); + bool CanCut(); + void Cut(); + bool CanPaste(); + void Paste(); + bool CanUndo(); + void Undo(); + bool CanRedo(); + void Redo(); + bool CanDelete(); + void Delete(); + + bool IsEdited() const; + + void SetFocus(); + +public: + bool OnToolTipNotify( const char *ident, char *tooltip, size_t tooltipSize ); + void OnChar( bool ctrl, bool shift, bool alt, int nChar ); + bool OnKeyDown(); + bool OnMouseButtonDown(); +private: + void OnMouseWheel(float wheel); + + void OnEditGoToLine(); + void OnEditFindNext(); + void OnFindDialogMessage( FindReplaceDialog::command_t command ); + +private: + + TextEditor * scriptEdit; + ImVec2 scriptEditPos; + ImVec2 scriptEditSize; + idStr errorText; + FindReplaceDialog findDlg; + GoToLineDialog gotoDlg; + MessageBoxDialog msgBoxDlg; + idStr findStr; + idStr replaceStr; + bool matchCase; + bool matchWholeWords; + bool searchForward; + int firstLine; + + // settings + //CHARFORMAT2 defaultCharFormat; + idVec3 defaultColor; + idVec3 singleLineCommentColor; + idVec3 multiLineCommentColor; + idVec3 stringColor[2]; + idVec3 literalColor; + idVec3 braceHighlightColor; + + typedef enum { + CT_WHITESPACE, + CT_COMMENT, + CT_STRING, + CT_LITERAL, + CT_NUMBER, + CT_NAME, + CT_PUNCTUATION + } charType_t; + + int charType[256]; + + idList keyWordsFromFile; + const keyWord_t * keyWords; + int * keyWordLengths; + idVec3 * keyWordColors; + idHashIndex keyWordHash; + + bool caseSensitive; + bool allowPathNames; + bool keyWordAutoCompletion; + + objectMemberCallback_t GetObjectMembers; + toolTipCallback_t GetFunctionParms; + toolTipCallback_t GetToolTip; + + bool updateSyntaxHighlighting; + int stringColorIndex; + int stringColorLine; + + int autoCompleteStart; + ImVec2 autoCompleteListBoxPos; + ImVec2 autoCompleteListBoxSize; + idStrList autoCompleteListBox; + idList autoCompleteListBoxFiltered; + int autoCompleteListBoxSel; + idStr autoCompleteInput; + int autoCompleteLastKeyDownTime; + + int funcParmToolTipStart; + ImVec2 funcParmToolTipPos; + ImVec2 funcParmToolTipSize; + idStr funcParmToolTip; + +private: + void InitSyntaxHighlighting( void ); + void SetCharType( int first, int last, int type ); + + void FreeKeyWordsFromFile( void ); + int FindKeyWord( const char *keyWord, int length ) const; + + bool GetNameBeforeCurrentSelection( idStr &name, int &charIndex ) const; + + void AutoCompleteInsertText( void ); + void AutoCompleteUpdate( void ); + void AutoCompleteShow( int columnIndex ); + void AutoCompleteHide( void ); + + void ToolTipShow( int charIndex, const char *string ); + void ToolTipHide( void ); +}; + +} + +#endif /* !__SYNTAXRICHEDITCTR_H__ */ diff --git a/neo/tools/imgui/util/TreeCtrl.cpp b/neo/tools/imgui/util/TreeCtrl.cpp new file mode 100644 index 000000000..0694fe295 --- /dev/null +++ b/neo/tools/imgui/util/TreeCtrl.cpp @@ -0,0 +1,280 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "ImGui_IdWidgets.h" +#include "imgui.h" + +#include "TreeCtrl.h" + +namespace ImGuiTools { + +void TreeCtrl::DeleteAllItems() { + DeleteAllItemsOfNode( root ); + numNodes = 1; +} + +void TreeCtrl::DeleteItem( TreeNode* item ) { + if ( !item ) { + return; + } + if ( item == root ) { + DeleteAllItems(); + return; + } + + DeleteAllItemsOfNode( item ); + SelectItem( NULL ); // TODO: figure out what to do with it? +} + +TreeNode *TreeCtrl::GetRootItem() const { + return const_cast( root ); +} + +void TreeCtrl::SetItemText( TreeNode *item, const char *text ) { + assert( item ); + item->SetLabel( text ); +} + +const idStr& TreeCtrl::GetItemText( TreeNode *item ) const { + assert( item ); + return item->GetLabel(); +} + +TreeNode *TreeCtrl::GetChildItem( TreeNode *item ) const { + if ( item ) { + return item->GetNode().GetChild(); + } + return NULL; +} + +TreeNode *TreeCtrl::GetParentItem( TreeNode *item ) const { + if ( item ) { + TreeNode *parent = item->GetNode().GetParent(); + return ( parent != root ) ? parent : NULL; + } + return NULL; +} + +TreeNode *TreeCtrl::GetNextSiblingItem( TreeNode *item ) const { + if ( item ) { + return item->GetNode().GetSibling(); + } + return NULL; +} + +TreeNode *TreeCtrl::GetSelectedItem() const { + return selectedItem; +} + +void TreeCtrl::SelectItem( TreeNode *item ) { + selectedItem = item; + UpdatePathToSelectedItem(); +} + +int SortTreeNodeByLabel( TreeNode * const *a, TreeNode * const *b ) { + return (*a)->GetLabel().Icmp( (*b)->GetLabel() ); +} + +void TreeCtrl::SortChildren( TreeNode *item ) { + TreeNode *child = GetChildItem( item ); + if ( !child ) { + return; + } + idList list; + + while ( child ) { + list.Append( child ); + child = GetNextSiblingItem( child ); + } + list.Sort( SortTreeNodeByLabel ); + + int i, n = list.Num(); + for ( i = 0; i < n; i++ ) { + list[i]->GetNode().ParentTo( item->GetNode() ); + } +} + +void TreeCtrl::EditLabel( TreeNode *item ) { + // TODO: implement +} + +TreeNode *TreeCtrl::InsertItem( const idStr &name, TreeNode *parent, TreeNode *after ) { + if ( !parent && name.IsEmpty() ) { + // do not insert the root node again + return root; + } + TreeNode *newNode = nodeAllocator.Alloc(); + int id = numNodes++; + newNode->Init( id ); + newNode->SetLabel( name ); + if ( after ) { + newNode->GetNode().MakeSiblingAfter( after->GetNode() ); + } else if ( parent ) { + newNode->GetNode().ParentTo( parent->GetNode() ); + } else { + newNode->GetNode().ParentTo( root->GetNode() ); + } + + return newNode; +} + +void TreeCtrl::SetItemData( TreeNode *item, int data ) { + assert( item ); + item->SetItem( data ); +} + +int TreeCtrl::GetItemData( TreeNode *item ) const { + assert( item ); + return item->GetItem(); +} + +void TreeCtrl::Draw( treeItemTooltip_t tooltip, treeItemSelected_t selected, treeItemContextMenu_t contextMenu, treeItemBeginDrag_t beginDrag, treeItemEndDrag_t endDrag, treeItemInput_t input, void *data ) { + TreeNode *parentNode = GetRootItem(); + TreeNode *node; + + for ( node = GetChildItem( parentNode ) ; node ; node = GetNextSiblingItem( node ) ) { + DrawNode( node, tooltip, selected, contextMenu, beginDrag, endDrag, input, data ); + } +} + +void TreeCtrl::UpdatePathToSelectedItem() { + pathToSelectedItem.Clear(); + if ( !selectedItem ) { + return; + } + + TreeNode *node = selectedItem; + while ( node ) { + pathToSelectedItem.Insert( node->GetID() ); + node = GetParentItem( node ); + } +} + +void TreeCtrl::DrawNode( TreeNode *node, treeItemTooltip_t tooltip, treeItemSelected_t selected, treeItemContextMenu_t contextMenu, treeItemBeginDrag_t beginDrag, treeItemEndDrag_t endDrag, treeItemInput_t input, void *data ) { + TreeNode * item; + TreeNode * childItem; + ImGuiTreeNodeFlags flags = 0; + idStr tooltipText; + bool scrollToNode = false; + bool openNode = false; + + childItem = GetChildItem( node ); + + if ( node == selectedItem ) { + flags |= ImGuiTreeNodeFlags_Selected; + scrollToNode = true; + } + if ( !childItem ) { + flags |= ImGuiTreeNodeFlags_Leaf; + flags |= ImGuiTreeNodeFlags_NoTreePushOnOpen; + } else { + if ( pathToSelectedItem.Num() && pathToSelectedItem[0] == node->GetID() ) { + openNode = true; + flags |= ImGuiTreeNodeFlags_DefaultOpen; + pathToSelectedItem.RemoveIndex( 0 ); + } + } + + input( data, true, node ); + + if ( openNode ) { + ImGui::SetNextItemOpen( openNode ); + } + + bool opened = ImGui::TreeNodeEx( static_cast(node), flags, "%s", node->GetLabel().c_str() ); + if ( scrollToNode ) { + ImGui::SetScrollHereY(); + } + contextMenu( data, node ); + if ( opened ) { + if ( ImGui::IsItemClicked( ImGuiMouseButton_Left ) && !ImGui::IsItemToggledOpen() ) { + SelectItem( node ); + selected( data, false ); + } + if ( ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked( ImGuiMouseButton_Left ) ) { + SelectItem( node ); + selected( data, true ); + } + if ( ImGui::IsItemHovered() ) { + tooltipText.Clear(); + if ( tooltip( data, node, tooltipText ) ) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted( tooltipText.c_str() ); + ImGui::EndTooltip(); + } + } + if ( ImGui::IsItemFocused() ) { + input( data, false, node ); + } + if ( ImGui::BeginDragDropSource( ImGuiDragDropFlags_None ) ) + { + ImGui::SetDragDropPayload( "TreeCtrl", node, sizeof( TreeNode * ) ); + + // Display preview + ImGui::Text( "%s", node->GetLabel().c_str() ); + ImGui::EndDragDropSource(); + beginDrag( data, node ); + } + if ( ImGui::BeginDragDropTarget() ) + { + if ( const ImGuiPayload* payload = ImGui::AcceptDragDropPayload( "TreeCtrl" ) ) + { + IM_ASSERT( payload->DataSize == sizeof( TreeNode * ) ); + TreeNode *dropSource = ( TreeNode * )payload->Data; + endDrag( data, dropSource, node ); + } + ImGui::EndDragDropTarget(); + } + + for ( item = childItem; item; item = GetNextSiblingItem( item ) ) { + DrawNode( item, tooltip, selected, contextMenu, beginDrag, endDrag, input, data ); + } + + if ( childItem ) { + ImGui::TreePop(); + } + } +} + +void TreeCtrl::DeleteAllItemsOfNode( TreeNode *node ) { + if ( !node ) { + return; + } + + TreeNode *child = GetChildItem( node ); + while ( child ) { + DeleteAllItemsOfNode( child ); + child = GetChildItem( node ); + } + if ( node != root ) { + node->Shutdown(); + nodeAllocator.Free( node ); + } +} + +} diff --git a/neo/tools/imgui/util/TreeCtrl.h b/neo/tools/imgui/util/TreeCtrl.h new file mode 100644 index 000000000..d3a0fcf5f --- /dev/null +++ b/neo/tools/imgui/util/TreeCtrl.h @@ -0,0 +1,170 @@ +/* +=========================================================================== + +Doom 3 GPL Source Code +Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. + +This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). + +Doom 3 Source Code is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Doom 3 Source Code is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Doom 3 Source Code. If not, see . + +In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#ifndef __TREECTRL_H__ +#define __TREECTRL_H__ + +#include "idlib/Str.h" +#include "idlib/Heap.h" +#include "idlib/containers/Hierarchy.h" +#include "idlib/containers/List.h" + +namespace ImGuiTools { + +/* +=============================================================================== + + Tree Control for path names. + +=============================================================================== +*/ + +class TreeNode { +public: + TreeNode() + : label() + , item(0) + , node() + , id(-1) + , imageId(-1) + { + + } + virtual ~TreeNode() { + Shutdown(); + } + + void Init(int _id) + { + label.Clear(); + item = 0; + node.SetOwner(this); + id = _id; + imageId = -1; + } + + virtual void Shutdown() + { + node.RemoveFromParent(); + label.Clear(); + id = 0; + imageId = -1; + } + + ID_INLINE int GetItem() { + return item; + } + ID_INLINE void SetItem( int _item ) { + item = _item; + } + ID_INLINE void SetLabel( const char *_label ) { + label = _label; + } + ID_INLINE const idStr & GetLabel() const { + return label; + } + ID_INLINE idHierarchy& GetNode() { + return node; + } + ID_INLINE int GetID() { + return id; + } + ID_INLINE void SetImageID( int _imageId ) { + imageId = _imageId; + } + ID_INLINE int GetImageID() { + return imageId; + } + +private: + idStr label; + int item; + idHierarchy node; + int id; + int imageId; +}; + +typedef bool (*treeItemCompare_t)( void *data, TreeNode *item, const char *name ); +typedef bool (*treeItemTooltip_t)( void *data, TreeNode *item, idStr &tooltipText ); +typedef void (*treeItemSelected_t)( void *data, bool doubleClick ); +typedef void (*treeItemContextMenu_t)( void *data, TreeNode *item ); +typedef void (*treeItemBeginDrag_t)( void *data, TreeNode *source ); +typedef void (*treeItemEndDrag_t)( void *data, TreeNode *source, TreeNode *destination ); +typedef void (*treeItemInput_t)( void *data, bool prepare, TreeNode *item ); + +class TreeCtrl { +public: + TreeCtrl() + : nodeAllocator() + , root() + , selectedItem(NULL) + , pathToSelectedItem() + , numNodes(0) + { + root = nodeAllocator.Alloc(); + root->Init(0); + numNodes++; + } + virtual ~TreeCtrl() { + DeleteAllItems(); + pathToSelectedItem.Clear(); + } + + void DeleteAllItems(); + void DeleteItem( TreeNode *item ); + TreeNode * GetRootItem() const; + void SetItemText( TreeNode *item, const char *text ); + const idStr& GetItemText( TreeNode *item ) const; + TreeNode * GetParentItem( TreeNode *item ) const; + TreeNode * GetChildItem( TreeNode *item ) const; + TreeNode * GetNextSiblingItem( TreeNode *item ) const; + TreeNode * InsertItem( const idStr &name, TreeNode *parent = NULL, TreeNode *after = NULL ); + void SetItemData( TreeNode *item, int data ); + int GetItemData( TreeNode *item ) const; + TreeNode * GetSelectedItem() const; + void SelectItem( TreeNode *item ); + void SortChildren( TreeNode *item ); + void EditLabel( TreeNode *item ); + + void Draw( treeItemTooltip_t tooltip, treeItemSelected_t selected, treeItemContextMenu_t contextMenu, treeItemBeginDrag_t beginDrag, treeItemEndDrag_t endDrag, treeItemInput_t input, void *data ); + +private: + void DrawNode( TreeNode *item, treeItemTooltip_t tooltip, treeItemSelected_t selected, treeItemContextMenu_t contextMenu, treeItemBeginDrag_t beginDrag, treeItemEndDrag_t endDrag, treeItemInput_t input, void *data ); + void UpdatePathToSelectedItem(); + void DeleteAllItemsOfNode( TreeNode *item ); + + idBlockAlloc nodeAllocator; + TreeNode * root; + TreeNode * selectedItem; + idList pathToSelectedItem; + int numNodes; +}; + +} + +#endif /* !__TREECTRL_H__ */