├── .gitignore ├── README.md ├── opengl_tutorial ├── _grpc │ ├── __init__.py │ ├── gen │ │ ├── __init__.py │ │ ├── threejs_pb2.py │ │ └── threejs_pb2_grpc.py │ └── proto │ │ └── threejs.proto ├── threejs │ ├── __init__.py │ ├── geometry.py │ ├── material.py │ └── three_mesh.py ├── tutorial.py ├── tutorial_utils.py └── update_protos.py ├── three-server ├── index.js ├── package-lock.json ├── package.json └── service.proto └── tutorial.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | node_modules/ 132 | three.js/ 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Code for the tutorial is located in `opengl_tutorial/`. 2 | 3 | `three-server/` contains a JavaScript gRPC server which serves 4 | geometries and materials from 5 | [eulertour/three.js](https://github.com/eulertour/three.js). 6 | 7 | In order to run the server first install [npm](https://www.npmjs.com/), then run: 8 | ``` 9 | cd three-server 10 | git clone https://github.com/eulertour/three.js 11 | npm install 12 | node index.js 13 | ``` 14 | -------------------------------------------------------------------------------- /opengl_tutorial/_grpc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eulertour/manim-opengl-tutorial/f6d325d228bfa70acd8016dadf3ed31c87c64045/opengl_tutorial/_grpc/__init__.py -------------------------------------------------------------------------------- /opengl_tutorial/_grpc/gen/__init__.py: -------------------------------------------------------------------------------- 1 | # https://github.com/protocolbuffers/protobuf/issues/1491#issuecomment-547504972 2 | 3 | import sys 4 | from pathlib import Path 5 | 6 | sys.path.append(str(Path(__file__).parent)) 7 | -------------------------------------------------------------------------------- /opengl_tutorial/_grpc/gen/threejs_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: threejs.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import message as _message 7 | from google.protobuf import reflection as _reflection 8 | from google.protobuf import symbol_database as _symbol_database 9 | # @@protoc_insertion_point(imports) 10 | 11 | _sym_db = _symbol_database.Default() 12 | 13 | 14 | 15 | 16 | DESCRIPTOR = _descriptor.FileDescriptor( 17 | name='threejs.proto', 18 | package='testservice', 19 | syntax='proto3', 20 | serialized_options=None, 21 | create_key=_descriptor._internal_create_key, 22 | serialized_pb=b'\n\rthreejs.proto\x12\x0btestservice\"\x16\n\x14\x42\x61sicMaterialRequest\"\x16\n\x14PhongMaterialRequest\"\x19\n\x17StandardMaterialRequest\"B\n\x10MaterialResponse\x12\x15\n\rvertex_shader\x18\x01 \x01(\t\x12\x17\n\x0f\x66ragment_shader\x18\x02 \x01(\t\"O\n\x10GeometryResponse\x12\x10\n\x08position\x18\x01 \x03(\x02\x12\x0e\n\x06normal\x18\x02 \x03(\x02\x12\n\n\x02uv\x18\x03 \x03(\x02\x12\r\n\x05index\x18\x04 \x03(\x05\"\x9e\x01\n\x12\x42oxGeometryRequest\x12\r\n\x05width\x18\x01 \x01(\x02\x12\x0e\n\x06height\x18\x02 \x01(\x02\x12\r\n\x05\x64\x65pth\x18\x03 \x01(\x02\x12\x16\n\x0ewidth_segments\x18\x04 \x01(\x05\x12\x17\n\x0fheight_segments\x18\x05 \x01(\x05\x12\x16\n\x0e\x64\x65pth_segments\x18\x06 \x01(\x05\x12\x11\n\twireframe\x18\x07 \x01(\x08\"\xbd\x01\n\x15SphereGeometryRequest\x12\x0e\n\x06radius\x18\x01 \x01(\x02\x12\x16\n\x0ewidth_segments\x18\x02 \x01(\x05\x12\x17\n\x0fheight_segments\x18\x03 \x01(\x05\x12\x11\n\tphi_start\x18\x04 \x01(\x02\x12\x12\n\nphi_length\x18\x05 \x01(\x02\x12\x13\n\x0btheta_start\x18\x06 \x01(\x02\x12\x14\n\x0ctheta_length\x18\x07 \x01(\x02\x12\x11\n\twireframe\x18\x08 \x01(\x08\"\xa1\x01\n\x18TorusKnotGeometryRequest\x12\x14\n\x0ctorus_radius\x18\x01 \x01(\x02\x12\x13\n\x0btube_radius\x18\x02 \x01(\x02\x12\x18\n\x10tubular_segments\x18\x03 \x01(\x05\x12\x17\n\x0fradial_segments\x18\x04 \x01(\x05\x12\t\n\x01p\x18\x05 \x01(\x05\x12\t\n\x01q\x18\x06 \x01(\x05\x12\x11\n\twireframe\x18\x07 \x01(\x08\"O\n\x1aIcosahedronGeometryRequest\x12\x0e\n\x06radius\x18\x01 \x01(\x02\x12\x0e\n\x06\x64\x65tail\x18\x02 \x01(\x05\x12\x11\n\twireframe\x18\x03 \x01(\x08\"O\n\x1aTetrahedronGeometryRequest\x12\x0e\n\x06radius\x18\x01 \x01(\x02\x12\x0e\n\x06\x64\x65tail\x18\x02 \x01(\x05\x12\x11\n\twireframe\x18\x03 \x01(\x08\"\xd8\x01\n\x17\x43ylinderGeometryRequest\x12\x12\n\nradius_top\x18\x01 \x01(\x02\x12\x15\n\rradius_bottom\x18\x02 \x01(\x02\x12\x0e\n\x06height\x18\x03 \x01(\x02\x12\x17\n\x0fradial_segments\x18\x04 \x01(\x05\x12\x17\n\x0fheight_segments\x18\x05 \x01(\x05\x12\x12\n\nopen_ended\x18\x06 \x01(\x08\x12\x13\n\x0btheta_start\x18\x07 \x01(\x02\x12\x14\n\x0ctheta_length\x18\x08 \x01(\x02\x12\x11\n\twireframe\x18\t \x01(\x08\"\xb9\x01\n\x13\x43oneGeometryRequest\x12\x0e\n\x06radius\x18\x01 \x01(\x02\x12\x0e\n\x06height\x18\x02 \x01(\x02\x12\x17\n\x0fradial_segments\x18\x03 \x01(\x05\x12\x17\n\x0fheight_segments\x18\x04 \x01(\x05\x12\x12\n\nopen_ended\x18\x05 \x01(\x08\x12\x13\n\x0btheta_start\x18\x06 \x01(\x02\x12\x14\n\x0ctheta_length\x18\x07 \x01(\x02\x12\x11\n\twireframe\x18\x08 \x01(\x08\"w\n\x15\x43ircleGeometryRequest\x12\x0e\n\x06radius\x18\x01 \x01(\x02\x12\x10\n\x08segments\x18\x02 \x01(\x05\x12\x13\n\x0btheta_start\x18\x03 \x01(\x02\x12\x14\n\x0ctheta_length\x18\x04 \x01(\x02\x12\x11\n\twireframe\x18\x05 \x01(\x08\"y\n\x14PlaneGeometryRequest\x12\r\n\x05width\x18\x01 \x01(\x02\x12\x0e\n\x06height\x18\x02 \x01(\x02\x12\x16\n\x0ewidth_segments\x18\x03 \x01(\x05\x12\x17\n\x0fheight_segments\x18\x04 \x01(\x05\x12\x11\n\twireframe\x18\x05 \x01(\x08\"\xe1\x01\n\x16\x45xtrudeGeometryRequest\x12\x0e\n\x06points\x18\x01 \x03(\x02\x12\x14\n\x0cpath_indices\x18\x02 \x03(\x05\x12\r\n\x05steps\x18\x03 \x01(\x05\x12\r\n\x05\x64\x65pth\x18\x04 \x01(\x02\x12\x15\n\rbevel_enabled\x18\x05 \x01(\x08\x12\x17\n\x0f\x62\x65vel_thickness\x18\x06 \x01(\x02\x12\x12\n\nbevel_size\x18\x07 \x01(\x02\x12\x14\n\x0c\x62\x65vel_offset\x18\x08 \x01(\x02\x12\x16\n\x0e\x62\x65vel_segments\x18\t \x01(\x05\x12\x11\n\twireframe\x18\n \x01(\x08\x32\x8b\x07\n\x0fGeometryService\x12O\n\x0b\x42oxGeometry\x12\x1f.testservice.BoxGeometryRequest\x1a\x1d.testservice.GeometryResponse\"\x00\x12U\n\x0eSphereGeometry\x12\".testservice.SphereGeometryRequest\x1a\x1d.testservice.GeometryResponse\"\x00\x12[\n\x11TorusKnotGeometry\x12%.testservice.TorusKnotGeometryRequest\x1a\x1d.testservice.GeometryResponse\"\x00\x12_\n\x13IcosahedronGeometry\x12\'.testservice.IcosahedronGeometryRequest\x1a\x1d.testservice.GeometryResponse\"\x00\x12_\n\x13TetrahedronGeometry\x12\'.testservice.TetrahedronGeometryRequest\x1a\x1d.testservice.GeometryResponse\"\x00\x12Y\n\x10\x43ylinderGeometry\x12$.testservice.CylinderGeometryRequest\x1a\x1d.testservice.GeometryResponse\"\x00\x12Q\n\x0c\x43oneGeometry\x12 .testservice.ConeGeometryRequest\x1a\x1d.testservice.GeometryResponse\"\x00\x12U\n\x0e\x43ircleGeometry\x12\".testservice.CircleGeometryRequest\x1a\x1d.testservice.GeometryResponse\"\x00\x12S\n\rPlaneGeometry\x12!.testservice.PlaneGeometryRequest\x1a\x1d.testservice.GeometryResponse\"\x00\x12W\n\x0f\x45xtrudeGeometry\x12#.testservice.ExtrudeGeometryRequest\x1a\x1d.testservice.GeometryResponse\"\x00\x32\x96\x02\n\x0fMaterialService\x12S\n\rBasicMaterial\x12!.testservice.BasicMaterialRequest\x1a\x1d.testservice.MaterialResponse\"\x00\x12S\n\rPhongMaterial\x12!.testservice.PhongMaterialRequest\x1a\x1d.testservice.MaterialResponse\"\x00\x12Y\n\x10StandardMaterial\x12$.testservice.StandardMaterialRequest\x1a\x1d.testservice.MaterialResponse\"\x00\x62\x06proto3' 23 | ) 24 | 25 | 26 | 27 | 28 | _BASICMATERIALREQUEST = _descriptor.Descriptor( 29 | name='BasicMaterialRequest', 30 | full_name='testservice.BasicMaterialRequest', 31 | filename=None, 32 | file=DESCRIPTOR, 33 | containing_type=None, 34 | create_key=_descriptor._internal_create_key, 35 | fields=[ 36 | ], 37 | extensions=[ 38 | ], 39 | nested_types=[], 40 | enum_types=[ 41 | ], 42 | serialized_options=None, 43 | is_extendable=False, 44 | syntax='proto3', 45 | extension_ranges=[], 46 | oneofs=[ 47 | ], 48 | serialized_start=30, 49 | serialized_end=52, 50 | ) 51 | 52 | 53 | _PHONGMATERIALREQUEST = _descriptor.Descriptor( 54 | name='PhongMaterialRequest', 55 | full_name='testservice.PhongMaterialRequest', 56 | filename=None, 57 | file=DESCRIPTOR, 58 | containing_type=None, 59 | create_key=_descriptor._internal_create_key, 60 | fields=[ 61 | ], 62 | extensions=[ 63 | ], 64 | nested_types=[], 65 | enum_types=[ 66 | ], 67 | serialized_options=None, 68 | is_extendable=False, 69 | syntax='proto3', 70 | extension_ranges=[], 71 | oneofs=[ 72 | ], 73 | serialized_start=54, 74 | serialized_end=76, 75 | ) 76 | 77 | 78 | _STANDARDMATERIALREQUEST = _descriptor.Descriptor( 79 | name='StandardMaterialRequest', 80 | full_name='testservice.StandardMaterialRequest', 81 | filename=None, 82 | file=DESCRIPTOR, 83 | containing_type=None, 84 | create_key=_descriptor._internal_create_key, 85 | fields=[ 86 | ], 87 | extensions=[ 88 | ], 89 | nested_types=[], 90 | enum_types=[ 91 | ], 92 | serialized_options=None, 93 | is_extendable=False, 94 | syntax='proto3', 95 | extension_ranges=[], 96 | oneofs=[ 97 | ], 98 | serialized_start=78, 99 | serialized_end=103, 100 | ) 101 | 102 | 103 | _MATERIALRESPONSE = _descriptor.Descriptor( 104 | name='MaterialResponse', 105 | full_name='testservice.MaterialResponse', 106 | filename=None, 107 | file=DESCRIPTOR, 108 | containing_type=None, 109 | create_key=_descriptor._internal_create_key, 110 | fields=[ 111 | _descriptor.FieldDescriptor( 112 | name='vertex_shader', full_name='testservice.MaterialResponse.vertex_shader', index=0, 113 | number=1, type=9, cpp_type=9, label=1, 114 | has_default_value=False, default_value=b"".decode('utf-8'), 115 | message_type=None, enum_type=None, containing_type=None, 116 | is_extension=False, extension_scope=None, 117 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 118 | _descriptor.FieldDescriptor( 119 | name='fragment_shader', full_name='testservice.MaterialResponse.fragment_shader', index=1, 120 | number=2, type=9, cpp_type=9, label=1, 121 | has_default_value=False, default_value=b"".decode('utf-8'), 122 | message_type=None, enum_type=None, containing_type=None, 123 | is_extension=False, extension_scope=None, 124 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 125 | ], 126 | extensions=[ 127 | ], 128 | nested_types=[], 129 | enum_types=[ 130 | ], 131 | serialized_options=None, 132 | is_extendable=False, 133 | syntax='proto3', 134 | extension_ranges=[], 135 | oneofs=[ 136 | ], 137 | serialized_start=105, 138 | serialized_end=171, 139 | ) 140 | 141 | 142 | _GEOMETRYRESPONSE = _descriptor.Descriptor( 143 | name='GeometryResponse', 144 | full_name='testservice.GeometryResponse', 145 | filename=None, 146 | file=DESCRIPTOR, 147 | containing_type=None, 148 | create_key=_descriptor._internal_create_key, 149 | fields=[ 150 | _descriptor.FieldDescriptor( 151 | name='position', full_name='testservice.GeometryResponse.position', index=0, 152 | number=1, type=2, cpp_type=6, label=3, 153 | has_default_value=False, default_value=[], 154 | message_type=None, enum_type=None, containing_type=None, 155 | is_extension=False, extension_scope=None, 156 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 157 | _descriptor.FieldDescriptor( 158 | name='normal', full_name='testservice.GeometryResponse.normal', index=1, 159 | number=2, type=2, cpp_type=6, label=3, 160 | has_default_value=False, default_value=[], 161 | message_type=None, enum_type=None, containing_type=None, 162 | is_extension=False, extension_scope=None, 163 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 164 | _descriptor.FieldDescriptor( 165 | name='uv', full_name='testservice.GeometryResponse.uv', index=2, 166 | number=3, type=2, cpp_type=6, label=3, 167 | has_default_value=False, default_value=[], 168 | message_type=None, enum_type=None, containing_type=None, 169 | is_extension=False, extension_scope=None, 170 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 171 | _descriptor.FieldDescriptor( 172 | name='index', full_name='testservice.GeometryResponse.index', index=3, 173 | number=4, type=5, cpp_type=1, label=3, 174 | has_default_value=False, default_value=[], 175 | message_type=None, enum_type=None, containing_type=None, 176 | is_extension=False, extension_scope=None, 177 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 178 | ], 179 | extensions=[ 180 | ], 181 | nested_types=[], 182 | enum_types=[ 183 | ], 184 | serialized_options=None, 185 | is_extendable=False, 186 | syntax='proto3', 187 | extension_ranges=[], 188 | oneofs=[ 189 | ], 190 | serialized_start=173, 191 | serialized_end=252, 192 | ) 193 | 194 | 195 | _BOXGEOMETRYREQUEST = _descriptor.Descriptor( 196 | name='BoxGeometryRequest', 197 | full_name='testservice.BoxGeometryRequest', 198 | filename=None, 199 | file=DESCRIPTOR, 200 | containing_type=None, 201 | create_key=_descriptor._internal_create_key, 202 | fields=[ 203 | _descriptor.FieldDescriptor( 204 | name='width', full_name='testservice.BoxGeometryRequest.width', index=0, 205 | number=1, type=2, cpp_type=6, label=1, 206 | has_default_value=False, default_value=float(0), 207 | message_type=None, enum_type=None, containing_type=None, 208 | is_extension=False, extension_scope=None, 209 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 210 | _descriptor.FieldDescriptor( 211 | name='height', full_name='testservice.BoxGeometryRequest.height', index=1, 212 | number=2, type=2, cpp_type=6, label=1, 213 | has_default_value=False, default_value=float(0), 214 | message_type=None, enum_type=None, containing_type=None, 215 | is_extension=False, extension_scope=None, 216 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 217 | _descriptor.FieldDescriptor( 218 | name='depth', full_name='testservice.BoxGeometryRequest.depth', index=2, 219 | number=3, type=2, cpp_type=6, label=1, 220 | has_default_value=False, default_value=float(0), 221 | message_type=None, enum_type=None, containing_type=None, 222 | is_extension=False, extension_scope=None, 223 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 224 | _descriptor.FieldDescriptor( 225 | name='width_segments', full_name='testservice.BoxGeometryRequest.width_segments', index=3, 226 | number=4, type=5, cpp_type=1, label=1, 227 | has_default_value=False, default_value=0, 228 | message_type=None, enum_type=None, containing_type=None, 229 | is_extension=False, extension_scope=None, 230 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 231 | _descriptor.FieldDescriptor( 232 | name='height_segments', full_name='testservice.BoxGeometryRequest.height_segments', index=4, 233 | number=5, type=5, cpp_type=1, label=1, 234 | has_default_value=False, default_value=0, 235 | message_type=None, enum_type=None, containing_type=None, 236 | is_extension=False, extension_scope=None, 237 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 238 | _descriptor.FieldDescriptor( 239 | name='depth_segments', full_name='testservice.BoxGeometryRequest.depth_segments', index=5, 240 | number=6, type=5, cpp_type=1, label=1, 241 | has_default_value=False, default_value=0, 242 | message_type=None, enum_type=None, containing_type=None, 243 | is_extension=False, extension_scope=None, 244 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 245 | _descriptor.FieldDescriptor( 246 | name='wireframe', full_name='testservice.BoxGeometryRequest.wireframe', index=6, 247 | number=7, type=8, cpp_type=7, label=1, 248 | has_default_value=False, default_value=False, 249 | message_type=None, enum_type=None, containing_type=None, 250 | is_extension=False, extension_scope=None, 251 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 252 | ], 253 | extensions=[ 254 | ], 255 | nested_types=[], 256 | enum_types=[ 257 | ], 258 | serialized_options=None, 259 | is_extendable=False, 260 | syntax='proto3', 261 | extension_ranges=[], 262 | oneofs=[ 263 | ], 264 | serialized_start=255, 265 | serialized_end=413, 266 | ) 267 | 268 | 269 | _SPHEREGEOMETRYREQUEST = _descriptor.Descriptor( 270 | name='SphereGeometryRequest', 271 | full_name='testservice.SphereGeometryRequest', 272 | filename=None, 273 | file=DESCRIPTOR, 274 | containing_type=None, 275 | create_key=_descriptor._internal_create_key, 276 | fields=[ 277 | _descriptor.FieldDescriptor( 278 | name='radius', full_name='testservice.SphereGeometryRequest.radius', index=0, 279 | number=1, type=2, cpp_type=6, label=1, 280 | has_default_value=False, default_value=float(0), 281 | message_type=None, enum_type=None, containing_type=None, 282 | is_extension=False, extension_scope=None, 283 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 284 | _descriptor.FieldDescriptor( 285 | name='width_segments', full_name='testservice.SphereGeometryRequest.width_segments', index=1, 286 | number=2, type=5, cpp_type=1, label=1, 287 | has_default_value=False, default_value=0, 288 | message_type=None, enum_type=None, containing_type=None, 289 | is_extension=False, extension_scope=None, 290 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 291 | _descriptor.FieldDescriptor( 292 | name='height_segments', full_name='testservice.SphereGeometryRequest.height_segments', index=2, 293 | number=3, type=5, cpp_type=1, label=1, 294 | has_default_value=False, default_value=0, 295 | message_type=None, enum_type=None, containing_type=None, 296 | is_extension=False, extension_scope=None, 297 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 298 | _descriptor.FieldDescriptor( 299 | name='phi_start', full_name='testservice.SphereGeometryRequest.phi_start', index=3, 300 | number=4, type=2, cpp_type=6, label=1, 301 | has_default_value=False, default_value=float(0), 302 | message_type=None, enum_type=None, containing_type=None, 303 | is_extension=False, extension_scope=None, 304 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 305 | _descriptor.FieldDescriptor( 306 | name='phi_length', full_name='testservice.SphereGeometryRequest.phi_length', index=4, 307 | number=5, type=2, cpp_type=6, label=1, 308 | has_default_value=False, default_value=float(0), 309 | message_type=None, enum_type=None, containing_type=None, 310 | is_extension=False, extension_scope=None, 311 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 312 | _descriptor.FieldDescriptor( 313 | name='theta_start', full_name='testservice.SphereGeometryRequest.theta_start', index=5, 314 | number=6, type=2, cpp_type=6, label=1, 315 | has_default_value=False, default_value=float(0), 316 | message_type=None, enum_type=None, containing_type=None, 317 | is_extension=False, extension_scope=None, 318 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 319 | _descriptor.FieldDescriptor( 320 | name='theta_length', full_name='testservice.SphereGeometryRequest.theta_length', index=6, 321 | number=7, type=2, cpp_type=6, label=1, 322 | has_default_value=False, default_value=float(0), 323 | message_type=None, enum_type=None, containing_type=None, 324 | is_extension=False, extension_scope=None, 325 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 326 | _descriptor.FieldDescriptor( 327 | name='wireframe', full_name='testservice.SphereGeometryRequest.wireframe', index=7, 328 | number=8, type=8, cpp_type=7, label=1, 329 | has_default_value=False, default_value=False, 330 | message_type=None, enum_type=None, containing_type=None, 331 | is_extension=False, extension_scope=None, 332 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 333 | ], 334 | extensions=[ 335 | ], 336 | nested_types=[], 337 | enum_types=[ 338 | ], 339 | serialized_options=None, 340 | is_extendable=False, 341 | syntax='proto3', 342 | extension_ranges=[], 343 | oneofs=[ 344 | ], 345 | serialized_start=416, 346 | serialized_end=605, 347 | ) 348 | 349 | 350 | _TORUSKNOTGEOMETRYREQUEST = _descriptor.Descriptor( 351 | name='TorusKnotGeometryRequest', 352 | full_name='testservice.TorusKnotGeometryRequest', 353 | filename=None, 354 | file=DESCRIPTOR, 355 | containing_type=None, 356 | create_key=_descriptor._internal_create_key, 357 | fields=[ 358 | _descriptor.FieldDescriptor( 359 | name='torus_radius', full_name='testservice.TorusKnotGeometryRequest.torus_radius', index=0, 360 | number=1, type=2, cpp_type=6, label=1, 361 | has_default_value=False, default_value=float(0), 362 | message_type=None, enum_type=None, containing_type=None, 363 | is_extension=False, extension_scope=None, 364 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 365 | _descriptor.FieldDescriptor( 366 | name='tube_radius', full_name='testservice.TorusKnotGeometryRequest.tube_radius', index=1, 367 | number=2, type=2, cpp_type=6, label=1, 368 | has_default_value=False, default_value=float(0), 369 | message_type=None, enum_type=None, containing_type=None, 370 | is_extension=False, extension_scope=None, 371 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 372 | _descriptor.FieldDescriptor( 373 | name='tubular_segments', full_name='testservice.TorusKnotGeometryRequest.tubular_segments', index=2, 374 | number=3, type=5, cpp_type=1, label=1, 375 | has_default_value=False, default_value=0, 376 | message_type=None, enum_type=None, containing_type=None, 377 | is_extension=False, extension_scope=None, 378 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 379 | _descriptor.FieldDescriptor( 380 | name='radial_segments', full_name='testservice.TorusKnotGeometryRequest.radial_segments', index=3, 381 | number=4, type=5, cpp_type=1, label=1, 382 | has_default_value=False, default_value=0, 383 | message_type=None, enum_type=None, containing_type=None, 384 | is_extension=False, extension_scope=None, 385 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 386 | _descriptor.FieldDescriptor( 387 | name='p', full_name='testservice.TorusKnotGeometryRequest.p', index=4, 388 | number=5, type=5, cpp_type=1, label=1, 389 | has_default_value=False, default_value=0, 390 | message_type=None, enum_type=None, containing_type=None, 391 | is_extension=False, extension_scope=None, 392 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 393 | _descriptor.FieldDescriptor( 394 | name='q', full_name='testservice.TorusKnotGeometryRequest.q', index=5, 395 | number=6, type=5, cpp_type=1, label=1, 396 | has_default_value=False, default_value=0, 397 | message_type=None, enum_type=None, containing_type=None, 398 | is_extension=False, extension_scope=None, 399 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 400 | _descriptor.FieldDescriptor( 401 | name='wireframe', full_name='testservice.TorusKnotGeometryRequest.wireframe', index=6, 402 | number=7, type=8, cpp_type=7, label=1, 403 | has_default_value=False, default_value=False, 404 | message_type=None, enum_type=None, containing_type=None, 405 | is_extension=False, extension_scope=None, 406 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 407 | ], 408 | extensions=[ 409 | ], 410 | nested_types=[], 411 | enum_types=[ 412 | ], 413 | serialized_options=None, 414 | is_extendable=False, 415 | syntax='proto3', 416 | extension_ranges=[], 417 | oneofs=[ 418 | ], 419 | serialized_start=608, 420 | serialized_end=769, 421 | ) 422 | 423 | 424 | _ICOSAHEDRONGEOMETRYREQUEST = _descriptor.Descriptor( 425 | name='IcosahedronGeometryRequest', 426 | full_name='testservice.IcosahedronGeometryRequest', 427 | filename=None, 428 | file=DESCRIPTOR, 429 | containing_type=None, 430 | create_key=_descriptor._internal_create_key, 431 | fields=[ 432 | _descriptor.FieldDescriptor( 433 | name='radius', full_name='testservice.IcosahedronGeometryRequest.radius', index=0, 434 | number=1, type=2, cpp_type=6, label=1, 435 | has_default_value=False, default_value=float(0), 436 | message_type=None, enum_type=None, containing_type=None, 437 | is_extension=False, extension_scope=None, 438 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 439 | _descriptor.FieldDescriptor( 440 | name='detail', full_name='testservice.IcosahedronGeometryRequest.detail', index=1, 441 | number=2, type=5, cpp_type=1, label=1, 442 | has_default_value=False, default_value=0, 443 | message_type=None, enum_type=None, containing_type=None, 444 | is_extension=False, extension_scope=None, 445 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 446 | _descriptor.FieldDescriptor( 447 | name='wireframe', full_name='testservice.IcosahedronGeometryRequest.wireframe', index=2, 448 | number=3, type=8, cpp_type=7, label=1, 449 | has_default_value=False, default_value=False, 450 | message_type=None, enum_type=None, containing_type=None, 451 | is_extension=False, extension_scope=None, 452 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 453 | ], 454 | extensions=[ 455 | ], 456 | nested_types=[], 457 | enum_types=[ 458 | ], 459 | serialized_options=None, 460 | is_extendable=False, 461 | syntax='proto3', 462 | extension_ranges=[], 463 | oneofs=[ 464 | ], 465 | serialized_start=771, 466 | serialized_end=850, 467 | ) 468 | 469 | 470 | _TETRAHEDRONGEOMETRYREQUEST = _descriptor.Descriptor( 471 | name='TetrahedronGeometryRequest', 472 | full_name='testservice.TetrahedronGeometryRequest', 473 | filename=None, 474 | file=DESCRIPTOR, 475 | containing_type=None, 476 | create_key=_descriptor._internal_create_key, 477 | fields=[ 478 | _descriptor.FieldDescriptor( 479 | name='radius', full_name='testservice.TetrahedronGeometryRequest.radius', index=0, 480 | number=1, type=2, cpp_type=6, label=1, 481 | has_default_value=False, default_value=float(0), 482 | message_type=None, enum_type=None, containing_type=None, 483 | is_extension=False, extension_scope=None, 484 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 485 | _descriptor.FieldDescriptor( 486 | name='detail', full_name='testservice.TetrahedronGeometryRequest.detail', index=1, 487 | number=2, type=5, cpp_type=1, label=1, 488 | has_default_value=False, default_value=0, 489 | message_type=None, enum_type=None, containing_type=None, 490 | is_extension=False, extension_scope=None, 491 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 492 | _descriptor.FieldDescriptor( 493 | name='wireframe', full_name='testservice.TetrahedronGeometryRequest.wireframe', index=2, 494 | number=3, type=8, cpp_type=7, label=1, 495 | has_default_value=False, default_value=False, 496 | message_type=None, enum_type=None, containing_type=None, 497 | is_extension=False, extension_scope=None, 498 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 499 | ], 500 | extensions=[ 501 | ], 502 | nested_types=[], 503 | enum_types=[ 504 | ], 505 | serialized_options=None, 506 | is_extendable=False, 507 | syntax='proto3', 508 | extension_ranges=[], 509 | oneofs=[ 510 | ], 511 | serialized_start=852, 512 | serialized_end=931, 513 | ) 514 | 515 | 516 | _CYLINDERGEOMETRYREQUEST = _descriptor.Descriptor( 517 | name='CylinderGeometryRequest', 518 | full_name='testservice.CylinderGeometryRequest', 519 | filename=None, 520 | file=DESCRIPTOR, 521 | containing_type=None, 522 | create_key=_descriptor._internal_create_key, 523 | fields=[ 524 | _descriptor.FieldDescriptor( 525 | name='radius_top', full_name='testservice.CylinderGeometryRequest.radius_top', index=0, 526 | number=1, type=2, cpp_type=6, label=1, 527 | has_default_value=False, default_value=float(0), 528 | message_type=None, enum_type=None, containing_type=None, 529 | is_extension=False, extension_scope=None, 530 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 531 | _descriptor.FieldDescriptor( 532 | name='radius_bottom', full_name='testservice.CylinderGeometryRequest.radius_bottom', index=1, 533 | number=2, type=2, cpp_type=6, label=1, 534 | has_default_value=False, default_value=float(0), 535 | message_type=None, enum_type=None, containing_type=None, 536 | is_extension=False, extension_scope=None, 537 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 538 | _descriptor.FieldDescriptor( 539 | name='height', full_name='testservice.CylinderGeometryRequest.height', index=2, 540 | number=3, type=2, cpp_type=6, label=1, 541 | has_default_value=False, default_value=float(0), 542 | message_type=None, enum_type=None, containing_type=None, 543 | is_extension=False, extension_scope=None, 544 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 545 | _descriptor.FieldDescriptor( 546 | name='radial_segments', full_name='testservice.CylinderGeometryRequest.radial_segments', index=3, 547 | number=4, type=5, cpp_type=1, label=1, 548 | has_default_value=False, default_value=0, 549 | message_type=None, enum_type=None, containing_type=None, 550 | is_extension=False, extension_scope=None, 551 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 552 | _descriptor.FieldDescriptor( 553 | name='height_segments', full_name='testservice.CylinderGeometryRequest.height_segments', index=4, 554 | number=5, type=5, cpp_type=1, label=1, 555 | has_default_value=False, default_value=0, 556 | message_type=None, enum_type=None, containing_type=None, 557 | is_extension=False, extension_scope=None, 558 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 559 | _descriptor.FieldDescriptor( 560 | name='open_ended', full_name='testservice.CylinderGeometryRequest.open_ended', index=5, 561 | number=6, type=8, cpp_type=7, label=1, 562 | has_default_value=False, default_value=False, 563 | message_type=None, enum_type=None, containing_type=None, 564 | is_extension=False, extension_scope=None, 565 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 566 | _descriptor.FieldDescriptor( 567 | name='theta_start', full_name='testservice.CylinderGeometryRequest.theta_start', index=6, 568 | number=7, type=2, cpp_type=6, label=1, 569 | has_default_value=False, default_value=float(0), 570 | message_type=None, enum_type=None, containing_type=None, 571 | is_extension=False, extension_scope=None, 572 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 573 | _descriptor.FieldDescriptor( 574 | name='theta_length', full_name='testservice.CylinderGeometryRequest.theta_length', index=7, 575 | number=8, type=2, cpp_type=6, label=1, 576 | has_default_value=False, default_value=float(0), 577 | message_type=None, enum_type=None, containing_type=None, 578 | is_extension=False, extension_scope=None, 579 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 580 | _descriptor.FieldDescriptor( 581 | name='wireframe', full_name='testservice.CylinderGeometryRequest.wireframe', index=8, 582 | number=9, type=8, cpp_type=7, label=1, 583 | has_default_value=False, default_value=False, 584 | message_type=None, enum_type=None, containing_type=None, 585 | is_extension=False, extension_scope=None, 586 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 587 | ], 588 | extensions=[ 589 | ], 590 | nested_types=[], 591 | enum_types=[ 592 | ], 593 | serialized_options=None, 594 | is_extendable=False, 595 | syntax='proto3', 596 | extension_ranges=[], 597 | oneofs=[ 598 | ], 599 | serialized_start=934, 600 | serialized_end=1150, 601 | ) 602 | 603 | 604 | _CONEGEOMETRYREQUEST = _descriptor.Descriptor( 605 | name='ConeGeometryRequest', 606 | full_name='testservice.ConeGeometryRequest', 607 | filename=None, 608 | file=DESCRIPTOR, 609 | containing_type=None, 610 | create_key=_descriptor._internal_create_key, 611 | fields=[ 612 | _descriptor.FieldDescriptor( 613 | name='radius', full_name='testservice.ConeGeometryRequest.radius', index=0, 614 | number=1, type=2, cpp_type=6, label=1, 615 | has_default_value=False, default_value=float(0), 616 | message_type=None, enum_type=None, containing_type=None, 617 | is_extension=False, extension_scope=None, 618 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 619 | _descriptor.FieldDescriptor( 620 | name='height', full_name='testservice.ConeGeometryRequest.height', index=1, 621 | number=2, type=2, cpp_type=6, label=1, 622 | has_default_value=False, default_value=float(0), 623 | message_type=None, enum_type=None, containing_type=None, 624 | is_extension=False, extension_scope=None, 625 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 626 | _descriptor.FieldDescriptor( 627 | name='radial_segments', full_name='testservice.ConeGeometryRequest.radial_segments', index=2, 628 | number=3, type=5, cpp_type=1, label=1, 629 | has_default_value=False, default_value=0, 630 | message_type=None, enum_type=None, containing_type=None, 631 | is_extension=False, extension_scope=None, 632 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 633 | _descriptor.FieldDescriptor( 634 | name='height_segments', full_name='testservice.ConeGeometryRequest.height_segments', index=3, 635 | number=4, type=5, cpp_type=1, label=1, 636 | has_default_value=False, default_value=0, 637 | message_type=None, enum_type=None, containing_type=None, 638 | is_extension=False, extension_scope=None, 639 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 640 | _descriptor.FieldDescriptor( 641 | name='open_ended', full_name='testservice.ConeGeometryRequest.open_ended', index=4, 642 | number=5, type=8, cpp_type=7, label=1, 643 | has_default_value=False, default_value=False, 644 | message_type=None, enum_type=None, containing_type=None, 645 | is_extension=False, extension_scope=None, 646 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 647 | _descriptor.FieldDescriptor( 648 | name='theta_start', full_name='testservice.ConeGeometryRequest.theta_start', index=5, 649 | number=6, type=2, cpp_type=6, label=1, 650 | has_default_value=False, default_value=float(0), 651 | message_type=None, enum_type=None, containing_type=None, 652 | is_extension=False, extension_scope=None, 653 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 654 | _descriptor.FieldDescriptor( 655 | name='theta_length', full_name='testservice.ConeGeometryRequest.theta_length', index=6, 656 | number=7, type=2, cpp_type=6, label=1, 657 | has_default_value=False, default_value=float(0), 658 | message_type=None, enum_type=None, containing_type=None, 659 | is_extension=False, extension_scope=None, 660 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 661 | _descriptor.FieldDescriptor( 662 | name='wireframe', full_name='testservice.ConeGeometryRequest.wireframe', index=7, 663 | number=8, type=8, cpp_type=7, label=1, 664 | has_default_value=False, default_value=False, 665 | message_type=None, enum_type=None, containing_type=None, 666 | is_extension=False, extension_scope=None, 667 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 668 | ], 669 | extensions=[ 670 | ], 671 | nested_types=[], 672 | enum_types=[ 673 | ], 674 | serialized_options=None, 675 | is_extendable=False, 676 | syntax='proto3', 677 | extension_ranges=[], 678 | oneofs=[ 679 | ], 680 | serialized_start=1153, 681 | serialized_end=1338, 682 | ) 683 | 684 | 685 | _CIRCLEGEOMETRYREQUEST = _descriptor.Descriptor( 686 | name='CircleGeometryRequest', 687 | full_name='testservice.CircleGeometryRequest', 688 | filename=None, 689 | file=DESCRIPTOR, 690 | containing_type=None, 691 | create_key=_descriptor._internal_create_key, 692 | fields=[ 693 | _descriptor.FieldDescriptor( 694 | name='radius', full_name='testservice.CircleGeometryRequest.radius', index=0, 695 | number=1, type=2, cpp_type=6, label=1, 696 | has_default_value=False, default_value=float(0), 697 | message_type=None, enum_type=None, containing_type=None, 698 | is_extension=False, extension_scope=None, 699 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 700 | _descriptor.FieldDescriptor( 701 | name='segments', full_name='testservice.CircleGeometryRequest.segments', index=1, 702 | number=2, type=5, cpp_type=1, label=1, 703 | has_default_value=False, default_value=0, 704 | message_type=None, enum_type=None, containing_type=None, 705 | is_extension=False, extension_scope=None, 706 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 707 | _descriptor.FieldDescriptor( 708 | name='theta_start', full_name='testservice.CircleGeometryRequest.theta_start', index=2, 709 | number=3, type=2, cpp_type=6, label=1, 710 | has_default_value=False, default_value=float(0), 711 | message_type=None, enum_type=None, containing_type=None, 712 | is_extension=False, extension_scope=None, 713 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 714 | _descriptor.FieldDescriptor( 715 | name='theta_length', full_name='testservice.CircleGeometryRequest.theta_length', index=3, 716 | number=4, type=2, cpp_type=6, label=1, 717 | has_default_value=False, default_value=float(0), 718 | message_type=None, enum_type=None, containing_type=None, 719 | is_extension=False, extension_scope=None, 720 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 721 | _descriptor.FieldDescriptor( 722 | name='wireframe', full_name='testservice.CircleGeometryRequest.wireframe', index=4, 723 | number=5, type=8, cpp_type=7, label=1, 724 | has_default_value=False, default_value=False, 725 | message_type=None, enum_type=None, containing_type=None, 726 | is_extension=False, extension_scope=None, 727 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 728 | ], 729 | extensions=[ 730 | ], 731 | nested_types=[], 732 | enum_types=[ 733 | ], 734 | serialized_options=None, 735 | is_extendable=False, 736 | syntax='proto3', 737 | extension_ranges=[], 738 | oneofs=[ 739 | ], 740 | serialized_start=1340, 741 | serialized_end=1459, 742 | ) 743 | 744 | 745 | _PLANEGEOMETRYREQUEST = _descriptor.Descriptor( 746 | name='PlaneGeometryRequest', 747 | full_name='testservice.PlaneGeometryRequest', 748 | filename=None, 749 | file=DESCRIPTOR, 750 | containing_type=None, 751 | create_key=_descriptor._internal_create_key, 752 | fields=[ 753 | _descriptor.FieldDescriptor( 754 | name='width', full_name='testservice.PlaneGeometryRequest.width', index=0, 755 | number=1, type=2, cpp_type=6, label=1, 756 | has_default_value=False, default_value=float(0), 757 | message_type=None, enum_type=None, containing_type=None, 758 | is_extension=False, extension_scope=None, 759 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 760 | _descriptor.FieldDescriptor( 761 | name='height', full_name='testservice.PlaneGeometryRequest.height', index=1, 762 | number=2, type=2, cpp_type=6, label=1, 763 | has_default_value=False, default_value=float(0), 764 | message_type=None, enum_type=None, containing_type=None, 765 | is_extension=False, extension_scope=None, 766 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 767 | _descriptor.FieldDescriptor( 768 | name='width_segments', full_name='testservice.PlaneGeometryRequest.width_segments', index=2, 769 | number=3, type=5, cpp_type=1, label=1, 770 | has_default_value=False, default_value=0, 771 | message_type=None, enum_type=None, containing_type=None, 772 | is_extension=False, extension_scope=None, 773 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 774 | _descriptor.FieldDescriptor( 775 | name='height_segments', full_name='testservice.PlaneGeometryRequest.height_segments', index=3, 776 | number=4, type=5, cpp_type=1, label=1, 777 | has_default_value=False, default_value=0, 778 | message_type=None, enum_type=None, containing_type=None, 779 | is_extension=False, extension_scope=None, 780 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 781 | _descriptor.FieldDescriptor( 782 | name='wireframe', full_name='testservice.PlaneGeometryRequest.wireframe', index=4, 783 | number=5, type=8, cpp_type=7, label=1, 784 | has_default_value=False, default_value=False, 785 | message_type=None, enum_type=None, containing_type=None, 786 | is_extension=False, extension_scope=None, 787 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 788 | ], 789 | extensions=[ 790 | ], 791 | nested_types=[], 792 | enum_types=[ 793 | ], 794 | serialized_options=None, 795 | is_extendable=False, 796 | syntax='proto3', 797 | extension_ranges=[], 798 | oneofs=[ 799 | ], 800 | serialized_start=1461, 801 | serialized_end=1582, 802 | ) 803 | 804 | 805 | _EXTRUDEGEOMETRYREQUEST = _descriptor.Descriptor( 806 | name='ExtrudeGeometryRequest', 807 | full_name='testservice.ExtrudeGeometryRequest', 808 | filename=None, 809 | file=DESCRIPTOR, 810 | containing_type=None, 811 | create_key=_descriptor._internal_create_key, 812 | fields=[ 813 | _descriptor.FieldDescriptor( 814 | name='points', full_name='testservice.ExtrudeGeometryRequest.points', index=0, 815 | number=1, type=2, cpp_type=6, label=3, 816 | has_default_value=False, default_value=[], 817 | message_type=None, enum_type=None, containing_type=None, 818 | is_extension=False, extension_scope=None, 819 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 820 | _descriptor.FieldDescriptor( 821 | name='path_indices', full_name='testservice.ExtrudeGeometryRequest.path_indices', index=1, 822 | number=2, type=5, cpp_type=1, label=3, 823 | has_default_value=False, default_value=[], 824 | message_type=None, enum_type=None, containing_type=None, 825 | is_extension=False, extension_scope=None, 826 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 827 | _descriptor.FieldDescriptor( 828 | name='steps', full_name='testservice.ExtrudeGeometryRequest.steps', index=2, 829 | number=3, type=5, cpp_type=1, label=1, 830 | has_default_value=False, default_value=0, 831 | message_type=None, enum_type=None, containing_type=None, 832 | is_extension=False, extension_scope=None, 833 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 834 | _descriptor.FieldDescriptor( 835 | name='depth', full_name='testservice.ExtrudeGeometryRequest.depth', index=3, 836 | number=4, type=2, cpp_type=6, label=1, 837 | has_default_value=False, default_value=float(0), 838 | message_type=None, enum_type=None, containing_type=None, 839 | is_extension=False, extension_scope=None, 840 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 841 | _descriptor.FieldDescriptor( 842 | name='bevel_enabled', full_name='testservice.ExtrudeGeometryRequest.bevel_enabled', index=4, 843 | number=5, type=8, cpp_type=7, label=1, 844 | has_default_value=False, default_value=False, 845 | message_type=None, enum_type=None, containing_type=None, 846 | is_extension=False, extension_scope=None, 847 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 848 | _descriptor.FieldDescriptor( 849 | name='bevel_thickness', full_name='testservice.ExtrudeGeometryRequest.bevel_thickness', index=5, 850 | number=6, type=2, cpp_type=6, label=1, 851 | has_default_value=False, default_value=float(0), 852 | message_type=None, enum_type=None, containing_type=None, 853 | is_extension=False, extension_scope=None, 854 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 855 | _descriptor.FieldDescriptor( 856 | name='bevel_size', full_name='testservice.ExtrudeGeometryRequest.bevel_size', index=6, 857 | number=7, type=2, cpp_type=6, label=1, 858 | has_default_value=False, default_value=float(0), 859 | message_type=None, enum_type=None, containing_type=None, 860 | is_extension=False, extension_scope=None, 861 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 862 | _descriptor.FieldDescriptor( 863 | name='bevel_offset', full_name='testservice.ExtrudeGeometryRequest.bevel_offset', index=7, 864 | number=8, type=2, cpp_type=6, label=1, 865 | has_default_value=False, default_value=float(0), 866 | message_type=None, enum_type=None, containing_type=None, 867 | is_extension=False, extension_scope=None, 868 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 869 | _descriptor.FieldDescriptor( 870 | name='bevel_segments', full_name='testservice.ExtrudeGeometryRequest.bevel_segments', index=8, 871 | number=9, type=5, cpp_type=1, label=1, 872 | has_default_value=False, default_value=0, 873 | message_type=None, enum_type=None, containing_type=None, 874 | is_extension=False, extension_scope=None, 875 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 876 | _descriptor.FieldDescriptor( 877 | name='wireframe', full_name='testservice.ExtrudeGeometryRequest.wireframe', index=9, 878 | number=10, type=8, cpp_type=7, label=1, 879 | has_default_value=False, default_value=False, 880 | message_type=None, enum_type=None, containing_type=None, 881 | is_extension=False, extension_scope=None, 882 | serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), 883 | ], 884 | extensions=[ 885 | ], 886 | nested_types=[], 887 | enum_types=[ 888 | ], 889 | serialized_options=None, 890 | is_extendable=False, 891 | syntax='proto3', 892 | extension_ranges=[], 893 | oneofs=[ 894 | ], 895 | serialized_start=1585, 896 | serialized_end=1810, 897 | ) 898 | 899 | DESCRIPTOR.message_types_by_name['BasicMaterialRequest'] = _BASICMATERIALREQUEST 900 | DESCRIPTOR.message_types_by_name['PhongMaterialRequest'] = _PHONGMATERIALREQUEST 901 | DESCRIPTOR.message_types_by_name['StandardMaterialRequest'] = _STANDARDMATERIALREQUEST 902 | DESCRIPTOR.message_types_by_name['MaterialResponse'] = _MATERIALRESPONSE 903 | DESCRIPTOR.message_types_by_name['GeometryResponse'] = _GEOMETRYRESPONSE 904 | DESCRIPTOR.message_types_by_name['BoxGeometryRequest'] = _BOXGEOMETRYREQUEST 905 | DESCRIPTOR.message_types_by_name['SphereGeometryRequest'] = _SPHEREGEOMETRYREQUEST 906 | DESCRIPTOR.message_types_by_name['TorusKnotGeometryRequest'] = _TORUSKNOTGEOMETRYREQUEST 907 | DESCRIPTOR.message_types_by_name['IcosahedronGeometryRequest'] = _ICOSAHEDRONGEOMETRYREQUEST 908 | DESCRIPTOR.message_types_by_name['TetrahedronGeometryRequest'] = _TETRAHEDRONGEOMETRYREQUEST 909 | DESCRIPTOR.message_types_by_name['CylinderGeometryRequest'] = _CYLINDERGEOMETRYREQUEST 910 | DESCRIPTOR.message_types_by_name['ConeGeometryRequest'] = _CONEGEOMETRYREQUEST 911 | DESCRIPTOR.message_types_by_name['CircleGeometryRequest'] = _CIRCLEGEOMETRYREQUEST 912 | DESCRIPTOR.message_types_by_name['PlaneGeometryRequest'] = _PLANEGEOMETRYREQUEST 913 | DESCRIPTOR.message_types_by_name['ExtrudeGeometryRequest'] = _EXTRUDEGEOMETRYREQUEST 914 | _sym_db.RegisterFileDescriptor(DESCRIPTOR) 915 | 916 | BasicMaterialRequest = _reflection.GeneratedProtocolMessageType('BasicMaterialRequest', (_message.Message,), { 917 | 'DESCRIPTOR' : _BASICMATERIALREQUEST, 918 | '__module__' : 'threejs_pb2' 919 | # @@protoc_insertion_point(class_scope:testservice.BasicMaterialRequest) 920 | }) 921 | _sym_db.RegisterMessage(BasicMaterialRequest) 922 | 923 | PhongMaterialRequest = _reflection.GeneratedProtocolMessageType('PhongMaterialRequest', (_message.Message,), { 924 | 'DESCRIPTOR' : _PHONGMATERIALREQUEST, 925 | '__module__' : 'threejs_pb2' 926 | # @@protoc_insertion_point(class_scope:testservice.PhongMaterialRequest) 927 | }) 928 | _sym_db.RegisterMessage(PhongMaterialRequest) 929 | 930 | StandardMaterialRequest = _reflection.GeneratedProtocolMessageType('StandardMaterialRequest', (_message.Message,), { 931 | 'DESCRIPTOR' : _STANDARDMATERIALREQUEST, 932 | '__module__' : 'threejs_pb2' 933 | # @@protoc_insertion_point(class_scope:testservice.StandardMaterialRequest) 934 | }) 935 | _sym_db.RegisterMessage(StandardMaterialRequest) 936 | 937 | MaterialResponse = _reflection.GeneratedProtocolMessageType('MaterialResponse', (_message.Message,), { 938 | 'DESCRIPTOR' : _MATERIALRESPONSE, 939 | '__module__' : 'threejs_pb2' 940 | # @@protoc_insertion_point(class_scope:testservice.MaterialResponse) 941 | }) 942 | _sym_db.RegisterMessage(MaterialResponse) 943 | 944 | GeometryResponse = _reflection.GeneratedProtocolMessageType('GeometryResponse', (_message.Message,), { 945 | 'DESCRIPTOR' : _GEOMETRYRESPONSE, 946 | '__module__' : 'threejs_pb2' 947 | # @@protoc_insertion_point(class_scope:testservice.GeometryResponse) 948 | }) 949 | _sym_db.RegisterMessage(GeometryResponse) 950 | 951 | BoxGeometryRequest = _reflection.GeneratedProtocolMessageType('BoxGeometryRequest', (_message.Message,), { 952 | 'DESCRIPTOR' : _BOXGEOMETRYREQUEST, 953 | '__module__' : 'threejs_pb2' 954 | # @@protoc_insertion_point(class_scope:testservice.BoxGeometryRequest) 955 | }) 956 | _sym_db.RegisterMessage(BoxGeometryRequest) 957 | 958 | SphereGeometryRequest = _reflection.GeneratedProtocolMessageType('SphereGeometryRequest', (_message.Message,), { 959 | 'DESCRIPTOR' : _SPHEREGEOMETRYREQUEST, 960 | '__module__' : 'threejs_pb2' 961 | # @@protoc_insertion_point(class_scope:testservice.SphereGeometryRequest) 962 | }) 963 | _sym_db.RegisterMessage(SphereGeometryRequest) 964 | 965 | TorusKnotGeometryRequest = _reflection.GeneratedProtocolMessageType('TorusKnotGeometryRequest', (_message.Message,), { 966 | 'DESCRIPTOR' : _TORUSKNOTGEOMETRYREQUEST, 967 | '__module__' : 'threejs_pb2' 968 | # @@protoc_insertion_point(class_scope:testservice.TorusKnotGeometryRequest) 969 | }) 970 | _sym_db.RegisterMessage(TorusKnotGeometryRequest) 971 | 972 | IcosahedronGeometryRequest = _reflection.GeneratedProtocolMessageType('IcosahedronGeometryRequest', (_message.Message,), { 973 | 'DESCRIPTOR' : _ICOSAHEDRONGEOMETRYREQUEST, 974 | '__module__' : 'threejs_pb2' 975 | # @@protoc_insertion_point(class_scope:testservice.IcosahedronGeometryRequest) 976 | }) 977 | _sym_db.RegisterMessage(IcosahedronGeometryRequest) 978 | 979 | TetrahedronGeometryRequest = _reflection.GeneratedProtocolMessageType('TetrahedronGeometryRequest', (_message.Message,), { 980 | 'DESCRIPTOR' : _TETRAHEDRONGEOMETRYREQUEST, 981 | '__module__' : 'threejs_pb2' 982 | # @@protoc_insertion_point(class_scope:testservice.TetrahedronGeometryRequest) 983 | }) 984 | _sym_db.RegisterMessage(TetrahedronGeometryRequest) 985 | 986 | CylinderGeometryRequest = _reflection.GeneratedProtocolMessageType('CylinderGeometryRequest', (_message.Message,), { 987 | 'DESCRIPTOR' : _CYLINDERGEOMETRYREQUEST, 988 | '__module__' : 'threejs_pb2' 989 | # @@protoc_insertion_point(class_scope:testservice.CylinderGeometryRequest) 990 | }) 991 | _sym_db.RegisterMessage(CylinderGeometryRequest) 992 | 993 | ConeGeometryRequest = _reflection.GeneratedProtocolMessageType('ConeGeometryRequest', (_message.Message,), { 994 | 'DESCRIPTOR' : _CONEGEOMETRYREQUEST, 995 | '__module__' : 'threejs_pb2' 996 | # @@protoc_insertion_point(class_scope:testservice.ConeGeometryRequest) 997 | }) 998 | _sym_db.RegisterMessage(ConeGeometryRequest) 999 | 1000 | CircleGeometryRequest = _reflection.GeneratedProtocolMessageType('CircleGeometryRequest', (_message.Message,), { 1001 | 'DESCRIPTOR' : _CIRCLEGEOMETRYREQUEST, 1002 | '__module__' : 'threejs_pb2' 1003 | # @@protoc_insertion_point(class_scope:testservice.CircleGeometryRequest) 1004 | }) 1005 | _sym_db.RegisterMessage(CircleGeometryRequest) 1006 | 1007 | PlaneGeometryRequest = _reflection.GeneratedProtocolMessageType('PlaneGeometryRequest', (_message.Message,), { 1008 | 'DESCRIPTOR' : _PLANEGEOMETRYREQUEST, 1009 | '__module__' : 'threejs_pb2' 1010 | # @@protoc_insertion_point(class_scope:testservice.PlaneGeometryRequest) 1011 | }) 1012 | _sym_db.RegisterMessage(PlaneGeometryRequest) 1013 | 1014 | ExtrudeGeometryRequest = _reflection.GeneratedProtocolMessageType('ExtrudeGeometryRequest', (_message.Message,), { 1015 | 'DESCRIPTOR' : _EXTRUDEGEOMETRYREQUEST, 1016 | '__module__' : 'threejs_pb2' 1017 | # @@protoc_insertion_point(class_scope:testservice.ExtrudeGeometryRequest) 1018 | }) 1019 | _sym_db.RegisterMessage(ExtrudeGeometryRequest) 1020 | 1021 | 1022 | 1023 | _GEOMETRYSERVICE = _descriptor.ServiceDescriptor( 1024 | name='GeometryService', 1025 | full_name='testservice.GeometryService', 1026 | file=DESCRIPTOR, 1027 | index=0, 1028 | serialized_options=None, 1029 | create_key=_descriptor._internal_create_key, 1030 | serialized_start=1813, 1031 | serialized_end=2720, 1032 | methods=[ 1033 | _descriptor.MethodDescriptor( 1034 | name='BoxGeometry', 1035 | full_name='testservice.GeometryService.BoxGeometry', 1036 | index=0, 1037 | containing_service=None, 1038 | input_type=_BOXGEOMETRYREQUEST, 1039 | output_type=_GEOMETRYRESPONSE, 1040 | serialized_options=None, 1041 | create_key=_descriptor._internal_create_key, 1042 | ), 1043 | _descriptor.MethodDescriptor( 1044 | name='SphereGeometry', 1045 | full_name='testservice.GeometryService.SphereGeometry', 1046 | index=1, 1047 | containing_service=None, 1048 | input_type=_SPHEREGEOMETRYREQUEST, 1049 | output_type=_GEOMETRYRESPONSE, 1050 | serialized_options=None, 1051 | create_key=_descriptor._internal_create_key, 1052 | ), 1053 | _descriptor.MethodDescriptor( 1054 | name='TorusKnotGeometry', 1055 | full_name='testservice.GeometryService.TorusKnotGeometry', 1056 | index=2, 1057 | containing_service=None, 1058 | input_type=_TORUSKNOTGEOMETRYREQUEST, 1059 | output_type=_GEOMETRYRESPONSE, 1060 | serialized_options=None, 1061 | create_key=_descriptor._internal_create_key, 1062 | ), 1063 | _descriptor.MethodDescriptor( 1064 | name='IcosahedronGeometry', 1065 | full_name='testservice.GeometryService.IcosahedronGeometry', 1066 | index=3, 1067 | containing_service=None, 1068 | input_type=_ICOSAHEDRONGEOMETRYREQUEST, 1069 | output_type=_GEOMETRYRESPONSE, 1070 | serialized_options=None, 1071 | create_key=_descriptor._internal_create_key, 1072 | ), 1073 | _descriptor.MethodDescriptor( 1074 | name='TetrahedronGeometry', 1075 | full_name='testservice.GeometryService.TetrahedronGeometry', 1076 | index=4, 1077 | containing_service=None, 1078 | input_type=_TETRAHEDRONGEOMETRYREQUEST, 1079 | output_type=_GEOMETRYRESPONSE, 1080 | serialized_options=None, 1081 | create_key=_descriptor._internal_create_key, 1082 | ), 1083 | _descriptor.MethodDescriptor( 1084 | name='CylinderGeometry', 1085 | full_name='testservice.GeometryService.CylinderGeometry', 1086 | index=5, 1087 | containing_service=None, 1088 | input_type=_CYLINDERGEOMETRYREQUEST, 1089 | output_type=_GEOMETRYRESPONSE, 1090 | serialized_options=None, 1091 | create_key=_descriptor._internal_create_key, 1092 | ), 1093 | _descriptor.MethodDescriptor( 1094 | name='ConeGeometry', 1095 | full_name='testservice.GeometryService.ConeGeometry', 1096 | index=6, 1097 | containing_service=None, 1098 | input_type=_CONEGEOMETRYREQUEST, 1099 | output_type=_GEOMETRYRESPONSE, 1100 | serialized_options=None, 1101 | create_key=_descriptor._internal_create_key, 1102 | ), 1103 | _descriptor.MethodDescriptor( 1104 | name='CircleGeometry', 1105 | full_name='testservice.GeometryService.CircleGeometry', 1106 | index=7, 1107 | containing_service=None, 1108 | input_type=_CIRCLEGEOMETRYREQUEST, 1109 | output_type=_GEOMETRYRESPONSE, 1110 | serialized_options=None, 1111 | create_key=_descriptor._internal_create_key, 1112 | ), 1113 | _descriptor.MethodDescriptor( 1114 | name='PlaneGeometry', 1115 | full_name='testservice.GeometryService.PlaneGeometry', 1116 | index=8, 1117 | containing_service=None, 1118 | input_type=_PLANEGEOMETRYREQUEST, 1119 | output_type=_GEOMETRYRESPONSE, 1120 | serialized_options=None, 1121 | create_key=_descriptor._internal_create_key, 1122 | ), 1123 | _descriptor.MethodDescriptor( 1124 | name='ExtrudeGeometry', 1125 | full_name='testservice.GeometryService.ExtrudeGeometry', 1126 | index=9, 1127 | containing_service=None, 1128 | input_type=_EXTRUDEGEOMETRYREQUEST, 1129 | output_type=_GEOMETRYRESPONSE, 1130 | serialized_options=None, 1131 | create_key=_descriptor._internal_create_key, 1132 | ), 1133 | ]) 1134 | _sym_db.RegisterServiceDescriptor(_GEOMETRYSERVICE) 1135 | 1136 | DESCRIPTOR.services_by_name['GeometryService'] = _GEOMETRYSERVICE 1137 | 1138 | 1139 | _MATERIALSERVICE = _descriptor.ServiceDescriptor( 1140 | name='MaterialService', 1141 | full_name='testservice.MaterialService', 1142 | file=DESCRIPTOR, 1143 | index=1, 1144 | serialized_options=None, 1145 | create_key=_descriptor._internal_create_key, 1146 | serialized_start=2723, 1147 | serialized_end=3001, 1148 | methods=[ 1149 | _descriptor.MethodDescriptor( 1150 | name='BasicMaterial', 1151 | full_name='testservice.MaterialService.BasicMaterial', 1152 | index=0, 1153 | containing_service=None, 1154 | input_type=_BASICMATERIALREQUEST, 1155 | output_type=_MATERIALRESPONSE, 1156 | serialized_options=None, 1157 | create_key=_descriptor._internal_create_key, 1158 | ), 1159 | _descriptor.MethodDescriptor( 1160 | name='PhongMaterial', 1161 | full_name='testservice.MaterialService.PhongMaterial', 1162 | index=1, 1163 | containing_service=None, 1164 | input_type=_PHONGMATERIALREQUEST, 1165 | output_type=_MATERIALRESPONSE, 1166 | serialized_options=None, 1167 | create_key=_descriptor._internal_create_key, 1168 | ), 1169 | _descriptor.MethodDescriptor( 1170 | name='StandardMaterial', 1171 | full_name='testservice.MaterialService.StandardMaterial', 1172 | index=2, 1173 | containing_service=None, 1174 | input_type=_STANDARDMATERIALREQUEST, 1175 | output_type=_MATERIALRESPONSE, 1176 | serialized_options=None, 1177 | create_key=_descriptor._internal_create_key, 1178 | ), 1179 | ]) 1180 | _sym_db.RegisterServiceDescriptor(_MATERIALSERVICE) 1181 | 1182 | DESCRIPTOR.services_by_name['MaterialService'] = _MATERIALSERVICE 1183 | 1184 | # @@protoc_insertion_point(module_scope) 1185 | -------------------------------------------------------------------------------- /opengl_tutorial/_grpc/gen/threejs_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | """Client and server classes corresponding to protobuf-defined services.""" 3 | import grpc 4 | 5 | import threejs_pb2 as threejs__pb2 6 | 7 | 8 | class GeometryServiceStub(object): 9 | """Missing associated documentation comment in .proto file.""" 10 | 11 | def __init__(self, channel): 12 | """Constructor. 13 | 14 | Args: 15 | channel: A grpc.Channel. 16 | """ 17 | self.BoxGeometry = channel.unary_unary( 18 | '/testservice.GeometryService/BoxGeometry', 19 | request_serializer=threejs__pb2.BoxGeometryRequest.SerializeToString, 20 | response_deserializer=threejs__pb2.GeometryResponse.FromString, 21 | ) 22 | self.SphereGeometry = channel.unary_unary( 23 | '/testservice.GeometryService/SphereGeometry', 24 | request_serializer=threejs__pb2.SphereGeometryRequest.SerializeToString, 25 | response_deserializer=threejs__pb2.GeometryResponse.FromString, 26 | ) 27 | self.TorusKnotGeometry = channel.unary_unary( 28 | '/testservice.GeometryService/TorusKnotGeometry', 29 | request_serializer=threejs__pb2.TorusKnotGeometryRequest.SerializeToString, 30 | response_deserializer=threejs__pb2.GeometryResponse.FromString, 31 | ) 32 | self.IcosahedronGeometry = channel.unary_unary( 33 | '/testservice.GeometryService/IcosahedronGeometry', 34 | request_serializer=threejs__pb2.IcosahedronGeometryRequest.SerializeToString, 35 | response_deserializer=threejs__pb2.GeometryResponse.FromString, 36 | ) 37 | self.TetrahedronGeometry = channel.unary_unary( 38 | '/testservice.GeometryService/TetrahedronGeometry', 39 | request_serializer=threejs__pb2.TetrahedronGeometryRequest.SerializeToString, 40 | response_deserializer=threejs__pb2.GeometryResponse.FromString, 41 | ) 42 | self.CylinderGeometry = channel.unary_unary( 43 | '/testservice.GeometryService/CylinderGeometry', 44 | request_serializer=threejs__pb2.CylinderGeometryRequest.SerializeToString, 45 | response_deserializer=threejs__pb2.GeometryResponse.FromString, 46 | ) 47 | self.ConeGeometry = channel.unary_unary( 48 | '/testservice.GeometryService/ConeGeometry', 49 | request_serializer=threejs__pb2.ConeGeometryRequest.SerializeToString, 50 | response_deserializer=threejs__pb2.GeometryResponse.FromString, 51 | ) 52 | self.CircleGeometry = channel.unary_unary( 53 | '/testservice.GeometryService/CircleGeometry', 54 | request_serializer=threejs__pb2.CircleGeometryRequest.SerializeToString, 55 | response_deserializer=threejs__pb2.GeometryResponse.FromString, 56 | ) 57 | self.PlaneGeometry = channel.unary_unary( 58 | '/testservice.GeometryService/PlaneGeometry', 59 | request_serializer=threejs__pb2.PlaneGeometryRequest.SerializeToString, 60 | response_deserializer=threejs__pb2.GeometryResponse.FromString, 61 | ) 62 | self.ExtrudeGeometry = channel.unary_unary( 63 | '/testservice.GeometryService/ExtrudeGeometry', 64 | request_serializer=threejs__pb2.ExtrudeGeometryRequest.SerializeToString, 65 | response_deserializer=threejs__pb2.GeometryResponse.FromString, 66 | ) 67 | 68 | 69 | class GeometryServiceServicer(object): 70 | """Missing associated documentation comment in .proto file.""" 71 | 72 | def BoxGeometry(self, request, context): 73 | """Missing associated documentation comment in .proto file.""" 74 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 75 | context.set_details('Method not implemented!') 76 | raise NotImplementedError('Method not implemented!') 77 | 78 | def SphereGeometry(self, request, context): 79 | """Missing associated documentation comment in .proto file.""" 80 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 81 | context.set_details('Method not implemented!') 82 | raise NotImplementedError('Method not implemented!') 83 | 84 | def TorusKnotGeometry(self, request, context): 85 | """Missing associated documentation comment in .proto file.""" 86 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 87 | context.set_details('Method not implemented!') 88 | raise NotImplementedError('Method not implemented!') 89 | 90 | def IcosahedronGeometry(self, request, context): 91 | """Missing associated documentation comment in .proto file.""" 92 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 93 | context.set_details('Method not implemented!') 94 | raise NotImplementedError('Method not implemented!') 95 | 96 | def TetrahedronGeometry(self, request, context): 97 | """Missing associated documentation comment in .proto file.""" 98 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 99 | context.set_details('Method not implemented!') 100 | raise NotImplementedError('Method not implemented!') 101 | 102 | def CylinderGeometry(self, request, context): 103 | """Missing associated documentation comment in .proto file.""" 104 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 105 | context.set_details('Method not implemented!') 106 | raise NotImplementedError('Method not implemented!') 107 | 108 | def ConeGeometry(self, request, context): 109 | """Missing associated documentation comment in .proto file.""" 110 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 111 | context.set_details('Method not implemented!') 112 | raise NotImplementedError('Method not implemented!') 113 | 114 | def CircleGeometry(self, request, context): 115 | """Missing associated documentation comment in .proto file.""" 116 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 117 | context.set_details('Method not implemented!') 118 | raise NotImplementedError('Method not implemented!') 119 | 120 | def PlaneGeometry(self, request, context): 121 | """Missing associated documentation comment in .proto file.""" 122 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 123 | context.set_details('Method not implemented!') 124 | raise NotImplementedError('Method not implemented!') 125 | 126 | def ExtrudeGeometry(self, request, context): 127 | """Missing associated documentation comment in .proto file.""" 128 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 129 | context.set_details('Method not implemented!') 130 | raise NotImplementedError('Method not implemented!') 131 | 132 | 133 | def add_GeometryServiceServicer_to_server(servicer, server): 134 | rpc_method_handlers = { 135 | 'BoxGeometry': grpc.unary_unary_rpc_method_handler( 136 | servicer.BoxGeometry, 137 | request_deserializer=threejs__pb2.BoxGeometryRequest.FromString, 138 | response_serializer=threejs__pb2.GeometryResponse.SerializeToString, 139 | ), 140 | 'SphereGeometry': grpc.unary_unary_rpc_method_handler( 141 | servicer.SphereGeometry, 142 | request_deserializer=threejs__pb2.SphereGeometryRequest.FromString, 143 | response_serializer=threejs__pb2.GeometryResponse.SerializeToString, 144 | ), 145 | 'TorusKnotGeometry': grpc.unary_unary_rpc_method_handler( 146 | servicer.TorusKnotGeometry, 147 | request_deserializer=threejs__pb2.TorusKnotGeometryRequest.FromString, 148 | response_serializer=threejs__pb2.GeometryResponse.SerializeToString, 149 | ), 150 | 'IcosahedronGeometry': grpc.unary_unary_rpc_method_handler( 151 | servicer.IcosahedronGeometry, 152 | request_deserializer=threejs__pb2.IcosahedronGeometryRequest.FromString, 153 | response_serializer=threejs__pb2.GeometryResponse.SerializeToString, 154 | ), 155 | 'TetrahedronGeometry': grpc.unary_unary_rpc_method_handler( 156 | servicer.TetrahedronGeometry, 157 | request_deserializer=threejs__pb2.TetrahedronGeometryRequest.FromString, 158 | response_serializer=threejs__pb2.GeometryResponse.SerializeToString, 159 | ), 160 | 'CylinderGeometry': grpc.unary_unary_rpc_method_handler( 161 | servicer.CylinderGeometry, 162 | request_deserializer=threejs__pb2.CylinderGeometryRequest.FromString, 163 | response_serializer=threejs__pb2.GeometryResponse.SerializeToString, 164 | ), 165 | 'ConeGeometry': grpc.unary_unary_rpc_method_handler( 166 | servicer.ConeGeometry, 167 | request_deserializer=threejs__pb2.ConeGeometryRequest.FromString, 168 | response_serializer=threejs__pb2.GeometryResponse.SerializeToString, 169 | ), 170 | 'CircleGeometry': grpc.unary_unary_rpc_method_handler( 171 | servicer.CircleGeometry, 172 | request_deserializer=threejs__pb2.CircleGeometryRequest.FromString, 173 | response_serializer=threejs__pb2.GeometryResponse.SerializeToString, 174 | ), 175 | 'PlaneGeometry': grpc.unary_unary_rpc_method_handler( 176 | servicer.PlaneGeometry, 177 | request_deserializer=threejs__pb2.PlaneGeometryRequest.FromString, 178 | response_serializer=threejs__pb2.GeometryResponse.SerializeToString, 179 | ), 180 | 'ExtrudeGeometry': grpc.unary_unary_rpc_method_handler( 181 | servicer.ExtrudeGeometry, 182 | request_deserializer=threejs__pb2.ExtrudeGeometryRequest.FromString, 183 | response_serializer=threejs__pb2.GeometryResponse.SerializeToString, 184 | ), 185 | } 186 | generic_handler = grpc.method_handlers_generic_handler( 187 | 'testservice.GeometryService', rpc_method_handlers) 188 | server.add_generic_rpc_handlers((generic_handler,)) 189 | 190 | 191 | # This class is part of an EXPERIMENTAL API. 192 | class GeometryService(object): 193 | """Missing associated documentation comment in .proto file.""" 194 | 195 | @staticmethod 196 | def BoxGeometry(request, 197 | target, 198 | options=(), 199 | channel_credentials=None, 200 | call_credentials=None, 201 | insecure=False, 202 | compression=None, 203 | wait_for_ready=None, 204 | timeout=None, 205 | metadata=None): 206 | return grpc.experimental.unary_unary(request, target, '/testservice.GeometryService/BoxGeometry', 207 | threejs__pb2.BoxGeometryRequest.SerializeToString, 208 | threejs__pb2.GeometryResponse.FromString, 209 | options, channel_credentials, 210 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 211 | 212 | @staticmethod 213 | def SphereGeometry(request, 214 | target, 215 | options=(), 216 | channel_credentials=None, 217 | call_credentials=None, 218 | insecure=False, 219 | compression=None, 220 | wait_for_ready=None, 221 | timeout=None, 222 | metadata=None): 223 | return grpc.experimental.unary_unary(request, target, '/testservice.GeometryService/SphereGeometry', 224 | threejs__pb2.SphereGeometryRequest.SerializeToString, 225 | threejs__pb2.GeometryResponse.FromString, 226 | options, channel_credentials, 227 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 228 | 229 | @staticmethod 230 | def TorusKnotGeometry(request, 231 | target, 232 | options=(), 233 | channel_credentials=None, 234 | call_credentials=None, 235 | insecure=False, 236 | compression=None, 237 | wait_for_ready=None, 238 | timeout=None, 239 | metadata=None): 240 | return grpc.experimental.unary_unary(request, target, '/testservice.GeometryService/TorusKnotGeometry', 241 | threejs__pb2.TorusKnotGeometryRequest.SerializeToString, 242 | threejs__pb2.GeometryResponse.FromString, 243 | options, channel_credentials, 244 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 245 | 246 | @staticmethod 247 | def IcosahedronGeometry(request, 248 | target, 249 | options=(), 250 | channel_credentials=None, 251 | call_credentials=None, 252 | insecure=False, 253 | compression=None, 254 | wait_for_ready=None, 255 | timeout=None, 256 | metadata=None): 257 | return grpc.experimental.unary_unary(request, target, '/testservice.GeometryService/IcosahedronGeometry', 258 | threejs__pb2.IcosahedronGeometryRequest.SerializeToString, 259 | threejs__pb2.GeometryResponse.FromString, 260 | options, channel_credentials, 261 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 262 | 263 | @staticmethod 264 | def TetrahedronGeometry(request, 265 | target, 266 | options=(), 267 | channel_credentials=None, 268 | call_credentials=None, 269 | insecure=False, 270 | compression=None, 271 | wait_for_ready=None, 272 | timeout=None, 273 | metadata=None): 274 | return grpc.experimental.unary_unary(request, target, '/testservice.GeometryService/TetrahedronGeometry', 275 | threejs__pb2.TetrahedronGeometryRequest.SerializeToString, 276 | threejs__pb2.GeometryResponse.FromString, 277 | options, channel_credentials, 278 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 279 | 280 | @staticmethod 281 | def CylinderGeometry(request, 282 | target, 283 | options=(), 284 | channel_credentials=None, 285 | call_credentials=None, 286 | insecure=False, 287 | compression=None, 288 | wait_for_ready=None, 289 | timeout=None, 290 | metadata=None): 291 | return grpc.experimental.unary_unary(request, target, '/testservice.GeometryService/CylinderGeometry', 292 | threejs__pb2.CylinderGeometryRequest.SerializeToString, 293 | threejs__pb2.GeometryResponse.FromString, 294 | options, channel_credentials, 295 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 296 | 297 | @staticmethod 298 | def ConeGeometry(request, 299 | target, 300 | options=(), 301 | channel_credentials=None, 302 | call_credentials=None, 303 | insecure=False, 304 | compression=None, 305 | wait_for_ready=None, 306 | timeout=None, 307 | metadata=None): 308 | return grpc.experimental.unary_unary(request, target, '/testservice.GeometryService/ConeGeometry', 309 | threejs__pb2.ConeGeometryRequest.SerializeToString, 310 | threejs__pb2.GeometryResponse.FromString, 311 | options, channel_credentials, 312 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 313 | 314 | @staticmethod 315 | def CircleGeometry(request, 316 | target, 317 | options=(), 318 | channel_credentials=None, 319 | call_credentials=None, 320 | insecure=False, 321 | compression=None, 322 | wait_for_ready=None, 323 | timeout=None, 324 | metadata=None): 325 | return grpc.experimental.unary_unary(request, target, '/testservice.GeometryService/CircleGeometry', 326 | threejs__pb2.CircleGeometryRequest.SerializeToString, 327 | threejs__pb2.GeometryResponse.FromString, 328 | options, channel_credentials, 329 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 330 | 331 | @staticmethod 332 | def PlaneGeometry(request, 333 | target, 334 | options=(), 335 | channel_credentials=None, 336 | call_credentials=None, 337 | insecure=False, 338 | compression=None, 339 | wait_for_ready=None, 340 | timeout=None, 341 | metadata=None): 342 | return grpc.experimental.unary_unary(request, target, '/testservice.GeometryService/PlaneGeometry', 343 | threejs__pb2.PlaneGeometryRequest.SerializeToString, 344 | threejs__pb2.GeometryResponse.FromString, 345 | options, channel_credentials, 346 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 347 | 348 | @staticmethod 349 | def ExtrudeGeometry(request, 350 | target, 351 | options=(), 352 | channel_credentials=None, 353 | call_credentials=None, 354 | insecure=False, 355 | compression=None, 356 | wait_for_ready=None, 357 | timeout=None, 358 | metadata=None): 359 | return grpc.experimental.unary_unary(request, target, '/testservice.GeometryService/ExtrudeGeometry', 360 | threejs__pb2.ExtrudeGeometryRequest.SerializeToString, 361 | threejs__pb2.GeometryResponse.FromString, 362 | options, channel_credentials, 363 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 364 | 365 | 366 | class MaterialServiceStub(object): 367 | """Missing associated documentation comment in .proto file.""" 368 | 369 | def __init__(self, channel): 370 | """Constructor. 371 | 372 | Args: 373 | channel: A grpc.Channel. 374 | """ 375 | self.BasicMaterial = channel.unary_unary( 376 | '/testservice.MaterialService/BasicMaterial', 377 | request_serializer=threejs__pb2.BasicMaterialRequest.SerializeToString, 378 | response_deserializer=threejs__pb2.MaterialResponse.FromString, 379 | ) 380 | self.PhongMaterial = channel.unary_unary( 381 | '/testservice.MaterialService/PhongMaterial', 382 | request_serializer=threejs__pb2.PhongMaterialRequest.SerializeToString, 383 | response_deserializer=threejs__pb2.MaterialResponse.FromString, 384 | ) 385 | self.StandardMaterial = channel.unary_unary( 386 | '/testservice.MaterialService/StandardMaterial', 387 | request_serializer=threejs__pb2.StandardMaterialRequest.SerializeToString, 388 | response_deserializer=threejs__pb2.MaterialResponse.FromString, 389 | ) 390 | 391 | 392 | class MaterialServiceServicer(object): 393 | """Missing associated documentation comment in .proto file.""" 394 | 395 | def BasicMaterial(self, request, context): 396 | """Missing associated documentation comment in .proto file.""" 397 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 398 | context.set_details('Method not implemented!') 399 | raise NotImplementedError('Method not implemented!') 400 | 401 | def PhongMaterial(self, request, context): 402 | """Missing associated documentation comment in .proto file.""" 403 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 404 | context.set_details('Method not implemented!') 405 | raise NotImplementedError('Method not implemented!') 406 | 407 | def StandardMaterial(self, request, context): 408 | """Missing associated documentation comment in .proto file.""" 409 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 410 | context.set_details('Method not implemented!') 411 | raise NotImplementedError('Method not implemented!') 412 | 413 | 414 | def add_MaterialServiceServicer_to_server(servicer, server): 415 | rpc_method_handlers = { 416 | 'BasicMaterial': grpc.unary_unary_rpc_method_handler( 417 | servicer.BasicMaterial, 418 | request_deserializer=threejs__pb2.BasicMaterialRequest.FromString, 419 | response_serializer=threejs__pb2.MaterialResponse.SerializeToString, 420 | ), 421 | 'PhongMaterial': grpc.unary_unary_rpc_method_handler( 422 | servicer.PhongMaterial, 423 | request_deserializer=threejs__pb2.PhongMaterialRequest.FromString, 424 | response_serializer=threejs__pb2.MaterialResponse.SerializeToString, 425 | ), 426 | 'StandardMaterial': grpc.unary_unary_rpc_method_handler( 427 | servicer.StandardMaterial, 428 | request_deserializer=threejs__pb2.StandardMaterialRequest.FromString, 429 | response_serializer=threejs__pb2.MaterialResponse.SerializeToString, 430 | ), 431 | } 432 | generic_handler = grpc.method_handlers_generic_handler( 433 | 'testservice.MaterialService', rpc_method_handlers) 434 | server.add_generic_rpc_handlers((generic_handler,)) 435 | 436 | 437 | # This class is part of an EXPERIMENTAL API. 438 | class MaterialService(object): 439 | """Missing associated documentation comment in .proto file.""" 440 | 441 | @staticmethod 442 | def BasicMaterial(request, 443 | target, 444 | options=(), 445 | channel_credentials=None, 446 | call_credentials=None, 447 | insecure=False, 448 | compression=None, 449 | wait_for_ready=None, 450 | timeout=None, 451 | metadata=None): 452 | return grpc.experimental.unary_unary(request, target, '/testservice.MaterialService/BasicMaterial', 453 | threejs__pb2.BasicMaterialRequest.SerializeToString, 454 | threejs__pb2.MaterialResponse.FromString, 455 | options, channel_credentials, 456 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 457 | 458 | @staticmethod 459 | def PhongMaterial(request, 460 | target, 461 | options=(), 462 | channel_credentials=None, 463 | call_credentials=None, 464 | insecure=False, 465 | compression=None, 466 | wait_for_ready=None, 467 | timeout=None, 468 | metadata=None): 469 | return grpc.experimental.unary_unary(request, target, '/testservice.MaterialService/PhongMaterial', 470 | threejs__pb2.PhongMaterialRequest.SerializeToString, 471 | threejs__pb2.MaterialResponse.FromString, 472 | options, channel_credentials, 473 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 474 | 475 | @staticmethod 476 | def StandardMaterial(request, 477 | target, 478 | options=(), 479 | channel_credentials=None, 480 | call_credentials=None, 481 | insecure=False, 482 | compression=None, 483 | wait_for_ready=None, 484 | timeout=None, 485 | metadata=None): 486 | return grpc.experimental.unary_unary(request, target, '/testservice.MaterialService/StandardMaterial', 487 | threejs__pb2.StandardMaterialRequest.SerializeToString, 488 | threejs__pb2.MaterialResponse.FromString, 489 | options, channel_credentials, 490 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 491 | -------------------------------------------------------------------------------- /opengl_tutorial/_grpc/proto/threejs.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package testservice; 3 | 4 | service GeometryService { 5 | rpc BoxGeometry(BoxGeometryRequest) returns (GeometryResponse) {} 6 | rpc SphereGeometry(SphereGeometryRequest) returns (GeometryResponse) {} 7 | rpc TorusKnotGeometry(TorusKnotGeometryRequest) returns (GeometryResponse) {} 8 | rpc IcosahedronGeometry(IcosahedronGeometryRequest) returns (GeometryResponse) {} 9 | rpc TetrahedronGeometry(TetrahedronGeometryRequest) returns (GeometryResponse) {} 10 | rpc CylinderGeometry(CylinderGeometryRequest) returns (GeometryResponse) {} 11 | rpc ConeGeometry(ConeGeometryRequest) returns (GeometryResponse) {} 12 | rpc CircleGeometry(CircleGeometryRequest) returns (GeometryResponse) {} 13 | rpc PlaneGeometry(PlaneGeometryRequest) returns (GeometryResponse) {} 14 | rpc ExtrudeGeometry(ExtrudeGeometryRequest) returns (GeometryResponse) {} 15 | } 16 | 17 | service MaterialService { 18 | rpc BasicMaterial(BasicMaterialRequest) returns (MaterialResponse) {} 19 | rpc PhongMaterial(PhongMaterialRequest) returns (MaterialResponse) {} 20 | rpc StandardMaterial(StandardMaterialRequest) returns (MaterialResponse) {} 21 | } 22 | 23 | message BasicMaterialRequest {} 24 | message PhongMaterialRequest {} 25 | message StandardMaterialRequest {} 26 | 27 | message MaterialResponse { 28 | string vertex_shader = 1; 29 | string fragment_shader = 2; 30 | } 31 | 32 | message GeometryResponse { 33 | repeated float position = 1; 34 | repeated float normal = 2; 35 | repeated float uv = 3; 36 | repeated int32 index = 4; 37 | } 38 | 39 | message BoxGeometryRequest { 40 | float width = 1; 41 | float height = 2; 42 | float depth = 3; 43 | int32 width_segments = 4; 44 | int32 height_segments = 5; 45 | int32 depth_segments = 6; 46 | bool wireframe = 7; 47 | } 48 | 49 | message SphereGeometryRequest { 50 | float radius = 1; 51 | int32 width_segments = 2; 52 | int32 height_segments = 3; 53 | float phi_start = 4; 54 | float phi_length = 5; 55 | float theta_start = 6; 56 | float theta_length = 7; 57 | bool wireframe = 8; 58 | } 59 | 60 | message TorusKnotGeometryRequest { 61 | float torus_radius = 1; 62 | float tube_radius = 2; 63 | int32 tubular_segments = 3; 64 | int32 radial_segments = 4; 65 | int32 p = 5; 66 | int32 q = 6; 67 | bool wireframe = 7; 68 | } 69 | 70 | message IcosahedronGeometryRequest { 71 | float radius = 1; 72 | int32 detail = 2; 73 | bool wireframe = 3; 74 | } 75 | 76 | message TetrahedronGeometryRequest { 77 | float radius = 1; 78 | int32 detail = 2; 79 | bool wireframe = 3; 80 | } 81 | 82 | message CylinderGeometryRequest { 83 | float radius_top = 1; 84 | float radius_bottom = 2; 85 | float height = 3; 86 | int32 radial_segments = 4; 87 | int32 height_segments = 5; 88 | bool open_ended = 6; 89 | float theta_start = 7; 90 | float theta_length = 8; 91 | bool wireframe = 9; 92 | } 93 | 94 | message ConeGeometryRequest { 95 | float radius = 1; 96 | float height = 2; 97 | int32 radial_segments = 3; 98 | int32 height_segments = 4; 99 | bool open_ended = 5; 100 | float theta_start = 6; 101 | float theta_length = 7; 102 | bool wireframe = 8; 103 | } 104 | 105 | message CircleGeometryRequest { 106 | float radius = 1; 107 | int32 segments = 2; 108 | float theta_start = 3; 109 | float theta_length = 4; 110 | bool wireframe = 5; 111 | } 112 | 113 | message PlaneGeometryRequest { 114 | float width = 1; 115 | float height = 2; 116 | int32 width_segments = 3; 117 | int32 height_segments = 4; 118 | bool wireframe = 5; 119 | } 120 | 121 | message ExtrudeGeometryRequest { 122 | repeated float points = 1; 123 | repeated int32 path_indices = 2; 124 | int32 steps = 3; 125 | float depth = 4; 126 | bool bevel_enabled = 5; 127 | float bevel_thickness = 6; 128 | float bevel_size = 7; 129 | float bevel_offset = 8; 130 | int32 bevel_segments = 9; 131 | bool wireframe = 10; 132 | } 133 | -------------------------------------------------------------------------------- /opengl_tutorial/threejs/__init__.py: -------------------------------------------------------------------------------- 1 | from .geometry import * 2 | from .material import * 3 | from .three_mesh import * 4 | -------------------------------------------------------------------------------- /opengl_tutorial/threejs/geometry.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | class Geometry: 5 | def __init__(self, position, normal, index=None): 6 | num_vertices = len(position) // 3 7 | dtype = [("position", np.float32, (3,))] 8 | if len(normal) > 0: 9 | dtype.append(("normal", np.float32, (3,))) 10 | self.attributes = np.zeros( 11 | num_vertices, 12 | dtype=dtype, 13 | ) 14 | self.attributes["position"] = np.array(position).reshape((-1, 3)) 15 | if len(normal) > 0: 16 | self.attributes["normal"] = np.array(normal).reshape((-1, 3)) 17 | self.index = np.array(index) 18 | -------------------------------------------------------------------------------- /opengl_tutorial/threejs/material.py: -------------------------------------------------------------------------------- 1 | from manim.renderer.shader import Shader 2 | 3 | 4 | # Move the #extension directive earlier in the file. 5 | def move_extension_to_start(shader): 6 | return shader 7 | # shader_lines = shader.split("\n") 8 | # extension_line = None 9 | # for i, line in enumerate(shader_lines): 10 | # # if line.startswith("#extension"): 11 | # # extension_line = shader_lines.pop(i) 12 | # if "extension" in line: 13 | # print(line) 14 | # new_shader_lines = shader_lines 15 | # new_shader_lines.insert(1, extension_line) 16 | # return "\n".join(new_shader_lines) 17 | 18 | 19 | class Material(Shader): 20 | def __init__(self, context, vertex_shader, fragment_shader): 21 | super().__init__( 22 | context, 23 | source={ 24 | "vertex_shader": vertex_shader, 25 | "fragment_shader": fragment_shader, 26 | }, 27 | ) 28 | 29 | 30 | class BasicMaterial(Material): 31 | def __init__(self, context, vertex_shader, fragment_shader, config): 32 | super().__init__(context, vertex_shader, fragment_shader) 33 | defaults = { 34 | "diffuse": (1, 1, 1), 35 | "opacity": 1, 36 | } 37 | self.set_uniform("diffuse", tuple(config.get("diffuse", defaults["diffuse"]))) 38 | self.set_uniform("opacity", config.get("opacity", defaults["opacity"])) 39 | 40 | 41 | class PhongMaterial(Material): 42 | def __init__(self, context, vertex_shader, fragment_shader, config): 43 | fragment_shader = move_extension_to_start(fragment_shader) 44 | super().__init__(context, vertex_shader, fragment_shader) 45 | 46 | defaults = { 47 | "diffuse": (1, 1, 1), 48 | "emissive": (0, 0, 0), 49 | "specular": (1 / 15, 1 / 15, 1 / 15), 50 | "shininess": 30, 51 | "opacity": 1, 52 | } 53 | self.set_uniform("diffuse", tuple(config.get("diffuse", defaults["diffuse"]))) 54 | self.set_uniform( 55 | "emissive", tuple(config.get("emissive", defaults["emissive"])) 56 | ) 57 | self.set_uniform( 58 | "specular", tuple(config.get("specular", defaults["specular"])) 59 | ) 60 | self.set_uniform("shininess", config.get("shininess", defaults["shininess"])) 61 | self.set_uniform("opacity", config.get("opacity", defaults["opacity"])) 62 | 63 | 64 | class StandardMaterial(Material): 65 | def __init__(self, context, vertex_shader, fragment_shader, config): 66 | fragment_shader = move_extension_to_start(fragment_shader) 67 | super().__init__(context, vertex_shader, fragment_shader) 68 | 69 | defaults = { 70 | "diffuse": (1, 1, 1), 71 | "emissive": (0, 0, 0), 72 | "roughness": 1, 73 | "metalness": 0, 74 | "opacity": 1, 75 | } 76 | self.set_uniform("diffuse", tuple(config.get("diffuse", defaults["diffuse"]))) 77 | self.set_uniform( 78 | "emissive", tuple(config.get("emissive", defaults["emissive"])) 79 | ) 80 | self.set_uniform("roughness", config.get("roughness", defaults["roughness"])) 81 | self.set_uniform("metalness", config.get("metalness", defaults["metalness"])) 82 | self.set_uniform("opacity", config.get("opacity", defaults["opacity"])) 83 | -------------------------------------------------------------------------------- /opengl_tutorial/threejs/three_mesh.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | import numpy as np 4 | 5 | import manim.utils.opengl as opengl 6 | from manim.renderer.shader import Mesh 7 | from manim.utils.color import color_to_rgb 8 | 9 | 10 | class ThreeMesh(Mesh): 11 | def set_uniforms(self, renderer): 12 | from moderngl.program_members.uniform import Uniform 13 | 14 | for k, v in self.shader.shader_program._members.items(): 15 | view_matrix = renderer.camera.get_view_matrix(format=False) 16 | if isinstance(v, Uniform): 17 | if k == "viewMatrix": 18 | self.shader.set_uniform( 19 | "viewMatrix", opengl.matrix_to_shader_input(view_matrix) 20 | ) 21 | if k == "projectionMatrix": 22 | self.shader.set_uniform( 23 | "projectionMatrix", 24 | renderer.camera.projection_matrix, 25 | ) 26 | if k == "modelViewMatrix": 27 | self.shader.set_uniform( 28 | "modelViewMatrix", 29 | opengl.matrix_to_shader_input( 30 | view_matrix @ self.hierarchical_model_matrix() 31 | ), 32 | ) 33 | if k == "normalMatrix": 34 | self.shader.set_uniform( 35 | "normalMatrix", 36 | opengl.matrix_to_shader_input( 37 | view_matrix[:3, :3] @ self.hierarchical_normal_matrix() 38 | ), 39 | ) 40 | if k == "isOrthographic": 41 | self.shader.set_uniform( 42 | "isOrthographic", renderer.camera.orthographic 43 | ) 44 | point_lights_pattern = re.compile("pointLights\[(\d+)\]") 45 | point_lights_match = re.match(point_lights_pattern, k) 46 | if point_lights_match is not None: 47 | point_light_index = int(point_lights_match.group(1)) 48 | # TODO: Use a while loop and warn if the shader can't contain all of 49 | # the lights. 50 | if len(renderer.scene.point_lights) > point_light_index: 51 | point_light_config = renderer.scene.point_lights[ 52 | point_light_index 53 | ] 54 | camera_space_light_position = view_matrix @ np.array( 55 | point_light_config["position"] + [1] 56 | ) 57 | camera_space_light_position = camera_space_light_position[:3] 58 | 59 | self.shader.set_uniform( 60 | f"pointLights[{point_light_index}].position", 61 | tuple(camera_space_light_position), 62 | ) 63 | self.shader.set_uniform( 64 | f"pointLights[{point_light_index}].color", 65 | tuple(point_light_config["color"]), 66 | ) 67 | self.shader.set_uniform( 68 | f"pointLights[{point_light_index}].distance", 69 | point_light_config["distance"], 70 | ) 71 | self.shader.set_uniform( 72 | f"pointLights[{point_light_index}].decay", 73 | point_light_config["decay"], 74 | ) 75 | if ( 76 | k == "ambientLightColor" 77 | and renderer.scene.ambient_light is not None 78 | ): 79 | ambient_light_rgb = color_to_rgb( 80 | renderer.scene.ambient_light["color"] 81 | ) 82 | ambient_light_values = [ 83 | col * renderer.scene.ambient_light["intensity"] 84 | for col in ambient_light_rgb 85 | ] 86 | self.shader.set_uniform( 87 | "ambientLightColor", 88 | tuple(ambient_light_values), 89 | ) 90 | -------------------------------------------------------------------------------- /opengl_tutorial/tutorial.py: -------------------------------------------------------------------------------- 1 | import collections 2 | 3 | import dearpygui.core 4 | import numpy as np 5 | from colour import Color 6 | 7 | import manim.utils.opengl as opengl 8 | import manim.utils.space_ops as space_ops 9 | import tutorial_utils 10 | from manim import * 11 | from manim.opengl import * 12 | from manim.renderer.opengl_renderer import OpenGLCamera 13 | 14 | 15 | class GrpcAgain(Scene): 16 | def construct(self): 17 | self.point_lights.append( 18 | { 19 | "position": [0, 0, 0], 20 | "color": [7, 7, 1], 21 | "distance": 100, 22 | "decay": 1, 23 | } 24 | ) 25 | self.ambient_light = { 26 | "color": BLUE_B, 27 | "intensity": 0.5, 28 | } 29 | 30 | sun = tutorial_utils.get_three_mesh( 31 | self.renderer.context, 32 | geometry_config={ 33 | "name": "SphereGeometry", 34 | "width_segments": 18, 35 | "height_segments": 18, 36 | }, 37 | material_config={"name": "StandardMaterial", "emissive": (1, 1, 0)}, 38 | ) 39 | s = OpenGLSquare() 40 | self.add(s) 41 | self.add(sun) 42 | self.wait(0.5) 43 | 44 | 45 | class OpenGLPipeline(Scene): 46 | def construct(self): 47 | # self.skip_animation_preview = True 48 | diagrams = OpenGLVGroup() 49 | vertices = OpenGLVGroup( 50 | OpenGLDot().move_to(-1 * RIGHT + 2 * UP), 51 | OpenGLDot().move_to(-2 * RIGHT - 1 * UP), 52 | OpenGLDot().move_to(1 * RIGHT + 0 * UP), 53 | ).shift(0.5 * DOWN) 54 | 55 | vertices_label = OpenGLText("Vertices").next_to(vertices, UP, buff=0.5) 56 | vertex_data = ( 57 | OpenGLVGroup( 58 | OpenGLVGroup( 59 | OpenGLText("position: (x1, y1, z1)"), 60 | OpenGLText("normal: (nx1, ny1, nz1)").shift(DOWN), 61 | ), 62 | OpenGLVGroup( 63 | OpenGLText("position: (x2, y2, z2)").shift(2 * DOWN), 64 | OpenGLText("normal: (nx2, ny2, nz2)").shift(3 * DOWN), 65 | ).shift(0.5 * DOWN), 66 | OpenGLVGroup( 67 | OpenGLText("position: (x3, y3, z3)").shift(4 * DOWN), 68 | OpenGLText("normal: (nx3, ny3, nz3)").shift(5 * DOWN), 69 | ).shift(DOWN), 70 | ) 71 | .scale(0.5) 72 | .next_to(vertices_label, DOWN, buff=0.5) 73 | ) 74 | self.add(vertex_data, vertices_label) 75 | 76 | # Vertices. 77 | self.play(FadeIn(vertices_label, shift=DOWN)) 78 | self.interactive_embed() 79 | 80 | vertices_diagram = OpenGLVGroup(vertex_data, vertices_label) 81 | diagrams.add(vertices_diagram.copy()) 82 | 83 | # Vertex Shader. 84 | vertex_shader_label = OpenGLText("Vertex Shader").move_to( 85 | vertices_label.get_center() 86 | ) 87 | self.play(FadeOut(vertices_label, shift=UP)) 88 | self.play( 89 | FadeIn(vertex_shader_label, shift=DOWN), 90 | ReplacementTransform(vertex_data, vertices), 91 | ) 92 | 93 | vertex_shader_diagram = OpenGLVGroup(vertex_shader_label, vertices) 94 | diagrams.add(vertex_shader_diagram.copy()) 95 | self.interactive_embed() 96 | 97 | # self.skip_animation_preview = False 98 | # Primitives Generation. 99 | primitives_generation_label = OpenGLText("Primitives Generation").move_to( 100 | vertex_shader_label.get_center() 101 | ) 102 | self.play(FadeOut(vertex_shader_label, shift=UP)) 103 | self.play(FadeIn(primitives_generation_label, shift=DOWN)) 104 | triangle = OpenGLPolygon(*[dot.get_center() for dot in vertices]) 105 | self.play(Create(triangle)) 106 | self.play(FadeOut(vertices)) 107 | 108 | primitives_generation_diagram = OpenGLVGroup( 109 | primitives_generation_label, triangle 110 | ) 111 | diagrams.add(primitives_generation_diagram.copy()) 112 | self.interactive_embed() 113 | 114 | # Rasterization. 115 | rasterization_label = OpenGLText("Rasterization").move_to( 116 | primitives_generation_label.get_center() 117 | ) 118 | self.play(FadeOut(primitives_generation_label, shift=UP)) 119 | self.play(FadeIn(rasterization_label, shift=DOWN)) 120 | 121 | top_left = OpenGLDot( 122 | triangle.get_left()[0] * RIGHT + triangle.get_top()[1] * UP 123 | ) 124 | bottom_right = OpenGLDot( 125 | triangle.get_right()[0] * RIGHT + triangle.get_bottom()[1] * UP 126 | ) 127 | grid_width = bottom_right.get_center()[0] - top_left.get_center()[0] 128 | grid_height = top_left.get_center()[1] - bottom_right.get_center()[1] 129 | 130 | triangle_points = triangle.get_vertices() 131 | triangle_normals = [] 132 | for i in range(3): 133 | start = triangle_points[i] 134 | end = triangle_points[(i + 1) % 3] 135 | vec = end - start 136 | triangle_normals.append(normalize(rotate_vector(vec, PI / 2))) 137 | 138 | def point_inside_triangle(point: np.ndarray, triangle: OpenGLPolygon): 139 | triangle_points = triangle.get_vertices() 140 | for i in range(3): 141 | translated_point = point - triangle_points[i] 142 | dot_product = np.dot(translated_point, triangle_normals[i]) 143 | if dot_product < 0: 144 | return False 145 | return True 146 | 147 | square_side_length = 0.18 148 | pixels = OpenGLVGroup() 149 | outside_pixels = OpenGLVGroup() 150 | inside_pixels = OpenGLVGroup() 151 | for j in range(int(grid_height // square_side_length) + 1): 152 | for i in range(int(grid_width // square_side_length) + 1): 153 | # Place a square. 154 | dot_location = top_left.get_center().copy() 155 | dot_location += RIGHT * ((i + 0.5) * square_side_length) 156 | dot_location += DOWN * ((j + 0.5) * square_side_length) 157 | inside = point_inside_triangle(dot_location, triangle) 158 | if inside: 159 | fill_color = WHITE 160 | else: 161 | fill_color = BLACK 162 | pixel = OpenGLSquare( 163 | side_length=0.15, 164 | stroke_width=1, 165 | fill_color=fill_color, 166 | fill_opacity=1, 167 | ).move_to(dot_location) 168 | pixels.add(pixel) 169 | if inside: 170 | inside_pixels.add(pixel) 171 | else: 172 | outside_pixels.add(pixel) 173 | 174 | self.play(Create(pixels)) 175 | self.play(FadeOut(triangle), FadeOut(outside_pixels)) 176 | 177 | rasterization_diagram = OpenGLVGroup(rasterization_label, inside_pixels) 178 | diagrams.add(rasterization_diagram.copy()) 179 | self.interactive_embed() 180 | 181 | # Fragment Shader. 182 | fragment_shader_label = OpenGLText("Fragment Shader").move_to( 183 | rasterization_label.get_center() 184 | ) 185 | self.play(FadeOut(rasterization_label, shift=UP)) 186 | self.play(FadeIn(fragment_shader_label, shift=DOWN)) 187 | self.play(inside_pixels.animate.set_color(RED)) 188 | 189 | fragment_shader_diagram = OpenGLVGroup(fragment_shader_label, inside_pixels) 190 | diagrams.add(fragment_shader_diagram.copy()) 191 | self.interactive_embed() 192 | 193 | # Blending. 194 | blending_label = OpenGLText("Blending").move_to( 195 | fragment_shader_label.get_center() 196 | ) 197 | self.play(FadeOut(fragment_shader_label, shift=UP)) 198 | self.play(FadeIn(blending_label, shift=DOWN)) 199 | self.play(inside_pixels.animate.set_opacity(0.7)) 200 | 201 | blending_diagram = OpenGLVGroup(blending_label, inside_pixels) 202 | diagrams.add(blending_diagram.copy()) 203 | self.interactive_embed() 204 | 205 | # Frame Buffer. 206 | frame_buffer_label = OpenGLText("Frame Buffer").move_to( 207 | blending_label.get_center() 208 | ) 209 | self.play(FadeOut(blending_label, shift=UP)) 210 | self.play(FadeIn(frame_buffer_label, shift=DOWN)) 211 | triangle.set_color(RED).set_opacity(0.7) 212 | self.play(FadeOut(inside_pixels), FadeIn(triangle), run_time=1) 213 | 214 | frame_buffer_diagram = OpenGLVGroup(frame_buffer_label, triangle) 215 | diagrams.add(frame_buffer_diagram.copy()) 216 | self.interactive_embed() 217 | 218 | # self.skip_animation_preview = False 219 | diagrams.scale(0.5) 220 | diagrams[0].move_to(-4.5 * RIGHT + 2.7 * UP) 221 | diagrams[1].move_to(0 * RIGHT + 2.7 * UP) 222 | diagrams[2].move_to(4.5 * RIGHT + 2.7 * UP) 223 | 224 | diagrams[3].move_to(-4.5 * RIGHT + 0 * UP) 225 | diagrams[4].move_to(0 * RIGHT + 0 * UP) 226 | diagrams[5].move_to(4.5 * RIGHT + 0 * UP) 227 | 228 | diagrams[6].move_to(0 * RIGHT - 2.7 * UP) 229 | 230 | triangle_with_label = OpenGLVGroup(frame_buffer_label, triangle) 231 | triangle_with_label.generate_target() 232 | triangle_with_label.target.scale(0.5).move_to(diagrams[6].get_center()) 233 | self.play(MoveToTarget(triangle_with_label)) 234 | self.play(FadeIn(OpenGLVGroup(diagrams[:-1]))) 235 | 236 | self.interactive_embed() 237 | 238 | 239 | class HalfScreenTriangle(Scene): 240 | def construct(self): 241 | shader = Shader( 242 | self.renderer.context, 243 | source=dict( 244 | vertex_shader=""" 245 | #version 330 246 | in vec3 position; 247 | void main() { 248 | gl_Position = vec4(position, 1); 249 | } 250 | """, 251 | fragment_shader=""" 252 | #version 330 253 | out vec4 frag_color; 254 | void main() { 255 | frag_color = vec4(1, 0, 0, 1); 256 | } 257 | """, 258 | ), 259 | ) 260 | 261 | attributes = np.zeros(3, dtype=[("position", np.float32, (3,))]) 262 | attributes["position"] = np.array( 263 | [ 264 | [-1, -1, 0], 265 | [-1, 1, 0], 266 | [1, 1, 0], 267 | ] 268 | ) 269 | 270 | mesh = Mesh(shader, attributes) 271 | self.add(mesh) 272 | 273 | # Uncomment to move points. 274 | orthographic_camera = OpenGLCamera(orthographic=True) 275 | self.renderer.camera = orthographic_camera 276 | 277 | # Add labels. 278 | labels = [] 279 | dots = [] 280 | offsets = [UP + RIGHT, DOWN + RIGHT, DOWN + LEFT] 281 | 282 | def update_label(index): 283 | point = mesh.attributes["position"][index][:3] 284 | ndc_location = ( 285 | point 286 | * np.array([config["frame_x_radius"], config["frame_y_radius"], 1, 1])[ 287 | :3 288 | ] 289 | ) 290 | dots[index].move_to(ndc_location) 291 | labels[index].next_to(dots[index], offsets[index]) 292 | 293 | for i, point in enumerate(attributes["position"]): 294 | ndc_location = point * np.array( 295 | [config["frame_x_radius"], config["frame_y_radius"], 1] 296 | ) 297 | dot = OpenGLDot().move_to(ndc_location) 298 | dots.append(dot) 299 | 300 | label = OpenGLMathTex(f"P_{i}").next_to(dot, offsets[i]) 301 | labels.append(label) 302 | self.add(label) 303 | 304 | def closure(idx): 305 | dot.add_updater(lambda mob: update_label(idx)) 306 | 307 | closure(i) 308 | self.add(dot) 309 | 310 | def slider_callback(sender, data): 311 | point = dearpygui.core.get_value(sender) 312 | mesh.attributes["position"][data] = np.array(point) 313 | 314 | for i in range(3): 315 | self.widgets.append( 316 | { 317 | "name": f"P{i}", 318 | "widget": "slider_float3", 319 | "callback": slider_callback, 320 | "min_value": -2, 321 | "max_value": 2, 322 | "callback_data": i, 323 | "default_value": mesh.attributes["position"][i], 324 | } 325 | ) 326 | 327 | self.interactive_embed() 328 | 329 | 330 | class ProjectionVisualization(Scene): 331 | def construct(self): 332 | shader = Shader( 333 | self.renderer.context, 334 | source=dict( 335 | vertex_shader=""" 336 | #version 330 337 | 338 | in vec4 position; 339 | 340 | void main() { 341 | gl_Position = position; 342 | } 343 | """, 344 | fragment_shader=""" 345 | #version 330 346 | 347 | out vec4 frag_color; 348 | 349 | void main() { 350 | frag_color = vec4(1, 0, 0, 1); 351 | } 352 | """, 353 | ), 354 | ) 355 | 356 | attributes = np.zeros(3, dtype=[("position", np.float32, (4,))]) 357 | attributes["position"] = np.array( 358 | [ 359 | [-1, -1, 0, 1], 360 | [-1, 1, 0, 1], 361 | [1, 1, 0, 1], 362 | ] 363 | ) 364 | 365 | untransformed_mesh = Mesh(shader, attributes) 366 | 367 | transformed_mesh = untransformed_mesh.copy() 368 | 369 | width = config["frame_width"] 370 | height = config["frame_height"] 371 | depth = 20 372 | projection = np.array( 373 | [ 374 | [2 / width, 0, 0, 0], 375 | [0, 2 / height, 0, 0], 376 | [0, 0, 2 / depth, 0], 377 | [0, 0, 0, 1], 378 | ] 379 | ) 380 | transformed_mesh.attributes["position"] = ( 381 | projection @ transformed_mesh.attributes["position"].T 382 | ).T 383 | 384 | untransformed_points = untransformed_mesh.attributes["position"].copy() 385 | transformed_points = transformed_mesh.attributes["position"].copy() 386 | 387 | mesh = Mesh(shader, attributes) 388 | 389 | # t = 0 390 | # def update_mesh(mesh, dt): 391 | # nonlocal t 392 | # start = untransformed_points 393 | # end = transformed_points 394 | # v = 0.5 * np.sin(1.5 * t) + 0.5 395 | # mesh.attributes["position"] = (1 - v) * start + v * end 396 | # t += dt 397 | 398 | def slider_callback(sender, data): 399 | t = dearpygui.core.get_value(sender) 400 | start = untransformed_points 401 | end = transformed_points 402 | mesh.attributes["position"] = (1 - t) * start + t * end 403 | 404 | self.widgets.append( 405 | { 406 | "name": "scale", 407 | "widget": "slider_float", 408 | "callback": slider_callback, 409 | "min_value": 0, 410 | "max_value": 1, 411 | "default_value": 0, 412 | } 413 | ) 414 | 415 | # mesh.add_updater(update_mesh) 416 | self.add(mesh) 417 | 418 | self.renderer.camera = OpenGLCamera(orthographic=True) 419 | grid = tutorial_utils.get_grid_lines(7, 5) 420 | 421 | def toggle_grid_lines(): 422 | if grid in self.mobjects: 423 | self.remove(grid) 424 | else: 425 | self.add(grid) 426 | 427 | self.set_key_function(" ", toggle_grid_lines) 428 | 429 | self.interactive_embed() 430 | 431 | 432 | class InputTypes(Scene): 433 | def construct(self): 434 | shader = Shader( 435 | self.renderer.context, 436 | source=dict( 437 | vertex_shader=""" 438 | #version 330 439 | 440 | in vec4 position; 441 | uniform mat4 projection; 442 | out vec4 color; 443 | 444 | void main() { 445 | if (position.x > 0.0) { 446 | color = vec4(0, 0, 1, 1); 447 | } else { 448 | color = vec4(1, 0, 0, 1); 449 | } 450 | gl_Position = projection * position; 451 | } 452 | """, 453 | fragment_shader=""" 454 | #version 330 455 | 456 | in vec4 color; 457 | out vec4 frag_color; 458 | 459 | void main() { 460 | frag_color = color; 461 | } 462 | """, 463 | ), 464 | ) 465 | 466 | attributes = np.zeros(3, dtype=[("position", np.float32, (4,))]) 467 | attributes["position"] = np.array( 468 | [ 469 | [-1, -1, 0, 1], 470 | [-1, 1, 0, 1], 471 | [1, 1, 0, 1], 472 | ] 473 | ) 474 | 475 | mesh = Mesh(shader, attributes) 476 | 477 | width = config["frame_width"] 478 | height = config["frame_height"] 479 | depth = 20 480 | projection = np.array( 481 | [ 482 | [2 / width, 0, 0, 0], 483 | [0, 2 / height, 0, 0], 484 | [0, 0, 2 / depth, 0], 485 | [0, 0, 0, 1], 486 | ] 487 | ) 488 | mesh.shader.set_uniform("projection", tuple(projection.T.ravel())) 489 | 490 | self.add(mesh) 491 | 492 | self.renderer.camera = OpenGLCamera(orthographic=True) 493 | grid = tutorial_utils.get_grid_lines(7, 5) 494 | 495 | def toggle_grid_lines(): 496 | if grid in self.mobjects: 497 | self.remove(grid) 498 | else: 499 | self.add(grid) 500 | 501 | self.set_key_function(" ", toggle_grid_lines) 502 | 503 | self.interactive_embed() 504 | 505 | 506 | class ModelVisualization(Scene): 507 | def construct(self): 508 | shader = Shader( 509 | self.renderer.context, 510 | source=dict( 511 | vertex_shader=""" 512 | #version 330 513 | 514 | uniform mat4 projection; 515 | uniform mat4 model; 516 | in vec4 position; 517 | 518 | void main() { 519 | gl_Position = projection * model * position; 520 | } 521 | """, 522 | fragment_shader=""" 523 | #version 330 524 | 525 | out vec4 frag_color; 526 | 527 | void main() { 528 | frag_color = vec4(1, 0, 0, 1); 529 | } 530 | """, 531 | ), 532 | ) 533 | 534 | projection = opengl.orthographic_projection_matrix(near=-10, far=10) 535 | shader.shader_program["projection"] = projection 536 | 537 | model = tuple(np.eye(4).T.ravel()) 538 | shader.shader_program["model"] = model 539 | 540 | attributes = np.zeros(3, dtype=[("position", np.float32, (4,))]) 541 | attributes["position"] = np.array( 542 | [ 543 | [-1, -1, 0, 1], 544 | [-1, 1, 0, 1], 545 | [1, 1, 0, 1], 546 | ] 547 | ) 548 | 549 | mesh = Mesh(shader, attributes) 550 | self.add(mesh) 551 | 552 | translation_matrix = np.eye(4) 553 | rotation_matrix = np.eye(4) 554 | scale_matrix = np.eye(4) 555 | 556 | def update_mesh(mesh, dt): 557 | model_matrix = translation_matrix @ rotation_matrix @ scale_matrix 558 | mesh.shader.shader_program["model"] = tuple(model_matrix.T.ravel()) 559 | 560 | def translation_callback(sender, data): 561 | nonlocal translation_matrix 562 | coords = dearpygui.core.get_value(sender) 563 | translation_matrix = opengl.translation_matrix(*coords) 564 | 565 | def rotation_callback(sender, data): 566 | nonlocal rotation_matrix 567 | coords = dearpygui.core.get_value(sender) 568 | rotation_matrix = opengl.rotation_matrix(*coords) 569 | 570 | def scale_callback(sender, data): 571 | nonlocal scale_matrix 572 | val = dearpygui.core.get_value(sender) 573 | scale_matrix = opengl.scale_matrix(val) 574 | 575 | self.widgets.extend( 576 | [ 577 | { 578 | "name": "translation", 579 | "widget": "slider_float3", 580 | "callback": translation_callback, 581 | "min_value": -10, 582 | "max_value": 10, 583 | }, 584 | { 585 | "name": "rotation", 586 | "widget": "slider_float3", 587 | "callback": rotation_callback, 588 | "min_value": -PI, 589 | "max_value": PI, 590 | }, 591 | { 592 | "name": "scale", 593 | "widget": "slider_float", 594 | "callback": scale_callback, 595 | "min_value": -PI, 596 | "max_value": PI, 597 | "default_value": 1, 598 | }, 599 | ] 600 | ) 601 | 602 | mesh.add_updater(update_mesh) 603 | self.interactive_embed() 604 | 605 | 606 | class UpdateMesh(Scene): 607 | def construct(self): 608 | shader = Shader( 609 | self.renderer.context, 610 | source=dict( 611 | vertex_shader=""" 612 | #version 330 613 | 614 | uniform mat4 projection; 615 | uniform mat4 model; 616 | in vec4 position; 617 | 618 | void main() { 619 | gl_Position = projection * model * position; 620 | } 621 | """, 622 | fragment_shader=""" 623 | #version 330 624 | 625 | out vec4 frag_color; 626 | 627 | void main() { 628 | frag_color = vec4(1, 0, 0, 1); 629 | } 630 | """, 631 | ), 632 | ) 633 | 634 | projection = opengl.orthographic_projection_matrix(near=-10, far=10) 635 | shader.shader_program["projection"] = projection 636 | 637 | model = tuple(np.eye(4).T.ravel()) 638 | shader.shader_program["model"] = model 639 | 640 | attributes = np.zeros(6, dtype=[("position", np.float32, (4,))]) 641 | attributes["position"] = np.array( 642 | [ 643 | [-1, -1, 0, 1], 644 | [-1, 1, 0, 1], 645 | [1, 1, 0, 1], 646 | [-1, -1, 0, 1], 647 | [1, -1, 0, 1], 648 | [1, 1, 0, 1], 649 | ] 650 | ) 651 | mesh = Mesh(shader, attributes) 652 | 653 | # attributes = np.zeros(4, dtype=[("position", np.float32, (4,))]) 654 | # attributes["position"] = np.array( 655 | # [ 656 | # [-1, -1, 0, 1], 657 | # [-1, 1, 0, 1], 658 | # [1, 1, 0, 1], 659 | # [1, -1, 0, 1], 660 | # ] 661 | # ) 662 | # mesh = Mesh(shader, attributes, indices=np.array([0, 1, 2, 0, 3, 2])) 663 | 664 | self.add(mesh) 665 | 666 | t = 0.1 667 | 668 | def update_mesh(dt): 669 | nonlocal t 670 | transformation_matrix = np.array( 671 | [ 672 | [1, 0, 0, 0], 673 | [0, 1, 0, 0], 674 | [0, 0, 1, 0], 675 | [0, 0, 0, 1], 676 | ] 677 | ) 678 | t += dt 679 | mesh.shader.set_uniform("model", tuple(transformation_matrix.T.ravel())) 680 | 681 | self.add_updater(update_mesh) 682 | 683 | self.interactive_embed() 684 | 685 | 686 | class ViewIntuitionVisualization(Scene): 687 | def construct(self): 688 | shader = Shader( 689 | self.renderer.context, 690 | source=dict( 691 | vertex_shader=""" 692 | #version 330 693 | 694 | uniform mat4 projection; 695 | uniform mat4 view; 696 | uniform mat4 model; 697 | in vec4 position; 698 | 699 | void main() { 700 | gl_Position = projection * view * model * position; 701 | } 702 | """, 703 | fragment_shader=""" 704 | #version 330 705 | 706 | out vec4 frag_color; 707 | 708 | void main() { 709 | frag_color = vec4(1, 0, 0, 1); 710 | } 711 | """, 712 | ), 713 | ) 714 | 715 | projection = opengl.orthographic_projection_matrix(near=1, far=21) 716 | shader.shader_program["projection"] = projection 717 | 718 | camera_model = np.eye(4) 719 | camera_model[:3, 3] = np.array([0, 0, 11]) 720 | shader.shader_program["view"] = tuple(np.linalg.inv(camera_model).T.ravel()) 721 | 722 | model = tuple(np.eye(4).T.ravel()) 723 | shader.shader_program["model"] = model 724 | 725 | attributes = np.zeros(3, dtype=[("position", np.float32, (4,))]) 726 | attributes["position"] = np.array( 727 | [ 728 | [-1, -1, 0, 1], 729 | [-1, 1, 0, 1], 730 | [1, 1, 0, 1], 731 | ] 732 | ) 733 | 734 | mesh = Mesh(shader, attributes) 735 | self.add(mesh) 736 | 737 | translation_matrix = camera_model 738 | rotation_matrix = np.eye(4) 739 | scale_matrix = np.eye(4) 740 | 741 | def update_mesh(mesh, dt): 742 | camera_model_matrix = translation_matrix @ rotation_matrix @ scale_matrix 743 | view_matrix = np.linalg.inv(camera_model_matrix) 744 | mesh.shader.shader_program["view"] = tuple(view_matrix.T.ravel()) 745 | 746 | def translation_callback(sender, data): 747 | nonlocal translation_matrix 748 | coords = dearpygui.core.get_value(sender) 749 | translation_matrix = opengl.translation_matrix(*coords) 750 | 751 | def rotation_callback(sender, data): 752 | nonlocal rotation_matrix 753 | coords = dearpygui.core.get_value(sender) 754 | rotation_matrix = opengl.rotation_matrix(*coords) 755 | 756 | def scale_callback(sender, data): 757 | nonlocal scale_matrix 758 | val = dearpygui.core.get_value(sender) 759 | scale_matrix = opengl.scale_matrix(val) 760 | 761 | self.widgets.extend( 762 | [ 763 | { 764 | "name": "translation", 765 | "widget": "slider_float3", 766 | "callback": translation_callback, 767 | "min_value": -20, 768 | "max_value": 20, 769 | "default_value": (0, 0, 11), 770 | }, 771 | { 772 | "name": "rotation", 773 | "widget": "slider_float3", 774 | "callback": rotation_callback, 775 | "min_value": -PI, 776 | "max_value": PI, 777 | }, 778 | { 779 | "name": "scale", 780 | "widget": "slider_float", 781 | "callback": scale_callback, 782 | "min_value": -PI, 783 | "max_value": PI, 784 | "default_value": 1, 785 | }, 786 | ] 787 | ) 788 | 789 | mesh.add_updater(update_mesh) 790 | self.interactive_embed() 791 | 792 | 793 | class ViewVisualization(Scene): 794 | def construct(self): 795 | self.grid_size = 5 796 | grid = OpenGLVGroup() 797 | 798 | # Add x gridlines. 799 | for i in range(-self.grid_size, self.grid_size + 1): 800 | grid.add( 801 | OpenGLLine( 802 | UP * self.grid_size, DOWN * self.grid_size, stroke_opacity=0.5 803 | ).shift(RIGHT * i) 804 | ) 805 | 806 | # Add y gridlines. 807 | for i in range(-self.grid_size, self.grid_size + 1): 808 | grid.add( 809 | OpenGLLine( 810 | LEFT * self.grid_size, RIGHT * self.grid_size, stroke_opacity=0.5 811 | ).shift(UP * i) 812 | ) 813 | 814 | self.add(grid) 815 | 816 | # Add the axes. 817 | z_radius = self.grid_size * 0.55 818 | self.add( 819 | OpenGLLine(LEFT * self.grid_size, RIGHT * self.grid_size), 820 | OpenGLLine(UP * self.grid_size, DOWN * self.grid_size), 821 | OpenGLLine(IN * z_radius, OUT * z_radius), 822 | ) 823 | 824 | def look_at_camera(mob): 825 | tutorial_utils.look_at(mob, self.camera.get_position(), looking_axis="z") 826 | 827 | # Add axis labels. 828 | x_label_group = OpenGLVGroup() 829 | x_label_group.model_matrix = opengl.translation_matrix( 830 | x=self.grid_size + 0.3 831 | ) @ opengl.scale_matrix(1.2) 832 | 833 | y_label_group = OpenGLVGroup() 834 | y_label_group.model_matrix = opengl.translation_matrix( 835 | y=self.grid_size + 0.3 836 | ) @ opengl.scale_matrix(1.2) 837 | 838 | z_label_group = OpenGLVGroup() 839 | z_label_group.model_matrix = opengl.translation_matrix( 840 | z=z_radius + 0.3 841 | ) @ opengl.scale_matrix(1.2) 842 | 843 | x_label = OpenGLMathTex("x") 844 | y_label = OpenGLMathTex("y") 845 | z_label = OpenGLMathTex("z") 846 | 847 | x_label_group.add(x_label, update_parent=True) 848 | y_label_group.add(y_label, update_parent=True) 849 | z_label_group.add(z_label, update_parent=True) 850 | 851 | self.add_updater(lambda dt: look_at_camera(x_label)) 852 | self.add_updater(lambda dt: look_at_camera(y_label)) 853 | self.add_updater(lambda dt: look_at_camera(z_label)) 854 | 855 | self.add(x_label) 856 | self.add(y_label) 857 | self.add(z_label) 858 | 859 | shader = Shader(self.renderer.context, name="default") 860 | shader.set_uniform("u_color", (1.0, 0.0, 0.0, 1.0)) 861 | attributes = np.zeros(3, dtype=[("in_vert", np.float32, (3,))]) 862 | attributes["in_vert"] = np.array( 863 | [ 864 | [-1, -1, 0], 865 | [-1, 1, 0], 866 | [1, 1, 0], 867 | ] 868 | ) 869 | mesh = Mesh(shader, attributes) 870 | self.add(mesh) 871 | 872 | camera_cone = tutorial_utils.get_three_mesh( 873 | self.renderer.context, 874 | geometry_config={ 875 | "name": "ConeGeometry", 876 | "radius": 0.5, 877 | "height": 0.6, 878 | "radial_segments": 40, 879 | }, 880 | material_config={"name": "StandardMaterial"}, 881 | ) 882 | camera_cone.model_matrix = ( 883 | opengl.translation_matrix(z=-0.15) 884 | @ opengl.rotation_matrix(x=PI / 2) 885 | @ camera_cone.model_matrix 886 | ) 887 | 888 | camera_body = tutorial_utils.get_three_mesh( 889 | self.renderer.context, 890 | geometry_config={"name": "BoxGeometry"}, 891 | material_config={"name": "StandardMaterial"}, 892 | ) 893 | camera_body.model_matrix = ( 894 | opengl.translation_matrix(z=0.5) @ camera_cone.model_matrix 895 | ) 896 | camera_mesh = Object3D( 897 | camera_body, 898 | camera_cone, 899 | ) 900 | camera_mesh.model_matrix = ( 901 | opengl.scale_matrix(scale_factor=0.7) @ camera_mesh.model_matrix 902 | ) 903 | self.camera_indicator = camera_mesh 904 | self.add(self.camera_indicator) 905 | 906 | self.point_lights.append( 907 | { 908 | "position": [0, 0, 0], 909 | "color": [5, 5, 5], 910 | "distance": 100, 911 | "decay": 1, 912 | } 913 | ) 914 | self.ambient_light = { 915 | "color": BLUE_B, 916 | "intensity": 0.5, 917 | } 918 | 919 | self.camera_rotation_speed = 0.002 920 | self.pause_camera_rotation = False 921 | 922 | def update_rotating_camera(dt): 923 | if self.pause_camera_rotation: 924 | return 925 | nonlocal rotating_camera 926 | r, theta, phi = space_ops.cartesian_to_spherical( 927 | rotating_camera.get_position() 928 | ) 929 | rotating_camera.set_position( 930 | space_ops.spherical_to_cartesian( 931 | r, theta, phi + self.camera_rotation_speed 932 | ) 933 | ) 934 | tutorial_utils.look_at(rotating_camera, ORIGIN) 935 | 936 | self.camera_indicator.set_position(rotating_camera.get_position()) 937 | tutorial_utils.look_at(self.camera_indicator, ORIGIN) 938 | 939 | self.add_updater(update_rotating_camera) 940 | 941 | def update_camera_indicator(dt): 942 | self.camera_indicator.set_position(rotating_camera.get_position()) 943 | tutorial_utils.look_at(self.camera_indicator, ORIGIN) 944 | 945 | self.add_updater(update_camera_indicator) 946 | 947 | rotating_camera = self.camera 948 | self.camera.maximum_polar_angle = PI / 2 949 | self.camera.minimum_polar_angle = 0.0001 950 | self.camera.set_position(space_ops.spherical_to_cartesian(11, PI / 4, -PI / 2)) 951 | tutorial_utils.look_at(self.camera, ORIGIN) 952 | self.camera.default_model_matrix = self.camera.model_matrix 953 | 954 | stationary_camera = OpenGLCamera() 955 | stationary_camera.maximum_polar_angle = PI / 2 956 | stationary_camera.minimum_polar_angle = 0.0001 957 | stationary_camera.set_position( 958 | space_ops.spherical_to_cartesian(30, PI / 3, -3 * PI / 4) 959 | ) 960 | tutorial_utils.look_at(stationary_camera, ORIGIN) 961 | stationary_camera.default_model_matrix = stationary_camera.model_matrix 962 | 963 | # self.renderer.camera = stationary_camera 964 | self.renderer.camera = rotating_camera 965 | 966 | def switch_camera(): 967 | if self.renderer.camera is rotating_camera: 968 | self.renderer.camera = stationary_camera 969 | else: 970 | self.renderer.camera = rotating_camera 971 | 972 | self.set_key_function(" ", switch_camera) 973 | 974 | camera_frame = OpenGLVGroup() 975 | camera_frame.model_matrix = opengl.translation_matrix( 976 | z=-2 977 | ) @ opengl.scale_matrix(scale_factor=1 / 6) 978 | rotating_camera.add(camera_frame, update_parent=True) 979 | 980 | shader_code = OpenGLText( 981 | """ 982 | // Vertex Shader 983 | in vec3 in_vert; 984 | uniform mat4 model; 985 | uniform mat4 view; 986 | uniform mat4 projection; 987 | 988 | void main() { 989 | gl_Position = projection * view * model * vec4(in_vert, 1.0); 990 | } 991 | 992 | 993 | // Fragment Shader 994 | uniform vec4 u_color; 995 | out vec4 frag_color; 996 | 997 | void main() { 998 | frag_color = u_color; 999 | } 1000 | """, 1001 | font="monospace", 1002 | t2c={"[117:121]": BLUE, "[122:127]": RED}, 1003 | ) 1004 | shader_code.model_matrix = ( 1005 | opengl.translation_matrix(x=-3.0, y=2.0) 1006 | @ opengl.scale_matrix(scale_factor=0.3) 1007 | @ shader_code.model_matrix 1008 | ) 1009 | camera_frame.add(shader_code, update_parent=True) 1010 | self.add(shader_code) 1011 | 1012 | projection_matrix = DecimalMatrix( 1013 | np.linalg.inv(self.camera.model_matrix), 1014 | element_to_mobject_config={"edge_to_fix": RIGHT, "num_decimal_places": 1}, 1015 | ) 1016 | projection_matrix.model_matrix = ( 1017 | opengl.scale_matrix(scale_factor=0.5) @ projection_matrix.model_matrix 1018 | ) 1019 | 1020 | decimal_number_cache = collections.defaultdict(list) 1021 | 1022 | def update_matrix(mob, matrix, color): 1023 | nonlocal decimal_number_cache 1024 | used_numbers = collections.defaultdict(int) 1025 | for i in range(4): 1026 | for j in range(4): 1027 | old_mob = mob.elements[4 * i + j] 1028 | number = matrix[i][j] 1029 | rounded_number = round(number, 1) 1030 | if round(old_mob.number, 1) == rounded_number: 1031 | continue 1032 | full_config = {} 1033 | full_config.update(old_mob.initial_config) 1034 | full_config.update(config) 1035 | cache_list = decimal_number_cache[rounded_number] 1036 | cache_list_index = used_numbers[rounded_number] 1037 | if len(cache_list) > cache_list_index: 1038 | new_decimal = cache_list[cache_list_index] 1039 | else: 1040 | new_decimal = DecimalNumber(number, **full_config) 1041 | decimal_number_cache[rounded_number].append(new_decimal) 1042 | used_numbers[rounded_number] += 1 1043 | new_decimal.scale(old_mob[-1].height / new_decimal[-1].height) 1044 | 1045 | new_decimal.move_to(old_mob, old_mob.edge_to_fix) 1046 | old_mob.become(new_decimal) 1047 | 1048 | old_mob.number = number 1049 | brackets = mob.brackets 1050 | mob.remove(*mob.brackets) 1051 | brackets[0].next_to(mob, LEFT, mob.bracket_h_buff) 1052 | brackets[1].next_to(mob, RIGHT, mob.bracket_h_buff) 1053 | mob.brackets = OpenGLVGroup(*brackets) 1054 | mob.add(*brackets) 1055 | mob.set_style(fill_color=color) 1056 | 1057 | projection_matrix.add_updater( 1058 | lambda mob: update_matrix( 1059 | mob, np.linalg.inv(self.camera.model_matrix), BLUE 1060 | ) 1061 | ) 1062 | 1063 | view_label = OpenGLText("View Matrix =", fill_color=BLUE) 1064 | view_label.model_matrix = ( 1065 | opengl.translation_matrix(x=-2.7) 1066 | @ opengl.scale_matrix(scale_factor=0.5) 1067 | @ view_label.model_matrix 1068 | ) 1069 | 1070 | view_matrix_obj = OpenGLVGroup() 1071 | view_matrix_obj.add(projection_matrix, update_parent=True) 1072 | view_matrix_obj.add(view_label, update_parent=True) 1073 | 1074 | view_matrix_obj.model_matrix = ( 1075 | opengl.translation_matrix(x=-3, y=-1) @ view_matrix_obj.model_matrix 1076 | ) 1077 | 1078 | camera_frame.add(view_matrix_obj, update_parent=True) 1079 | self.add(view_matrix_obj) 1080 | 1081 | camera_button = tutorial_utils.get_button(self, "Pause Camera") 1082 | camera_button.model_matrix = ( 1083 | opengl.translation_matrix(x=2, y=-2) 1084 | @ opengl.scale_matrix(scale_factor=0.7) 1085 | @ camera_button.model_matrix 1086 | ) 1087 | camera_frame.add(camera_button, update_parent=True) 1088 | 1089 | def mouse_over_button(button): 1090 | if tutorial_utils.mouse_over_camera_frame_mob(self, button): 1091 | button.set_style(stroke_color=YELLOW, fill_color=YELLOW) 1092 | else: 1093 | button.set_style(stroke_color=WHITE, fill_color=WHITE) 1094 | 1095 | self.add_updater(lambda dt: mouse_over_button(camera_button)) 1096 | 1097 | def click_camera_button(): 1098 | if tutorial_utils.mouse_over_camera_frame_mob(self, camera_button): 1099 | self.pause_camera_rotation = not self.pause_camera_rotation 1100 | 1101 | self.mouse_press_callbacks.append(click_camera_button) 1102 | 1103 | self.model_rotation_speed = 0.012 1104 | self.pause_model_rotation = False 1105 | 1106 | def rotate_triangle(dt): 1107 | if self.pause_model_rotation: 1108 | return 1109 | mesh.model_matrix = ( 1110 | opengl.x_rotation_matrix(x=self.model_rotation_speed) 1111 | @ mesh.model_matrix 1112 | ) 1113 | 1114 | self.add_updater(rotate_triangle) 1115 | 1116 | model_matrix = DecimalMatrix( 1117 | np.linalg.inv(mesh.model_matrix), 1118 | element_to_mobject_config={"edge_to_fix": RIGHT, "num_decimal_places": 1}, 1119 | ) 1120 | model_matrix.model_matrix = ( 1121 | opengl.scale_matrix(scale_factor=0.5) @ model_matrix.model_matrix 1122 | ) 1123 | model_matrix.add_updater(lambda mob: update_matrix(mob, mesh.model_matrix, RED)) 1124 | 1125 | model_matrix_obj = OpenGLVGroup() 1126 | model_matrix_obj.add(model_matrix, update_parent=True) 1127 | 1128 | model_label = OpenGLText("Model Matrix =", fill_color=RED) 1129 | model_label.model_matrix = ( 1130 | opengl.translation_matrix(x=-2.7) 1131 | @ opengl.scale_matrix(scale_factor=0.5) 1132 | @ model_label.model_matrix 1133 | ) 1134 | model_matrix_obj.add(model_label, update_parent=True) 1135 | 1136 | model_matrix_obj.model_matrix = ( 1137 | opengl.translation_matrix(x=-3, y=-3) @ model_matrix_obj.model_matrix 1138 | ) 1139 | 1140 | camera_frame.add(model_matrix_obj, update_parent=True) 1141 | self.add(model_matrix_obj) 1142 | 1143 | model_button = tutorial_utils.get_button(self, "Pause Model") 1144 | model_button.model_matrix = ( 1145 | opengl.translation_matrix(x=2, y=-3) 1146 | @ opengl.scale_matrix(scale_factor=0.7) 1147 | @ model_button.model_matrix 1148 | ) 1149 | camera_frame.add(model_button, update_parent=True) 1150 | self.add_updater(lambda dt: mouse_over_button(model_button)) 1151 | 1152 | def click_model_button(): 1153 | if tutorial_utils.mouse_over_camera_frame_mob(self, model_button): 1154 | self.pause_model_rotation = not self.pause_model_rotation 1155 | 1156 | self.mouse_press_callbacks.append(click_model_button) 1157 | 1158 | self.interactive_embed() 1159 | # self.wait(10) 1160 | 1161 | 1162 | class BarycentricInterpolation(Scene): 1163 | def construct(self): 1164 | self.renderer.camera = OpenGLCamera(orthographic=True) 1165 | # self.skip_animation_preview = True 1166 | # config["background_color"] = "#656565" 1167 | self.triangle_corners = [ 1168 | np.array([-4, -3, 0]), 1169 | np.array([5, -3, 0]), 1170 | np.array([0, 1, 0]), 1171 | ] 1172 | self.triangle_corner_label_offsets = [ 1173 | np.array([-0.5, 0, 0]), 1174 | np.array([0.5, 0, 0]), 1175 | np.array([-0.5, 0, 0]), 1176 | ] 1177 | self.text_shift = OUT * 0.1 1178 | shader = Shader( 1179 | self.renderer.context, 1180 | source=dict( 1181 | vertex_shader=""" 1182 | #version 330 1183 | 1184 | in vec3 position; 1185 | in vec3 color; 1186 | out vec3 v_color; 1187 | uniform mat4 u_view_matrix; 1188 | uniform mat4 u_projection_matrix; 1189 | 1190 | void main() { 1191 | v_color = color; 1192 | gl_Position = u_projection_matrix * u_view_matrix * vec4(position, 1); 1193 | } 1194 | """, 1195 | fragment_shader=""" 1196 | #version 330 1197 | 1198 | in vec3 v_color; 1199 | out vec4 frag_color; 1200 | 1201 | void main() { 1202 | frag_color = vec4(v_color, 1); 1203 | } 1204 | """, 1205 | ), 1206 | ) 1207 | attributes = np.zeros( 1208 | 3, 1209 | dtype=[ 1210 | ("position", np.float32, (3,)), 1211 | ("color", np.float32, (3,)), 1212 | ], 1213 | ) 1214 | attributes["position"] = np.array(self.triangle_corners) 1215 | attributes["color"] = np.array( 1216 | [ 1217 | [2, 0, 0], 1218 | [0, 2, 0], 1219 | [0, 0, 2], 1220 | ] 1221 | ) 1222 | mesh = Mesh(attributes=attributes, shader=shader) 1223 | self.add(mesh) 1224 | 1225 | vertex_dots = OpenGLVGroup( 1226 | *[ 1227 | OpenGLDot(stroke_width=2.5, depth_test=True) 1228 | .move_to(point) 1229 | .shift(self.text_shift) 1230 | for point in self.triangle_corners 1231 | ] 1232 | ) 1233 | for dot, color in zip(vertex_dots, np.array([2 * RIGHT, 2 * UP, 2 * OUT])): 1234 | dot.data["fill_rgba"] = np.array([np.hstack((color, 1))]) 1235 | 1236 | self.fragment_location = 1.5 * DOWN + LEFT 1237 | vertex_fragment_lines = OpenGLVGroup( 1238 | *[ 1239 | OpenGLLine(point, self.fragment_location, depth_test=True).shift( 1240 | self.text_shift 1241 | ) 1242 | for point in self.triangle_corners 1243 | ] 1244 | ) 1245 | 1246 | fragment_variables = OpenGLVGroup( 1247 | *[ 1248 | OpenGLTex(f"$f_{i+1}$", depth_test=True).shift( 1249 | self.triangle_corners[i] 1250 | + self.triangle_corner_label_offsets[i] 1251 | + self.text_shift 1252 | ) 1253 | for i in range(3) 1254 | ], 1255 | ) 1256 | 1257 | interpolated_fragment = ( 1258 | OpenGLTex("$f$", stroke_width=2.5, depth_test=True) 1259 | .move_to(self.fragment_location) 1260 | .shift(0.25 * RIGHT + 0.2 * UP + self.text_shift) 1261 | ) 1262 | interpolated_dot = ( 1263 | OpenGLDot(stroke_width=2.5, depth_test=True) 1264 | .move_to(self.fragment_location) 1265 | .shift(self.text_shift) 1266 | ) 1267 | 1268 | # Draw lines. 1269 | self.play( 1270 | FadeIn(vertex_dots), 1271 | Create(vertex_fragment_lines), 1272 | Write(fragment_variables), 1273 | Write(interpolated_fragment), 1274 | FadeIn(interpolated_dot), 1275 | ) 1276 | self.interactive_embed() 1277 | 1278 | self.triangle_label_locations = [ 1279 | RIGHT * 1 + UP * -1, 1280 | RIGHT * -1.5 + UP * -1, 1281 | RIGHT * -0.5 + UP * -2.5, 1282 | ] 1283 | triangle_labels = OpenGLVGroup( 1284 | *[ 1285 | OpenGLTex(f"$A_{i+1}$", depth_test=True).shift( 1286 | self.triangle_label_locations[i] + self.text_shift 1287 | ) 1288 | for i in range(3) 1289 | ], 1290 | ) 1291 | self.play(Write(triangle_labels)) 1292 | self.interactive_embed() 1293 | 1294 | def update_mesh(mesh, dt): 1295 | mesh.attributes["position"][:, 0] -= 1.8 * dt 1296 | 1297 | mesh.add_updater(update_mesh) 1298 | self.play( 1299 | OpenGLVGroup( 1300 | vertex_fragment_lines, 1301 | vertex_dots, 1302 | triangle_labels, 1303 | fragment_variables, 1304 | interpolated_dot, 1305 | interpolated_fragment, 1306 | ).animate.shift(1.8 * LEFT), 1307 | rate_func=linear, 1308 | ) 1309 | self.triangle_corners += 1.8 * LEFT 1310 | self.fragment_location += 1.8 * LEFT 1311 | mesh.clear_updaters() 1312 | 1313 | lambda_definition = ( 1314 | OpenGLTex("$\lambda_i = \\frac{A_i}{A_1+A_2+A_3}$", depth_test=True) 1315 | .scale(1.2) 1316 | .shift(RIGHT * 3.5 + self.text_shift) 1317 | ) 1318 | self.play(Write(lambda_definition)) 1319 | 1320 | barycentric_interpolation_description = OpenGLTex( 1321 | "The Barycentric interpolation\\\\$f$ of a fragment $p$ is given by\\\\$f=\lambda_1f_1+\lambda_2f_2+\lambda_3f_3$" 1322 | ).to_edge(UP) 1323 | self.play(Write(barycentric_interpolation_description)) 1324 | 1325 | def point_inside_triangle(point): 1326 | triangle_vectors = np.array( 1327 | [ 1328 | self.triangle_corners[1] - self.triangle_corners[0], 1329 | self.triangle_corners[2] - self.triangle_corners[1], 1330 | self.triangle_corners[0] - self.triangle_corners[2], 1331 | ] 1332 | ) 1333 | point_vectors = np.array( 1334 | [ 1335 | point - self.triangle_corners[0], 1336 | point - self.triangle_corners[1], 1337 | point - self.triangle_corners[2], 1338 | ] 1339 | ) 1340 | crosses = np.cross(triangle_vectors, point_vectors) 1341 | return np.all(crosses[:, 2] > 0) 1342 | 1343 | def triangle_area(p0, p1, p2): 1344 | vectors = np.array([p1 - p0, p2 - p1, p0 - p2]) 1345 | norms = np.linalg.norm(vectors, axis=1) 1346 | s = 0.5 * np.sum(norms) 1347 | return np.sqrt(s * (s - norms[0]) * (s - norms[1]) * (s - norms[2])) 1348 | 1349 | def update_color(dt): 1350 | mouse_point = self.mouse_point.get_center() 1351 | if point_inside_triangle(mouse_point): 1352 | update_triangle_mobjects(mouse_point) 1353 | 1354 | def update_triangle_mobjects(point): 1355 | for i, line in enumerate(vertex_fragment_lines): 1356 | line.put_start_and_end_on(point, self.triangle_corners[i]) 1357 | line.shift(self.text_shift) 1358 | interpolated_dot.move_to(point + self.text_shift) 1359 | interpolated_fragment.move_to( 1360 | point + 0.25 * LEFT + 0.27 * UP + self.text_shift 1361 | ) 1362 | triangle_areas = [] 1363 | for i, label in enumerate(triangle_labels): 1364 | triangle_points = np.array( 1365 | [ 1366 | point, 1367 | self.triangle_corners[(i + 1) % 3], 1368 | self.triangle_corners[(i + 2) % 3], 1369 | ], 1370 | ) 1371 | location = np.sum(triangle_points, axis=0) / 3 1372 | label.move_to(location + self.text_shift) 1373 | triangle_areas.append(triangle_area(*triangle_points)) 1374 | 1375 | total_triangle_area = np.sum(triangle_areas) 1376 | lambdas = np.array([area / total_triangle_area for area in triangle_areas]) 1377 | colors = np.array([2 * RIGHT, 2 * UP, 2 * OUT]) 1378 | fragment_color_vec = np.sum(lambdas * colors, axis=0) 1379 | interpolated_dot.data["fill_rgba"] = np.array( 1380 | [np.hstack((fragment_color_vec, 1))] 1381 | ) 1382 | interpolated_fragment[0][0].data["fill_rgba"] = np.array( 1383 | [np.hstack((fragment_color_vec, 1))] 1384 | ) 1385 | barycentric_interpolation_description[0][49].data["fill_rgba"] = np.array( 1386 | [np.hstack((fragment_color_vec, 1))] 1387 | ) 1388 | 1389 | update_triangle_mobjects(self.fragment_location) 1390 | barycentric_interpolation_description[0][53].data["fill_rgba"] = np.array( 1391 | [[1, 0, 0, 1]] 1392 | ) 1393 | barycentric_interpolation_description[0][54].data["fill_rgba"] = np.array( 1394 | [[1, 0, 0, 1]] 1395 | ) 1396 | barycentric_interpolation_description[0][58].data["fill_rgba"] = np.array( 1397 | [[0, 1, 0, 1]] 1398 | ) 1399 | barycentric_interpolation_description[0][59].data["fill_rgba"] = np.array( 1400 | [[0, 1, 0, 1]] 1401 | ) 1402 | barycentric_interpolation_description[0][63].data["fill_rgba"] = np.array( 1403 | [[0, 0, 1, 1]] 1404 | ) 1405 | barycentric_interpolation_description[0][64].data["fill_rgba"] = np.array( 1406 | [[0, 0, 1, 1]] 1407 | ) 1408 | for fragment_variable, color in zip( 1409 | fragment_variables, np.array([2 * RIGHT, 2 * UP, 2 * OUT]) 1410 | ): 1411 | for child in fragment_variable[0]: 1412 | child.data["fill_rgba"] = np.array([np.hstack((color, 1))]) 1413 | 1414 | self.add_updater(update_color) 1415 | # self.skip_animation_preview = False 1416 | 1417 | self.interactive_embed() 1418 | 1419 | 1420 | class HierarchicalModelMatrices(Scene): 1421 | def construct(self): 1422 | self.point_lights.append( 1423 | { 1424 | "position": [0, 0, 0], 1425 | "color": [7, 7, 1], 1426 | "distance": 100, 1427 | "decay": 1, 1428 | } 1429 | ) 1430 | self.ambient_light = { 1431 | "color": BLUE_B, 1432 | "intensity": 0.5, 1433 | } 1434 | 1435 | sun = tutorial_utils.get_three_mesh( 1436 | self.renderer.context, 1437 | geometry_config={ 1438 | "name": "SphereGeometry", 1439 | "width_segments": 18, 1440 | "height_segments": 18, 1441 | }, 1442 | material_config={"name": "StandardMaterial", "emissive": (1, 1, 0)}, 1443 | ) 1444 | self.add(sun) 1445 | 1446 | earth = tutorial_utils.get_three_mesh( 1447 | self.renderer.context, 1448 | geometry_config={ 1449 | "name": "SphereGeometry", 1450 | "width_segments": 18, 1451 | "height_segments": 18, 1452 | }, 1453 | material_config={"name": "PhongMaterial", "diffuse": (0.3, 0.3, 1)}, 1454 | ) 1455 | earth.model_matrix = ( 1456 | opengl.translation_matrix(x=5) 1457 | @ opengl.scale_matrix(0.5) 1458 | @ earth.model_matrix 1459 | ) 1460 | sun.add(earth) 1461 | 1462 | moon = tutorial_utils.get_three_mesh( 1463 | self.renderer.context, 1464 | geometry_config={ 1465 | "name": "SphereGeometry", 1466 | "width_segments": 18, 1467 | "height_segments": 18, 1468 | }, 1469 | material_config={"name": "PhongMaterial", "diffuse": (0.3, 0.3, 1)}, 1470 | ) 1471 | moon.model_matrix = ( 1472 | opengl.translation_matrix(x=3) 1473 | @ opengl.scale_matrix(0.3) 1474 | @ moon.model_matrix 1475 | ) 1476 | earth.add(moon) 1477 | 1478 | def update_sun(mob, dt): 1479 | sun.model_matrix = opengl.z_rotation_matrix(z=0.01) @ sun.model_matrix 1480 | sun.normal_matrix = opengl.z_rotation_matrix(z=0.01) @ sun.normal_matrix 1481 | 1482 | sun.add_updater(update_sun) 1483 | 1484 | def update_earth(mob, dt): 1485 | rotation_about_axis = ( 1486 | opengl.translation_matrix(x=5) 1487 | @ opengl.z_rotation_matrix(z=0.05) 1488 | @ opengl.translation_matrix(x=-5) 1489 | ) 1490 | earth.model_matrix = rotation_about_axis @ earth.model_matrix 1491 | earth.normal_matrix = rotation_about_axis @ earth.normal_matrix 1492 | 1493 | earth.add_updater(update_earth) 1494 | 1495 | self.interactive_embed() 1496 | 1497 | 1498 | class Gallery(Scene): 1499 | def construct(self): 1500 | config["background_color"] = "#2A2A2A" 1501 | default_light_position = [3, 3, 2] 1502 | self.point_lights.append( 1503 | { 1504 | "position": default_light_position, 1505 | "color": [5, 5, 5], 1506 | "distance": 100, 1507 | "decay": 1, 1508 | } 1509 | ) 1510 | self.ambient_light = { 1511 | "color": BLUE_B, 1512 | "intensity": 0.5, 1513 | } 1514 | 1515 | self.light_indicator = tutorial_utils.get_three_mesh( 1516 | self.renderer.context, 1517 | geometry_config={ 1518 | "name": "IcosahedronGeometry", 1519 | }, 1520 | material_config={ 1521 | "name": "BasicMaterial", 1522 | }, 1523 | ) 1524 | self.light_indicator.model_matrix = opengl.scale_matrix(1 / 3) 1525 | self.light_indicator.model_matrix[:3, 3] = np.array( 1526 | np.array(default_light_position) 1527 | ) 1528 | self.add(self.light_indicator) 1529 | 1530 | default_geometry_name = "BoxGeometry" 1531 | default_material_name = "StandardMaterial" 1532 | default_diffuse = np.array([55, 55, 125]) / 255 1533 | default_mesh = tutorial_utils.get_three_mesh( 1534 | self.renderer.context, 1535 | geometry_config={ 1536 | "name": default_geometry_name, 1537 | }, 1538 | material_config={ 1539 | "name": default_material_name, 1540 | "diffuse": default_diffuse, 1541 | }, 1542 | ) 1543 | self.add(default_mesh) 1544 | 1545 | current_geometry_name = default_geometry_name 1546 | new_geometry_name = default_geometry_name 1547 | current_material_name = default_material_name 1548 | new_material_name = default_material_name 1549 | current_mesh = default_mesh 1550 | 1551 | def update_mesh(dt): 1552 | nonlocal current_geometry_name, current_material_name, current_mesh 1553 | if ( 1554 | current_geometry_name != new_geometry_name 1555 | or current_material_name != new_material_name 1556 | ): 1557 | new_mesh = tutorial_utils.get_three_mesh( 1558 | self.renderer.context, 1559 | geometry_config={ 1560 | "name": new_geometry_name, 1561 | }, 1562 | material_config={ 1563 | "name": new_material_name, 1564 | "diffuse": default_diffuse, 1565 | }, 1566 | ) 1567 | self.remove(current_mesh) 1568 | self.add(new_mesh) 1569 | current_mesh = new_mesh 1570 | current_geometry_name = new_geometry_name 1571 | current_material_name = new_material_name 1572 | 1573 | self.add_updater(update_mesh) 1574 | 1575 | def geometry_callback(sender, data): 1576 | nonlocal new_geometry_name 1577 | new_geometry_name = dearpygui.core.get_value(sender) 1578 | 1579 | def material_callback(sender, data): 1580 | nonlocal new_material_name 1581 | new_material_name = dearpygui.core.get_value(sender) 1582 | 1583 | def background_color_callback(sender, data): 1584 | arr = np.array(dearpygui.core.get_value(sender))[:3] / 255 1585 | color = Color(rgb=tuple(arr)) 1586 | config["background_color"] = color.hex_l 1587 | 1588 | def light_position_callback(sender, data): 1589 | self.point_lights[0]["position"] = list(dearpygui.core.get_value(sender)) 1590 | self.light_indicator.model_matrix[:3, 3] = np.array( 1591 | dearpygui.core.get_value(sender) 1592 | ) 1593 | 1594 | self.widgets.append( 1595 | { 1596 | "name": "geometry", 1597 | "widget": "combo", 1598 | "items": [ 1599 | "SphereGeometry", 1600 | "BoxGeometry", 1601 | "TorusKnotGeometry", 1602 | "IcosahedronGeometry", 1603 | "TetrahedronGeometry", 1604 | "CylinderGeometry", 1605 | "ConeGeometry", 1606 | "CircleGeometry", 1607 | "PlaneGeometry", 1608 | ], 1609 | "default_value": default_geometry_name, 1610 | "callback": geometry_callback, 1611 | } 1612 | ) 1613 | self.widgets.append( 1614 | { 1615 | "name": "material", 1616 | "widget": "combo", 1617 | "items": [ 1618 | "BasicMaterial", 1619 | "PhongMaterial", 1620 | "StandardMaterial", 1621 | ], 1622 | "default_value": default_material_name, 1623 | "callback": material_callback, 1624 | } 1625 | ) 1626 | self.widgets.append( 1627 | { 1628 | "name": "background color", 1629 | "widget": "color_edit3", 1630 | "callback": background_color_callback, 1631 | "default_value": (42, 42, 42, 0), 1632 | } 1633 | ) 1634 | self.widgets.append( 1635 | { 1636 | "name": "light position", 1637 | "widget": "slider_float3", 1638 | "callback": light_position_callback, 1639 | "min_value": -20, 1640 | "max_value": 20, 1641 | "default_value": tuple(default_light_position), 1642 | } 1643 | ) 1644 | 1645 | translation_matrix = np.eye(4) 1646 | rotation_matrix = np.eye(4) 1647 | scale_matrix = np.eye(4) 1648 | 1649 | def update_mesh(dt): 1650 | model_matrix = translation_matrix @ rotation_matrix @ scale_matrix 1651 | current_mesh.model_matrix = model_matrix 1652 | current_mesh.normal_matrix = model_matrix 1653 | 1654 | def translation_callback(sender, data): 1655 | nonlocal translation_matrix 1656 | coords = dearpygui.core.get_value(sender) 1657 | translation_matrix = opengl.translation_matrix(*coords) 1658 | 1659 | def rotation_callback(sender, data): 1660 | nonlocal rotation_matrix 1661 | coords = dearpygui.core.get_value(sender) 1662 | rotation_matrix = opengl.rotation_matrix(*coords) 1663 | 1664 | def scale_callback(sender, data): 1665 | nonlocal scale_matrix 1666 | val = dearpygui.core.get_value(sender) 1667 | scale_matrix = opengl.scale_matrix(val) 1668 | 1669 | self.widgets.extend( 1670 | [ 1671 | { 1672 | "name": "translation", 1673 | "widget": "slider_float3", 1674 | "callback": translation_callback, 1675 | "min_value": -10, 1676 | "max_value": 10, 1677 | }, 1678 | { 1679 | "name": "rotation", 1680 | "widget": "slider_float3", 1681 | "callback": rotation_callback, 1682 | "min_value": -PI, 1683 | "max_value": PI, 1684 | }, 1685 | { 1686 | "name": "scale", 1687 | "widget": "slider_float", 1688 | "callback": scale_callback, 1689 | "min_value": -PI, 1690 | "max_value": PI, 1691 | "default_value": 1, 1692 | }, 1693 | ] 1694 | ) 1695 | 1696 | self.add_updater(update_mesh) 1697 | 1698 | self.interactive_embed() 1699 | 1700 | 1701 | class ThreeDLogo(Scene): 1702 | def construct(self): 1703 | config["background_color"] = "#ece6e2" 1704 | self.renderer.camera = OpenGLCamera(orthographic=True) 1705 | self.point_lights.append( 1706 | { 1707 | "position": [12, -12, 12], 1708 | "color": [5, 5, 5], 1709 | "distance": 100, 1710 | "decay": 5, 1711 | } 1712 | ) 1713 | self.ambient_light = { 1714 | "color": WHITE, 1715 | "intensity": 0.5, 1716 | } 1717 | 1718 | letter = OpenGLMathTex("\\mathbb{M}").scale_to_fit_height(3) 1719 | 1720 | subpaths = letter[0][0].get_subpaths() 1721 | num_points = 0 1722 | path_indices = [] 1723 | for path in subpaths: 1724 | path_indices.append(num_points) 1725 | num_points += len(path) 1726 | points = np.empty((num_points, 3)) 1727 | 1728 | i = 0 1729 | for path in subpaths: 1730 | points[i : i + path.shape[0]] = path 1731 | i += path.shape[0] 1732 | 1733 | extrude = tutorial_utils.get_three_mesh( 1734 | self.renderer.context, 1735 | geometry_config={ 1736 | "name": "ExtrudeGeometry", 1737 | "points": points.ravel(), 1738 | "path_indices": path_indices, 1739 | "depth": 0.7, 1740 | }, 1741 | material_config={ 1742 | "name": "PhongMaterial", 1743 | "diffuse": (0.09, 0.09, 0.09), 1744 | }, 1745 | ) 1746 | extrude.model_matrix = opengl.translation_matrix(x=-1) @ extrude.model_matrix 1747 | 1748 | circle_diffuse = np.array([95, 165, 95]) / 255 1749 | square_diffuse = np.array([55, 55, 125]) / 255 1750 | triangle_diffuse = np.array([180, 80, 30]) / 255 1751 | 1752 | circle = tutorial_utils.get_three_mesh( 1753 | self.renderer.context, 1754 | geometry_config={ 1755 | "name": "SphereGeometry", 1756 | "width_segments": 30, 1757 | "height_segments": 30, 1758 | }, 1759 | material_config={"name": "StandardMaterial", "diffuse": circle_diffuse}, 1760 | ) 1761 | circle.model_matrix = ( 1762 | opengl.translation_matrix(x=0.5, y=-2, z=-1.35) 1763 | @ opengl.scale_matrix(scale_factor=1.25) 1764 | @ circle.model_matrix 1765 | ) 1766 | 1767 | square = tutorial_utils.get_three_mesh( 1768 | self.renderer.context, 1769 | geometry_config={ 1770 | "name": "BoxGeometry", 1771 | }, 1772 | material_config={"name": "StandardMaterial", "diffuse": square_diffuse}, 1773 | ) 1774 | square.model_matrix = ( 1775 | opengl.translation_matrix(x=1.7, y=-0.8, z=-4) 1776 | @ opengl.scale_matrix(scale_factor=2.4) 1777 | @ square.model_matrix 1778 | ) 1779 | 1780 | triangle = tutorial_utils.get_three_mesh( 1781 | self.renderer.context, 1782 | geometry_config={ 1783 | "name": "ConeGeometry", 1784 | "height": 0.75, 1785 | "radius": 0.45, 1786 | "radial_segments": 60, 1787 | }, 1788 | material_config={"name": "StandardMaterial", "diffuse": triangle_diffuse}, 1789 | ) 1790 | triangle.model_matrix = ( 1791 | opengl.translation_matrix(x=2.9, y=-1.7, z=-6.2) 1792 | @ opengl.scale_matrix(scale_factor=2.4) 1793 | @ triangle.model_matrix 1794 | ) 1795 | 1796 | # # Circle updaters. 1797 | # def circle_callback(sender, _): 1798 | # nonlocal circle_diffuse 1799 | # circle_diffuse = tuple(np.array(dearpygui.core.get_value(sender)[:3]) / 255) 1800 | 1801 | # def update_circle(mesh): 1802 | # mesh.shader.set_uniform("diffuse", circle_diffuse) 1803 | 1804 | # circle.add_updater(update_circle) 1805 | 1806 | # # Square updaters. 1807 | # def square_callback(sender, _): 1808 | # nonlocal square_diffuse 1809 | # square_diffuse = tuple(np.array(dearpygui.core.get_value(sender)[:3]) / 255) 1810 | 1811 | # def update_square(mesh): 1812 | # mesh.shader.set_uniform("diffuse", square_diffuse) 1813 | 1814 | # square.add_updater(update_square) 1815 | 1816 | # # Triangle updaters. 1817 | # def triangle_callback(sender, _): 1818 | # nonlocal triangle_diffuse 1819 | # triangle_diffuse = tuple( 1820 | # np.array(dearpygui.core.get_value(sender)[:3]) / 255 1821 | # ) 1822 | 1823 | # def update_triangle(mesh): 1824 | # mesh.shader.set_uniform("diffuse", triangle_diffuse) 1825 | 1826 | # triangle.add_updater(update_triangle) 1827 | 1828 | # self.widgets.extend( 1829 | # [ 1830 | # { 1831 | # "name": "Circle Color", 1832 | # "widget": "color_edit3", 1833 | # "callback": circle_callback, 1834 | # "default_value": (0.3 * 255, 1 * 255, 0.3 * 255, 1 * 255), 1835 | # }, 1836 | # { 1837 | # "name": "Square Color", 1838 | # "widget": "color_edit3", 1839 | # "callback": square_callback, 1840 | # "default_value": (0.3 * 255, 0.3 * 255, 1 * 255, 1 * 255), 1841 | # }, 1842 | # { 1843 | # "name": "Triangle Color", 1844 | # "widget": "color_edit3", 1845 | # "callback": triangle_callback, 1846 | # "default_value": (1 * 255, 0.5 * 255, 0.0 * 255, 1 * 255), 1847 | # }, 1848 | # ] 1849 | # ) 1850 | 1851 | logo = Object3D() 1852 | logo.add(extrude) 1853 | logo.add(circle) 1854 | logo.add(square) 1855 | logo.add(triangle) 1856 | logo.model_matrix = ( 1857 | opengl.translation_matrix(y=-2.5, z=0.6) 1858 | @ opengl.x_rotation_matrix(x=PI / 2) 1859 | @ logo.model_matrix 1860 | ) 1861 | self.add(logo) 1862 | 1863 | self.camera.model_matrix = ( 1864 | opengl.x_rotation_matrix(PI / 2) @ self.camera.model_matrix 1865 | ) 1866 | self.camera.default_model_matrix = self.camera.model_matrix 1867 | 1868 | camera_to_light = np.array([12, -12, 12]) - self.camera.get_position() 1869 | 1870 | def update_rotating_camera(dt): 1871 | r, theta, phi = space_ops.cartesian_to_spherical(self.camera.get_position()) 1872 | self.camera.set_position( 1873 | space_ops.spherical_to_cartesian(r, theta, phi + (1 / 8) * 2 * PI / 60) 1874 | ) 1875 | self.point_lights[0]["position"] = list( 1876 | self.camera.get_position() + camera_to_light 1877 | ) 1878 | 1879 | tutorial_utils.look_at(self.camera, ORIGIN) 1880 | 1881 | self.add_updater(update_rotating_camera) 1882 | 1883 | self.interactive_embed() 1884 | # self.wait(16) 1885 | -------------------------------------------------------------------------------- /opengl_tutorial/tutorial_utils.py: -------------------------------------------------------------------------------- 1 | import grpc 2 | 3 | import manim.utils.opengl as opengl 4 | import threejs 5 | from manim import * 6 | from _grpc.gen import threejs_pb2, threejs_pb2_grpc 7 | from manim.opengl import * 8 | from manim.utils import space_ops 9 | from threejs import * 10 | 11 | channel = grpc.insecure_channel( 12 | "localhost:50051", options=[("grpc.max_receive_message_length", 8 * 1024 * 1024)] 13 | ) 14 | geometry_stub = threejs_pb2_grpc.GeometryServiceStub(channel) 15 | material_stub = threejs_pb2_grpc.MaterialServiceStub(channel) 16 | 17 | 18 | def grpc_again(): 19 | geometry_response = getattr(geometry_stub, f"Hello")( 20 | getattr(threejs_pb2, f"HelloRequest")() 21 | ) 22 | print(geometry_response.response) 23 | 24 | 25 | def get_geometry(name, config=None, wireframe=False): 26 | if config is None: 27 | config = {} 28 | geometry_response = getattr(geometry_stub, f"{name}")( 29 | getattr(threejs_pb2, f"{name}Request")(**config, wireframe=wireframe) 30 | ) 31 | geometry = Geometry( 32 | geometry_response.position, 33 | geometry_response.normal, 34 | geometry_response.index, 35 | ) 36 | return geometry 37 | 38 | 39 | def get_material(context, name, config=None): 40 | if config is None: 41 | config = {} 42 | material_response = getattr(material_stub, f"{name}")( 43 | getattr(threejs_pb2, f"{name}Request")() 44 | ) 45 | material = getattr(threejs, name)( 46 | context, 47 | material_response.vertex_shader, 48 | material_response.fragment_shader, 49 | config, 50 | ) 51 | return material 52 | 53 | 54 | def get_three_mesh(context, geometry_config=None, material_config=None): 55 | geometry_name = geometry_config["name"] 56 | material_name = material_config["name"] 57 | del geometry_config["name"] 58 | del material_config["name"] 59 | geometry = get_geometry(geometry_name, config=geometry_config) 60 | material = get_material(context, material_name, config=material_config) 61 | return ThreeMesh(material, geometry.attributes, indices=geometry.index) 62 | 63 | 64 | def get_2d_box(width, height, material, radius): 65 | height_geometry = get_geometry( 66 | "CylinderGeometry", 67 | { 68 | "radius_top": radius, 69 | "radius_bottom": radius, 70 | "height": width, 71 | }, 72 | ) 73 | width_geometry = get_geometry( 74 | "CylinderGeometry", 75 | { 76 | "radius_top": radius, 77 | "radius_bottom": radius, 78 | "height": height, 79 | }, 80 | ) 81 | 82 | line = Mesh(geometry=width_geometry, material=material) 83 | line2 = Mesh(geometry=width_geometry, material=material) 84 | line3 = Mesh(geometry=height_geometry, material=material) 85 | line4 = Mesh(geometry=height_geometry, material=material) 86 | 87 | line.model_matrix = opengl.translation_matrix(x=width / 2) @ line.model_matrix 88 | line2.model_matrix = opengl.translation_matrix(x=-width / 2) @ line2.model_matrix 89 | line3.model_matrix = ( 90 | opengl.translation_matrix(y=height / 2) 91 | @ opengl.rotation_matrix(z=PI / 2) 92 | @ line3.model_matrix 93 | ) 94 | line4.model_matrix = ( 95 | opengl.translation_matrix(y=-height / 2) 96 | @ opengl.rotation_matrix(z=PI / 2) 97 | @ line4.model_matrix 98 | ) 99 | 100 | line3.normal_matrix = opengl.rotation_matrix(z=PI / 2) @ line3.normal_matrix 101 | line4.normal_matrix = opengl.rotation_matrix(z=PI / 2) @ line4.normal_matrix 102 | 103 | return Object3D(line, line2, line3, line4) 104 | 105 | 106 | def get_3d_box(width, height, depth, material, radius): 107 | # Top. 108 | manim_space_top = get_2d_box(width, depth, material, radius) 109 | manim_space_top.model_matrix = ( 110 | opengl.translation_matrix(y=height / 2) 111 | @ opengl.rotation_matrix(x=PI / 2) 112 | @ manim_space_top.model_matrix 113 | ) 114 | manim_space_top.normal_matrix = ( 115 | opengl.translation_matrix(y=height / 2) 116 | @ opengl.rotation_matrix(x=PI / 2) 117 | @ manim_space_top.normal_matrix 118 | ) 119 | 120 | # Bottom. 121 | manim_space_bottom = get_2d_box(width, depth, material, radius) 122 | manim_space_bottom.model_matrix = ( 123 | opengl.translation_matrix(y=-height / 2) 124 | @ opengl.rotation_matrix(x=PI / 2) 125 | @ manim_space_bottom.model_matrix 126 | ) 127 | manim_space_bottom.normal_matrix = ( 128 | opengl.translation_matrix(y=-height / 2) 129 | @ opengl.rotation_matrix(x=PI / 2) 130 | @ manim_space_bottom.normal_matrix 131 | ) 132 | 133 | # Front. 134 | manim_space_front = get_2d_box(width, height, material, radius) 135 | manim_space_front.model_matrix = ( 136 | opengl.translation_matrix(z=depth / 2) @ manim_space_front.model_matrix 137 | ) 138 | manim_space_front.normal_matrix = ( 139 | opengl.translation_matrix(z=depth / 2) @ manim_space_front.normal_matrix 140 | ) 141 | 142 | # Back. 143 | manim_space_back = get_2d_box(width, height, material, radius) 144 | manim_space_back.model_matrix = ( 145 | opengl.translation_matrix(z=-depth / 2) @ manim_space_back.model_matrix 146 | ) 147 | manim_space_back.normal_matrix = ( 148 | opengl.translation_matrix(z=-depth / 2) @ manim_space_back.normal_matrix 149 | ) 150 | 151 | return Object3D( 152 | manim_space_top, 153 | manim_space_bottom, 154 | manim_space_front, 155 | manim_space_back, 156 | ) 157 | 158 | 159 | def get_axes(context, length): 160 | # x axis. 161 | x_axis_material = get_material( 162 | context, 163 | "PhongMaterial", 164 | { 165 | "diffuse": [1.0, 0, 0], 166 | "emissive": [0, 0, 0], 167 | "specular": [1 / 3.0, 1 / 3.0, 1 / 3.0], 168 | "shininess": 5.0, 169 | "opacity": 1, 170 | }, 171 | ) 172 | x_axis = Mesh( 173 | geometry=get_geometry( 174 | "CylinderGeometry", 175 | { 176 | "radius_top": 0.05, 177 | "radius_bottom": 0.05, 178 | "height": length, 179 | }, 180 | ), 181 | material=x_axis_material, 182 | ) 183 | x_axis.model_matrix = ( 184 | opengl.translation_matrix(x=length / 2) 185 | @ opengl.rotation_matrix(z=PI / 2) 186 | @ x_axis.model_matrix 187 | ) 188 | x_axis.normal_matrix = opengl.rotation_matrix(z=PI / 2) @ x_axis.normal_matrix 189 | 190 | # y axis. 191 | y_axis_material = get_material( 192 | context, 193 | "PhongMaterial", 194 | { 195 | "diffuse": [0, 1.0, 0], 196 | "emissive": [0, 0, 0], 197 | "specular": [1 / 3.0, 1 / 3.0, 1 / 3.0], 198 | "shininess": 5.0, 199 | "opacity": 1, 200 | }, 201 | ) 202 | y_axis = Mesh( 203 | geometry=get_geometry( 204 | "CylinderGeometry", 205 | { 206 | "radius_top": 0.05, 207 | "radius_bottom": 0.05, 208 | "height": length, 209 | }, 210 | ), 211 | material=y_axis_material, 212 | ) 213 | y_axis.model_matrix = ( 214 | opengl.rotation_matrix(z=PI / 2) 215 | @ opengl.translation_matrix(x=length / 2) 216 | @ opengl.rotation_matrix(z=PI / 2) 217 | @ y_axis.model_matrix 218 | ) 219 | y_axis.normal_matrix = opengl.rotation_matrix(z=PI / 2) @ y_axis.normal_matrix 220 | 221 | # z axis. 222 | z_axis_material = get_material( 223 | context, 224 | "PhongMaterial", 225 | { 226 | "diffuse": [0, 0, 1.0], 227 | "emissive": [0, 0, 0], 228 | "specular": [1 / 3.0, 1 / 3.0, 1 / 3.0], 229 | "shininess": 5.0, 230 | "opacity": 1, 231 | }, 232 | ) 233 | z_axis = Mesh( 234 | geometry=get_geometry( 235 | "CylinderGeometry", 236 | { 237 | "radius_top": 0.05, 238 | "radius_bottom": 0.05, 239 | "height": length, 240 | }, 241 | ), 242 | material=z_axis_material, 243 | ) 244 | z_axis.model_matrix = ( 245 | opengl.rotation_matrix(x=PI / 2) 246 | @ opengl.rotation_matrix(z=PI / 2) 247 | @ opengl.translation_matrix(x=length / 2) 248 | @ opengl.rotation_matrix(z=PI / 2) 249 | @ z_axis.model_matrix 250 | ) 251 | z_axis.normal_matrix = opengl.rotation_matrix(z=PI / 2) @ z_axis.normal_matrix 252 | 253 | return Object3D(x_axis, y_axis, z_axis) 254 | 255 | 256 | def get_camera(context): 257 | camera_width = 1.5 258 | lens_height = 0.6 259 | phong_material = get_material( 260 | context, 261 | "PhongMaterial", 262 | { 263 | "diffuse": [0.3, 0.3, 0.3], 264 | "emissive": [0, 0, 0], 265 | "specular": [1 / 3.0, 1 / 3.0, 1 / 3.0], 266 | "shininess": 1.0, 267 | "opacity": 1, 268 | }, 269 | ) 270 | camera_body = ThreeMesh( 271 | geometry=get_geometry("BoxGeometry", {"width": camera_width}), 272 | material=phong_material, 273 | ) 274 | camera_lens = ThreeMesh( 275 | geometry=get_geometry("ConeGeometry", {"radius": 0.5, "height": lens_height}), 276 | material=phong_material, 277 | ) 278 | camera_lens.model_matrix = ( 279 | opengl.translation_matrix(x=camera_width / 2 + lens_height / 2) 280 | @ opengl.rotation_matrix(z=PI / 2) 281 | @ camera_lens.model_matrix 282 | ) 283 | return Object3D(camera_body, camera_lens) 284 | 285 | 286 | def look_at(camera, target, up_vector=OUT, looking_axis="-z"): 287 | # Rotate the camera so that it points toward the target. 288 | if looking_axis == "-z": 289 | camera_front_dir = -camera.model_matrix[:3, 2] 290 | elif looking_axis == "z": 291 | camera_front_dir = camera.model_matrix[:3, 2] 292 | else: 293 | raise RuntimeError(f"Unknown looking axis {looking_axis}") 294 | 295 | camera_to_target = normalize(target - camera.model_matrix[:, 3][:3]) 296 | rotation_axis = np.cross(camera_to_target, camera_front_dir) 297 | angle = 2 * np.arctan2( 298 | np.linalg.norm(camera_to_target - normalize(camera_front_dir)), 299 | np.linalg.norm(camera_to_target + normalize(camera_front_dir)), 300 | ) 301 | rotation_matrix = space_ops.rotation_matrix( 302 | -angle, 303 | rotation_axis, 304 | homogeneous=True, 305 | ) 306 | 307 | camera_position = camera.model_matrix[:, 3][:3] 308 | camera.model_matrix = ( 309 | opengl.translation_matrix(*camera_position) 310 | @ rotation_matrix 311 | @ opengl.translation_matrix(*-camera_position) 312 | @ camera.model_matrix 313 | ) 314 | if isinstance(camera, Mesh): 315 | camera.normal_matrix = rotation_matrix @ camera.normal_matrix 316 | 317 | # Rotate the camera so that it's right-side-up. 318 | # Project the z-axis onto the plane defined by the camera->target vector. 319 | projected_z_axis = normalize( 320 | up_vector - np.dot(up_vector, camera_to_target) * camera_to_target 321 | ) 322 | 323 | # Rotate the camera so that its y-axis is aligned with the z-axis. 324 | camera_y_vec = camera.model_matrix[:3, 1] 325 | rotation_axis = np.cross(projected_z_axis, camera_y_vec) 326 | angle = 2 * np.arctan2( 327 | np.linalg.norm(projected_z_axis - normalize(camera_y_vec)), 328 | np.linalg.norm(projected_z_axis + normalize(camera_y_vec)), 329 | ) 330 | rotation_matrix = space_ops.rotation_matrix( 331 | -angle, 332 | rotation_axis, 333 | homogeneous=True, 334 | ) 335 | 336 | camera.model_matrix = ( 337 | opengl.translation_matrix(*-camera_position) 338 | @ rotation_matrix 339 | @ opengl.translation_matrix(*camera_position) 340 | @ camera.model_matrix 341 | ) 342 | if isinstance(camera, Mesh): 343 | camera.normal_matrix = rotation_matrix @ camera.normal_matrix 344 | 345 | 346 | def mouse_over_camera_frame_mob(scene, mob): 347 | model = mob.hierarchical_model_matrix() 348 | model_inv = np.linalg.inv(model) 349 | 350 | near = 2 351 | camera_x = scene.camera.model_matrix[:3, 0] 352 | camera_y = scene.camera.model_matrix[:3, 1] 353 | camera_z = scene.camera.model_matrix[:3, 2] 354 | point = scene.mouse_point.get_center() 355 | world_space_point = ( 356 | scene.camera.get_position() 357 | - camera_z * near 358 | + camera_x 359 | * (point[0] / config["frame_x_radius"]) 360 | * (config["frame_x_radius"] / 6) 361 | + camera_y 362 | * (point[1] / config["frame_y_radius"]) 363 | * (config["frame_y_radius"] / 6) 364 | ) 365 | 366 | homogeneous_point = np.hstack((world_space_point, 1)) 367 | model_space_point = model_inv @ homogeneous_point 368 | 369 | bounding_box = mob.get_bounding_box() 370 | x_condition = bounding_box[0][0] <= model_space_point[0] <= bounding_box[2][0] 371 | y_condition = bounding_box[0][1] <= model_space_point[1] <= bounding_box[2][1] 372 | return x_condition and y_condition 373 | 374 | 375 | def get_button(scene, text): 376 | # Add a button. 377 | camera_button = OpenGLVGroup() 378 | 379 | camera_button_text = OpenGLText(text, font="Sans Serif") 380 | camera_button.add(camera_button_text, update_parent=True) 381 | scene.add(camera_button_text) 382 | 383 | text_bounding_box = camera_button_text.get_bounding_box() 384 | text_width = text_bounding_box[2][0] - text_bounding_box[0][0] 385 | text_height = text_bounding_box[2][1] - text_bounding_box[0][1] 386 | 387 | padding = 0.2 388 | padded_width = text_width + 2 * padding 389 | padded_height = text_height + 2 * padding 390 | 391 | camera_button_border = OpenGLVMobject(stroke_width=8) 392 | camera_button_border.data["points"] = [ 393 | LEFT * padded_width / 2 + UP * padded_height / 2 394 | ] 395 | camera_button_border.add_line_to(RIGHT * padded_width / 2 + UP * padded_height / 2) 396 | camera_button_border.add_line_to( 397 | RIGHT * padded_width / 2 + DOWN * padded_height / 2 398 | ) 399 | camera_button_border.add_line_to(LEFT * padded_width / 2 + DOWN * padded_height / 2) 400 | camera_button_border.add_line_to(LEFT * padded_width / 2 + UP * padded_height / 2) 401 | camera_button.add(camera_button_border, update_parent=True) 402 | scene.add(camera_button_border) 403 | return camera_button 404 | 405 | 406 | def get_grid_lines(x_size, y_size): 407 | grid = OpenGLVGroup() 408 | 409 | # Add vertical gridlines. 410 | for i in range(-x_size, x_size + 1): 411 | grid.add( 412 | OpenGLLine(UP * y_size, DOWN * y_size, stroke_opacity=0.5).shift(RIGHT * i) 413 | ) 414 | 415 | # Add horizontal gridlines. 416 | for i in range(-y_size, y_size + 1): 417 | grid.add( 418 | OpenGLLine(LEFT * x_size, RIGHT * x_size, stroke_opacity=0.5).shift(UP * i) 419 | ) 420 | return grid 421 | -------------------------------------------------------------------------------- /opengl_tutorial/update_protos.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | This is intended to be run from the tutorial root. 4 | """ 5 | 6 | import os 7 | 8 | grpc_dir = "_grpc" 9 | CMD_STRING = f""" 10 | python \ 11 | -m grpc_tools.protoc \ 12 | -I {grpc_dir}/proto \ 13 | --python_out={grpc_dir}/gen \ 14 | --grpc_python_out={grpc_dir}/gen \ 15 | {grpc_dir}/proto/threejs.proto 16 | """ 17 | os.system(CMD_STRING) 18 | -------------------------------------------------------------------------------- /three-server/index.js: -------------------------------------------------------------------------------- 1 | const puppeteer = require('puppeteer'); 2 | THREE = require('three') 3 | var grpc = require("@grpc/grpc-js"); 4 | var protoLoader = require("@grpc/proto-loader"); 5 | var packageDefinition = protoLoader.loadSync("service.proto", { 6 | keepCase: true, 7 | longs: Number, 8 | enums: String, 9 | defaults: true, 10 | oneofs: true, 11 | }); 12 | var testservice = grpc.loadPackageDefinition(packageDefinition).testservice; 13 | 14 | let browser, page; 15 | (async () => { 16 | browser = await puppeteer.launch(); 17 | page = await browser.newPage(); 18 | await page.addScriptTag({path: './three.js/build/three.min.js'}); 19 | 20 | var threeServer = getServer(); 21 | threeServer.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => { 22 | threeServer.start(); 23 | }); 24 | 25 | console.log("Listening on 0.0.0.0:50051..."); 26 | })(); 27 | 28 | function getServer() { 29 | var server = new grpc.Server(); 30 | server.addService(testservice.GeometryService.service, { 31 | boxGeometry: boxGeometry, 32 | sphereGeometry: sphereGeometry, 33 | torusKnotGeometry: torusKnotGeometry, 34 | icosahedronGeometry: icosahedronGeometry, 35 | tetrahedronGeometry: tetrahedronGeometry, 36 | cylinderGeometry: cylinderGeometry, 37 | coneGeometry: coneGeometry, 38 | circleGeometry: circleGeometry, 39 | planeGeometry: planeGeometry, 40 | extrudeGeometry: extrudeGeometry, 41 | }); 42 | server.addService(testservice.MaterialService.service, { 43 | basicMaterial: basicMaterial, 44 | phongMaterial: phongMaterial, 45 | standardMaterial: standardMaterial, 46 | }); 47 | return server; 48 | } 49 | 50 | function standardMaterial(call, callback) { 51 | (async () => { 52 | const puppetResponse = await page.evaluate(() => { 53 | const scene = new THREE.Scene(); 54 | const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); 55 | const renderer = new THREE.WebGLRenderer(); 56 | renderer.setSize(window.innerWidth, window.innerHeight); 57 | document.body.appendChild(renderer.domElement); 58 | 59 | const geometry = new THREE.BoxGeometry(1, 1, 1); 60 | const material = new THREE.MeshStandardMaterial({}); 61 | const object = new THREE.Mesh(geometry, material); 62 | scene.add(object); 63 | 64 | const pointLight = new THREE.PointLight(0xffffff, 5, 100); 65 | pointLight.position.set(0, 0, 0); 66 | scene.add(pointLight); 67 | 68 | const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x000000, 1); 69 | scene.add(hemisphereLight); 70 | 71 | renderer.render(scene, camera); 72 | 73 | return { 74 | vertex_shader: renderer.lastProgram.vertexGlsl, 75 | fragment_shader: renderer.lastProgram.fragmentGlsl, 76 | }; 77 | }); 78 | callback(null, puppetResponse); 79 | })(); 80 | } 81 | 82 | function phongMaterial(call, callback) { 83 | (async () => { 84 | const puppetResponse = await page.evaluate(() => { 85 | const scene = new THREE.Scene(); 86 | const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); 87 | const renderer = new THREE.WebGLRenderer(); 88 | renderer.setSize(window.innerWidth, window.innerHeight); 89 | document.body.appendChild(renderer.domElement); 90 | 91 | const geometry = new THREE.BoxGeometry(1, 1, 1); 92 | const material = new THREE.MeshPhongMaterial({}); 93 | const object = new THREE.Mesh(geometry, material); 94 | scene.add(object); 95 | 96 | const pointLight = new THREE.PointLight(0xffffff, 5, 100); 97 | pointLight.position.set(0, 0, 0); 98 | scene.add(pointLight); 99 | 100 | const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x000000, 1); 101 | scene.add(hemisphereLight); 102 | 103 | renderer.render(scene, camera); 104 | 105 | return { 106 | vertex_shader: renderer.lastProgram.vertexGlsl, 107 | fragment_shader: renderer.lastProgram.fragmentGlsl, 108 | }; 109 | }); 110 | callback(null, puppetResponse); 111 | })(); 112 | } 113 | 114 | function basicMaterial(call, callback) { 115 | (async () => { 116 | const puppetResponse = await page.evaluate(() => { 117 | const scene = new THREE.Scene(); 118 | const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); 119 | const renderer = new THREE.WebGLRenderer(); 120 | renderer.setSize(window.innerWidth, window.innerHeight); 121 | document.body.appendChild(renderer.domElement); 122 | 123 | const geometry = new THREE.BoxGeometry(1, 1, 1); 124 | const material = new THREE.MeshBasicMaterial({}); 125 | const object = new THREE.Mesh(geometry, material); 126 | scene.add(object); 127 | 128 | const pointLight = new THREE.PointLight(0xffffff, 5, 100); 129 | pointLight.position.set(0, 0, 0); 130 | scene.add(pointLight); 131 | 132 | const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x000000, 1); 133 | scene.add(hemisphereLight); 134 | 135 | renderer.render(scene, camera); 136 | 137 | return { 138 | vertex_shader: renderer.lastProgram.vertexGlsl, 139 | fragment_shader: renderer.lastProgram.fragmentGlsl, 140 | }; 141 | }); 142 | callback(null, puppetResponse); 143 | })(); 144 | } 145 | 146 | function icosahedronGeometry(call, callback) { 147 | (async () => { 148 | const puppetResponse = await page.evaluate((call) => { 149 | const geometry = new THREE.IcosahedronGeometry( 150 | call.request.raius || 1, 151 | call.request.detail || 0, 152 | ); 153 | return { geometry }; 154 | }, call); 155 | callback(null, serializePuppetGeometry(puppetResponse.geometry)); 156 | })(); 157 | } 158 | 159 | function torusKnotGeometry(call, callback) { 160 | (async () => { 161 | const puppetResponse = await page.evaluate((call) => { 162 | const geometry = new THREE.TorusKnotGeometry( 163 | call.request.torus_radius || 1, 164 | call.request.tube_radius || 0.4, 165 | call.request.tubular_segments || 64, 166 | call.request.radial_segments || 8, 167 | call.request.p || 2, 168 | call.request.q || 3, 169 | ); 170 | return { geometry }; 171 | }, call); 172 | callback(null, serializePuppetGeometry(puppetResponse.geometry)); 173 | })(); 174 | } 175 | 176 | function boxGeometry(call, callback) { 177 | (async () => { 178 | const puppetResponse = await page.evaluate((call) => { 179 | const geometry = new THREE.BoxGeometry( 180 | call.request.width || 1, 181 | call.request.height || 1, 182 | call.request.depth || 1, 183 | call.request.width_segments || 1, 184 | call.request.height_segments || 1, 185 | call.request.depth_segments || 1, 186 | ); 187 | return { geometry }; 188 | }, call); 189 | callback(null, serializePuppetGeometry(puppetResponse.geometry)); 190 | })(); 191 | } 192 | 193 | function sphereGeometry(call, callback) { 194 | (async () => { 195 | const puppetResponse = await page.evaluate((call) => { 196 | const geometry = new THREE.SphereGeometry( 197 | call.request.radius || 1, 198 | call.request.width_segments || 8, 199 | call.request.height_segments || 6, 200 | call.request.phi_start || 0, 201 | call.request.phi_length || 2 * Math.PI, 202 | call.request.theta_start || 0, 203 | call.request.theta_length || Math.PI, 204 | ); 205 | return { geometry }; 206 | }, call); 207 | callback(null, serializePuppetGeometry(puppetResponse.geometry)); 208 | })(); 209 | } 210 | 211 | function tetrahedronGeometry(call, callback) { 212 | (async () => { 213 | const puppetResponse = await page.evaluate((call) => { 214 | const geometry = new THREE.TetrahedronGeometry( 215 | call.request.radius || 1, 216 | call.request.detail || 0, 217 | ); 218 | return { geometry }; 219 | }, call); 220 | callback(null, serializePuppetGeometry(puppetResponse.geometry)); 221 | })(); 222 | } 223 | 224 | function cylinderGeometry(call, callback) { 225 | (async () => { 226 | const puppetResponse = await page.evaluate((call) => { 227 | const geometry = new THREE.CylinderGeometry( 228 | call.request.radius_top || 1, 229 | call.request.radius_bottom || 1, 230 | call.request.height || 1, 231 | call.request.radial_segments || 8, 232 | call.request.height_segments || 1, 233 | call.request.open_ended || false, 234 | call.request.theta_start || 0, 235 | call.request.theta_length || 2 * Math.PI, 236 | ); 237 | return { geometry }; 238 | }, call); 239 | callback(null, serializePuppetGeometry(puppetResponse.geometry)); 240 | })(); 241 | } 242 | 243 | function coneGeometry(call, callback) { 244 | (async () => { 245 | const puppetResponse = await page.evaluate((call) => { 246 | const geometry = new THREE.ConeGeometry( 247 | call.request.radius || 1, 248 | call.request.height || 1, 249 | call.request.radial_segments || 8, 250 | call.request.height_segments || 1, 251 | call.request.open_ended || false, 252 | call.request.theta_start || 0, 253 | call.request.theta_length || 2 * Math.PI, 254 | ); 255 | return { geometry }; 256 | }, call); 257 | callback(null, serializePuppetGeometry(puppetResponse.geometry)); 258 | })(); 259 | } 260 | 261 | function circleGeometry(call, callback) { 262 | (async () => { 263 | const puppetResponse = await page.evaluate((call) => { 264 | const geometry = new THREE.CircleGeometry( 265 | call.request.radius || 1, 266 | call.request.segments || 8, 267 | call.request.theta_start || 0, 268 | call.request.theta_length || 2 * Math.PI, 269 | ); 270 | return { geometry }; 271 | }, call); 272 | callback(null, serializePuppetGeometry(puppetResponse.geometry)); 273 | })(); 274 | } 275 | 276 | function planeGeometry(call, callback) { 277 | (async () => { 278 | const puppetResponse = await page.evaluate((call) => { 279 | const geometry = new THREE.PlaneGeometry( 280 | call.request.width || 1, 281 | call.request.height || 1, 282 | call.request.width_segments || 1, 283 | call.request.height_segments || 1, 284 | ); 285 | return { geometry }; 286 | }, call); 287 | callback(null, serializePuppetGeometry(puppetResponse.geometry)); 288 | })(); 289 | } 290 | 291 | function extrudeGeometry(call, callback) { 292 | (async () => { 293 | const puppetResponse = await page.evaluate((call) => { 294 | let points = call.request.points; 295 | let pathIndices = call.request.path_indices; 296 | 297 | // Group individual points into groups of three. 298 | let groupedPoints = []; 299 | for(let i = 0; i < points.length; i+=3) { 300 | groupedPoints.push(points.slice(i, i+3)); 301 | } 302 | 303 | // Divide the points into subpaths. 304 | pathIndices.push(points.length / 3); 305 | let shapePoints = []; 306 | for (let i = 0; i < pathIndices.length - 1; i++) { 307 | shapePoints.push(groupedPoints.slice(pathIndices[i], pathIndices[i+1])); 308 | } 309 | 310 | function pointInsidePolygon(point, vs) { 311 | // ray-casting algorithm based on 312 | // https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html/pnpoly.html 313 | var x = point[0], y = point[1]; 314 | var inside = false; 315 | for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) { 316 | var xi = vs[i][0], yi = vs[i][1]; 317 | var xj = vs[j][0], yj = vs[j][1]; 318 | var intersect = ((yi > y) != (yj > y)) 319 | && (x < (xj - xi) * (y - yi) / (yj - yi) + xi); 320 | if (intersect) inside = !inside; 321 | } 322 | return inside; 323 | } 324 | 325 | // Assume a path is a hole if its first point is inside the previous shape. Assumes each shape 326 | // is followed by all of its holes. 327 | let shapes = []; 328 | let holes = []; 329 | for (let i = 0; i < shapePoints.length; i++) { 330 | let isHole = (shapes.length > 0 && pointInsidePolygon(shapePoints[i][0], shapes[shapes.length-1])); 331 | if (isHole) { 332 | holes.push(shapePoints[i]); 333 | } else { 334 | shapes.push(shapePoints[i]); 335 | } 336 | } 337 | 338 | // Construct the final shape. 339 | let shape = new THREE.Shape(); 340 | for (let i = 0; i < shapes.length; i++) { 341 | /** 342 | [p00, p01, p02] 343 | [p10, p11, p12] 344 | [p20, p21, p22] 345 | **/ 346 | for (let j = 0; j < shapes[i].length; j+=3) { 347 | if (j == 0) { 348 | shape.moveTo(shapes[i][j][0], shapes[i][j][1]); 349 | } 350 | shape.quadraticCurveTo(shapes[i][j+1][0], shapes[i][j+1][1], shapes[i][j+2][0], shapes[i][j+2][1]); 351 | } 352 | } 353 | 354 | for (let i = 0; i < holes.length; i++) { 355 | let hole = new THREE.Path(); 356 | for (let j = 0; j < holes[i].length; j+=3) { 357 | if (j == 0) { 358 | hole.moveTo(holes[i][j][0], holes[i][j][1]); 359 | } 360 | hole.quadraticCurveTo(holes[i][j+1][0], holes[i][j+1][1], holes[i][j+2][0], holes[i][j+2][1]); 361 | } 362 | shape.holes.push(hole); 363 | } 364 | 365 | const extrudeSettings = { 366 | steps: call.request.steps || 1, 367 | depth: call.request.depth || 1, 368 | bevelEnabled: call.request.bevel_enabled || false, 369 | bevelThickness: call.request.bevel_thickness || 0.1, 370 | bevelSize: call.request.bevel_size || 0.1, 371 | bevelOffset: call.request.bevel_offset || 0, 372 | bevelSegments: call.request.bevel_segments || 8 373 | }; 374 | 375 | const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); 376 | return { geometry }; 377 | }, call); 378 | callback(null, serializePuppetGeometry(puppetResponse.geometry)); 379 | })(); 380 | } 381 | 382 | function serializePuppetGeometry(geometry) { 383 | let response = { 384 | position: puppetObjectToArray(geometry.attributes.position.array), 385 | normal: puppetObjectToArray(geometry.attributes.normal.array), 386 | uv: puppetObjectToArray(geometry.attributes.uv.array), 387 | }; 388 | if (geometry.index !== null) { 389 | response.index = puppetObjectToArray(geometry.index.array); 390 | } 391 | return response; 392 | } 393 | 394 | function puppetObjectToArray(obj) { 395 | let ret = []; 396 | let i = 0; 397 | while (1) { 398 | let key = `${i}`; 399 | if (key in obj) { 400 | ret.push(obj[key]) 401 | i++; 402 | } else { 403 | return ret; 404 | } 405 | } 406 | } 407 | -------------------------------------------------------------------------------- /three-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three-server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@grpc/grpc-js": "^1.3.0", 13 | "@grpc/proto-loader": "^0.6.1", 14 | "puppeteer": "^10.2.0", 15 | "three": "file:./three.js" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /three-server/service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package testservice; 3 | 4 | service GeometryService { 5 | rpc BoxGeometry(BoxGeometryRequest) returns (GeometryResponse) {} 6 | rpc SphereGeometry(SphereGeometryRequest) returns (GeometryResponse) {} 7 | rpc TorusKnotGeometry(TorusKnotGeometryRequest) returns (GeometryResponse) {} 8 | rpc IcosahedronGeometry(IcosahedronGeometryRequest) returns (GeometryResponse) {} 9 | rpc TetrahedronGeometry(TetrahedronGeometryRequest) returns (GeometryResponse) {} 10 | rpc CylinderGeometry(CylinderGeometryRequest) returns (GeometryResponse) {} 11 | rpc ConeGeometry(ConeGeometryRequest) returns (GeometryResponse) {} 12 | rpc CircleGeometry(CircleGeometryRequest) returns (GeometryResponse) {} 13 | rpc PlaneGeometry(PlaneGeometryRequest) returns (GeometryResponse) {} 14 | rpc ExtrudeGeometry(ExtrudeGeometryRequest) returns (GeometryResponse) {} 15 | } 16 | 17 | service MaterialService { 18 | rpc BasicMaterial(BasicMaterialRequest) returns (MaterialResponse) {} 19 | rpc PhongMaterial(PhongMaterialRequest) returns (MaterialResponse) {} 20 | rpc StandardMaterial(StandardMaterialRequest) returns (MaterialResponse) {} 21 | } 22 | 23 | message BasicMaterialRequest {} 24 | message PhongMaterialRequest {} 25 | message StandardMaterialRequest {} 26 | 27 | message MaterialResponse { 28 | string vertex_shader = 1; 29 | string fragment_shader = 2; 30 | } 31 | 32 | message GeometryResponse { 33 | repeated float position = 1; 34 | repeated float normal = 2; 35 | repeated float uv = 3; 36 | repeated int32 index = 4; 37 | } 38 | 39 | message BoxGeometryRequest { 40 | float width = 1; 41 | float height = 2; 42 | float depth = 3; 43 | int32 width_segments = 4; 44 | int32 height_segments = 5; 45 | int32 depth_segments = 6; 46 | bool wireframe = 7; 47 | } 48 | 49 | message SphereGeometryRequest { 50 | float radius = 1; 51 | int32 width_segments = 2; 52 | int32 height_segments = 3; 53 | float phi_start = 4; 54 | float phi_length = 5; 55 | float theta_start = 6; 56 | float theta_length = 7; 57 | bool wireframe = 8; 58 | } 59 | 60 | message TorusKnotGeometryRequest { 61 | float torus_radius = 1; 62 | float tube_radius = 2; 63 | int32 tubular_segments = 3; 64 | int32 radial_segments = 4; 65 | int32 p = 5; 66 | int32 q = 6; 67 | bool wireframe = 7; 68 | } 69 | 70 | message IcosahedronGeometryRequest { 71 | float radius = 1; 72 | int32 detail = 2; 73 | bool wireframe = 3; 74 | } 75 | 76 | message TetrahedronGeometryRequest { 77 | float radius = 1; 78 | int32 detail = 2; 79 | bool wireframe = 3; 80 | } 81 | 82 | message CylinderGeometryRequest { 83 | float radius_top = 1; 84 | float radius_bottom = 2; 85 | float height = 3; 86 | int32 radial_segments = 4; 87 | int32 height_segments = 5; 88 | bool open_ended = 6; 89 | float theta_start = 7; 90 | float theta_length = 8; 91 | bool wireframe = 9; 92 | } 93 | 94 | message ConeGeometryRequest { 95 | float radius = 1; 96 | float height = 2; 97 | int32 radial_segments = 3; 98 | int32 height_segments = 4; 99 | bool open_ended = 5; 100 | float theta_start = 6; 101 | float theta_length = 7; 102 | bool wireframe = 8; 103 | } 104 | 105 | message CircleGeometryRequest { 106 | float radius = 1; 107 | int32 segments = 2; 108 | float theta_start = 3; 109 | float theta_length = 4; 110 | bool wireframe = 5; 111 | } 112 | 113 | message PlaneGeometryRequest { 114 | float width = 1; 115 | float height = 2; 116 | int32 width_segments = 3; 117 | int32 height_segments = 4; 118 | bool wireframe = 5; 119 | } 120 | 121 | message ExtrudeGeometryRequest { 122 | repeated float points = 1; 123 | repeated int32 path_indices = 2; 124 | int32 steps = 3; 125 | float depth = 4; 126 | bool bevel_enabled = 5; 127 | float bevel_thickness = 6; 128 | float bevel_size = 7; 129 | float bevel_offset = 8; 130 | int32 bevel_segments = 9; 131 | bool wireframe = 10; 132 | } 133 | -------------------------------------------------------------------------------- /tutorial.md: -------------------------------------------------------------------------------- 1 | * OpenGL pipeline 2 | * HalfScreenTriangle 3 | * Projection matrix 4 | * Model matrix 5 | * View matrix 6 | * Barycentric Interpolation 7 | * Hierarchical Model Matrices 8 | * Attributes, varyings, and uniforms 9 | * Fullscreen fragment shader example 10 | 11 | # Somewhat beyond manim 12 | * Rendering Geometries 13 | * Geometries and materials 14 | * Lighting 15 | * Normal matrices 16 | * Textures 17 | * Putting it together 18 | 19 | TODO: Add a FullScreenQuad example. 20 | 21 | ## Vocabulary 22 | * **Shader** - A shader is a program that runs on a GPU. 23 | 24 | * **Homogeneous Coordinates** - The coordinate system that OpenGL draws in is constrained to 25 | a 2x2x2 cube with the x, y, and z axes ranging from -1 to +1. 26 | 27 | * **Normalized Device Coordinates** - The coordinate system that OpenGL draws in is constrained to 28 | a 2x2x2 cube with the x, y, and z axes ranging from -1 to +1. 29 | 30 | * **Fragment** - A pixel-sized piece of a primitive. 31 | 32 | * **Frame Buffer** - A collection of buffers that can be used as a target for rendering. 33 | 34 | * **Geometry** - A list of vertices representing the shape of an object. 35 | 36 | * **Material** - A collection of shaders representing the way the object's physical 37 | properties e.g. color, shininess, opacity. 38 | 39 | * **Mesh** - A combination of a geometry and material which represents 40 | a single drawable entity in OpenGL. 41 | 42 | * **Model Space** - A coordinate space which is local to a geometry (v). 43 | 44 | * **World Space** - The global coordinate space (M * v). 45 | 46 | * **Camera Space** - The camera's model space (V * M * v). 47 | 48 | * **Clip Space** - A coordinate system in normalized device coordinates (P * V * M * v). 49 | 50 | * **Affine Transformation** - A transformation that preserves lines and parallelism 51 | (but not necessarily distances and angles). 52 | 53 | 54 | ## OpenGL pipeline 55 | * **Vertices** - A list of data points each of which have the same attributes. 56 | 57 | * **Vertex Shader** - Read in vertices and outputs positional data. 58 | 59 | * **Primitives Generation** - Convert vertex positions into one of a few different primitives. 60 | 61 | * **Rasterization** - Convert each primitive into fragments (pixel-sized parts). 62 | 63 | * **Fragment Shader** - Determine color and opacity for each fragment. 64 | 65 | * **Blending** - Determine colors for each pixel by combining fragment data. 66 | 67 | * **Frame Buffer** - Pixel data is written to a frame buffer and rendered to the screen. 68 | 69 | ## Using Manim to render a triangle with OpenGL 70 | ```py 71 | shader = Shader( 72 | self.renderer.context, 73 | source=dict( 74 | vertex_shader=""" 75 | #version 330 76 | in vec3 position; 77 | void main() { 78 | gl_Position = vec4(position, 1); 79 | } 80 | """, 81 | fragment_shader=""" 82 | #version 330 83 | out vec4 frag_color; 84 | void main() { 85 | frag_color = vec4(1, 0, 0, 1); 86 | } 87 | """, 88 | ), 89 | ) 90 | 91 | attributes = np.zeros(3, dtype=[("position", np.float32, (3,))]) 92 | attributes["position"] = np.array( 93 | [ 94 | [-1, -1, 0], 95 | [-1, 1, 0], 96 | [1, 1, 0], 97 | ] 98 | ) 99 | 100 | mesh = Mesh(shader, attributes) 101 | self.add(mesh) 102 | ``` 103 | 104 | Let's walk through the shader used above, starting with the vertices. 105 | ```py 106 | attributes = np.zeros(3, dtype=[("position", np.float32, (3,))]) 107 | attributes["position"] = np.array( 108 | [ 109 | [-1, -1, 0], 110 | [-1, 1, 0], 111 | [1, 1, 0], 112 | ] 113 | ) 114 | ``` 115 | These lines specify the data format for each vertex. The vertices 116 | in this shader have a single attribute specifying their position. 117 | 118 | ``` 119 | vertex_shader=""" 120 | #version 330 121 | in vec3 position; 122 | void main() { 123 | gl_Position = vec4(position, 1); 124 | } 125 | """ 126 | ``` 127 | 128 | This vertex shader simply outputs the homogeneous equivalent of 129 | the point that was passed to it. 131 | 132 | After the vertex shader runs, the data it outputs is used to draw 133 | triangles, the most common type of primitive. 134 | 135 | These triangles are rasterized into fragments which are then 136 | colored by the fragment shader. 137 | 138 | ``` 139 | fragment_shader=""" 140 | #version 330 141 | out vec4 frag_color; 142 | void main() { 143 | frag_color = vec4(1, 0, 0, 1); 144 | } 145 | """ 146 | ``` 147 | This fragment shader colors each fragment red by filling the r, g, b, and a channels of 148 | each fragment with 1, 0, 0, and 1, respectively. This is output as a vector passed to 149 | the frag_color variable. 150 | 151 | The fragment data from the fragment shader is then blended in 152 | order to determine the color of each pixel and written to the 153 | frame buffer. 154 | 155 | ## Normalized Device Coordinates 156 | 157 | The coordinate system that OpenGL draws in is constrained to 158 | a 2x2x2 cube with the x, y, and z axes ranging from -1 to +1. 159 | 160 | ## Projection Matrix 161 | 162 | Since we generally don't think of scenes in terms of normalized device 163 | coordinates, we'll need a way to convert points in our preferred 164 | coordinate space to their equivalents in normalized device coordinates. 165 | Since this requires mapping points in your preferred 166 | coordinate space into the 2x2x2 cube centered at the origin, 167 | the x, y, and z coordinates will have to scaled 168 | by factors of 2 / w, 2 / h, and 2 / d, respectively, 169 | where w, h, and d represent the width, height, and depth of 170 | your preferred coordinate space, respectively. 171 | This is done by transforming them with what's called a 172 | projection matrix. 173 | 174 | ```py 175 | width = config["frame_width"] 176 | height = config["frame_height"] 177 | depth = 20 178 | projection = np.array( 179 | [ 180 | [2 / width, 0, 0, 0], 181 | [0, 2 / height, 0, 0], 182 | [0, 0, 2 / depth, 0], 183 | [0, 0, 0, 1], 184 | ] 185 | ) 186 | transformed_mesh.attributes["position"] = ( 187 | projection @ transformed_mesh.attributes["position"].T 188 | ).T 189 | ``` 190 | 191 | ## Model Matrix 192 | If we associate each mesh with a transformation matrix and 193 | transform it with that matrix before rendering we can use 194 | linear algebra to perform any affine transformation on our 195 | meshes. 196 | 197 | If we were to use a traditional 3x3 transformation matrix as 198 | a model matrix it wouldn't be possible to encode translations. 199 | However, homogeneous transformation matrices allow us to 200 | encode translations, rotations, scales, and shears within 201 | model matrices. 202 | 203 | This gives us a powerful tool to transform any mesh using the 204 | same method. 205 | 206 | ```py 207 | vertex_shader=""" 208 | #version 330 209 | 210 | uniform mat4 projection; 211 | uniform mat4 model; 212 | in vec4 position; 213 | 214 | void main() { 215 | gl_Position = projection * model * position; 216 | } 217 | """ 218 | ``` 219 | This vertex shader takes a projection and model matrix and 220 | uses them to transform each point prior to outputting them. 221 | 222 | ## Homogeneous Coordinates 223 | Homogeneous coordinates offer a way of representing 3D 224 | coordinates as 4 dimensional vectors. As a rule of 225 | thumb, points represented with homogeneous coordinates 226 | should have 1 as their w coordinate and vectors 227 | represented with homogeneous coordinates should have 228 | 0 as their w coordinate. More information is at 229 | https://www.tomdalling.com/blog/modern-opengl/explaining-homogenous-coordinates-and-projective-geometry/. 230 | 231 | ## View Matrix 232 | The view matrix encodes the position of our world's camera. 233 | Since any affine transformation to the camera is equivalent 234 | to the opposite transformation on each object in the scene, 235 | the view matrix is the inverse of the camera's model matrix. 236 | 237 | ## Projection Matrix Revisited 238 | When we went over projected matrices before, we only used them 239 | to scale objects. However, more sophisticated projection 240 | matrices can be used to incorporate perspective in a scene. 241 | 242 | There is a lot of theory one could go into regarding this, but 243 | for the purposes of this lesson just know that **orthographic** 244 | projection matrices do not have a concept of perspective, while 245 | **perspective** projection matrices do. 246 | 247 | More information is at https://jsantell.com/3d-projection/. 248 | 249 | ## Barycentric Interpolation 250 | OpenGL determines the inputs to the fragment shader for each 251 | fragment by interpolating vertex data. OpenGL uses Barycentric 252 | interpolation to determine these values. 253 | 254 | A fragment color f is computed by 255 | f = lambda_1 * f_1 + lambda_2 * f_2 + lambda_3 * f_3 256 | where f_i is the data point being interpolated and 257 | lambda_i is the ratio of the area of the triangle opposite 258 | the vertex in question to the total area of the triangle. 259 | 260 | ## Hierarchical Model Matrices 261 | Meshes (or empty coordinate spaces) with associated model 262 | matrices can be organized hierarchically to simply positioning. 263 | 264 | ## Logo 265 | Modeling is never done manually, if you ever need a complex shape 266 | you should find a way to model it in a dedicated program. 267 | --------------------------------------------------------------------------------