diff --git a/Elements/_version.py b/Elements/_version.py index d89bb73b..2d2c7e49 100644 --- a/Elements/_version.py +++ b/Elements/_version.py @@ -5,4 +5,4 @@ # 1) we don't load dependencies by storing it in __init__.py # 2) we can import it in setup.py for the same reason # 3) we can import it into your module -__version__ = '1.2.1' \ No newline at end of file +__version__ = '1.3.0' \ No newline at end of file diff --git a/Elements/examples/1.Introductory/example_2_empty_window_with_GUI.py b/Elements/examples/1.Introductory/example_2_empty_window_with_GUI.py index 2234399b..338403be 100644 --- a/Elements/examples/1.Introductory/example_2_empty_window_with_GUI.py +++ b/Elements/examples/1.Introductory/example_2_empty_window_with_GUI.py @@ -4,7 +4,7 @@ from Elements.pyGLV.GUI.Viewer import SDL2Window, ImGUIDecorator -from Elements.utils.helper_function import displayGUI_text +from Elements.utils.Shortcuts import displayGUI_text example_description = "This is a simple empty scene with ImGUI enabled. Feel free to add your own widgets!" diff --git a/Elements/examples/1.Introductory/example_3_cube_lookAt.py b/Elements/examples/1.Introductory/example_3_cube_lookAt.py index d334459f..fc2a7f4e 100644 --- a/Elements/examples/1.Introductory/example_3_cube_lookAt.py +++ b/Elements/examples/1.Introductory/example_3_cube_lookAt.py @@ -8,10 +8,10 @@ from Elements.pyGLV.GL.Shader import InitGLShaderSystem, Shader, ShaderGLDecorator, RenderGLShaderSystem from Elements.pyGLV.GL.VertexArray import VertexArray -from Elements.utils.helper_function import displayGUI_text +from Elements.utils.Shortcuts import displayGUI_text example_description = \ "This is a scene with some simple geometry, i.e., a colored cube. \n\ -The cube and axes are rendered with a simple shader. \n\ +The cube is rendered via the simplest vertex and fragment shader. \n\ You cannot move the camera through the GUI. Hit ESC OR Close the window to quit." winWidth = 1024 diff --git a/Elements/examples/1.Introductory/example_4_cube_axes_terrain.py b/Elements/examples/1.Introductory/example_4_cube_axes_terrain.py index 0f535f03..8cbf58b8 100644 --- a/Elements/examples/1.Introductory/example_4_cube_axes_terrain.py +++ b/Elements/examples/1.Introductory/example_4_cube_axes_terrain.py @@ -14,11 +14,16 @@ from OpenGL.GL import GL_LINES -from Elements.utils.helper_function import displayGUI_text +from Elements.utils.Shortcuts import displayGUI_text example_description = \ "This is a scene with a cube, a terrain and axes. \n\ The cube and axes are rendered with a simple shader. \n\ -You move the camera through the GUI or the camera. Hit ESC OR Close the window to quit." +that allow camera movement too, via the Elements GUI. \n\n\ +An ECSS Graph shows the Entities and Components of the \n\ +scene, in read only way, i.e., you cannot manipulate \n\ +any information via the ECSS Graph GUI. \n\n\ +You can move the camera through the Elements GUI \n\ +or the mouse. Hit ESC OR Close the window to quit." winWidth = 1024 winHeight = 768 diff --git a/Elements/examples/1.Introductory/example_5_lights_cube.py b/Elements/examples/1.Introductory/example_5_lights_cube.py index 17f8cb76..646ab057 100644 --- a/Elements/examples/1.Introductory/example_5_lights_cube.py +++ b/Elements/examples/1.Introductory/example_5_lights_cube.py @@ -19,7 +19,7 @@ from Elements.utils.terrain import generateTerrain from Elements.utils.obj_to_mesh import obj_to_mesh -from Elements.utils.helper_function import displayGUI_text +from Elements.utils.Shortcuts import displayGUI_text example_description = \ "This is a scene with a cube, a terrain and axes. The scene is being lit using \n\ the Blinn-Phong algorithm. You may move the camera using the mouse or the GUI. \n\ diff --git a/Elements/examples/1.Introductory/example_6_import_objects.py b/Elements/examples/1.Introductory/example_6_import_objects.py index 34e78430..669d09e7 100644 --- a/Elements/examples/1.Introductory/example_6_import_objects.py +++ b/Elements/examples/1.Introductory/example_6_import_objects.py @@ -20,14 +20,14 @@ from Elements.utils.terrain import generateTerrain from Elements.utils.obj_to_mesh import obj_to_mesh -from Elements.utils.helper_function import displayGUI_text +from Elements.utils.Shortcuts import displayGUI_text example_description = \ "This is a scene with the famous Newell teapot. This example demonstrates the \n\ ability to load complex objects instead of inputing them manually. \n\ The scene is being lit using the Blinn-Phong algorithm. \n\ You may move the camera using the mouse or the GUI. \n\ You may see the ECS Scenegraph showing Entities & Components of the scene and \n\ -various information about them. Hit ESC OR Close the window to quit." +various information about them (read-only). Hit ESC OR Close the window to quit." #Light Lposition = util.vec(2.0, 5.5, 2.0) #uniform lightpos @@ -58,14 +58,6 @@ # projMat = util.perspective(90.0, 1.33, 0.1, 100) projMat = util.perspective(50.0, 1.0, 1.0, 10.0) -m = np.linalg.inv(projMat @ view) - - -entityCam2 = scene.world.createEntity(Entity(name="Entity_Camera")) -scene.world.addEntityChild(entityCam1, entityCam2) -trans2 = scene.world.addComponent(entityCam2, BasicTransform(name="Camera_TRS", trs=util.identity())) -# orthoCam = scene.world.addComponent(entityCam2, Camera(util.ortho(-100.0, 100.0, -100.0, 100.0, 1.0, 100.0), "orthoCam","Camera","500")) -orthoCam = scene.world.addComponent(entityCam2, Camera(m, "orthoCam","Camera","500")) node4 = scene.world.createEntity(Entity(name="Object")) scene.world.addEntityChild(rootEntity, node4) @@ -111,7 +103,6 @@ # Systems transUpdate = scene.world.createSystem(TransformSystem("transUpdate", "TransformSystem", "001")) -camUpdate = scene.world.createSystem(CameraSystem("camUpdate", "CameraUpdate", "200")) renderUpdate = scene.world.createSystem(RenderGLShaderSystem()) initUpdate = scene.world.createSystem(InitGLShaderSystem()) @@ -172,7 +163,7 @@ # MAIN RENDERING LOOP running = True -scene.init(imgui=True, windowWidth = winWidth, windowHeight = winHeight, windowTitle = "Elements: Tea anyone?", openGLversion = 4) +scene.init(imgui=True, windowWidth = winWidth, windowHeight = winHeight, windowTitle = "Elements: Tea anyone?", openGLversion = 4, customImGUIdecorator=ImGUIecssDecorator) # pre-pass scenegraph to initialise all GL context dependent geometry, shader classes # needs an active GL context @@ -204,26 +195,24 @@ gWindow._myCamera = view # otherwise, an imgui slider must be moved to properly update model_terrain_axes = util.translate(0.0,0.0,0.0) -model_cube = util.scale(0.1) @ util.translate(0.0,0.5,0.0) +model_teapot = util.scale(0.1) @ util.translate(0.0,0.5,0.0) while running: running = scene.render() displayGUI_text(example_description) - scene.world.traverse_visit(renderUpdate, scene.world.root) - scene.world.traverse_visit_pre_camera(camUpdate, orthoCam) - scene.world.traverse_visit(camUpdate, scene.world.root) + scene.world.traverse_visit(transUpdate, scene.world.root) view = gWindow._myCamera # updates view via the imgui - #mvp_cube = projMat @ view @ model_cube - mvp_object = projMat @ view @ trans4.trs - mvp_terrain = projMat @ view @ terrain_trans.trs - mvp_axes = projMat @ view @ axes_trans.trs + + mvp_object = projMat @ view @ trans4.l2world + mvp_terrain = projMat @ view @ terrain_trans.l2world + mvp_axes = projMat @ view @ axes_trans.l2world axes_shader.setUniformVariable(key='modelViewProj', value=mvp_axes, mat4=True) terrain_shader.setUniformVariable(key='modelViewProj', value=mvp_terrain, mat4=True) shaderDec4.setUniformVariable(key='modelViewProj', value=mvp_object, mat4=True) - shaderDec4.setUniformVariable(key='model',value=model_cube,mat4=True) + shaderDec4.setUniformVariable(key='model',value=trans4.l2world,mat4=True) shaderDec4.setUniformVariable(key='ambientColor',value=Lambientcolor,float3=True) shaderDec4.setUniformVariable(key='ambientStr',value=Lambientstr,float1=True) shaderDec4.setUniformVariable(key='viewPos',value=LviewPos,float3=True) @@ -234,6 +223,7 @@ shaderDec4.setUniformVariable(key='matColor',value=Mcolor,float3=True) + scene.world.traverse_visit(renderUpdate, scene.world.root) scene.render_post() scene.shutdown() diff --git a/Elements/examples/2.Intermediate/example_10_cube_mapping.py b/Elements/examples/2.Intermediate/example_10_cube_mapping.py index 7aff32c4..e3691caa 100644 --- a/Elements/examples/2.Intermediate/example_10_cube_mapping.py +++ b/Elements/examples/2.Intermediate/example_10_cube_mapping.py @@ -15,7 +15,7 @@ from Elements.definitions import TEXTURE_DIR -from Elements.utils.helper_function import displayGUI_text +from Elements.utils.Shortcuts import displayGUI_text example_description = \ "This example demonstrates the cube map texture, i.e., \n\ we encapsulate the scene into a huge cube and apply texture to them\n\ @@ -27,30 +27,20 @@ winWidth = 1024 winHeight = 768 -scene = Scene() - -# Scenegraph with Entities, Components -rootEntity = scene.world.createEntity(Entity(name="RooT")) -entityCam1 = scene.world.createEntity(Entity(name="entityCam1")) -scene.world.addEntityChild(rootEntity, entityCam1) -trans1 = scene.world.addComponent(entityCam1, BasicTransform(name="trans1", trs=util.identity())) eye = util.vec(1, 0.54, 1.0) target = util.vec(0.02, 0.14, 0.217) up = util.vec(0.0, 1.0, 0.0) view = util.lookat(eye, target, up) projMat = util.perspective(50.0, 1.0, 1.0, 10.0) -m = np.linalg.inv(projMat @ view) -entityCam2 = scene.world.createEntity(Entity(name="entityCam2")) -scene.world.addEntityChild(entityCam1, entityCam2) -trans2 = scene.world.addComponent(entityCam2, BasicTransform(name="trans2", trs=util.identity())) -# orthoCam = scene.world.addComponent(entityCam2, Camera(util.ortho(-100.0, 100.0, -100.0, 100.0, 1.0, 100.0), "orthoCam","Camera","500")) -orthoCam = scene.world.addComponent(entityCam2, Camera(m, "orthoCam","Camera","500")) +# Scenegraph with Entities, Components +scene = Scene() +rootEntity = scene.world.createEntity(Entity(name="RooT")) skybox = scene.world.createEntity(Entity(name="Skybox")) scene.world.addEntityChild(rootEntity, skybox) -transSkybox = scene.world.addComponent(skybox, BasicTransform(name="transSkybox", trs=util.identity)) #util.identity() +transSkybox = scene.world.addComponent(skybox, BasicTransform(name="transSkybox", trs=util.identity())) #util.identity() meshSkybox = scene.world.addComponent(skybox, RenderMesh(name="meshSkybox")) node4 = scene.world.createEntity(Entity(name="node4")) @@ -102,7 +92,6 @@ # Systems transUpdate = scene.world.createSystem(TransformSystem("transUpdate", "TransformSystem", "001")) -camUpdate = scene.world.createSystem(CameraSystem("camUpdate", "CameraUpdate", "200")) renderUpdate = scene.world.createSystem(RenderGLShaderSystem()) initUpdate = scene.world.createSystem(InitGLShaderSystem()) @@ -172,24 +161,22 @@ shaderSkybox.setUniformVariable(key='cubemap', value=face_data, texture3D=True) shaderDec4.setUniformVariable(key='cubemap', value=face_data_2, texture3D=True) -model_cube = util.translate(0.0,0.5,0.0) while running: running = scene.render() displayGUI_text(example_description) - scene.world.traverse_visit(renderUpdate, scene.world.root) - scene.world.traverse_visit_pre_camera(camUpdate, orthoCam) - scene.world.traverse_visit(camUpdate, scene.world.root) + scene.world.traverse_visit(transUpdate, scene.world.root) view = gWindow._myCamera # updates view via the imgui shaderDec4.setUniformVariable(key='Proj', value=projMat, mat4=True) shaderDec4.setUniformVariable(key='View', value=view, mat4=True) - shaderDec4.setUniformVariable(key='model', value=model_cube, mat4=True) + shaderDec4.setUniformVariable(key='model', value=trans4.l2world, mat4=True) shaderSkybox.setUniformVariable(key='Proj', value=projMat, mat4=True) shaderSkybox.setUniformVariable(key='View', value=view, mat4=True) + scene.world.traverse_visit(renderUpdate, scene.world.root) scene.render_post() scene.shutdown() \ No newline at end of file diff --git a/Elements/examples/2.Intermediate/example_7_cameraSystem.py b/Elements/examples/2.Intermediate/example_7_cameraSystem.py index c1e9b856..d4764f66 100644 --- a/Elements/examples/2.Intermediate/example_7_cameraSystem.py +++ b/Elements/examples/2.Intermediate/example_7_cameraSystem.py @@ -32,7 +32,7 @@ from Elements.utils.normals import Convert from OpenGL.GL import GL_LINES -from Elements.utils.helper_function import displayGUI_text +from Elements.utils.Shortcuts import displayGUI_text example_description = \ "This is the first examples that demonstrates the usage of the Camera System \n\ instead of the use of the lookAt function to create the view matrix. \n\ diff --git a/Elements/examples/2.Intermediate/example_8_textures.py b/Elements/examples/2.Intermediate/example_8_textures.py index d001e810..cc0047e8 100644 --- a/Elements/examples/2.Intermediate/example_8_textures.py +++ b/Elements/examples/2.Intermediate/example_8_textures.py @@ -18,7 +18,7 @@ from OpenGL.GL import GL_LINES -from Elements.utils.helper_function import displayGUI_text +from Elements.utils.Shortcuts import displayGUI_text example_description = \ "This example demonstrates the ability to apply image textures to geometry. \n\ You may move the camera using the mouse or the GUI. \n\ @@ -31,34 +31,19 @@ # Scenegraph with Entities, Components rootEntity = scene.world.createEntity(Entity(name="RooT")) -entityCam1 = scene.world.createEntity(Entity(name="entityCam1")) -scene.world.addEntityChild(rootEntity, entityCam1) -trans1 = scene.world.addComponent(entityCam1, BasicTransform(name="trans1", trs=util.identity())) eye = util.vec(1, 0.54, 1.0) target = util.vec(0.02, 0.14, 0.217) up = util.vec(0.0, 1.0, 0.0) view = util.lookat(eye, target, up) projMat = util.perspective(50.0, 1.0, 1.0, 10.0) -m = np.linalg.inv(projMat @ view) -entityCam2 = scene.world.createEntity(Entity(name="entityCam2")) -scene.world.addEntityChild(entityCam1, entityCam2) -trans2 = scene.world.addComponent(entityCam2, BasicTransform(name="trans2", trs=util.identity())) -# orthoCam = scene.world.addComponent(entityCam2, Camera(util.ortho(-100.0, 100.0, -100.0, 100.0, 1.0, 100.0), "orthoCam","Camera","500")) -orthoCam = scene.world.addComponent(entityCam2, Camera(m, "orthoCam","Camera","500")) node4 = scene.world.createEntity(Entity(name="node4")) scene.world.addEntityChild(rootEntity, node4) trans4 = scene.world.addComponent(node4, BasicTransform(name="trans4", trs=util.translate(0,0.5,0))) #util.identity() mesh4 = scene.world.addComponent(node4, RenderMesh(name="mesh4")) - -axes = scene.world.createEntity(Entity(name="axes")) -scene.world.addEntityChild(rootEntity, axes) -axes_trans = scene.world.addComponent(axes, BasicTransform(name="axes_trans", trs=util.identity())) -axes_mesh = scene.world.addComponent(axes, RenderMesh(name="axes_mesh")) - # a simple triangle vertexData = np.array([ [0.0, 0.0, 0.0, 1.0], @@ -116,7 +101,6 @@ # Systems transUpdate = scene.world.createSystem(TransformSystem("transUpdate", "TransformSystem", "001")) -camUpdate = scene.world.createSystem(CameraSystem("camUpdate", "CameraUpdate", "200")) renderUpdate = scene.world.createSystem(RenderGLShaderSystem()) initUpdate = scene.world.createSystem(InitGLShaderSystem()) @@ -214,14 +198,14 @@ running = scene.render() displayGUI_text(example_description) scene.world.traverse_visit(renderUpdate, scene.world.root) - scene.world.traverse_visit_pre_camera(camUpdate, orthoCam) - scene.world.traverse_visit(camUpdate, scene.world.root) + scene.world.traverse_visit(transUpdate, scene.world.root) view = gWindow._myCamera # updates view via the imgui - mvp_terrain_axes = projMat @ view @ model_terrain_axes - - axes_shader.setUniformVariable(key='modelViewProj', value=mvp_terrain_axes, mat4=True) - terrain_shader.setUniformVariable(key='modelViewProj', value=mvp_terrain_axes, mat4=True) + mvp_terrain = projMat @ view @ terrain_trans.l2world + mvp_axes = projMat @ view @ axes_trans.l2world + model_cube = trans4.l2world + axes_shader.setUniformVariable(key='modelViewProj', value=mvp_axes, mat4=True) + terrain_shader.setUniformVariable(key='modelViewProj', value=mvp_terrain, mat4=True) shaderDec4.setUniformVariable(key='model', value=model_cube, mat4=True) shaderDec4.setUniformVariable(key='View', value=view, mat4=True) shaderDec4.setUniformVariable(key='Proj', value=projMat, mat4=True) diff --git a/Elements/examples/2.Intermediate/example_9_textures_with_lights.py b/Elements/examples/2.Intermediate/example_9_textures_with_lights.py index de3560db..28bfd81a 100644 --- a/Elements/examples/2.Intermediate/example_9_textures_with_lights.py +++ b/Elements/examples/2.Intermediate/example_9_textures_with_lights.py @@ -21,7 +21,7 @@ from Elements.definitions import TEXTURE_DIR -from Elements.utils.helper_function import displayGUI_text +from Elements.utils.Shortcuts import displayGUI_text example_description = \ "This example demonstrates the ability to apply image textures to geometry. \n\ The scene is being lit using the Blinn-Phong algorithm. \n\n\ @@ -32,9 +32,9 @@ various information about them. Hit ESC OR Close the window to quit." #Light -Lposition = util.vec(2.0, 5.5, 2.0) #uniform lightpos +Lposition = util.vec(5.0, 2.0, 2.0) #uniform lightpos Lambientcolor = util.vec(1.0, 1.0, 1.0) #uniform ambient color -Lambientstr = 0.3 #uniform ambientStr +Lambientstr = 0.2 #uniform ambientStr LviewPos = util.vec(2.5, 2.8, 5.0) #uniform viewpos Lcolor = util.vec(1.0,1.0,1.0) Lintensity = 0.8 @@ -48,30 +48,20 @@ # Scenegraph with Entities, Components rootEntity = scene.world.createEntity(Entity(name="RooT")) -entityCam1 = scene.world.createEntity(Entity(name="Entity1")) -scene.world.addEntityChild(rootEntity, entityCam1) -trans1 = scene.world.addComponent(entityCam1, BasicTransform(name="Entity1_TRS", trs=util.translate(0,0,-8))) + eye = util.vec(1, 0.54, 1.0) target = util.vec(0.02, 0.14, 0.217) up = util.vec(0.0, 1.0, 0.0) view = util.lookat(eye, target, up) # projMat = util.ortho(-10.0, 10.0, -10.0, 10.0, -1.0, 10.0) -# projMat = util.perspective(90.0, 1.33, 0.1, 100) -projMat = util.perspective(50.0, 1.0, 1.0, 10.0) - -m = np.linalg.inv(projMat @ view) +# projMat = util.perspective(90.0, 1.33, 0.1, 100) projMat = util.perspective(50.0, 1.0, 1.0, 10.0) -entityCam2 = scene.world.createEntity(Entity(name="Entity_Camera")) -scene.world.addEntityChild(entityCam1, entityCam2) -trans2 = scene.world.addComponent(entityCam2, BasicTransform(name="Camera_TRS", trs=util.identity())) -# orthoCam = scene.world.addComponent(entityCam2, Camera(util.ortho(-100.0, 100.0, -100.0, 100.0, 1.0, 100.0), "orthoCam","Camera","500")) -orthoCam = scene.world.addComponent(entityCam2, Camera(m, "orthoCam","Camera","500")) node4 = scene.world.createEntity(Entity(name="Object")) scene.world.addEntityChild(rootEntity, node4) -trans4 = scene.world.addComponent(node4, BasicTransform(name="Object_TRS", trs=util.scale(0.3)@util.translate(0,0.5,0) )) +trans4 = scene.world.addComponent(node4, BasicTransform(name="Object_TRS", trs=util.translate(0,0.5,0) )) mesh4 = scene.world.addComponent(node4, RenderMesh(name="Object_mesh")) @@ -139,7 +129,6 @@ # Systems transUpdate = scene.world.createSystem(TransformSystem("transUpdate", "TransformSystem", "001")) -camUpdate = scene.world.createSystem(CameraSystem("camUpdate", "CameraUpdate", "200")) renderUpdate = scene.world.createSystem(RenderGLShaderSystem()) initUpdate = scene.world.createSystem(InitGLShaderSystem()) @@ -222,20 +211,18 @@ model_cube = util.translate(0.0,0.5,0.0) rotate_y = 0.0 -rotation_speed = 1.0 +rotation_speed = 0.5 -texturePath = TEXTURE_DIR / "dark_wood_texture.jpg" +texturePath = TEXTURE_DIR / "uoc_logo.png" texture = Texture(texturePath) shaderDec4.setUniformVariable(key='ImageTexture', value=texture, texture=True) -want_to_rotate = True +want_to_rotate = False while running: running = scene.render() displayGUI_text(example_description) - scene.world.traverse_visit(renderUpdate, scene.world.root) - scene.world.traverse_visit_pre_camera(camUpdate, orthoCam) - scene.world.traverse_visit(camUpdate, scene.world.root) + scene.world.traverse_visit(transUpdate, scene.world.root) view = gWindow._myCamera # updates view via the imgui # mvp_cube = projMat @ view @ model_cube @@ -244,10 +231,10 @@ if want_to_rotate: rotate_y += rotation_speed else: - model_cube = trans4.trs + model_cube = trans4.l2world - mvp_terrain = projMat @ view @ terrain_trans.trs - mvp_axes = projMat @ view @ axes_trans.trs + mvp_terrain = projMat @ view @ terrain_trans.l2world + mvp_axes = projMat @ view @ axes_trans.l2world axes_shader.setUniformVariable(key='modelViewProj', value = mvp_axes, mat4=True) terrain_shader.setUniformVariable(key='modelViewProj', value=mvp_terrain, mat4=True) @@ -264,7 +251,7 @@ shaderDec4.setUniformVariable(key='shininess',value=Mshininess,float1=True) #shaderDec4.setUniformVariable(key='matColor',value=Mcolor,float3=True) - + scene.world.traverse_visit(renderUpdate, scene.world.root) scene.render_post() scene.shutdown() \ No newline at end of file diff --git a/Elements/examples/3.Advanced/example_11_universal_importer_advanced_lighting.py b/Elements/examples/3.Advanced/example_11_universal_importer_advanced_lighting.py index 0d83c8bc..b267cbf3 100644 --- a/Elements/examples/3.Advanced/example_11_universal_importer_advanced_lighting.py +++ b/Elements/examples/3.Advanced/example_11_universal_importer_advanced_lighting.py @@ -15,7 +15,7 @@ from Elements.utils.terrain import generateTerrain from Elements.definitions import MODEL_DIR -from Elements.utils.helper_function import displayGUI_text +from Elements.utils.Shortcuts import displayGUI_text example_description = \ "This example demonstrates the ability to import \n\ really complex objects along with their textures. \n\ diff --git a/Elements/examples/3.Advanced/example_12_usd_scene.py b/Elements/examples/3.Advanced/example_12_usd_scene.py index a483e83e..410def9b 100644 --- a/Elements/examples/3.Advanced/example_12_usd_scene.py +++ b/Elements/examples/3.Advanced/example_12_usd_scene.py @@ -1,15 +1,3 @@ -######################################################### -# Example Usage # -######################################################### -# Example13 provides functionality to save and load # -# an ECSS scene from a USD file # -# # -# In the scene IMGUI user interface, enter the filepath # -# to the file that you want to save or load and click # -# click the corresponding button to save or load the # -# USD scene. # -######################################################### - import numpy as np import Elements.pyECSS.math_utilities as util from Elements.pyECSS.Entity import Entity @@ -28,12 +16,12 @@ from Elements.utils.terrain import generateTerrain from Elements.definitions import SCENES_DIR -from Elements.utils.helper_function import displayGUI_text +from Elements.utils.Shortcuts import displayGUI_text example_description = \ "This example demonstrates the ability to import \n\ basic USD files. By pressing the button load scene \n\ on a blank scene, the default usd is loaded and \n\ -3 red cubes are imported in the scene. You can choose to \n\ +3 yellow cubes are imported in the scene. You can choose to \n\ import the usd file of your choice, by inputing the \n\ appropriate filepath in the text box. Pressing \n\ button save scene the current scene is saved in usd format file." @@ -72,26 +60,12 @@ def SceneGUI(scene, initUpdate): # Scenegraph with Entities, Components rootEntity = scene.world.createEntity(Entity(name="RooT")) -entityCam1 = scene.world.createEntity(Entity(name="Entity1")) -scene.world.addEntityChild(rootEntity, entityCam1) -trans1 = scene.world.addComponent(entityCam1, BasicTransform(name="Entity1_TRS", trs=util.translate(0, 0, -8))) - -eye = util.vec(2.5, 2.5, -2.5) -target = util.vec(0.0, 0.0, 0.0) -up = util.vec(0.0, 1.0, 0.0) -view = util.lookat(eye, target, up) -projMat = util.perspective(50.0, 1.0, 1.0, 10.0) -m = np.linalg.inv(projMat @ view) -entityCam2 = scene.world.createEntity(Entity(name="Entity_Camera")) -scene.world.addEntityChild(entityCam1, entityCam2) -trans2 = scene.world.addComponent(entityCam2, BasicTransform(name="Camera_TRS", trs=util.identity())) -orthoCam = scene.world.addComponent(entityCam2, Camera(m, "orthoCam", "Camera", "500")) -light_node = scene.world.createEntity(Entity(name="LightPos")) -scene.world.addEntityChild(rootEntity, light_node) -light_transform = scene.world.addComponent(light_node, BasicTransform(name="Light_TRS", trs=util.scale(1.0, 1.0, 1.0))) +# light_node = scene.world.createEntity(Entity(name="LightPos")) +# scene.world.addEntityChild(rootEntity, light_node) +# light_transform = scene.world.addComponent(light_node, BasicTransform(name="Light_TRS", trs=util.scale(1.0, 1.0, 1.0))) # Systems transUpdate = scene.world.createSystem(TransformSystem("transUpdate", "TransformSystem", "001")) @@ -116,9 +90,9 @@ def SceneGUI(scene, initUpdate): ]) tetrahedron_indices = np.array([0, 2, 1, 0, 1, 3, 2, 3, 1, 3, 2, 0]) -light_vArray = scene.world.addComponent(light_node, VertexArray()) -light_shader_decorator = scene.world.addComponent(light_node, ShaderGLDecorator( - Shader(vertex_source=Shader.COLOR_VERT_MVP, fragment_source=Shader.COLOR_FRAG))) +# light_vArray = scene.world.addComponent(light_node, VertexArray()) +# light_shader_decorator = scene.world.addComponent(light_node, ShaderGLDecorator( +# Shader(vertex_source=Shader.COLOR_VERT_MVP, fragment_source=Shader.COLOR_FRAG))) # Generate terrain vertexTerrain, indexTerrain, colorTerrain = generateTerrain(size=4, N=20) @@ -202,26 +176,35 @@ def SceneGUI(scene, initUpdate): model_terrain_axes = util.translate(0.0, 0.0, 0.0) -White_Map = (b'\xff\xff\xff\xff', 1, 1) +White_Map = (b'\xff\xff\x00\xff', 1, 1) +#q : what does the above line do? +#a: it creates a white texture map, with 1 pixel, 1 channel, 1 byte per channel +# q: what is the purpose of this? +# a: it is used as a default texture map for the shader, so that the shader can be used without a texture map +# q: what if I wanted a different color? +# a: you can change the color by changing the first argument of the tuple, which is a byte string +# q: what is the byte string for blue? +# a: b'\x00\x00\xff\xff' while running: running = scene.render() displayGUI_text(example_description) - scene.world.traverse_visit(renderUpdate, scene.world.root) - scene.world.traverse_visit_pre_camera(camUpdate, orthoCam) - scene.world.traverse_visit(camUpdate, scene.world.root) SceneGUI(scene, initUpdate) + scene.world.traverse_visit(transUpdate, scene.world.root) view = gWindow._myCamera # updates view via the imgui # mvp_cube = projMat @ view @ model_cube - light_shader_decorator.setUniformVariable(key="modelViewProj", value=projMat @ view @ ( + try: # if light is not in the scene, this will throw an error + light_shader_decorator.setUniformVariable(key="modelViewProj", value=projMat @ view @ ( util.translate(Lposition[0], Lposition[1], Lposition[2]) @ util.scale(1, 1, 1)), mat4=True) - mvp_terrain = projMat @ view @ terrain_trans.trs - mvp_axes = projMat @ view @ axes_trans.trs + except: + pass + mvp_terrain = projMat @ view @ terrain_trans.l2world + mvp_axes = projMat @ view @ axes_trans.l2world axes_shader.setUniformVariable(key='modelViewProj', value=mvp_axes, mat4=True) terrain_shader.setUniformVariable(key='modelViewProj', value=mvp_terrain, mat4=True) # Set Object Real Time Shader Data for shader in newShaders: - model_cube = shader.parent.getChildByType(BasicTransform.getClassName()).trs + model_cube = shader.parent.getChildByType(BasicTransform.getClassName()).l2world # --- Set vertex shader data --- shader.setUniformVariable(key='projection', value=projMat, mat4=True) shader.setUniformVariable(key='view', value=view, mat4=True) @@ -231,7 +214,7 @@ def SceneGUI(scene, initUpdate): normalMatrix = np.transpose(util.inverse(model_cube)) shader.setUniformVariable(key='normalMatrix', value=normalMatrix, mat4=True) - shader.setUniformVariable(key='albedoColor', value=np.array([255,0,0,0]), float3=True) + shader.setUniformVariable(key='albedoColor', value=np.array([100,100,100,0]), float3=True) texture = Texture(img_data=White_Map, texture_channel=0) shader.setUniformVariable(key='lightPos', value=Lposition, float3=True) @@ -240,6 +223,8 @@ def SceneGUI(scene, initUpdate): # Camera position shader.setUniformVariable(key='camPos', value=eye, float3=True) + + scene.world.traverse_visit(renderUpdate, scene.world.root) scene.render_post() diff --git a/Elements/examples/3.Advanced/example_13_proper_resize.py b/Elements/examples/3.Advanced/example_13_proper_resize.py index 401defc3..14fe4657 100644 --- a/Elements/examples/3.Advanced/example_13_proper_resize.py +++ b/Elements/examples/3.Advanced/example_13_proper_resize.py @@ -21,7 +21,7 @@ from Elements.definitions import MODEL_DIR -from Elements.utils.helper_function import displayGUI_text +from Elements.utils.Shortcuts import displayGUI_text example_description = \ "This example demonstrates a window where the projection matrix\n\ is properly reset after a window resize. \n\ diff --git a/Elements/examples/3.Advanced/example_14_ECS_behavior.py b/Elements/examples/3.Advanced/example_14_ECS_behavior.py index ca5fe8fc..c8d89e26 100644 --- a/Elements/examples/3.Advanced/example_14_ECS_behavior.py +++ b/Elements/examples/3.Advanced/example_14_ECS_behavior.py @@ -17,7 +17,7 @@ from Elements.utils.terrain import generateTerrain from Elements.definitions import MODEL_DIR -from Elements.utils.helper_function import displayGUI_text +from Elements.utils.Shortcuts import displayGUI_text example_description = \ "This example demonstrates how we can detect behavior patterns, i.e., \n\ introduce some game logic in our scene. \n\n\ diff --git a/Elements/features/Gizmos/Gizmos.py b/Elements/features/Gizmos/Gizmos.py index cc33b700..01b5446b 100644 --- a/Elements/features/Gizmos/Gizmos.py +++ b/Elements/features/Gizmos/Gizmos.py @@ -16,7 +16,7 @@ def generateCircle(axis='X',points=50,color=[1.0,0.0,0.0,1.0]): """ - Generates and returns data for a circle + Generates and returns data for a circle, used to create part of the rotation gizmo Arguments: axis: where the circle is on points: number of total points corresponding to the circle @@ -75,6 +75,11 @@ def generateCircle(axis='X',points=50,color=[1.0,0.0,0.0,1.0]): return ver, ind, col +RX_GIZMOS, rindex_x, rcolor_x = generateCircle(axis='X',color=[1.0,0.0,0.0,1.0]) +RY_GIZMOS, rindex_y, rcolor_y = generateCircle(axis='Y',color=[0.0,1.0,0.0,1.0]) +RZ_GIZMOS, rindex_z, rcolor_z = generateCircle(axis='Z',color=[0.0,0.0,1.0,1.0]) + +# Vertex data for the translate gizmos VERTEX_GIZMOS_X = np.array([[0.1, 0.1, -0.1, 1.0], [0.1, -0.1, -0.1, 1.0], [0.1, 0.1, 0.1, 1.0], @@ -139,47 +144,39 @@ def generateCircle(axis='X',points=50,color=[1.0,0.0,0.0,1.0]): [0.0, 0.0, 1.0, 1.0] ], dtype=np.float32) -#scale -ARROW_INDEX = np.array((0,1,3, 2,0,3, +#translate cubes indices +ARROW_INDEX2 = np.array((0,1,3, 2,0,3, 5,4,0, 1,0,4, 7,2,6, 6,2,3, 4,5,7, 4,7,6, 7,5,2, 5,0,2, 4,3,1, 4,6,3), np.int32) -#translate -ARROW_INDEX2 = np.array((0,1,3, 2,0,3, + + +# SCALE VERTICES, INDICES, COLORS, NORMALS + +# cubes indices for the scale cube at the end of the scale gizmo +ARROW_INDEX = np.array((0,1,3, 2,0,3, 5,4,0, 1,0,4, 7,2,6, 6,2,3, 4,5,7, 4,7,6, 7,5,2, 5,0,2, 4,3,1, 4,6,3), np.int32) -XS_LINE = np.array([[0.0,0.0,0.0,1.0], - [1.0,0.0,0.0,1.0]],dtype=np.float32) - -YS_LINE = np.array([[0.0,0.0,0.0,1.0], - [0.0,1.0,0.0,1.0]],dtype=np.float32) +# regarding the lines coordinates of the scale gizmo +XS_LINE = np.array([[0.0,0.0,0.0,1.0], [1.0,0.0,0.0,1.0]],dtype=np.float32) +YS_LINE = np.array([[0.0,0.0,0.0,1.0], [0.0,1.0,0.0,1.0]],dtype=np.float32) +ZS_LINE = np.array([[0.0,0.0,0.0,1.0], [0.0,0.0,1.0,1.0]],dtype=np.float32) -ZS_LINE = np.array([[0.0,0.0,0.0,1.0], - [0.0,0.0,1.0,1.0]],dtype=np.float32) - -XS_LINE_COLOR = np.array([ - [1.0, 0.0, 0.0, 1.0], - [1.0, 0.0, 0.0, 1.0] - ], dtype=np.float32) - -YS_LINE_COLOR = np.array([ - [0.0, 1.0, 0.0, 1.0], - [0.0, 1.0, 0.0, 1.0] - ], dtype=np.float32) - -ZS_LINE_COLOR = np.array([ - [0.0, 0.0, 1.0, 1.0], - [0.0, 0.0, 1.0, 1.0] - ], dtype=np.float32) +# regarding the lines colors of the scale gizmo +XS_LINE_COLOR = np.array([ [1.0, 0.0, 0.0, 1.0], [1.0, 0.0, 0.0, 1.0] ], dtype=np.float32) +YS_LINE_COLOR = np.array([ [0.0, 1.0, 0.0, 1.0], [0.0, 1.0, 0.0, 1.0] ], dtype=np.float32) +ZS_LINE_COLOR = np.array([ [0.0, 0.0, 1.0, 1.0], [0.0, 0.0, 1.0, 1.0] ], dtype=np.float32) +# regarding the lines indices of the scale gizmo LINE_INDEX = np.array((0,1),dtype=np.int32) +# regarding the cube coordinates of the scale gizmo XS_GIZMOS = np.array([[1.0, 0.1, -0.1, 1.0], [1.0, -0.1, -0.1, 1.0], [1.0, 0.1, 0.1, 1.0], @@ -188,7 +185,7 @@ def generateCircle(axis='X',points=50,color=[1.0,0.0,0.0,1.0]): [1.2, 0.1, -0.1, 1.0], [1.2, -0.1, 0.1, 1.0], [1.2, 0.1, 0.1, 1.0]],dtype=np.float32) - + YS_GIZMOS = np.array([[0.1, 1.0, -0.1, 1.0], [-0.1, 1.0, -0.1, 1.0], [0.1, 1.0, 0.1, 1.0], @@ -207,15 +204,15 @@ def generateCircle(axis='X',points=50,color=[1.0,0.0,0.0,1.0]): [0.1, -0.1, 1.2, 1.0], [0.1, 0.1, 1.2, 1.0]],dtype=np.float32) +# generates the scale gizmo's cube's colors, indices and normals XS_GIZMOS, INDEX_XS, COLOR_XS, NORMALS_XS = norm.generateFlatNormalsMesh(XS_GIZMOS,ARROW_INDEX,COLOR_X) YS_GIZMOS, INDEX_YS, COLOR_YS, NORMALS_YS = norm.generateFlatNormalsMesh(YS_GIZMOS,ARROW_INDEX,COLOR_Y) ZS_GIZMOS, INDEX_ZS, COLOR_ZS, NORMALS_ZS = norm.generateFlatNormalsMesh(ZS_GIZMOS,ARROW_INDEX,COLOR_Z) -RX_GIZMOS, rindex_x, rcolor_x = generateCircle(axis='X',color=[1.0,0.0,0.0,1.0]) -RY_GIZMOS, rindex_y, rcolor_y = generateCircle(axis='Y',color=[0.0,1.0,0.0,1.0]) -RZ_GIZMOS, rindex_z, rcolor_z = generateCircle(axis='Z',color=[0.0,0.0,1.0,1.0]) + class Mode(enum.Enum): + # Enum class for the gizmos' modes TRANSLATE="Translate" ROTATE="Rotate" SCALE="Scale" @@ -234,21 +231,22 @@ class Gizmos: def __init__(self,rootEntity: Entity): sdl.ext.init() - self.scene = Scene() - self.selected = 0 - self.total = 0 - self.mouse_x, self.mouse_y = c_int(0), c_int(0) - self.key_states = sdl.SDL_GetKeyboardState(None) - self.tab_down = False - self.lmb_down = False - self.projection = np.array([4,4],dtype=np.float32) - self.view = np.array([4,4],dtype=np.float32) - - self.is_selected = False - self.selected_trans = None - self.selected_mesh = None - self.selected_comp = "None" - self.mode = Mode.TRANSLATE + self.scene = Scene() # Scene object + self.selected = 0 # Selected entity + self.total = 0 # Total number of entities on the scene + self.mouse_x, self.mouse_y = c_int(0), c_int(0) # Mouse coordinates + self.key_states = sdl.SDL_GetKeyboardState(None) # Keyboard state + self.tab_down = False # Tab key state + self.lmb_down = False # Left mouse button state + self.projection = np.array([4,4],dtype=np.float32) # Projection matrix + self.view = np.array([4,4],dtype=np.float32) # View matrix + + self.is_selected = False # Is an entity selected + self.selected_trans = None # Transform component of the selected entity + self.selected_mesh = None # Mesh component of the selected entity + self.selected_comp = "None" # Component of the selected entity + self.mode = Mode.TRANSLATE # Current mode of the gizmos + #a set of node names that are ignored in change_target self.gizmos_comps = set(["Gizmos_X","Gizmos_X_trans","Gizmos_X_mesh", "Gizmos_Y","Gizmos_Y_trans","Gizmos_Y_mesh", @@ -263,23 +261,23 @@ def __init__(self,rootEntity: Entity): "Gizmos_y_R","Gizmos_y_R_trans","Gizmos_y_R_mesh", "Gizmos_z_R","Gizmos_z_R_trans","Gizmos_z_R_mesh"]) - self.seperate_transformations = {} - self.initial_transformations = {} - self.entity_dict = {} + self.seperate_transformations = {} # Dictionary for storing each entity's unique transformations seperately + self.initial_transformations = {} # Dictionary for storing each entity's initial transformations seperately + self.entity_dict = {} # Dictionary for storing each entity's name and id seperately - self.cameraInUse = "" + self.cameraInUse = "" # Camera in use - in order to ignore it self.screen_width = 1024.0 self.screen_height = 768.0 - self.picked = False - self.selected_gizmo = '' - self.previous_distance = 0.0 - self.rotation_distance = 0.0 - self.previous_x = 0.0 - self.previous_y = 0.0 - self.previous_z = 0.0 + self.picked = False # Is an entity picked + self.selected_gizmo = '' # Selected gizmo, X, Y or Z + self.previous_distance = 0.0 # Previous distance between the mouse and the gizmo + self.rotation_distance = 0.0 # Distance between the mouse and the gizmo for rotation + self.previous_x = 0.0 # Previous x coordinate of the mouse + self.previous_y = 0.0 # Previous y coordinate of the mouse + self.previous_z = 0.0 # Previous z coordinate of the mouse - self.rotation = util.identity() - self.rotation_modifier = 45 + self.rotation = util.identity() # Rotation matrix for rotation mode - stores the previous rotation until you release the mouse button + self.rotation_modifier = 45 # Rotation modifier for rotation mode: greater value = faster rotation #Light parameters for scale cubes self.Lambientcolor = util.vec(1.0, 1.0, 1.0) @@ -434,16 +432,16 @@ def __init__(self,rootEntity: Entity): self.yrot_min_bb, self.yrot_max_bb = self.calculate_bounding_box(self.gizmos_y_R_mesh.vertex_attributes[0]) self.zrot_min_bb, self.zrot_max_bb = self.calculate_bounding_box(self.gizmos_z_R_mesh.vertex_attributes[0]) - self.count_components() + self.count_components() # Count Basic transform components in the scene, besides the Gizmos - def __remove_rotation(self,model): + def __remove_rotation__(self,model): """ - Creates and returns a copy of a given model matrix after removing its rotation + Creates and returns a copy of a given TRS model matrix after removing its rotation Arguments: self: self - model: a matrix + model: a TRS matrix Returns: - The model matrix without rotation and scaling + The TRS matrix without rotation and scaling """ M = np.array(model,copy=True) for i in range(3): @@ -456,14 +454,14 @@ def __remove_rotation(self,model): def reset_to_None(self): """ - Resets Gizmos to initial state + Resets Gizmos to initial state if someone tries to unsuccessfully pick something Arguments: self: self Returns: None """ prev = self.mode - self.mode = Mode.DISAPPEAR # Temporarily so that Gizmos can disappear in case pick() did not intersect with any Entity + self.mode = Mode.DISAPPEAR self.__update_positions() self.mode = prev self.is_selected = False @@ -473,7 +471,7 @@ def reset_to_None(self): def reset_to_default(self): """ - Resets selected Entity's transformations to identity + Resets selected Entity's transformations to inital state (when clicking 0) Arguments: self: self Returns: @@ -487,7 +485,7 @@ def reset_to_default(self): def change_target(self): """ - Change selected entity + Change selected entity (when clicking TAB) Arguments: self: self Returns: @@ -521,39 +519,25 @@ def change_target(self): def update_ray_start(self): """ - Update mouse position and mouse state. Additionally Raycast or try to pick an Entity + Update mouse position and mouse state. Additionally Raycast (or try to pick an Entity) Arguments: self: self Returns: None """ - mouse_state = sdl.mouse.SDL_GetMouseState(byref(self.mouse_x), byref(self.mouse_y)) + mouse_state = sdl.mouse.SDL_GetMouseState(byref(self.mouse_x), byref(self.mouse_y)) # Mouse state = 1 if Left Mouse Button is pressed or 0 if not #Raycast only when LMB is pressed if mouse_state==1: if self.key_states[sdl.SDL_SCANCODE_LALT] and self.selected_trans is not None: self.raycast() - elif self.lmb_down==False: - self.lmb_down = True - name = self.pick() - if name is not None: - entity: Entity - entity = self.entity_dict[name] - self.is_selected = True - self.selected_comp = name - self.selected_trans = entity.getChildByType(BasicTransform.getClassName()) - self.selected_mesh = entity.getChildByType(RenderMesh.getClassName()) - self.__update_gizmos_trans() - self.__update_positions() - else: - self.reset_to_None() - else: + else: # assume that you have picked a gizmo and you manipulated it, what happens when you release the mouse button self.lmb_down = False self.selected_gizmo = '' self.picked = False - self.rotation = util.identity() + self.rotation = util.identity() # the gizmos are reset to their initial state if self.is_selected: - self.__update_gizmos_trans() - self.__update_positions() + self.__update_gizmos_trans() # update gizmos' positions matrices + self.__update_positions() # update gizmos' positions shaders def count_components(self): """ @@ -609,14 +593,14 @@ def get_Event(self): def __update_gizmos(self): """ - Update Gizmos uniform variables + Update Gizmos uniform variables Arguments: self: self Returns: None """ if self.is_selected: - self.__update_lights() + self.__update_lights() self.__update_positions() def __update_lights(self): @@ -669,7 +653,7 @@ def __update_lights(self): def __update_positions(self): """ - Update model matrices of all Gizmo components + Update model matrices of all Gizmo components and update their shaders Arguments: self: self Returns: @@ -732,7 +716,8 @@ def __update_positions(self): model_ry = self.gizmos_y_R_trans.trs model_rz = self.gizmos_z_R_trans.trs - mvp_rx = 0.0 + # if rotate is not used, then the gizmos are not visible + mvp_rx = 0.0 mvp_ry = 0.0 mvp_rz = 0.0 if self.mode==Mode.ROTATE: @@ -802,6 +787,7 @@ def set_camera_in_use(self,camera: str): def update_screen_dimensions(self,window_width,window_height): """ update saved window width height and field of view + Used to help raycasting Arguments: self: self window_width: window's current width @@ -854,7 +840,7 @@ def calculate_ray(self): Arguments: self: self Returns: - a ray's starting position and direction + a ray's starting position and direction in world space Source: http://www.opengl-tutorial.org/miscellaneous/clicking-on-objects/picking-with-custom-ray-obb-function/ """ @@ -965,7 +951,10 @@ def raycast(self): previous_distance = self.previous_distance #divide the Gizmos' meshes in smaller bounding boxes and test each one of them during the first iteration - if self.selected_gizmo=='': + if self.selected_gizmo=='': # could be X, Y or Z + + # evaluate the distance from the ray origin to the gizmo's mesh for both X, Y and Z and determine which one is the closest + # therefore, which one the user meant to pick x_intersects, x_in_point = self.testRayCircleIntersection(ray_origin, ray_direction, mesh_x, @@ -992,9 +981,10 @@ def raycast(self): z_distance = self.previous_distance else: z_distance = 1000000.0 - - else: #Use the whole mesh for the other iterations + + # we are here, AFTER clicking and WHILE holding the mouse button + elif self.selected_gizmo=='X': x_intersects, x_in_point = self.testRayBoundingBoxIntesection(ray_origin,ray_direction, self.xrot_min_bb, self.xrot_max_bb, @@ -1002,27 +992,37 @@ def raycast(self): if x_intersects: x_distance = self.previous_distance + y_distance = 1000000.0 + z_distance = 1000000.0 else: x_distance = 1000000.0 - + + elif self.selected_gizmo=='Y': y_intersects, y_in_point = self.testRayBoundingBoxIntesection(ray_origin,ray_direction, self.yrot_min_bb, self.yrot_max_bb, model_y) if y_intersects: y_distance = self.previous_distance + x_distance = 1000000.0 + z_distance = 1000000.0 else: y_distance = 1000000.0 - + + elif self.selected_gizmo=='Z': z_intersects, z_in_point = self.testRayBoundingBoxIntesection(ray_origin,ray_direction, self.zrot_min_bb, self.zrot_max_bb, model_z) if z_intersects: z_distance = self.previous_distance + x_distance = 1000000.0 + y_distance = 1000000.0 else: z_distance = 1000000.0 + + #When the ray intersects with more than one gizmo apply rotation to the one closest to the ray origin if x_intersects and (x_distance > y_distance or x_distance > z_distance): x_intersects = False @@ -1153,7 +1153,7 @@ def testRayBoundingBoxIntesection(self,ray_origin,ray_direction,minbb,maxbb,mode model: the element's model matrix Returns: True if there is an intersection, False otherwise. Additionally it returns the intersection point on the bounding box - + Sets seld.previous_distance to the distance between the ray's origin and the intersection point Source: http://www.opengl-tutorial.org/miscellaneous/clicking-on-objects/picking-with-custom-ray-obb-function/ """ tmin = 0.0 @@ -1309,7 +1309,7 @@ def __translate_selected(self,x=0.0,y=0.0,z=0.0): def __rotate_selected(self,_angle=0.0,_axis=(1.0,0.0,0.0)): """ - Rotate Selected Element + Rotate Selected Element around a given axis on local space Arguments: self: self _angle: Rotation Angle @@ -1318,8 +1318,10 @@ def __rotate_selected(self,_angle=0.0,_axis=(1.0,0.0,0.0)): None """ selected = self.seperate_transformations[self.selected_comp] + translation = self.__remove_rotation__(self.selected_trans.trs) - self.selected_trans.trs = self.selected_trans.trs @ util.rotate(angle=_angle,axis=_axis) + #self.selected_trans.trs = self.selected_trans.trs @ util.rotate(angle=_angle,axis=_axis) + self.selected_trans.trs = translation @ util.rotate(angle=_angle,axis=_axis) @ util.inverse(translation) @ self.selected_trans.trs selected.rotation = selected.rotation @ util.rotate(angle=_angle,axis=_axis) self.rotation = self.rotation @ util.rotate(angle=_angle,axis=_axis) @@ -1328,7 +1330,7 @@ def __rotate_selected(self,_angle=0.0,_axis=(1.0,0.0,0.0)): def __scale_selected(self,x=1.0,y=1.0,z=1.0): """ - Scale Selected Element + Scale Selected Element on local space Arguments: self: self x: Scaling on x-axis @@ -1353,7 +1355,7 @@ def __update_gizmos_trans(self): #selected Entity's local-2-world without rotation or scaling #Used for Translation and Scaling Gizmos - no_rotation = self.__remove_rotation(self.selected_trans.l2world) + no_rotation = self.__remove_rotation__(self.selected_trans.l2world) self.gizmos_x_trans.trs = no_rotation self.gizmos_y_trans.trs = no_rotation @@ -1375,39 +1377,4 @@ def __update_gizmos_trans(self): r_gizmos = no_rotation @ self.rotation self.gizmos_x_R_trans.trs = r_gizmos self.gizmos_y_R_trans.trs = r_gizmos - self.gizmos_z_R_trans.trs = r_gizmos - - def pick(self): - """ - Try to pick an Entity - Arguments: - self: self - Returns: - Closest Entity's name if the raycast intersected with one, None otherwise - """ - hit_entities = {} - entity_name : str - for entity_name in self.entity_dict: - obj : Entity - obj = self.entity_dict[entity_name] - trans: BasicTransform - trans = obj.getChildByType(BasicTransform.getClassName()) - mesh:RenderMesh - mesh = obj.getChildByType(RenderMesh.getClassName()) - if trans is None or mesh is None: - continue - - model = trans.l2world - vertices_world = np.array(mesh.vertex_attributes[0],copy=True) @ model - - minbb, maxbb = self.calculate_bounding_box(vertices_world) - - ray_origin, ray_direction = self.calculate_ray() - - intersects, point = self.testRayBoundingBoxIntesection(ray_origin,ray_direction,minbb,maxbb,model) - if intersects: - hit_entities[entity_name] = self.previous_distance - if len(hit_entities)==0: - return None - - return min(hit_entities, key = hit_entities.get) \ No newline at end of file + self.gizmos_z_R_trans.trs = r_gizmos \ No newline at end of file diff --git a/Elements/features/Gizmos/example_13_gizmos.py b/Elements/features/Gizmos/Gizmos_example.py similarity index 94% rename from Elements/features/Gizmos/example_13_gizmos.py rename to Elements/features/Gizmos/Gizmos_example.py index efa67a30..325a6ed5 100644 --- a/Elements/features/Gizmos/example_13_gizmos.py +++ b/Elements/features/Gizmos/Gizmos_example.py @@ -14,6 +14,24 @@ from Elements.utils.obj_to_mesh import obj_to_mesh from Elements.pyGLV.GL.Textures import get_texture_faces, Texture from Elements.pyGLV.GL.Textures import get_single_texture_faces +from Elements.utils.Shortcuts import displayGUI_text + +example_description = """ +This is a scene that contains a floor with a table on which there is a teapot. Camera movement +is possible via the mouse or the GUI + +Gizmos Instructions: +You can change the selected Object by pressing TAB +You can Also reset an Object by pressing '0' + +Use the following keys to change transformation mode: +T: translation +R: Rotation +S: Scaling + +To use the Gizmos hover over them, press and hold the Left-alt-key + Left-mouse-button and +move the cursor to see the result +""" #Light Lposition = util.vec(0.0, 2.5, 1.2) #uniform lightpos @@ -135,8 +153,8 @@ mesh_TableLeg4 = scene.world.addComponent(TableLeg4, RenderMesh(name="mesh_TableLeg4")) teapot = scene.world.createEntity(Entity(name="Teapot")) -scene.world.addEntityChild(rootEntity, teapot) -trans_teapot = scene.world.addComponent(teapot, BasicTransform(name="Teapot_TRS", trs=util.translate(y=0.3) @ util.scale(0.1, 0.1, 0.1) )) +scene.world.addEntityChild(TableTop, teapot) +trans_teapot = scene.world.addComponent(teapot, BasicTransform(name="Teapot_TRS", trs=util.translate(y=0.1) @ util.scale(0.1, 0.1, 0.1) )) teapot_mesh = scene.world.addComponent(teapot, RenderMesh(name="Teapot_mesh")) # Systems @@ -254,11 +272,11 @@ face_data = get_texture_faces(front_img,back_img,top_img,bottom_img,left_img,right_img) -texturePath_Ground = TEXTURE_DIR / "Texture_Grass.png" +texturePath_Ground = TEXTURE_DIR / "black_stones_floor.jpg" texture = Texture(texturePath_Ground) ground_shader.setUniformVariable(key='ImageTexture', value=texture, texture=True) -texturePath_Wood_Material = TEXTURE_DIR / "dark_wood_texture.jpg" +texturePath_Wood_Material = TEXTURE_DIR / "Black_Wooden_Floor.png" texture_Wood = Texture(texturePath_Wood_Material) shaderDec_TableTop.setUniformVariable(key='ImageTexture', value=texture_Wood, texture=True) @@ -281,6 +299,7 @@ running = scene.render() scene.world.traverse_visit(transUpdate, scene.world.root) scene.world.traverse_visit(renderUpdate, scene.world.root) + displayGUI_text(example_description) view = gWindow._myCamera # updates view via the imgui height = scene.renderWindow._windowHeight width = scene.renderWindow._windowWidth @@ -298,7 +317,7 @@ model_TableLeg3 = trans_TableLeg3.l2world model_TableLeg4 = trans_TableLeg4.l2world - mvp_teapot = projMat @ view @ trans_teapot.trs + mvp_teapot = projMat @ view @ trans_teapot.l2world #Update Ground Variables ground_shader.setUniformVariable(key='Proj', value=projMat, mat4=True) diff --git a/Elements/features/Gizmos/test_gizmos.py b/Elements/features/Gizmos/test_gizmos.py index 0bd45163..b4513f5c 100644 --- a/Elements/features/Gizmos/test_gizmos.py +++ b/Elements/features/Gizmos/test_gizmos.py @@ -20,6 +20,7 @@ from OpenGL.GL import GL_LINES import OpenGL.GL as gl +from Elements.utils.Shortcuts import displayGUI_text class TestGizmos(unittest.TestCase): """ @@ -39,6 +40,61 @@ def setUp(self): self.renderUpdate = self.scene.world.createSystem(RenderGLShaderSystem()) self.initUpdate = self.scene.world.createSystem(InitGLShaderSystem()) + self.emptymsg = """ +This is an empty scene where camera movement is possible via +the mouse or the GUI +When trying to change the selected entity we can see that no +Entity is selected and the program does not crash + +Gizmos Instructions: +You can change the selected Object by pressing TAB +You can Also reset an Object by pressing '0' + +Use the following keys to change transformation mode: +T: translation +R: Rotation +S: Scaling + +To use the Gizmos hover over them, press and hold the Left-alt-key + Left-mouse-button and +move the cursor to see the result + """ + self.singlemsg = """ +This is a scene that contains a pink cube where camera movement is possible via +the mouse or the GUI +When TAB is pressed repeatedly you can see that the Gizmos always remain on +the cube + +Gizmos Instructions: +You can change the selected Object by pressing TAB +You can Also reset an Object by pressing '0' + +Use the following keys to change transformation mode: +T: translation +R: Rotation +S: Scaling + +To use the Gizmos hover over them, press and hold the Left-alt-key + Left-mouse-button and +move the cursor to see the result + """ + self.multiplemsg = """ +This is a scene that contains a pink and a yellow cube. Camera movement is possible +via the mouse or the GUI +In this test the pink cube is parent of the yellow one. Therefore, when we apply a +transformation to the parent, the same transformation is applied to its child + +Gizmos Instructions: +You can change the selected Object by pressing TAB +You can Also reset an Object by pressing '0' + +Use the following keys to change transformation mode: +T: translation +R: Rotation +S: Scaling + +To use the Gizmos hover over them, press and hold the Left-alt-key + Left-mouse-button and +move the cursor to see the result + """ + self.vertexCube = np.array([ [-0.5, -0.5, 0.5, 1.0], [-0.5, 0.5, 0.5, 1.0], @@ -180,6 +236,7 @@ def testEmpty(self): running = self.scene.render() self.scene.world.traverse_visit(self.transUpdate, self.scene.world.root) self.scene.world.traverse_visit(self.renderUpdate, self.scene.world.root) + displayGUI_text(self.emptymsg) gizmos.update_ray_start() gizmos.update_view(view) gizmos.get_Event() @@ -194,7 +251,7 @@ def testSingle(self): """ node4_pink = self.scene.world.createEntity(Entity(name="node4_pink")) self.scene.world.addEntityChild(self.rootEntity, node4_pink) - trans4_pink = self.scene.world.addComponent(node4_pink, BasicTransform(name="trans4_pink", trs=util.translate(-1.5,0.0,-1.5))) + trans4_pink = self.scene.world.addComponent(node4_pink, BasicTransform(name="trans4_pink", trs=util.translate(-1.5,0.5,0.0))) mesh4_pink = self.scene.world.addComponent(node4_pink, RenderMesh(name="mesh4_pink")) mesh4_pink.vertex_attributes.append(self.vertex_pink) @@ -254,6 +311,7 @@ def testSingle(self): running = self.scene.render() self.scene.world.traverse_visit(self.transUpdate, self.scene.world.root) self.scene.world.traverse_visit(self.renderUpdate, self.scene.world.root) + displayGUI_text(self.singlemsg) view = gWindow._myCamera height = self.scene.renderWindow._windowHeight width = self.scene.renderWindow._windowWidth @@ -361,6 +419,7 @@ def testMultiple(self): running = self.scene.render() self.scene.world.traverse_visit(self.transUpdate, self.scene.world.root) self.scene.world.traverse_visit(self.renderUpdate, self.scene.world.root) + displayGUI_text(self.multiplemsg) view = gWindow._myCamera height = self.scene.renderWindow._windowHeight width = self.scene.renderWindow._windowWidth diff --git a/Elements/features/GravityBB/AABoundingBox.py b/Elements/features/GravityBB/AABoundingBox.py new file mode 100644 index 00000000..5744d5f6 --- /dev/null +++ b/Elements/features/GravityBB/AABoundingBox.py @@ -0,0 +1,136 @@ +""" +Axis Alinged bounding box class + +@author Nikos Iliakis csd4375 +""" +import numpy as np +from Elements.pyECSS.Component import Component +from Elements.features.GravityBB.GravityCollisonSystem import GravityCollisionSystem + +"""Axis Aligned bounding boxes Class""" +class AABoundingBox(Component): + def __init__(self, name=None, type=None, id=None, vertices=None, objectCollisionList=None, density=0.001, hasGravity=True): + super().__init__(name, type, id) + + # Calculate maximum coordinates and minimum coordinates and use them to make a cube (bounding box) + max_x = max(vertex[0] for vertex in vertices) + max_y = max(vertex[1] for vertex in vertices) + max_z = max(vertex[2] for vertex in vertices) + + min_x = min(vertex[0] for vertex in vertices) + min_y = min(vertex[1] for vertex in vertices) + min_z = min(vertex[2] for vertex in vertices) + + self._vertices = [ + [min_x, min_y, min_z, 1], + [min_x, min_y, max_z, 1], + [min_x, max_y, min_z, 1], + [min_x, max_y, max_z, 1], + [max_x, min_y, min_z, 1], + [max_x, min_y, max_z, 1], + [max_x, max_y, min_z, 1], + [max_x, max_y, max_z, 1], + ] + + self._trans_max_points = [max_x, max_y, max_z] + self._trans_min_points = [min_x, min_y, min_z] + + self._objectCollisionList = objectCollisionList + self._density = density + self._hasGravity = hasGravity + self._isColliding = False + + @property + def vertices(self): + return self._vertices + + @vertices.setter + def vertices(self, vertices): + self._vertices = vertices + + @property + def trans_max_points(self): + return self._trans_max_points + + @trans_max_points.setter + def trans_max_points(self, trans_max_points): + self._trans_max_points = trans_max_points + + @property + def trans_min_points(self): + return self._trans_min_points + + @trans_min_points.setter + def trans_min_points(self, trans_min_points): + self._trans_min_points = trans_min_points + + @property + def objectCollisionList(self): + return self._objectCollisionList + + @objectCollisionList.setter + def objectCollisionList(self, objectCollisionList): + self._objectCollisionList = objectCollisionList + + @property + def isColliding(self): + return self._isColliding + + @isColliding.setter + def isColliding(self, isColliding): + self._isColliding = isColliding + + @property + def mass(self): + return self._mass + + @mass.setter + def mass(self, mass): + self._mass = mass + + @property + def volume(self): + return self._volume + + @volume.setter + def volume(self, volume): + self._volume = volume + + @property + def density(self): + return self._density + + @density.setter + def density(self, density): + self._density = density + + @property + def hasGravity(self): + return self._hasGravity + + @hasGravity.setter + def hasGravity(self, hasGravity): + self._hasGravity = hasGravity + + + def init(self): + pass + + def update(self, **kwargs): + "Debug Only" + pass + + def accept(self, system: GravityCollisionSystem, event = None): + """ + Accepts a class object to operate on the Component, based on the Visitor pattern. + + :param system: [a System object] + :type system: [System] + """ + + # We need this to check if the current system in accept is the GravityCollisionSystem + # Because if it isnt it wont have apply2BoundingBox function and it will throw an error + if hasattr(system, 'apply2BoundingBox'): + system.apply2BoundingBox(self) # Call the method if it exists + + \ No newline at end of file diff --git a/Elements/features/GravityBB/GravityCollisonSystem.py b/Elements/features/GravityBB/GravityCollisonSystem.py new file mode 100644 index 00000000..599f9b65 --- /dev/null +++ b/Elements/features/GravityBB/GravityCollisonSystem.py @@ -0,0 +1,106 @@ +""" +A Gravity Collision System that applies the force of gravity on entities, +that have a RenderMesh a GravityCollisionSystem and a Transform + +@author Nikos Iliakis csd4375 +""" + +import Elements +import Elements.pyECSS.math_utilities as util +import numpy as np +from Elements.pyECSS.System import System + +''' +Conventions: +We assume objects can collide with the objects in its collisionList +We assume the moment an object collides with the floor its gravity is not applied anymore +We assume the moment an object collides with another it is added to the collisionList +''' +class GravityCollisionSystem(System): + def __init__(self, name=None, type=None, id=None): + super().__init__(name, type, id) + + def update(self): + """ + method to be subclassed for behavioral or logic computation + when visits Components of an EntityNode. + + """ + pass + + '''aabb_comp: Elements.pyECSS.Component.AABoundingBox''' + def apply2BoundingBox(self, aabb_comp: Elements.pyECSS.Component): + + # Checking if the aa bound box is not of the floors and if its not colliding with anything + if(aabb_comp.hasGravity and aabb_comp.isColliding == False): + self.apply_force_of_gravity(aabb_comp, 9.8) + + # Updating translated bounding_box_points + basicTrans = self.getBasicTransformComp(aabb_comp.parent) + if(basicTrans != None): + trs_vertices = [] + for i in range(0, len(aabb_comp.vertices)): + trs_vertices.append(list(basicTrans.trs @ aabb_comp.vertices[i])) + + # Calculate the maximum and minimum values for each component (x, y, z) + max_x = max(vertex[0] for vertex in trs_vertices) + max_y = max(vertex[1] for vertex in trs_vertices) + max_z = max(vertex[2] for vertex in trs_vertices) + + min_x = min(vertex[0] for vertex in trs_vertices) + min_y = min(vertex[1] for vertex in trs_vertices) + min_z = min(vertex[2] for vertex in trs_vertices) + + # Set the transformed maximum and minimum points + aabb_comp.trans_max_points = [max_x, max_y, max_z] + aabb_comp.trans_min_points = [min_x, min_y, min_z] + + + isItAlreadyColliding = aabb_comp.isColliding + + # Check if aa bound box is colliding with floor if so then set collision bool to true else false + if(aabb_comp.objectCollisionList != None): + aabb_comp.isColliding = self.check_list_for_collision(aabb_comp=aabb_comp) + + if(isItAlreadyColliding == False and aabb_comp.isColliding == True): + aabb_comp.objectCollisionList.append(aabb_comp) + + ## Run through the entire list of possible collision objects + def check_list_for_collision(self, aabb_comp: Elements.pyECSS.Component): + for col_object in aabb_comp.objectCollisionList: + collision_bool = self.collision_with_other_aabb(aabb_comp=aabb_comp, other_aabb_comp=col_object) + if(collision_bool == True): return True + + return False + + def collision_with_other_aabb(self, aabb_comp: Elements.pyECSS.Component, other_aabb_comp: Elements.pyECSS.Component): + # Check if this AABB intersects with another AABB + return all( + aabb_comp.trans_min_points[i] <= other_aabb_comp.trans_max_points[i] + and aabb_comp.trans_max_points[i] >= other_aabb_comp.trans_min_points[i] + for i in range(3) + ) + + def calculate_volume_mass_of_object(self, aabb_comp: Elements.pyECSS.Component): + aabb_comp.volume = self.volume(aabb_comp) + aabb_comp.mass = aabb_comp.density * aabb_comp.volume + + def apply_force_of_gravity(self, aabb_comp: Elements.pyECSS.Component, gravityAcceleration): + self.calculate_volume_mass_of_object(aabb_comp) + self.getBasicTransformComp(aabb_comp.parent).trs = util.translate(0, -aabb_comp.mass * gravityAcceleration, 0) @ self.getBasicTransformComp(aabb_comp.parent).trs + + + def volume(self, bb_comp: Elements.pyECSS.Component): + # Calculate the volume of the AABB + width = bb_comp.trans_max_points[0] - bb_comp.trans_min_points[0] + height = bb_comp.trans_max_points[1] - bb_comp.trans_min_points[1] + depth = bb_comp.trans_max_points[2] - bb_comp.trans_min_points[2] + return 1 + + def getBasicTransformComp(self, parent): + for i in range(0, parent.getNumberOfChildren()): + childComp = parent.getChild(i) + if(childComp.getClassName() == "BasicTransform"): + return childComp + + return None \ No newline at end of file diff --git a/Elements/features/GravityBB/__init__.py b/Elements/features/GravityBB/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Elements/features/GravityBB/example_GravityCollisionWithBB.py b/Elements/features/GravityBB/example_GravityCollisionWithBB.py new file mode 100644 index 00000000..e9c7e0ad --- /dev/null +++ b/Elements/features/GravityBB/example_GravityCollisionWithBB.py @@ -0,0 +1,285 @@ +""" +Example using bounding boxes components and a gravity collision system + +@author Nikos Iliakis csd4375 +""" +from __future__ import annotations +import numpy as np + +import OpenGL.GL as gl; +import Elements.pyECSS.math_utilities as util +import random as rand +from Elements.pyECSS.System import TransformSystem, CameraSystem +from Elements.pyECSS.Entity import Entity +from Elements.pyECSS.Component import BasicTransform, RenderMesh +from Elements.pyECSS.Event import Event + +from Elements.pyGLV.GUI.Viewer import RenderGLStateSystem, ImGUIecssDecorator +from Elements.pyGLV.GL.Shader import InitGLShaderSystem, Shader, ShaderGLDecorator, RenderGLShaderSystem +from Elements.pyGLV.GL.VertexArray import VertexArray +from Elements.pyGLV.GL.Scene import Scene +from Elements.pyGLV.GL.SimpleCamera import SimpleCamera +from Elements.utils.normals import Convert + +from Elements.features.GravityBB.AABoundingBox import AABoundingBox +from Elements.features.GravityBB.GravityCollisonSystem import GravityCollisionSystem +from Elements.features.GravityBB.floor import generate_floor_with_bb + +from Elements.pyGLV.GL.Textures import Texture +from Elements.definitions import TEXTURE_DIR +from Elements.utils.Shortcuts import displayGUI_text + + +class GameObjectEntity(Entity): + def __init__(self, name=None, type=None, id=None) -> None: + super().__init__(name, type, id); + + # Gameobject basic properties + self._color = [1, 0.5, 0.2, 1.0]; # this will be used as a uniform var + # Create basic components of a primitive object + self.trans = BasicTransform(name="trans", trs=util.identity()); + self.mesh = RenderMesh(name="mesh"); + self.shaderDec = ShaderGLDecorator(Shader(vertex_source=Shader.VERT_PHONG_MVP, fragment_source=Shader.FRAG_PHONG)); + self.vArray = VertexArray(); + # Add components to entity + scene = Scene(); + scene.world.createEntity(self); + scene.world.addComponent(self, self.trans); + scene.world.addComponent(self, self.mesh); + scene.world.addComponent(self, self.shaderDec); + scene.world.addComponent(self, self.vArray); + + @property + def color(self): + return self._color; + @color.setter + def color(self, colorArray): + self._color = colorArray; + + def drawSelfGui(self, imgui): + changed, value = imgui.color_edit3("Color", self.color[0], self.color[1], self.color[2]); + self.color = [value[0], value[1], value[2], 1.0]; + + def SetVertexAttributes(self, vertex, color, index, normals = None): + self.mesh.vertex_attributes.append(vertex); + self.mesh.vertex_attributes.append(color); + if normals is not None: + self.mesh.vertex_attributes.append(normals); + self.mesh.vertex_index.append(index); + +def CubeSpawn(cubename = "Cube"): + cube = GameObjectEntity(cubename); + vertices = [ + [-0.5, -0.5, 0.5, 1.0], + [-0.5, 0.5, 0.5, 1.0], + [0.5, 0.5, 0.5, 1.0], + [0.5, -0.5, 0.5, 1.0], + [-0.5, -0.5, -0.5, 1.0], + [-0.5, 0.5, -0.5, 1.0], + [0.5, 0.5, -0.5, 1.0], + [0.5, -0.5, -0.5, 1.0] + ]; + + color = [rand.uniform(0,1), rand.uniform(0,1), rand.uniform(0,1), 1.0] + colors = [ + color, + color, + color, + color, + color, + color, + color, + color + ] + # OR + # colors = [cube.color] * len(vertices) + + + #index arrays for above vertex Arrays + indices = np.array( + ( + 1,0,3, 1,3,2, + 2,3,7, 2,7,6, + 3,0,4, 3,4,7, + 6,5,1, 6,1,2, + 4,5,6, 4,6,7, + 5,4,0, 5,0,1 + ), + dtype=np.uint32 + ) #rhombus out of two triangles + + vertices, colors, indices, normals = Convert(vertices, colors, indices, produceNormals=True); + cube.SetVertexAttributes(vertices, colors, indices, normals); + + return cube; + +example_description = \ +"This example shows the application of gravity on objects with \n\ +axis aligned bounding boxes. As well as the collision between \n\ +the bounding boxes of the cubes and the floor. The moment a cube hits \n\ +the floor it is considered as part of it and more cubes can land on it \n\n\ +You may move the camera using the mouse: \n\ +* Move holding Right Click: Change camera angle \n\ +* Move holding Right Click and Shift: Move camera target\n\ +* Scroll holding Ctrl: Zoom In/Out\n\ +or the ECSS Graph (not the Elements ImGUI window) by \n\ +* Changing Translation, Rotation or Scale of \n\ +the trans component of either EntityCam2 (or EntityCam1)\n\n\ +You may see the ECS Scenegraph showing Entities & Components of the scene and \n\ +various information about them. Hit ESC OR Close the window to quit." + +def main(imguiFlag = False): + ########################################################## + # Instantiate a simple complete ECSS with Entities, + # Components, Camera, Shader, VertexArray and RenderMesh + ######################################################### + + winWidth = 1024 + winHeight = 1024 + + scene = Scene() + + # Initialize Systems used for this script + gravitycollisionSystem = scene.world.createSystem(GravityCollisionSystem()) + transUpdate = scene.world.createSystem(TransformSystem("transUpdate", "TransformSystem", "001")) + camUpdate = scene.world.createSystem(CameraSystem("camUpdate", "CameraUpdate", "200")) + renderUpdate = scene.world.createSystem(RenderGLShaderSystem()) + initUpdate = scene.world.createSystem(InitGLShaderSystem()) + + # Scenegraph with Entities, Components + rootEntity = scene.world.createEntity(Entity(name="Root")) + + # Spawn Camera + mainCamera = SimpleCamera("Simple Camera") + # Camera Settings + mainCamera.trans2.trs = util.translate(0, 0, 12) # VIEW + mainCamera.trans1.trs = util.rotate((1, 0, 0), -45); + + #----------------------------------------- + # Spawn Two Homes on top of each other + Cubes = scene.world.createEntity(Entity("Cubes")) + scene.world.addEntityChild(rootEntity, Cubes) + + trans = BasicTransform(name="trans", trs=util.identity()); + scene.world.addComponent(Cubes, trans) + + # Generating floor with bounding box so that the objects can collide + floor_trans, floor_shader, floor_bb = generate_floor_with_bb(rootEntity) + + # We typically add only the floor but you can add more objects if you wish so + collisionObjectList = [floor_bb] # NEED TO PASS THE SAME LIST TO ALL OF THEM + + number_of_cubes_in_scene = 100 + + # Spawning x cubes with random transformations with bounding boxes + for i in range(0, number_of_cubes_in_scene): + cube: GameObjectEntity = CubeSpawn() + scene.world.addEntityChild(Cubes, cube) + cube.trans.trs = util.translate(rand.uniform(-5, 5), rand.uniform(2, 20), rand.uniform(-5, 5)) + #cube.trans.trs = cube.trans.trs @ util.rotate(axis= [rand.randint(0,1), rand.randint(0,1), rand.randint(0,1)], angle = rand.uniform(0,360)) + cube.trans.trs = cube.trans.trs @ util.scale(rand.uniform(0.2, 1),rand.uniform(0.2, 1),rand.uniform(0.2, 1)) + scene.world.addComponent(cube, AABoundingBox(name="AABoundingBox", + vertices = cube.mesh.vertex_attributes[0], + objectCollisionList = collisionObjectList)) + + Cubes.getChild(0).trs = util.translate(0, 0, 0) + + + # MAIN RENDERING LOOP + running = True + scene.init(imgui=True, windowWidth = 1024, windowHeight = 768, windowTitle = "Elements: A CameraSystem Example", customImGUIdecorator = ImGUIecssDecorator) + + #imGUIecss = scene.gContext + + + # --------------------------------------------------------- + # Run pre render GLInit traversal for once! + # pre-pass scenegraph to initialise all GL context dependent geometry, shader classes + # needs an active GL context + # --------------------------------------------------------- + + gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) + gl.glDisable(gl.GL_CULL_FACE); + + # gl.glDepthMask(gl.GL_FALSE); + gl.glEnable(gl.GL_DEPTH_TEST); + gl.glDepthFunc(gl.GL_LESS); + scene.world.traverse_visit(initUpdate, rootEntity) + + + ############################################ + # Instantiate all Event-related key objects + ############################################ + + # instantiate new EventManager + # need to pass that instance to all event publishers e.g. ImGUIDecorator + eManager = scene.world.eventManager + gWindow = scene.renderWindow + gGUI = scene.gContext + + #simple Event actuator System + renderGLEventActuator = RenderGLStateSystem() + + #setup Events and add them to the EventManager + updateTRS = Event(name="OnUpdateTRS", id=100, value=None) + updateBackground = Event(name="OnUpdateBackground", id=200, value=None) + eManager._events[updateTRS.name] = updateTRS + eManager._events[updateBackground.name] = updateBackground + + + eManager._subscribers[updateTRS.name] = gGUI + eManager._subscribers[updateBackground.name] = gGUI + + eManager._subscribers['OnUpdateWireframe'] = gWindow + eManager._actuators['OnUpdateWireframe'] = renderGLEventActuator + eManager._subscribers['OnUpdateCamera'] = gWindow + eManager._actuators['OnUpdateCamera'] = renderGLEventActuator + + # Apply dark_wood texture to floor ! + texturePath = TEXTURE_DIR / "dark_wood_texture.jpg" + texture = Texture(texturePath) + floor_shader.setUniformVariable(key='ImageTexture', value=texture, texture=True) + + # Add RenderWindow to the EventManager publishers + eManager._publishers[updateBackground.name] = gGUI + + while running: + running = scene.render() + scene.world.traverse_visit(renderUpdate, scene.world.root) + + displayGUI_text(example_description) + + # Here we traverse with the gravity Collision System + scene.world.traverse_visit(gravitycollisionSystem, scene.world.root) + scene.world.traverse_visit(transUpdate, scene.world.root) + scene.world.traverse_visit_pre_camera(camUpdate, mainCamera.camera) + scene.world.traverse_visit(camUpdate, scene.world.root) + + for i in range(1, number_of_cubes_in_scene + 1): + Cubes.getChild(i).shaderDec.setUniformVariable(key='modelViewProj', value=Cubes.getChild(i).trans.l2cam, mat4=True); + Cubes.getChild(i).shaderDec.setUniformVariable(key='model', value=Cubes.getChild(i).trans.trs, mat4=True) + + Cubes.getChild(i).shaderDec.setUniformVariable(key='ambientColor', value=[1, 0.5, 0.3], float3=True) + Cubes.getChild(i).shaderDec.setUniformVariable(key='ambientStr', value=0.5, float1=True) + + Cubes.getChild(i).shaderDec.setUniformVariable(key='viewPos', value=mainCamera.trans2.trs, float3=True) + Cubes.getChild(i).shaderDec.setUniformVariable(key='lightPos', value=[0, 4, 0], float3=True) + Cubes.getChild(i).shaderDec.setUniformVariable(key='lightColor', value=[0.5, 0.5, 0.5], float3=True) + Cubes.getChild(i).shaderDec.setUniformVariable(key='lightIntensity', value=3, float1=True) + + Cubes.getChild(i).shaderDec.setUniformVariable(key='shininess', value=0, float1=True) + Cubes.getChild(i).shaderDec.setUniformVariable(key='matColor', value=[0.4, 0.4, 0.4], float3=True) + + floor_shader.setUniformVariable(key='model', value=floor_trans.l2cam, mat4=True) + floor_shader.setUniformVariable(key='View', value=util.identity(), mat4=True) + floor_shader.setUniformVariable(key='Proj', value=util.identity(), mat4=True) + + + # ImGUI post-display calls and SDLWindow swap + scene.render_post() + + scene.shutdown() + + +if __name__ == "__main__": + main(imguiFlag = False) \ No newline at end of file diff --git a/Elements/features/GravityBB/floor.py b/Elements/features/GravityBB/floor.py new file mode 100644 index 00000000..72baa77d --- /dev/null +++ b/Elements/features/GravityBB/floor.py @@ -0,0 +1,69 @@ +""" +Generating floor and adding bounding box to test gravity Collision with Texture ! + +@author Nikos Iliakis csd4375 +""" +from __future__ import annotations +import Elements.pyECSS.math_utilities as util +import numpy as np +from Elements.pyECSS.Entity import Entity +from Elements.pyECSS.Component import BasicTransform, RenderMesh + +from Elements.pyGLV.GL.Shader import Shader, ShaderGLDecorator +from Elements.pyGLV.GL.VertexArray import VertexArray +from Elements.pyGLV.GL.Scene import Scene +from Elements.features.GravityBB.AABoundingBox import AABoundingBox +from Elements.pyGLV.GL.Textures import Texture + +# Generates a floor with a bounding box WITH TEXTURE SHADER +def generate_floor_with_bb(rootEntity, size=12): + scene = Scene() + # --------------------------- + # Generate floor = terrain + + vertexTerrain = [ + [-0.5, -0.5, 0.5, 1.0], + [-0.5, 0.5, 0.5, 1.0], + [0.5, 0.5, 0.5, 1.0], + [0.5, -0.5, 0.5, 1.0], + [-0.5, -0.5, -0.5, 1.0], + [-0.5, 0.5, -0.5, 1.0], + [0.5, 0.5, -0.5, 1.0], + [0.5, -0.5, -0.5, 1.0] + ]; + + #index arrays for above vertex Arrays + indexTerrain = np.array( + ( + 1,0,3, 1,3,2, + 2,3,7, 2,7,6, + 3,0,4, 3,4,7, + 6,5,1, 6,1,2, + 4,5,6, 4,6,7, + 5,4,0, 5,0,1 + ), + dtype=np.uint32 + ) + + # Add terrain + floor = scene.world.createEntity(Entity(name="floor")) + scene.world.addEntityChild(rootEntity, floor) + + floor_trans = scene.world.addComponent(floor, BasicTransform(name="floor_trans", trs=util.identity())) + floor_trans.trs = util.scale(size, 0.5, size) + + floor_mesh = scene.world.addComponent(floor, RenderMesh(name="floor_mesh")) + floor_mesh.vertex_attributes.append(vertexTerrain) + floor_mesh.vertex_attributes.append(Texture.CUBE_TEX_COORDINATES) + floor_mesh.vertex_index.append(indexTerrain) + + vertices = floor_mesh.vertex_attributes[0] + + # create the bounding box of the floor Gravity should be FALSE so that the floor wont just fall + floor_bb = scene.world.addComponent(floor, AABoundingBox(name="floor_bb", vertices=vertices, hasGravity=False)) + + scene.world.addComponent(floor, VertexArray()) + + floor_shader = scene.world.addComponent(floor, ShaderGLDecorator(Shader(vertex_source = Shader.SIMPLE_TEXTURE_VERT, fragment_source=Shader.SIMPLE_TEXTURE_FRAG))) + + return floor_trans, floor_shader, floor_bb \ No newline at end of file diff --git a/Elements/features/GravityBB/test_gravity_collision_withBB.py b/Elements/features/GravityBB/test_gravity_collision_withBB.py new file mode 100644 index 00000000..43a1be12 --- /dev/null +++ b/Elements/features/GravityBB/test_gravity_collision_withBB.py @@ -0,0 +1,185 @@ +import unittest +import numpy as np + +import OpenGL.GL as gl; +import Elements.pyECSS.math_utilities as util +import random as rand +from Elements.pyECSS.System import TransformSystem, CameraSystem +from Elements.pyECSS.Entity import Entity +from Elements.pyECSS.Component import BasicTransform +from Elements.pyECSS.Event import Event + +from Elements.pyGLV.GUI.Viewer import RenderGLStateSystem, ImGUIecssDecorator +from Elements.pyGLV.GL.Shader import InitGLShaderSystem, Shader, ShaderGLDecorator, RenderGLShaderSystem +from Elements.pyGLV.GL.Scene import Scene +from Elements.pyGLV.GL.SimpleCamera import SimpleCamera + +from Elements.features.GravityBB.AABoundingBox import AABoundingBox +from Elements.features.GravityBB.GravityCollisonSystem import GravityCollisionSystem +from Elements.features.GravityBB.example_GravityCollisionWithBB import GameObjectEntity +from Elements.features.GravityBB.example_GravityCollisionWithBB import CubeSpawn +from Elements.features.GravityBB.floor import generate_floor_with_bb +from Elements.utils.Shortcuts import displayGUI_text + +from Elements.pyGLV.GL.Textures import Texture +from Elements.definitions import TEXTURE_DIR + +class TestMainFunction(unittest.TestCase): + def test_gravity_collision_on_floor_with_cubes(self): + ########################################################## + # Instantiate a simple complete ECSS with Entities, + # Components, Camera, Shader, VertexArray and RenderMesh + ######################################################### + + example_description = \ +"This example shows the application of gravity on objects with \n\ +axis aligned bounding boxes. As well as the collision between \n\ +the bounding boxes of the cubes and the floor. The moment a cube hits \n\ +the floor it is considered as part of it and more cubes can collide with it \n\ +In this example we test only 3 cubes \n\ +You may move the camera using the mouse or the GUI. \n\ +You may see the ECS Scenegraph showing Entities & Components of the scene and \n\ +various information about them. Hit ESC OR Close the window to quit." + + winWidth = 1024 + winHeight = 1024 + + scene = Scene() + + # Initialize Systems used for this script + gravitycollisionSystem = scene.world.createSystem(GravityCollisionSystem()) + transUpdate = scene.world.createSystem(TransformSystem("transUpdate", "TransformSystem", "001")) + camUpdate = scene.world.createSystem(CameraSystem("camUpdate", "CameraUpdate", "200")) + renderUpdate = scene.world.createSystem(RenderGLShaderSystem()) + initUpdate = scene.world.createSystem(InitGLShaderSystem()) + + # Scenegraph with Entities, Components + rootEntity = scene.world.createEntity(Entity(name="Root")) + + # Spawn Camera + mainCamera = SimpleCamera("Simple Camera") + # Camera Settings + mainCamera.trans2.trs = util.translate(0, 0, 12) # VIEW + mainCamera.trans1.trs = util.rotate((1, 0, 0), -45); + + Cubes = scene.world.createEntity(Entity("Cubes")) + scene.world.addEntityChild(rootEntity, Cubes) + + trans = BasicTransform(name="trans", trs=util.identity()); + scene.world.addComponent(Cubes, trans) + + # Generating floor with bounding box so that the objects can collide + floor_trans, floor_shader, floor_bb = generate_floor_with_bb(rootEntity) + + collisionObjectList = [floor_bb] # NEED TO PASS THE SAME LIST TO ALL OF THEM + number_of_cubes_in_scene = 3 + + # Spawning 3 cubes with random transformations with bounding boxes + for i in range(0, number_of_cubes_in_scene): + cube: GameObjectEntity = CubeSpawn() + scene.world.addEntityChild(Cubes, cube) + cube.trans.trs = util.translate(0, (i + 1)*5, 0) + #cube.trans.trs = cube.trans.trs @ util.rotate(axis= [rand.randint(0,1), rand.randint(0,1), rand.randint(0,1)], angle = rand.uniform(0,360)) + #cube.trans.trs = cube.trans.trs @ util.scale(rand.uniform(0.2, 1),rand.uniform(0.2, 1),rand.uniform(0.2, 1)) + scene.world.addComponent(cube, AABoundingBox(name="AABoundingBox", + vertices = cube.mesh.vertex_attributes[0], + objectCollisionList = collisionObjectList, + density= 0.005)) + + + Cubes.getChild(0).trs = util.translate(0, 0, 0) + + + # MAIN RENDERING LOOP + running = True + scene.init(imgui=True, windowWidth = 1024, windowHeight = 768, windowTitle = "Elements: A CameraSystem Example", customImGUIdecorator = ImGUIecssDecorator) + + #imGUIecss = scene.gContext + + + # --------------------------------------------------------- + # Run pre render GLInit traversal for once! + # pre-pass scenegraph to initialise all GL context dependent geometry, shader classes + # needs an active GL context + # --------------------------------------------------------- + + gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) + gl.glDisable(gl.GL_CULL_FACE); + + # gl.glDepthMask(gl.GL_FALSE); + gl.glEnable(gl.GL_DEPTH_TEST); + gl.glDepthFunc(gl.GL_LESS); + scene.world.traverse_visit(initUpdate, rootEntity) + + + ############################################ + # Instantiate all Event-related key objects + ############################################ + + # instantiate new EventManager + # need to pass that instance to all event publishers e.g. ImGUIDecorator + eManager = scene.world.eventManager + gWindow = scene.renderWindow + gGUI = scene.gContext + + #simple Event actuator System + renderGLEventActuator = RenderGLStateSystem() + + #setup Events and add them to the EventManager + updateTRS = Event(name="OnUpdateTRS", id=100, value=None) + updateBackground = Event(name="OnUpdateBackground", id=200, value=None) + eManager._events[updateTRS.name] = updateTRS + eManager._events[updateBackground.name] = updateBackground + + + eManager._subscribers[updateTRS.name] = gGUI + eManager._subscribers[updateBackground.name] = gGUI + + eManager._subscribers['OnUpdateWireframe'] = gWindow + eManager._actuators['OnUpdateWireframe'] = renderGLEventActuator + eManager._subscribers['OnUpdateCamera'] = gWindow + eManager._actuators['OnUpdateCamera'] = renderGLEventActuator + + texturePath = TEXTURE_DIR / "dark_wood_texture.jpg" + texture = Texture(texturePath) + floor_shader.setUniformVariable(key='ImageTexture', value=texture, texture=True) + + # Add RenderWindow to the EventManager publishers + eManager._publishers[updateBackground.name] = gGUI + + while running: + running = scene.render() + scene.world.traverse_visit(renderUpdate, scene.world.root) + + displayGUI_text(example_description) + + # Here we traverse with the gravity Collision System + scene.world.traverse_visit(gravitycollisionSystem, scene.world.root) + scene.world.traverse_visit(transUpdate, scene.world.root) + scene.world.traverse_visit_pre_camera(camUpdate, mainCamera.camera) + scene.world.traverse_visit(camUpdate, scene.world.root) + + for i in range(1, number_of_cubes_in_scene + 1): + Cubes.getChild(i).shaderDec.setUniformVariable(key='modelViewProj', value=Cubes.getChild(i).trans.l2cam, mat4=True); + Cubes.getChild(i).shaderDec.setUniformVariable(key='model', value=Cubes.getChild(i).trans.trs, mat4=True) + + Cubes.getChild(i).shaderDec.setUniformVariable(key='ambientColor', value=[1, 0.5, 0.3], float3=True) + Cubes.getChild(i).shaderDec.setUniformVariable(key='ambientStr', value=0.5, float1=True) + + Cubes.getChild(i).shaderDec.setUniformVariable(key='viewPos', value=mainCamera.trans2.trs, float3=True) + Cubes.getChild(i).shaderDec.setUniformVariable(key='lightPos', value=[0, 4, 0], float3=True) + Cubes.getChild(i).shaderDec.setUniformVariable(key='lightColor', value=[0.5, 0.5, 0.5], float3=True) + Cubes.getChild(i).shaderDec.setUniformVariable(key='lightIntensity', value=3, float1=True) + + Cubes.getChild(i).shaderDec.setUniformVariable(key='shininess', value=0, float1=True) + Cubes.getChild(i).shaderDec.setUniformVariable(key='matColor', value=[0.4, 0.4, 0.4], float3=True) + + floor_shader.setUniformVariable(key='model', value=floor_trans.l2cam, mat4=True) + floor_shader.setUniformVariable(key='View', value=util.identity(), mat4=True) + floor_shader.setUniformVariable(key='Proj', value=util.identity(), mat4=True) + + + # ImGUI post-display calls and SDLWindow swap + scene.render_post() + + scene.shutdown() \ No newline at end of file diff --git a/Elements/features/PointCloudToMesh/PointCloudToMesh.py b/Elements/features/PointCloudToMesh/PointCloudToMesh.py new file mode 100644 index 00000000..db9e78e5 --- /dev/null +++ b/Elements/features/PointCloudToMesh/PointCloudToMesh.py @@ -0,0 +1,85 @@ +""" +point Cloud To Mesh py + +generateTrianglesFromCustomList can transform custom point clouds to meshes + +@author Nikos Iliakis csd4375 +""" +import open3d as o3d +import numpy as np +import Elements.pyECSS.math_utilities as util + +''' +This function takes a list of points (point cloud) and using the point_cloud_ball_pivoting algorithm it generates +the triangles (or indices) of the given point cloud, using open3d library +If its a sphere set isItASphere to true to ensure normals are point outwards +''' +def generateTrianglesFromCustomList(point_list, isItASphere = False): + # Create a point cloud from the vertices + point_cloud = o3d.geometry.PointCloud() + + point_list = [row[:3] for row in point_list] + # print(point_list) + point_cloud.points = o3d.utility.Vector3dVector(point_list) + + # Estimate normals for the point cloud + point_cloud.estimate_normals() + + # Generate a mesh using point_cloud_poisson + # mesh, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(point_cloud, depth=9, width=0, scale=1.1, linear_fit=False) + # bbox = point_cloud.get_axis_aligned_bounding_box() + # mesh = mesh.crop(bbox) + + + # Generate a mesh using point_cloud_ball_pivoting + distances = point_cloud.compute_nearest_neighbor_distance() + avg_dist = np.mean(distances) + radius = 3 * avg_dist + + mesh = o3d.geometry.TriangleMesh.create_from_point_cloud_ball_pivoting( + point_cloud, + o3d.utility.DoubleVector([radius, radius * 2]) + ) + + mesh = mesh.remove_duplicated_vertices() + mesh = mesh.remove_duplicated_triangles() + mesh = mesh.remove_degenerate_triangles() + mesh = mesh.remove_non_manifold_edges() + + center = np.mean(mesh.vertices, axis=0) + mesh.compute_vertex_normals() + + if(isItASphere == True): + # Ensuring normals point outwards by checking with the direction from the center of the sphere + for i in range(len(mesh.vertex_normals)): + direction = mesh.vertices[i] - center + dot_product = np.dot(mesh.vertex_normals[i], direction) + if dot_product < 0: + mesh.vertex_normals[i] *= -1 + + return mesh.vertices, mesh.vertex_normals, mesh.triangles + + +# This is just an example of an already made point Cloud To mesh classic bunny +def generateBunnyExample(): + bunny = o3d.data.BunnyMesh() + mesh = o3d.io.read_triangle_mesh(bunny.path) + mesh.compute_vertex_normals() + + #o3d.visualization.draw_geometries_with_vertex_selection([mesh]) + return np.asarray(mesh.vertices), mesh.vertex_normals, np.asarray(mesh.triangles) + + +# vertexCube = np.array([ +# [-0.5, -0.5, 0.5, 1.0], +# [-0.5, 0.5, 0.5, 1.0], +# [0.5, 0.5, 0.5, 1.0], +# [0.5, -0.5, 0.5, 1.0], +# [-0.5, -0.5, -0.5, 1.0], +# [-0.5, 0.5, -0.5, 1.0], +# [0.5, 0.5, -0.5, 1.0], +# [0.5, -0.5, -0.5, 1.0] +# ],dtype=np.float32) + +# generateTrianglesFromCustomList(vertexCube) +# generateBunnyExample() \ No newline at end of file diff --git a/Elements/features/PointCloudToMesh/__init__.py b/Elements/features/PointCloudToMesh/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Elements/features/PointCloudToMesh/example_pointCloudToMesh.py b/Elements/features/PointCloudToMesh/example_pointCloudToMesh.py new file mode 100644 index 00000000..874f87e5 --- /dev/null +++ b/Elements/features/PointCloudToMesh/example_pointCloudToMesh.py @@ -0,0 +1,230 @@ +""" +Simple example converting point cloud to mesh + +@author Nikos Iliakis csd4375 +""" + +import numpy as np + +import Elements.pyECSS.math_utilities as util +from Elements.pyECSS.Entity import Entity +from Elements.pyECSS.Component import BasicTransform, RenderMesh +from Elements.pyECSS.System import TransformSystem +from Elements.pyGLV.GL.Scene import Scene +from Elements.pyGLV.GUI.Viewer import RenderGLStateSystem + +from Elements.pyGLV.GL.Shader import InitGLShaderSystem, Shader, ShaderGLDecorator, RenderGLShaderSystem +from Elements.pyGLV.GL.VertexArray import VertexArray +from Elements.utils.terrain import generateTerrain +from OpenGL.GL import GL_POINTS +from OpenGL.GL import GL_LINES + +import imgui +from PointCloudToMesh import generateTrianglesFromCustomList +from PointCloudToMesh import generateBunnyExample +from Elements.utils.Shortcuts import displayGUI_text + +example_description = \ +"This example shows the conversion of a point cloud to a mesh. \n\ +One point cloud to mesh is the bunny and the other point cloud n\ +to mesh is a randomly generated sphere using the ball_pivoting algorithm n\ +The scene is being lit using the Blinn-Phong algorithm. \n\ +You may move the camera using the mouse or the GUI. \n\ +You may alter the variables of the lighting through the Shader vars window \n\ +Hit ESC OR Close the window to quit." + +ambColor, ambStr = [1,0,0], 0 +vwPos, lghtPos, lghtCol, lghtInt = [0,0,0], [0,2,0], [0.5, 0.5, 0.5], 3 +str, matCol= 0.0, [0.5, 0.5, 0.5] + +def displayGUI(): + global ambColor, ambStr + global vwPos, lghtPos, lghtCol, lghtInt + global str, matCol + + imgui.begin("Shader vars") + + changed, ambColor = imgui.drag_float3("ambientColor", *ambColor, change_speed=0.05) + changed, ambStr = imgui.drag_float("ambientStr", ambStr, change_speed=0.05) + + changed, vwPos = imgui.drag_float3("viewPos", *vwPos, change_speed=0.05) + changed, lghtPos = imgui.drag_float3("lightPos", *lghtPos, change_speed=0.05) + changed, lghtCol = imgui.drag_float3("lightColor", *lghtCol, change_speed=0.05) + changed, lghtInt = imgui.drag_float("lightIntensity", lghtInt, change_speed=0.05) + + changed, str = imgui.drag_float("shininess", str, change_speed=0.05) + changed, matCol = imgui.drag_float3("matColor", *matCol, change_speed=0.05) + + imgui.end() + +def main(): + scene = Scene() + + # Scenegraph with Entities, Components + rootEntity = scene.world.createEntity(Entity(name="RooT")) + entityCam1 = scene.world.createEntity(Entity(name="entityCam1")) + scene.world.addEntityChild(rootEntity, entityCam1) + trans1 = scene.world.addComponent(entityCam1, BasicTransform(name="trans1", trs=util.identity())) + + object1 = scene.world.createEntity(Entity(name="object1")) + scene.world.addEntityChild(rootEntity, object1) + trans4 = scene.world.addComponent(object1, BasicTransform(name="trans4", trs=util.identity())) #util.identity() + mesh4 = scene.world.addComponent(object1, RenderMesh(name="mesh4")) + + bunny = scene.world.createEntity(Entity(name="bunny")) + scene.world.addEntityChild(rootEntity, bunny) + trans5 = scene.world.addComponent(bunny, BasicTransform(name="trans5", trs=util.identity())) #util.identity() + mesh5 = scene.world.addComponent(bunny, RenderMesh(name="mesh5")) + + floor = scene.world.createEntity(Entity(name="floor")) + scene.world.addEntityChild(rootEntity, floor) + trans6 = scene.world.addComponent(floor, BasicTransform(name="trans6", trs=util.scale(100, 0.5, 100))) #util.identity() + mesh6 = scene.world.addComponent(floor, RenderMesh(name="mesh6")) + + + # Systems + transUpdate = scene.world.createSystem(TransformSystem("transUpdate", "TransformSystem", "001")) + renderUpdate = scene.world.createSystem(RenderGLShaderSystem()) + initUpdate = scene.world.createSystem(InitGLShaderSystem()) + + # Number of points representing sphere the more the better the sphere will be + num_points = 100000 + + # Generate random values for θ and ϕ (for a sphere) + theta = np.random.uniform(0, 2 * np.pi, num_points) + phi = np.random.uniform(0, np.pi, num_points) + + # Initialize an empty list to store the points + points_on_sphere = [] + + # Convert spherical coordinates to Cartesian coordinates and append to the list + for i in range(num_points): + x = np.sin(phi[i]) * np.cos(theta[i]) + y = np.sin(phi[i]) * np.sin(theta[i]) + z = np.cos(phi[i]) + points_on_sphere.append([x, y, z]) + + + # Now, points_on_sphere contains the list of points in the desired format + vertices = np.array(points_on_sphere, dtype=np.float32) + + mesh_vertices, mesh_normals, mesh_indices = generateTrianglesFromCustomList(vertices, isItASphere = True) + bunny_vertices, bunny_normals, bunny_indices = generateBunnyExample() + + # Define the white color as an RGB array. + white_color = np.array([1, 1, 1]) + + # attach object + mesh4.vertex_attributes.append(mesh_vertices) + mesh4.vertex_attributes.append(np.tile(white_color, (len(mesh_vertices), 1))) + mesh4.vertex_attributes.append(mesh_normals) + mesh4.vertex_index.append(mesh_indices) + vArray4 = scene.world.addComponent(object1, VertexArray()) + shaderDec4 = scene.world.addComponent(object1, ShaderGLDecorator(Shader(vertex_source = Shader.VERT_PHONG_MVP, fragment_source=Shader.FRAG_PHONG))) + + # attach object + mesh5.vertex_attributes.append(bunny_vertices) + mesh5.vertex_attributes.append(np.tile(white_color, (len(bunny_vertices), 1))) + mesh5.vertex_attributes.append(bunny_normals) + mesh5.vertex_index.append(bunny_indices) + vArray5 = scene.world.addComponent(bunny, VertexArray()) + shaderDec5 = scene.world.addComponent(bunny, ShaderGLDecorator(Shader(vertex_source = Shader.VERT_PHONG_MVP, fragment_source=Shader.FRAG_PHONG))) + + # Generate terrain + + vertexTerrain, indexTerrain, colorTerrain= generateTerrain(size=4,N=20) + # Add terrain + terrain = scene.world.createEntity(Entity(name="terrain")) + scene.world.addEntityChild(rootEntity, terrain) + terrain_trans = scene.world.addComponent(terrain, BasicTransform(name="terrain_trans", trs=util.identity())) + terrain_mesh = scene.world.addComponent(terrain, RenderMesh(name="terrain_mesh")) + terrain_mesh.vertex_attributes.append(vertexTerrain) + terrain_mesh.vertex_attributes.append(colorTerrain) + terrain_mesh.vertex_index.append(indexTerrain) + terrain_vArray = scene.world.addComponent(terrain, VertexArray(primitive=GL_LINES)) + terrain_shader = scene.world.addComponent(terrain, ShaderGLDecorator(Shader(vertex_source = Shader.COLOR_VERT_MVP, fragment_source=Shader.COLOR_FRAG))) + # terrain_shader.setUniformVariable(key='modelViewProj', value=mvpMat, mat4=True) + + # MAIN RENDERING LOOP + + running = True + scene.init(imgui=True, windowWidth = 1024, windowHeight = 768, windowTitle = "Elements: point Cloud to Mesh - Bunny and the ball", openGLversion = 4) + scene.world.traverse_visit(initUpdate, scene.world.root) + + ################### EVENT MANAGER ################### + + eManager = scene.world.eventManager + gWindow = scene.renderWindow + gGUI = scene.gContext + + renderGLEventActuator = RenderGLStateSystem() + + + eManager._subscribers['OnUpdateWireframe'] = gWindow + eManager._actuators['OnUpdateWireframe'] = renderGLEventActuator + eManager._subscribers['OnUpdateCamera'] = gWindow + eManager._actuators['OnUpdateCamera'] = renderGLEventActuator + + + + eye = util.vec(2.5, 3.5, 2.5) + target = util.vec(0.5, 0.5, 0.0) + up = util.vec(0.0, 1.0, 0.0) + view = util.lookat(eye, target, up) + + gGUI._colorEditor = 0.2, 0.2, 0 + gGUI._eye = eye + gGUI._target = target + gGUI._up = up + + projMat = util.perspective(50.0, 1.0, 0.01, 10.0) + + gWindow._myCamera = view # otherwise, an imgui slider must be moved to properly update + + object1_trans = trans4.trs @ util.translate(0, 0.25, 0.5) @ util.scale(0.3, 0.3, 0.3) + bunny_trans = trans5.trs @ util.translate(1,-0.25,0) @ util.scale(7,7,7) + + model_terrain_axes = util.translate(0.0,0.0,0.0) ## COMPLETELY OVERRIDE OBJECT's TRS + + while running: + running = scene.render() + scene.world.traverse_visit(renderUpdate, scene.world.root) + view = gWindow._myCamera # updates view via the imgui + + ovp_1 = projMat @ view @ object1_trans + ovp_2 = projMat @ view @ bunny_trans + mvp_terrain_axes = projMat @ view @ model_terrain_axes + + displayGUI() + displayGUI_text(example_description) + + shaderDec4.setUniformVariable(key='modelViewProj', value=ovp_1, mat4=True) + shaderDec4.setUniformVariable(key='model', value=object1_trans, mat4=True) + shaderDec4.setUniformVariable(key='ambientColor', value=ambColor, float3=True) + shaderDec4.setUniformVariable(key='ambientStr', value=ambStr, float1=True) + shaderDec4.setUniformVariable(key='viewPos', value=vwPos, float3=True) + shaderDec4.setUniformVariable(key='lightPos', value=lghtPos, float3=True) + shaderDec4.setUniformVariable(key='lightColor', value=lghtCol, float3=True) + shaderDec4.setUniformVariable(key='lightIntensity', value=lghtInt, float1=True) + shaderDec4.setUniformVariable(key='shininess', value=str, float1=True) + shaderDec4.setUniformVariable(key='matColor', value=matCol, float3=True) + + shaderDec5.setUniformVariable(key='modelViewProj', value=ovp_2, mat4=True) + shaderDec5.setUniformVariable(key='model', value=bunny_trans, mat4=True) + shaderDec5.setUniformVariable(key='ambientColor', value=ambColor, float3=True) + shaderDec5.setUniformVariable(key='ambientStr', value=ambStr, float1=True) + shaderDec5.setUniformVariable(key='viewPos', value=vwPos, float3=True) + shaderDec5.setUniformVariable(key='lightPos', value=lghtPos, float3=True) + shaderDec5.setUniformVariable(key='lightColor', value=lghtCol, float3=True) + shaderDec5.setUniformVariable(key='lightIntensity', value=lghtInt, float1=True) + shaderDec5.setUniformVariable(key='shininess', value=str, float1=True) + shaderDec5.setUniformVariable(key='matColor', value=matCol, float3=True) + + terrain_shader.setUniformVariable(key='modelViewProj', value=mvp_terrain_axes, mat4=True) + + scene.render_post() + + scene.shutdown() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Elements/features/PointCloudToMesh/test_pointCloudToMesh.py b/Elements/features/PointCloudToMesh/test_pointCloudToMesh.py new file mode 100644 index 00000000..c2d40741 --- /dev/null +++ b/Elements/features/PointCloudToMesh/test_pointCloudToMesh.py @@ -0,0 +1,41 @@ +import unittest +import numpy as np + +from Elements.features.PointCloudToMesh.PointCloudToMesh import generateTrianglesFromCustomList, generateBunnyExample + +class TestPointCloudToMesh(unittest.TestCase): + + def test_generateTrianglesFromCustomList(self): + # Create a sample input (vertices) for the function + num_points = 100 + + # Generate random values for θ and ϕ + theta = np.random.uniform(0, 2 * np.pi, num_points) + phi = np.random.uniform(0, np.pi, num_points) + + # Initialize an empty list to store the points + points_on_sphere = [] + + # Convert spherical coordinates to Cartesian coordinates and append to the list + for i in range(num_points): + x = np.sin(phi[i]) * np.cos(theta[i]) + y = np.sin(phi[i]) * np.sin(theta[i]) + z = np.cos(phi[i]) + points_on_sphere.append([x, y, z]) + + + # Now, points_on_sphere contains the list of points in the desired format + vertices = np.array(points_on_sphere, dtype=np.float32) + + mesh_vertices, mesh_normals, mesh_indices = generateTrianglesFromCustomList(vertices) + + self.assertEqual(len(mesh_vertices), num_points) + self.assertEqual(len(mesh_normals), num_points) + self.assertTrue(len(mesh_indices) > 0) + + def test_generateBunnyExample(self): + bunny_vertices, bunny_normals, bunny_indices = generateBunnyExample() + + self.assertTrue(len(bunny_vertices) > 0) + self.assertTrue(len(bunny_normals) > 0) + self.assertTrue(len(bunny_indices) > 0) \ No newline at end of file diff --git a/Elements/features/SkinnedMesh/example_12_pyassimp_import.py b/Elements/features/SkinnedMesh/skinned_mesh_example.py similarity index 100% rename from Elements/features/SkinnedMesh/example_12_pyassimp_import.py rename to Elements/features/SkinnedMesh/skinned_mesh_example.py diff --git a/Elements/features/Slicing/Slicing_example.py b/Elements/features/Slicing/Slicing_example.py index 891f9b82..14876652 100644 --- a/Elements/features/Slicing/Slicing_example.py +++ b/Elements/features/Slicing/Slicing_example.py @@ -22,6 +22,10 @@ #Task code from Elements.features.Slicing import Slicing from Elements.definitions import MODEL_DIR +from Elements.utils.Shortcuts import displayGUI_text +example_description = \ +"This is a scene with two cow models. The cow model is either properly imported\n" +"or is being sliced by multiple planes and the intersection countour are then displayed." #Light @@ -232,6 +236,7 @@ scene.world.traverse_visit(renderUpdate, scene.world.root) scene.world.traverse_visit_pre_camera(camUpdate, orthoCam) scene.world.traverse_visit(camUpdate, scene.world.root) + displayGUI_text(example_description) view = gWindow._myCamera # updates view via the imgui # mvp_cube = projMat @ view @ model_cube mvp_cube = projMat @ view @ trans4.trs diff --git a/Elements/features/Voronoi2D/example_voronoi.py b/Elements/features/Voronoi2D/example_voronoi.py index f3d78b59..e4690593 100644 --- a/Elements/features/Voronoi2D/example_voronoi.py +++ b/Elements/features/Voronoi2D/example_voronoi.py @@ -18,10 +18,12 @@ import OpenGL.GL as gl import Elements.features.Voronoi2D.voronoi as voronoi - -import random -#gl.glPointSize(1.) - +from Elements.utils.Shortcuts import displayGUI_text +example_description = \ +"This scene demonstrates the Voronoi diagram of 2D points \n" +"(represented as coplanar points). Distinct colors are used\n" +"to highlight different Voronoi cells, i.e., \n" +"the polygons that are equidistant from each point." scene = Scene() @@ -106,6 +108,7 @@ while running: running = scene.render() + displayGUI_text(example_description) scene.world.traverse_visit(renderUpdate, scene.world.root) view = gWindow._myCamera # updates view via the imgui mvp_cube = projMat @ view @ model_cube diff --git a/Elements/features/plotting/plotting_example.py b/Elements/features/plotting/plotting_example.py index 617530c1..2957ba5f 100644 --- a/Elements/features/plotting/plotting_example.py +++ b/Elements/features/plotting/plotting_example.py @@ -14,6 +14,9 @@ from OpenGL.GL import GL_LINES from Elements.features.plotting.plotting_base import FunctionPlotting +from Elements.utils.Shortcuts import displayGUI_text +example_description = \ +"This is a scene where you can plot a function either in 2D or 3D \n" scene = Scene() @@ -159,6 +162,7 @@ for shader in shader_2d: shader.setUniformVariable(key='modelViewProj', value=mvp_terrain_axes, mat4=True) + displayGUI_text(example_description) # set uniform variables for all phong shaders for shader in shader_3d: Lposition = util.vec(1,3,2) diff --git a/Elements/features/rigid_body_animation/example_rigidbody_animation.py b/Elements/features/rigid_body_animation/example_rigidbody_animation.py index 2194ec7b..9cf1cb12 100644 --- a/Elements/features/rigid_body_animation/example_rigidbody_animation.py +++ b/Elements/features/rigid_body_animation/example_rigidbody_animation.py @@ -15,6 +15,10 @@ from Elements.features.rigid_body_animation.animationCS import * import imgui +from Elements.utils.Shortcuts import displayGUI_text +example_description = \ +"This is a scene where you can animate a simple cube. The cube can either move linearly or \n" +"following a bezier curve. You can define the speed and toggle anymation on/off.\n" #Simple Cube vertexCube = np.array([ [-0.5, -0.5, 0.5, 1.0], [-0.5, 0.5, 0.5, 1.0], [0.5, 0.5, 0.5, 1.0], [0.5, -0.5, 0.5, 1.0], [-0.5, -0.5, -0.5, 1.0], [-0.5, 0.5, -0.5, 1.0], [0.5, 0.5, -0.5, 1.0], [0.5, -0.5, -0.5, 1.0] ],dtype=np.float32) @@ -172,6 +176,7 @@ def displayGUI(): scene.world.traverse_visit(renderUpdate, scene.world.root) view = gWindow._myCamera # updates view via the imgui mvp_cube = projMat @ view @ util.scale(0.2) @ trans4.trs + displayGUI_text(example_description) # if trans4._current_frame <99: # trans4.update_frame(1) shaderDec4.setUniformVariable(key='modelViewProj', value=mvp_cube, mat4=True) diff --git a/Elements/files/textures/3x3.jpg b/Elements/files/textures/3x3.jpg new file mode 100644 index 00000000..96244dbb Binary files /dev/null and b/Elements/files/textures/3x3.jpg differ diff --git a/Elements/files/textures/Black_Wooden_Floor.png b/Elements/files/textures/Black_Wooden_Floor.png new file mode 100644 index 00000000..a67b78a8 Binary files /dev/null and b/Elements/files/textures/Black_Wooden_Floor.png differ diff --git a/Elements/files/textures/black_stones_floor.jpg b/Elements/files/textures/black_stones_floor.jpg new file mode 100644 index 00000000..38a084a5 Binary files /dev/null and b/Elements/files/textures/black_stones_floor.jpg differ diff --git a/Elements/files/textures/earth.jpg b/Elements/files/textures/earth.jpg new file mode 100644 index 00000000..9e38f5ab Binary files /dev/null and b/Elements/files/textures/earth.jpg differ diff --git a/Elements/pyECSS/ECSSManager.py b/Elements/pyECSS/ECSSManager.py index a87f6556..1780c910 100644 --- a/Elements/pyECSS/ECSSManager.py +++ b/Elements/pyECSS/ECSSManager.py @@ -37,7 +37,7 @@ def __new__(cls): :rtype: ECSSManagger """ if cls._instance is None: - print('Creating Scene Singleton Object') + print('Creating ECSSManager Singleton Object') cls._instance = super(ECSSManager, cls).__new__(cls) # add further init here return cls._instance diff --git a/Elements/pyECSS/System.py b/Elements/pyECSS/System.py index 1a5d4dbd..6bb71dbb 100644 --- a/Elements/pyECSS/System.py +++ b/Elements/pyECSS/System.py @@ -382,4 +382,4 @@ class RenderSystem(System): Basically this needs to be redefined in each rendering context: OpenGL, RayTracing etc. """ pass - + \ No newline at end of file diff --git a/Elements/pyEEL/notebooks/GATE/2.Skinning_Animation.ipynb b/Elements/pyEEL/notebooks/GATE/2.Skinning_Animation.ipynb index ddc63f88..419c5b27 100644 --- a/Elements/pyEEL/notebooks/GATE/2.Skinning_Animation.ipynb +++ b/Elements/pyEEL/notebooks/GATE/2.Skinning_Animation.ipynb @@ -129,8 +129,8 @@ "source": [ "wanna_save = True\n", "\n", - "from pathlib import Path\n", - "path_to_save = Path(PICKLES_DIR)/\"astroboy_walk\"\n", + "\n", + "path_to_save = PICKLES_DIR/\"astroboy_walk\"\n", "\n", "if wanna_save:\n", " with open(path_to_save/'vertices.pkl', 'wb') as file: pickle.dump(v, file)\n", @@ -169,7 +169,8 @@ "# M[1] = np.dot(np.diag([2,2,2,1]),M[1])\n", "MM1 = read_tree(object,mesh_id,M,transform)\n", "\n", - "with open('pickles/MM1.pkl', 'wb') as file: pickle.dump(MM1, file)\n", + "path_to_save = PICKLES_DIR/\"astroboy_walk\"\n", + "with open(path_to_save/'MM1.pkl', 'wb') as file: pickle.dump(MM1, file)\n", "\n", "\n", "# =================================================\n", @@ -210,7 +211,7 @@ "\n", "wanna_load = True\n", "\n", - "path_to_load = Path(PICKLES_DIR)/\"astroboy_walk\"\n", + "path_to_load = PICKLES_DIR/\"astroboy_walk\"\n", "\n", "if wanna_load:\n", " with open(path_to_load/'vertices.pkl', 'rb') as file: v=pickle.load(file)\n", @@ -262,8 +263,9 @@ "metadata": {}, "outputs": [], "source": [ - "with open('pickles/faces.pkl', 'rb') as file: f=pickle.load(file)\n", - "with open('pickles/newv.pkl', 'rb') as file: newv = pickle.load(file)\n", + "path_to_load = PICKLES_DIR/\"astroboy_walk\"\n", + "with open(path_to_load/'faces.pkl', 'rb') as file: f=pickle.load(file)\n", + "with open(path_to_load/'newv.pkl', 'rb') as file: newv = pickle.load(file)\n", "\n", "p = mp.plot(newv, f,newv[:, 1],shading={\"scale\": 2.5,\"wireframe\":True},return_plot=True) " ] @@ -292,7 +294,8 @@ "M[1] = np.dot(np.diag([2,2,2,1]),M[1])\n", "\n", "MM2 = read_tree(object,mesh_id,M,transform)\n", - "with open('pickles/MM2.pkl', 'wb') as file: pickle.dump(MM2, file)" + "path_to_save = PICKLES_DIR/\"astroboy_walk\"\n", + "with open(path_to_save/'MM2.pkl', 'wb') as file: pickle.dump(MM2, file)" ] }, { diff --git a/Elements/pyGLV/GL/Shader.py b/Elements/pyGLV/GL/Shader.py index b59784a3..a1a72cb2 100644 --- a/Elements/pyGLV/GL/Shader.py +++ b/Elements/pyGLV/GL/Shader.py @@ -253,6 +253,22 @@ class Shader(Component): fragmentTexCoord = vTexCoord; } """ + SIMPLE_TEXTURE_VERT_MVP = """ + #version 410 + + layout (location=0) in vec4 vPos; + layout (location=1) in vec2 vTexCoord; + + out vec2 fragmentTexCoord; + + uniform mat4 modelViewProj; + + void main() + { + gl_Position = modelViewProj * vPos; + fragmentTexCoord = vTexCoord; + } + """ SIMPLE_TEXTURE_FRAG = """ #version 410 diff --git a/Elements/pyGLV/GL/Textures.py b/Elements/pyGLV/GL/Textures.py index eee7d121..4a780123 100644 --- a/Elements/pyGLV/GL/Textures.py +++ b/Elements/pyGLV/GL/Textures.py @@ -45,10 +45,10 @@ def __init__(self, filepath:str=None, img_data:Tuple[bytes, int, int]=None, text gl.glBindTexture(gl.GL_TEXTURE_2D, self._texture) - gl.glTexParameteri(gl.GL_TEXTURE_2D,gl.GL_TEXTURE_WRAP_S,gl.GL_MIRRORED_REPEAT) - gl.glTexParameteri(gl.GL_TEXTURE_2D,gl.GL_TEXTURE_WRAP_T,gl.GL_MIRRORED_REPEAT) - # gl.glTexParameteri(gl.GL_TEXTURE_2D,gl.GL_TEXTURE_WRAP_S,gl.GL_REPEAT) - # gl.glTexParameteri(gl.GL_TEXTURE_2D,gl.GL_TEXTURE_WRAP_T,gl.GL_REPEAT) + # gl.glTexParameteri(gl.GL_TEXTURE_2D,gl.GL_TEXTURE_WRAP_S,gl.GL_MIRRORED_REPEAT) + # gl.glTexParameteri(gl.GL_TEXTURE_2D,gl.GL_TEXTURE_WRAP_T,gl.GL_MIRRORED_REPEAT) + gl.glTexParameteri(gl.GL_TEXTURE_2D,gl.GL_TEXTURE_WRAP_S,gl.GL_REPEAT) + gl.glTexParameteri(gl.GL_TEXTURE_2D,gl.GL_TEXTURE_WRAP_T,gl.GL_REPEAT) gl.glTexParameteri(gl.GL_TEXTURE_2D,gl.GL_TEXTURE_MIN_FILTER,gl.GL_LINEAR) gl.glTexParameteri(gl.GL_TEXTURE_2D,gl.GL_TEXTURE_MAG_FILTER,gl.GL_LINEAR) diff --git a/Elements/pyGLV/GL/VertexArray.py b/Elements/pyGLV/GL/VertexArray.py index 30bb49a3..8782ddac 100644 --- a/Elements/pyGLV/GL/VertexArray.py +++ b/Elements/pyGLV/GL/VertexArray.py @@ -22,7 +22,7 @@ import Elements.pyECSS.System from Elements.pyECSS.Component import Component, CompNullIterator - +import atexit class VertexArray(Component): """ @@ -45,6 +45,7 @@ def __init__(self, name=None, type=None, id=None, attributes=None, index=None, p self._index = index self._usage = usage self._primitive = primitive #e.g. GL.GL_TRIANGLES + atexit.register(self.__del__) #self.init(attributes, index, usage) #init after a valid GL context is active @property @@ -85,6 +86,8 @@ def primitive(self, value): self._primitive = value def __del__(self): + # using atexit to ensure correct destruction order and to avoid yielding errors and exceptions + # see https://stackoverflow.com/questions/72238460/python-importerror-sys-meta-path-is-none-python-is-likely-shutting-down gl.glDeleteVertexArrays(1, [self._glid]) gl.glDeleteBuffers(len(self._buffers), self._buffers) diff --git a/Elements/pyGLV/GUI/Viewer.py b/Elements/pyGLV/GUI/Viewer.py index 519f732f..9029c557 100644 --- a/Elements/pyGLV/GUI/Viewer.py +++ b/Elements/pyGLV/GUI/Viewer.py @@ -11,36 +11,38 @@ • https://github.com/faif/python-patterns/blob/master/patterns/structural/decorator.py """ -from __future__ import annotations -from abc import ABC, abstractmethod -from typing import List, Dict, Any -from collections.abc import Iterable, Iterator +from __future__ import annotations +from abc import ABC, abstractmethod +from typing import List, Dict, Any +from collections.abc import Iterable, Iterator from sys import platform import sdl2 import sdl2.ext -from sdl2.keycode import SDLK_ESCAPE +from sdl2.keycode import SDLK_ESCAPE,SDLK_w from sdl2.video import SDL_WINDOWPOS_CENTERED, SDL_WINDOW_ALLOW_HIGHDPI import OpenGL.GL as gl from OpenGL.GL import shaders import imgui from imgui.integrations.sdl2 import SDL2Renderer -import Elements.pyECSS.System +import Elements.pyECSS.System import Elements.pyECSS.math_utilities as util import Elements.pyECSS.Event -from Elements.pyECSS.System import System +from Elements.pyECSS.System import System from Elements.pyECSS.Component import BasicTransform import numpy as np +# from Elements.pyGLV.GL.Scene import Scene +import Elements.utils.Shortcuts as Shortcuts class RenderWindow(ABC): """ The Abstract base class of the Viewer GUI/Display sub-system of pyglGA based on the Decorator Pattern, this class is "wrapped" by decorators - in order to provide extra cpapabilities e.g. SDL2 window, context and ImGUI widgets - """ - + in order to provide extra cpapabilities e.g. SDL2 window, context and ImGUI widgets + """ + def __init__(self): self._eventManager = None self._scene = None @@ -48,7 +50,7 @@ def __init__(self): #define properties for EventManager, Scene objects @property #name def eventManager(self): - """ Get RenderWindow's eventManager """ + """ Get RenderWindow's eventManager """ return self._eventManager @eventManager.setter def eventManager(self, value): @@ -56,36 +58,36 @@ def eventManager(self, value): @property #name def scene(self): - """ Get RenderWindow's Scene reference """ + """ Get RenderWindow's Scene reference """ return self._scene @scene.setter def scene(self, value): self._scene = value - + @abstractmethod def init(self): raise NotImplementedError - + abstractmethod def init_post(self): raise NotImplementedError - + @abstractmethod def display(self): raise NotImplementedError - + @abstractmethod def display_post(self): raise NotImplementedError - + @abstractmethod def shutdown(self): raise NotImplementedError - + @abstractmethod def event_input_process(self, running = True): raise NotImplementedError - + @abstractmethod def accept(self, system: Elements.pyECSS.System, event = None): """ @@ -95,7 +97,7 @@ def accept(self, system: Elements.pyECSS.System, event = None): :type system: [System] """ raise NotImplementedError - + @classmethod def getClassName(cls): return cls.__name__ @@ -119,7 +121,7 @@ def __init__(self, windowWidth = None, windowHeight = None, windowTitle = None, :type windowTitle: [type], optional """ super().__init__() - + self._gWindow = None self._gContext = None self._gVersionLabel = "None" @@ -135,16 +137,16 @@ def __init__(self, windowWidth = None, windowHeight = None, windowTitle = None, self._windowHeight = 768 else: self._windowHeight = windowHeight - + if windowTitle is None: self._windowTitle = "SDL2Window" else: self._windowTitle = windowTitle - + if eventManager is not None and scene is None: # in case we are testing without a Scene and just an EventManager self.eventManager = eventManager - + if scene is not None: # set the reference of parent RenderWindow to Scene # get the reference to EventManager from Scene.ECSSManager @@ -155,17 +157,15 @@ def __init__(self, windowWidth = None, windowHeight = None, windowTitle = None, self._wireframeMode = False self._colorEditor = 0.0, 0.0, 0.0 self._myCamera = np.identity(4) - + @property def gWindow(self): return self._gWindow - - + @property def gContext(self): return self._gContext - - + def init(self): """ Initialise an SDL2 RenderWindow, not directly but via the SDL2Decorator @@ -186,17 +186,15 @@ def init(self): sdl2.SDL_GL_CONTEXT_PROFILE_CORE ) sdl2.SDL_GL_SetAttribute(sdl2.SDL_GL_DOUBLEBUFFER, 1) - + sdl2.SDL_GL_SetAttribute(sdl2.SDL_GL_ACCELERATED_VISUAL, 1) - sdl2.SDL_GL_SetAttribute(sdl2.SDL_GL_DEPTH_SIZE, 24) - sdl2.SDL_GL_SetAttribute(sdl2.SDL_GL_STENCIL_SIZE, 8) + sdl2.SDL_GL_SetAttribute(sdl2.SDL_GL_STENCIL_SIZE, 8) sdl2.SDL_GL_SetAttribute(sdl2.SDL_GL_MULTISAMPLEBUFFERS, 1) - - if self.openGLversion==3: - print("="*24) + if self.openGLversion == 3: + print("=" * 24) print("Using OpenGL version 3.2") print("="*24) sdl2.SDL_GL_SetAttribute(sdl2.SDL_GL_CONTEXT_MAJOR_VERSION, 3) @@ -216,7 +214,7 @@ def init(self): # pass # else: # sdl2.SDL_GL_SetAttribute(sdl2.SDL_GL_MULTISAMPLESAMPLES, 16) - + sdl2.SDL_SetHint(sdl2.SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, b"1") sdl2.SDL_SetHint(sdl2.SDL_HINT_VIDEO_HIGHDPI_DISABLED, b"1") @@ -245,56 +243,48 @@ def init(self): #obtain the GL versioning system info self._gVersionLabel = f'OpenGL {gl.glGetString(gl.GL_VERSION).decode()} GLSL {gl.glGetString(gl.GL_SHADING_LANGUAGE_VERSION).decode()} Renderer {gl.glGetString(gl.GL_RENDERER).decode()}' print(self._gVersionLabel) - - + def init_post(self): """ Post init method for SDL2 this should be ctypiically alled AFTER all other GL contexts have been created """ pass - - + def display(self): """ Main display window method to be called standalone or from within a concrete Decorator """ - #GPTODO make background clear color as parameter at class level + # GPTODO make background clear color as parameter at class level - gl.glClearColor(*self._colorEditor, 1.0) gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) gl.glDisable(gl.GL_CULL_FACE) gl.glEnable(gl.GL_DEPTH_TEST) gl.glDepthFunc(gl.GL_LESS) # gl.glDepthFunc(gl.GL_LEQUAL); - - - - # gl.glDepthMask(gl.GL_FALSE); - - - #gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL) - - #setup some extra GL state flags + + # gl.glDepthMask(gl.GL_FALSE); + + # gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL) + + # setup some extra GL state flags if self._wireframeMode: gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_LINE) #print(f"SDL2Window:display() set wireframemode: {self._wireframeMode}") else: gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL) - #print(f"SDL2Window:display() set wireframemode: {self._wireframeMode}") - - #print(f'{self.getClassName()}: display()') - - + # print(f"SDL2Window:display() set wireframemode: {self._wireframeMode}") + + # print(f'{self.getClassName()}: display()') + def display_post(self): """ To be called at the end of each drawn frame to swap double buffers """ sdl2.SDL_GL_SwapWindow(self._gWindow) - #print(f'{self.getClassName()}: display_post()') - - + # print(f'{self.getClassName()}: display_post()') + def shutdown(self): """ Shutdown and cleanup SDL2 operations @@ -303,15 +293,12 @@ def shutdown(self): if (self._gContext and self._gWindow is not None): sdl2.SDL_GL_DeleteContext(self._gContext) sdl2.SDL_DestroyWindow(self._gWindow) - sdl2.SDL_Quit() - + sdl2.SDL_Quit() - def event_input_process(self): - #def event_input_process(self, running = True): + def event_input_process(self, running=True): """ process SDL2 basic events and input """ - running = True events = sdl2.ext.get_events() for event in events: if event.type == sdl2.SDL_KEYDOWN: @@ -366,8 +353,7 @@ def __init__(self, wrapee: RenderWindow): self.lctrl = False self.traverseCamera() - - + @property def wrapeeWindow(self): return self._wrapeeWindow @@ -409,7 +395,7 @@ def traverseCamera(self): except StopIteration: done_traversing = True else: - if "Camera" in comp.name: # just put the "Camera" string in the Entity that holds the camera + if "camera" in comp.name.lower(): # just put the "Camera" string in the Entity that holds the camera self.cam = comp found = True @@ -548,8 +534,16 @@ def event_input_process(self): events = sdl2.ext.get_events() width = self.wrapeeWindow._windowWidth height = self.wrapeeWindow._windowHeight + + ### set up a hot key to easily switch between common keys like shift,ctrl etc + ### default at left alt + alt_Key = sdl2.KMOD_ALT + leftShift_Key = sdl2.KMOD_LSHIFT + rightShift_Key = sdl2.KMOD_RSHIFT + ctrl_Key = sdl2.KMOD_CTRL + + shortcut_HotKey = alt_Key - #if not imgui.is_window_focused(): for event in events: if event.type == sdl2.SDL_MOUSEWHEEL: x = event.wheel.x @@ -563,25 +557,95 @@ def event_input_process(self): x = -event.motion.xrel y = event.motion.yrel self.cameraHandling(x, y, height, width) - + #keyboard events elif event.type == sdl2.SDL_KEYDOWN: - if event.key.keysym.sym == sdl2.SDLK_UP or event.key.keysym.sym == sdl2.SDLK_w : - pass - if event.key.keysym.sym == sdl2.SDLK_DOWN or event.key.keysym.sym == sdl2.SDLK_s : - pass - if event.key.keysym.sym == sdl2.SDLK_LEFT or event.key.keysym.sym == sdl2.SDLK_a : - pass - if event.key.keysym.sym == sdl2.SDLK_RIGHT or event.key.keysym.sym == sdl2.SDLK_d : - pass - if event.key.keysym.sym == sdl2.SDLK_LCTRL: - self.lctrl=True + # if event.key.keysym.sym == sdl2.SDLK_UP or event.key.keysym.sym == sdl2.SDLK_w : + # pass + # if event.key.keysym.sym == sdl2.SDLK_DOWN or event.key.keysym.sym == sdl2.SDLK_s : + # pass + # if event.key.keysym.sym == sdl2.SDLK_LEFT or event.key.keysym.sym == sdl2.SDLK_a : + # pass + # if event.key.keysym.sym == sdl2.SDLK_RIGHT or event.key.keysym.sym == sdl2.SDLK_d : + # pass + ################## toggle the wireframe using the alt+F buttons ############################# + if (event.key.keysym.sym == sdl2.SDLK_f and (sdl2.SDL_GetModState() & shortcut_HotKey)): + self.toggle_Wireframe() + + ########## shortcuts for selected node from the tree ########### + if hasattr(self._wrapeeWindow._scene, "_gContext") and self._wrapeeWindow._scene._gContext.__class__.__name__ == "ImGUIecssDecorator" and self.selected: + # we must first check if the ImGUIecssDecorator is active otherwise we will get an error on click + ################# - translate on x axis when node is selected using W+alt ########################### + if(event.key.keysym.sym == sdl2.SDLK_w and (sdl2.SDL_GetModState() & shortcut_HotKey)): + self.translation["x"] -= 0.1 + ################# + translate on x axis when node is selected using W ########################### + elif(event.key.keysym.sym == sdl2.SDLK_w): + self.translation["x"] += 0.1 + + # ################# - translate on y axis when node is selected using E+alt ########################### + if(event.key.keysym.sym == sdl2.SDLK_e and (sdl2.SDL_GetModState() & shortcut_HotKey)): + self.translation["y"] -= 0.1 + ################# + translate on y axis when node is selected using E ########################### + elif(event.key.keysym.sym == sdl2.SDLK_e): + self.translation["y"] += 0.1 + + # ################# - translate on z axis when node is selected using R+alt ########################### + if(event.key.keysym.sym == sdl2.SDLK_r and (sdl2.SDL_GetModState() & shortcut_HotKey)): + self.translation["z"] -= 0.1 + # ################# + translate on z axis when node is selected using R ########################### + elif(event.key.keysym.sym == sdl2.SDLK_r): + self.translation["z"] += 0.1 + + + # ################# - rotate on x axis when node is selected using T+alt ########################### + if(event.key.keysym.sym == sdl2.SDLK_t and (sdl2.SDL_GetModState() & shortcut_HotKey)): + self.rotation["x"] -= 0.1 + # ################# + rotate on x axis when node is selected using T ########################### + elif(event.key.keysym.sym == sdl2.SDLK_t): + self.rotation["x"] += 0.1 + + # ################# - rotate on y axis when node is selected using Y+alt ########################### + if(event.key.keysym.sym == sdl2.SDLK_y and (sdl2.SDL_GetModState() & shortcut_HotKey)): + self.rotation["y"] -= 0.1 + # ################# + rotate on y axis when node is selected using Y ########################### + elif(event.key.keysym.sym == sdl2.SDLK_y): + self.rotation["y"] += 0.1 + + # ################# - rotate on z axis when node is selected using U+alt ########################### + if(event.key.keysym.sym == sdl2.SDLK_u and (sdl2.SDL_GetModState() & shortcut_HotKey)): + self.rotation["z"] -= 0.1 + # ################# + rotate on z axis when node is selected using U ########################### + elif(event.key.keysym.sym == sdl2.SDLK_u): + self.rotation["z"] += 0.1 + + ################# scale down on x axis when node is selected using I+alt ########################### + if(event.key.keysym.sym == sdl2.SDLK_i and (sdl2.SDL_GetModState() & shortcut_HotKey)): + self.scale["x"] -= 0.1 + ################# scale up on x axis when node is selected using I ########################### + elif(event.key.keysym.sym == sdl2.SDLK_i ): + self.scale["x"] += 0.1 + + ################# scale down on y axis when node is selected using O+alt ########################### + if(event.key.keysym.sym == sdl2.SDLK_o and (sdl2.SDL_GetModState() & shortcut_HotKey)): + self.scale["y"] -= 0.1 + ################# scale up on y axis when node is selected using O ########################### + elif(event.key.keysym.sym == sdl2.SDLK_o ): + self.scale["y"] += 0.1 + + ################# scale down on z axis when node is selected using P+alt ########################### + if(event.key.keysym.sym == sdl2.SDLK_p and (sdl2.SDL_GetModState() & shortcut_HotKey)): + self.scale["z"] -= 0.1 + ################# scale up on z axis when node is selected using P ########################### + elif(event.key.keysym.sym == sdl2.SDLK_p ): + self.scale["z"] += 0.1 + if event.key.keysym.sym == sdl2.SDLK_ESCAPE: running = False - elif event.type == sdl2.SDL_KEYUP and event.key.keysym.sym == sdl2.SDLK_LCTRL: self.lctrl = False + + elif event.type == sdl2.SDL_QUIT: running = False @@ -617,6 +681,7 @@ def init_post(self): def accept(self, system: Elements.pyECSS.System, event = None): pass + class ImGUIDecorator(RenderDecorator): """ ImGUI decorator @@ -639,27 +704,26 @@ def __init__(self, wrapee: RenderWindow, imguiContext = None): self._changed = False self._checkbox = False self._colorEditor = wrapee._colorEditor - # self._eye = (2.5, 2.5, 2.5) - # self._target = (0.0, 0.0, 0.0) - # self._up = (0.0, 1.0, 0.0) - # # TRS Variables - # self.translation = {}; - # self.translation["x"] = 0; self.translation["y"] = 0; self.translation["z"] = 0; + ### Bool variables for Scenegraph Visualizer imgui + self.collapseElementsWindow = False + self.collapseScenegraphVisualizer = True - # self.rotation = {}; - # self.rotation["x"] = 0; self.rotation["y"] = 0; self.rotation["z"] = 0; + ### Bool variables to collapse and close the ECSS graph + self.showScenegraphVisualizer = True + self.collapseScenegraphVisualizer = True - # self.scale = {}; - # self.scale["x"] = 0; self.scale["y"] = 0; self.scale["z"] = 0; - - # #this is not used anywhere - # self.color = [255, 50, 50]; - - # self.lctrl = False - # self.traverseCamera() - + ### Bool variables for Elements imgui ### + self.showElementsWindow = True + self.elements_x = 10 + self.elements_y = 30 + + #TODO:add comment for these vars + self.graph_x = 10 + self.graph_y = 100 + + def init(self): """ Calls Decoratee init() and also sets up events @@ -671,12 +735,12 @@ def init(self): else: # print("Yay! ImGUI context created successfully") pass - + # GPTODO here is the issue: SDL2Decorator takes an SDLWindow as wrappee wheras # ImGUIDEcorator takes and SDL2Decorator and decorates it! - if isinstance(self.wrapeeWindow, SDL2Window): + if isinstance(self.wrapeeWindow, SDL2Window): self._imguiRenderer = SDL2Renderer(self.wrapeeWindow._gWindow) - + # # Setting up events that this class is publishing (if the EventManager is present in the decorated wrappee) # @@ -692,8 +756,7 @@ def init(self): self._wrapeeWindow.eventManager._publishers[self._updateCamera.name] = self # print(f'{self.getClassName()}: init()') - - + def display(self): """ ImGUI decorator display: calls wrapee (RenderWindow::display) as well as extra ImGUI widgets @@ -703,309 +766,329 @@ def display(self): gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) #render the ImGUI widgets self.extra() - #draw scenegraph tree widget + # draw scenegraph tree widget self.scenegraphVisualiser() - #print(f'{self.getClassName()}: display()') - - # def traverseCamera(self): - # self.cam = None - # found = False - # if self.wrapeeWindow.scene is not None: - # rootComp = self.wrapeeWindow.scene.world.root - # if rootComp._children is not None: - # Iterator = iter(rootComp._children) - # done_traversing = False - # while not found and not done_traversing: - # try: - # comp = next(Iterator) - # except StopIteration: - # done_traversing = True - # else: - # if "Camera" in comp.name: # just put the "Camera" string in the Entity that holds the camera - # self.cam = comp - # found = True - - # def updateCamera(self, moveX, moveY, moveZ, rotateX, rotateY): - # if self.cam != None: - # #for examples 7-11 and pyJANVRED implementations - # cameraspeed = 5 - # scaleMat = util.scale(self.scale["x"], self.scale["y"], self.scale["z"]) - # combinedMat = scaleMat - # if rotateX or rotateY: - # rotMatX = util.rotate((1, 0, 0), -self.rotation["y"] * cameraspeed) - # rotMatY = util.rotate((0, 1, 0), self.rotation["x"] * cameraspeed) - # rotMatZ = util.rotate((0, 0, 1), self.rotation["z"] * cameraspeed) - # combinedMat = rotMatX @ rotMatY @ rotMatZ @ combinedMat - # if moveX or moveY or moveZ: - # transMat = util.translate(self.translation["x"], self.translation["y"], -self.translation["z"]) - # combinedMat = transMat @ combinedMat - # self.cam.trans1.trs = self.cam.trans1.trs @ combinedMat - # else: - # #for examples 4-5-6-8-9-10 implementations - # cameraspeed = 0.2 - # teye = np.array(self._eye) - # ttarget = np.array(self._target) - # tup = np.array(self._up) - - # forwardDir = util.normalise(ttarget - teye) - # rightDir = util.normalise(np.cross(tup, forwardDir)) - - # eyeUpd = np.array([0.0, 0.0, 0.0]) - # targetUpd = np.array([0.0, 0.0, 0.0]) - - # if rotateX: - # eyeUpd = rightDir * self.rotation["x"] * cameraspeed - # elif rotateY: - # s,c = util.sincos(1) - # rotDir = util.normalise(util.vec(s, c, 0.0)) * tup - # eyeUpd = rotDir * self.rotation["y"] * cameraspeed - - # if moveX: - # eyeUpd = -cameraspeed * self.translation["x"] * rightDir - # targetUpd = eyeUpd - # if moveY: - # eyeUpd = -self.translation["y"] * cameraspeed * tup - # targetUpd = eyeUpd - # if moveZ: - # eyeUpd = np.sign(self.translation["z"]) * cameraspeed * forwardDir - - # teye += eyeUpd - # ttarget += targetUpd - # if (rotateX or rotateY): - # newForwardDir = util.normalise(ttarget - teye) - # tup = util.normalise(np.cross(newForwardDir, rightDir)) - - # self._eye = tuple(teye) - # self._target = tuple(ttarget) - # self._up = tuple(tup) - - # self._updateCamera.value = util.lookat(util.vec(self._eye), util.vec(self._target), util.vec(self._up)) - # if self._wrapeeWindow.eventManager is not None: - # self.wrapeeWindow.eventManager.notify(self, self._updateCamera) - - - # def on_mouse_motion(self, event, x, y, dx, dy): - # """Called when the mouse is moved. - - # event: sdl2.events.SDL_Event, - # x: horiz coord relative to window, y: vert coord relative to window, - # dx: relative horizontal motion, dy: relative vertical motion - # """ - # pass + self.menuBar() + # print(f'{self.getClassName()}: display()') + + + def moveEye(self, value, direction): + if value == 0: # first element of eye(,,) + if direction == 0: # decrease + new_eye = (self._eye[0] - self.offset,) + self._eye[1:] + self._eye = new_eye + else: # increase + new_eye = (self._eye[0] + self.offset,) + self._eye[1:] + self._eye = new_eye + elif value == 1: # second element of eye(,,) + if direction == 0: # decrease + new_eye = ( + self._eye[:1] + ((self._eye[1] - self.offset),) + self._eye[2:] + ) + self._eye = new_eye + else: # increase + new_eye = ( + self._eye[:1] + ((self._eye[1] + self.offset),) + self._eye[2:] + ) + self._eye = new_eye + elif value == 2: # third element of eye(,,) + if direction == 0: # decrease + new_eye = ( + self._eye[:2] + ((self._eye[2] - self.offset),) + self._eye[3:] + ) + self._eye = new_eye + else: # increase + new_eye = ( + self._eye[:2] + ((self._eye[2] + self.offset),) + self._eye[3:] + ) + self._eye = new_eye + + self._updateCamera.value = util.lookat( + util.vec(self._eye), util.vec(self._target), util.vec(self._up) + ) + if self._wrapeeWindow.eventManager is not None: + self.wrapeeWindow.eventManager.notify(self, self._updateCamera) + + def moveTarget(self, value, direction): + if value == 0: # first element of target(,,) + if direction == 0: # decrease + new_target = (self._target[0] - self.offset,) + self._target[1:] + self._target = new_target + else: # increase + new_target = (self._target[0] + self.offset,) + self._target[1:] + self._target = new_target + elif value == 1: # second element of target(,,) + if direction == 0: # decrease + new_target = ( + self._target[:1] + + ((self._target[1] - self.offset),) + + self._target[2:] + ) + self._target = new_target + else: # increase + new_target = ( + self._target[:1] + + ((self._target[1] + self.offset),) + + self._target[2:] + ) + self._target = new_target + elif value == 2: # third element of target(,,) + if direction == 0: # decrease + new_target = ( + self._target[:2] + + ((self._target[2] - self.offset),) + + self._target[3:] + ) + self._target = new_target + else: # increase + new_target = ( + self._target[:2] + + ((self._target[2] + self.offset),) + + self._target[3:] + ) + self._target = new_target + + self._updateCamera.value = util.lookat( + util.vec(self._eye), util.vec(self._target), util.vec(self._up) + ) + if self._wrapeeWindow.eventManager is not None: + self.wrapeeWindow.eventManager.notify(self, self._updateCamera) + + def moveUp(self, value, direction): + if value == 0: # first element of up(,,) + if direction == 0: # decrease + new_up = (self._up[0] - self.offset,) + self._up[1:] + self._up = new_up + else: # increase + new_up = (self._up[0] + self.offset,) + self._up[1:] + self._up = new_up + elif value == 1: # second element of up(,,) + if direction == 0: # decrease + new_up = self._up[:1] + ((self._up[1] - self.offset),) + self._up[2:] + self._up = new_up + else: # increase + new_up = self._up[:1] + ((self._up[1] + self.offset),) + self._up[2:] + self._up = new_up + elif value == 2: # third element of up(,,) + if direction == 0: # decrease + new_up = self._up[:2] + ((self._up[2] - self.offset),) + self._up[3:] + self._up = new_up + else: # increase + new_up = self._up[:2] + ((self._up[2] + self.offset),) + self._up[3:] + self._up = new_up + + self._updateCamera.value = util.lookat( + util.vec(self._eye), util.vec(self._target), util.vec(self._up) + ) + if self._wrapeeWindow.eventManager is not None: + self.wrapeeWindow.eventManager.notify(self, self._updateCamera) - # def on_mouse_press(self, event, x, y, button, dclick): - # """Called when mouse buttons are pressed. + ################## toggle the wireframe ############################# + def toggle_Wireframe(self): + self._wireframeMode = not self._wireframeMode + self._updateWireframe.value = self._wireframeMode + self.wrapeeWindow.eventManager.notify(self, self._updateWireframe) - # event: sdl2.events.SDL_Event, - # x: horiz coord relative to window, y: vert coord relative to window, - # dx: relative horizontal motion, dy: relative vertical motion - # button: RIGHT - MIDDLE - LEFT - # dclick: True - False if button was double click - # """ - # pass - - # def resetAll(self): - # self.translation["x"] = 0.0 - # self.translation["y"] = 0.0 - # self.translation["z"] = 0.0 - # self.rotation["x"] = 0.0 - # self.rotation["y"] = 0.0 - # self.rotation["z"] = 0.0 - # self.scale["x"]= 1.0 - # self.scale["y"]= 1.0 - # self.scale["z"]= 1.0 - - # def cameraHandling(self, x, y, height, width): - # keystatus = sdl2.SDL_GetKeyboardState(None) - # self.resetAll() - - # if keystatus[sdl2.SDL_SCANCODE_LSHIFT]: - # if abs(x) > abs(y): - # self.translation["x"] = x/width*60 #np.sign(event.wheel.x) - # self.updateCamera(True, False, False, False, False) - # else: - # self.translation["y"] = y/height*60 #np.sign(event.wheel.y) - # self.updateCamera(False, True, False, False, False) - # elif keystatus[sdl2.SDL_SCANCODE_LCTRL] or self.lctrl: - # self.translation["z"] = y/height*60 #-np.sign(event.wheel.y) - # self.updateCamera(False, False, True, False, False) - # else: - # if abs(x) > abs(y): - # self.rotation["x"] = np.sign(x) #event.wheel.x/height*180 - # self.updateCamera(False, False,False, True, False) - # else: - # self.rotation["y"] = np.sign(y) #event.wheel.y/width*180 - # self.updateCamera(False, False,False, False, True) - - # def event_input_process(self): - # """ - # process SDL2 basic events and input - # """ - # running = True - # events = sdl2.ext.get_events() - # width = self.wrapeeWindow._windowWidth - # height = self.wrapeeWindow._windowHeight - - # #if not imgui.is_window_focused(): - # for event in events: - - # if event.type == sdl2.SDL_MOUSEWHEEL: - # x = event.wheel.x - # y = event.wheel.y - # self.cameraHandling(x,y,height,width) - # continue - - # if event.type == sdl2.SDL_MOUSEBUTTONUP: - # pass - - # # on_mouse_press - # buttons = event.motion.state - # if buttons & sdl2.SDL_BUTTON_RMASK: - # x = -event.motion.xrel - # y = event.motion.yrel - # self.cameraHandling(x, y, height, width) - - # continue - - # #keyboard events - # if event.type == sdl2.SDL_KEYDOWN: - # if event.key.keysym.sym == sdl2.SDLK_UP or event.key.keysym.sym == sdl2.SDLK_w : - # pass - # if event.key.keysym.sym == sdl2.SDLK_DOWN or event.key.keysym.sym == sdl2.SDLK_s : - # pass - # if event.key.keysym.sym == sdl2.SDLK_LEFT or event.key.keysym.sym == sdl2.SDLK_a : - # pass - # if event.key.keysym.sym == sdl2.SDLK_RIGHT or event.key.keysym.sym == sdl2.SDLK_d : - # pass - # if event.key.keysym.sym == sdl2.SDLK_LCTRL: - # self.lctrl=True - # if event.key.keysym.sym == sdl2.SDLK_ESCAPE: - # running = False - - # if event.type == sdl2.SDL_KEYUP and event.key.keysym.sym == sdl2.SDLK_LCTRL: - # self.lctrl = False - - # if event.type == sdl2.SDL_QUIT: - # running = False - - # if event.type == sdl2.SDL_WINDOWEVENT: - # window = self.wrapeeWindow - # if event.window.event == sdl2.SDL_WINDOWEVENT_RESIZED: - # print("Window Resized to ", event.window.data1, " X " , event.window.data2) - # window._windowWidth = event.window.data1 - # window._windowHeight = event.window.data2 - # # new width and height: event.window.data1 and event.window.data2 - # gl.glViewport(0, 0, event.window.data1, event.window.data2) - - # #imgui event - # self._imguiRenderer.process_event(event) - # #imgui input - # self._imguiRenderer.process_inputs() - # return running - def display_post(self): # this is important to draw the ImGUI in full mode and not wireframe! gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL) - + # render imgui (after 3D scene and just before the SDL double buffer swap window) imgui.render() self._imguiRenderer.render(imgui.get_draw_data()) - # call the SDL window window swapping in the end of the scene as final render action self.wrapeeWindow.display_post() - - + def extra(self): - """sample ImGUI widgets to be rendered on a RenderWindow - """ + """sample ImGUI widgets to be rendered on a RenderWindow""" imgui.set_next_window_size(300.0, 200.0) - - #start new ImGUI frame context + + # start new ImGUI frame context imgui.new_frame() #demo ImGUI window with all widgets # imgui.show_test_window() - #new custom imgui window - imgui.begin("Elements ImGUI window", True) - #labels inside the window - imgui.text("PyImgui + PySDL2 integration successful!") - imgui.text(self._wrapeeWindow._gVersionLabel) - - # populate window with extra UI elements - imgui.separator() - imgui.new_line() - # - # wireframe Event updates the GL state - self._changed, self._checkbox = imgui.checkbox("Wireframe", self._wireframeMode) - if self._changed: - if self._checkbox is True: - self._wireframeMode = True - self._updateWireframe.value = self._wireframeMode + + ########## Added bool variable to enable to close the imgui window ############### + if self.showElementsWindow: + # new custom imgui window + imgui.core.set_next_window_collapsed(not self.collapseElementsWindow, imgui.FIRST_USE_EVER) + self.collapseElementsWindow, self.showElementsWindow = imgui.begin("Elements ImGUI window", True) + ###### do this so we can be able to move the window after it was collapsed ######### + ####### and we re open it ######### + if self.collapseElementsWindow: + imgui.set_window_position(self.elements_x,self.elements_y,imgui.FIRST_USE_EVER) + imgui.set_window_collapsed_labeled("Elements ImGUI window", True, imgui.FIRST_USE_EVER) + else: + imgui.set_window_position(self.elements_x,self.elements_y, imgui.FIRST_USE_EVER) + + + # labels inside the window + imgui.text("PyImgui + PySDL2 integration successful!") + imgui.text(self._wrapeeWindow._gVersionLabel) + + # populate window with extra UI elements + imgui.separator() + imgui.new_line() + # + # wireframe Event updates the GL state + self._changed, self._checkbox = imgui.checkbox("Wireframe", self._wireframeMode) + if self._changed: + if self._checkbox is True: + self._wireframeMode = True + self._updateWireframe.value = self._wireframeMode + if self._wrapeeWindow.eventManager is not None: + self.wrapeeWindow.eventManager.notify(self, self._updateWireframe) + print(f"wireframe: {self._wireframeMode}") + if self._checkbox is False: + self._wireframeMode = False + self._updateWireframe.value = self._wireframeMode + if self._wrapeeWindow.eventManager is not None: + self.wrapeeWindow.eventManager.notify(self, self._updateWireframe) + print(f"wireframe: {self._wireframeMode}") + # + # simple slider for color + self._changed, self._colorEditor = imgui.color_edit3("Color edit", *self._colorEditor) + if self._changed: + print(f"_colorEditor: {self._colorEditor}") + imgui.separator() + # + # START + # simple slider for eye - IMPORTANT PART HERE + + self.traverseCamera() + if self.cam is not None: + imgui.text("If your camera is not defined via the lookAt function, \nthe sliders below will not work") + self._changed, self._eye = imgui.drag_float3( "Eye", *self._eye, change_speed = 0.01, min_value=-10, max_value=10,format="%.3f") + if self._changed: + self._updateCamera.value = util.lookat(util.vec(self._eye), util.vec(self._target), util.vec(self._up)) + print ("NEW CAMERA VALUE", self._updateCamera.value) if self._wrapeeWindow.eventManager is not None: - self.wrapeeWindow.eventManager.notify(self, self._updateWireframe) - print(f"wireframe: {self._wireframeMode}") - if self._checkbox is False: - self._wireframeMode = False - self._updateWireframe.value = self._wireframeMode + self.wrapeeWindow.eventManager.notify(self, self._updateCamera) + print(f"_eye: {self._eye}") + imgui.separator() + # + # simple slider for target + self._changed, self._target = imgui.drag_float3( "Target", *self._target, change_speed = 0.01, min_value=-10, max_value=10,format="%.3f") + if self._changed: + self._updateCamera.value = util.lookat(util.vec(self._eye), util.vec(self._target), util.vec(self._up)) + print ("NEW CAMERA VALUE", self._updateCamera.value) if self._wrapeeWindow.eventManager is not None: - self.wrapeeWindow.eventManager.notify(self, self._updateWireframe) - print(f"wireframe: {self._wireframeMode}") - # - # simple slider for color - self._changed, self._colorEditor = imgui.color_edit3("Color edit", *self._colorEditor) - if self._changed: - print(f"_colorEditor: {self._colorEditor}") - imgui.separator() - # - # START - # simple slider for eye - IMPORTANT PART HERE - self._changed, self._eye = imgui.drag_float3( "Eye", *self._eye, change_speed = 0.01, min_value=-10, max_value=10,format="%.3f") - if self._changed: - self._updateCamera.value = util.lookat(util.vec(self._eye), util.vec(self._target), util.vec(self._up)) - print ("NEW CAMERA VALUE", self._updateCamera.value) - if self._wrapeeWindow.eventManager is not None: self.wrapeeWindow.eventManager.notify(self, self._updateCamera) - print(f"_eye: {self._eye}") - imgui.separator() - # - # simple slider for target - self._changed, self._target = imgui.drag_float3( "Target", *self._target, change_speed = 0.01, min_value=-10, max_value=10,format="%.3f") - if self._changed: - self._updateCamera.value = util.lookat(util.vec(self._eye), util.vec(self._target), util.vec(self._up)) - print ("NEW CAMERA VALUE", self._updateCamera.value) - if self._wrapeeWindow.eventManager is not None: - self.wrapeeWindow.eventManager.notify(self, self._updateCamera) - print(f"_target: {self._target}") - imgui.separator() - # simple slider for up - self._changed, self._up = imgui.drag_float3( "Up", *self._up, change_speed = 0.01 ,min_value=-5, max_value=5,format="%.3f") - if self._changed: - self._updateCamera.value = util.lookat(util.vec(self._eye), util.vec(self._target), util.vec(self._up)) - print ("NEW CAMERA VALUE", self._updateCamera.value) - if self._wrapeeWindow.eventManager is not None: - self.wrapeeWindow.eventManager.notify(self, self._updateCamera) - print(f"_up: {self._up}") - imgui.separator() - # END - # simple FPS counter - strFrameRate = str(("Application average: ", imgui.get_io().framerate, " FPS")) - imgui.text(strFrameRate) - #end imgui frame context - imgui.end() - - #print(f'{self.getClassName()}: extra()') - + print(f"_target: {self._target}") + imgui.separator() + # simple slider for up + self._changed, self._up = imgui.drag_float3( "Up", *self._up, change_speed = 0.01 ,min_value=-5, max_value=5,format="%.3f") + if self._changed: + self._updateCamera.value = util.lookat(util.vec(self._eye), util.vec(self._target), util.vec(self._up)) + print ("NEW CAMERA VALUE", self._updateCamera.value) + if self._wrapeeWindow.eventManager is not None: + self.wrapeeWindow.eventManager.notify(self, self._updateCamera) + print(f"_up: {self._up}") + imgui.separator() + # END + # simple FPS counter + framerate = imgui.get_io().framerate + strFrameRate = "Application average: {:.2f} FPS".format(framerate) + imgui.text(strFrameRate) + # end imgui frame context + imgui.end() + + # print(f'{self.getClassName()}: extra()') + def scenegraphVisualiser(self): """display the ECSS in an ImGUI tree node structure - Typically this is a custom widget to be extended in an ImGUIDecorator subclass + Typically this is a custom widget to be extended in an ImGUIDecorator subclass """ pass - - - def accept(self, system: Elements.pyECSS.System, event = None): + + def accept(self, system: Elements.pyECSS.System, event=None): system.apply2ImGUIDecorator(self, event) + ### After collapsing windows, move all windows to top left corner + ### Use the top corner to cover all window sizes + ### since for now we dont knowt the screen height dynamically + def align_windows_top_left(self): + starting_y = 20 + + self.elements_x = 10 + self.elements_y = starting_y + + starting_y += 20 + + if Shortcuts.show_shortcuts_window: + Shortcuts.shortcuts_x = 10 + Shortcuts.shortcuts_y = starting_y + starting_y += 20 + if Shortcuts.showGUI_text: + Shortcuts.GUItext_x = 10 + Shortcuts.GUItext_y = starting_y + starting_y += 20 + self.graph_x = 10 + # self.graph_y = starting_y + 20 + + ###### MENU BAR ######### + def menuBar(self): + # Create the header bar + imgui.begin_main_menu_bar() + + # Create the "File" dropdown menu + if imgui.begin_menu("File", True): + # imgui.menu_item("Save") + # imgui.separator() + # imgui.menu_item("Save as") + # imgui.separator() + if imgui.menu_item("Minimize")[1]: + sdl2.SDL_MinimizeWindow(self.wrapeeWindow._gWindow) + imgui.separator() + if imgui.menu_item("Exit")[1]: + exit() + + imgui.end_menu() + + # Create the "View" dropdown menu + if imgui.begin_menu("View"): + # Add a "Shortcuts" submenu + if imgui.menu_item("Toggle Elements ImGUI Window")[1]: + self.showElementsWindow = not self.showElementsWindow + if imgui.menu_item("Toggle ECSS Graph")[1]: + self.showScenegraphVisualizer = not self.showScenegraphVisualizer + if imgui.menu_item("Toggle Shortcuts")[1]: + Shortcuts.show_shortcuts_window = not Shortcuts.show_shortcuts_window + if Shortcuts.show_shortcuts_window: + Shortcuts.displayShortcutsGUI() + if imgui.menu_item("Example Description")[1]: + Shortcuts.showGUI_text = not Shortcuts.showGUI_text + if imgui.menu_item("Show All")[1]: + self.showElementsWindow = True + self.showScenegraphVisualizer = True + Shortcuts.show_shortcuts_window = True + Shortcuts.showGUI_text = True + if imgui.menu_item("Hide All")[1]: + self.showElementsWindow = False + self.showScenegraphVisualizer = False + Shortcuts.show_shortcuts_window = False + Shortcuts.showGUI_text = False + if imgui.menu_item("Collapse Windows")[1]: + # self.collapseElementsWindow = False + imgui.set_window_collapsed_labeled("Elements ImGUI window", True) + imgui.set_window_collapsed_labeled("ECSS graph", True) + imgui.set_window_collapsed_labeled("Shortcuts", True) + imgui.set_window_collapsed_labeled("Example Description", True) + # self.align_windows_top_left() + imgui.end_menu() + + # Create the "Help" dropdown menu + if imgui.begin_menu("Help"): + # Add a "Shortcuts" submenu + if imgui.menu_item("FAQ")[1]: + pass + imgui.end_menu() + # End the main menu bar + imgui.end_main_menu_bar() + class ImGUIecssDecorator(ImGUIDecorator): + """custom ImGUI decorator for this example :param ImGUIDecorator: [description] @@ -1025,74 +1108,64 @@ def scenegraphVisualiser(self): twoColumn = False - if twoColumn: - # 2 Column Version - imgui.begin("ECSS graph") - imgui.columns(2,"Properties") - if imgui.tree_node(sceneRoot, imgui.TREE_NODE_OPEN_ON_ARROW): - self.drawNode(self.wrapeeWindow.scene.world.root) - imgui.tree_pop() - imgui.next_column() - imgui.text("Properties") - imgui.separator() - else: - imgui.begin("ECSS graph") - imgui.columns(1,"Properties") - # below is a recursive call to build-up the whole scenegraph as ImGUI tree - # if imgui.tree_node(sceneRoot, imgui.TREE_NODE_OPEN_ON_ARROW): - # self.drawNode(self.wrapeeWindow.scene.world.root) - # imgui.tree_pop() - # imgui.next_column() - imgui.text("Properties") - imgui.separator() - + ########## Added bool variable to enable to close the graph window ############### + if self.showScenegraphVisualizer: + imgui.core.set_next_window_collapsed(not self.collapseScenegraphVisualizer, imgui.FIRST_USE_EVER) + + if twoColumn: + # 2 Column Version + self.collapseScenegraphVisualizer, self.showScenegraphVisualizer = imgui.begin("ECSS graph",True) + imgui.columns(2, "Properties") + if imgui.tree_node(sceneRoot, imgui.TREE_NODE_OPEN_ON_ARROW): + self.drawNode(self.wrapeeWindow.scene.world.root) + imgui.tree_pop() + imgui.next_column() + imgui.text("Properties") + imgui.separator() + else: + self.collapseScenegraphVisualizer, self.showScenegraphVisualizer = imgui.begin("ECSS graph",True) + imgui.columns(1, "Properties") + imgui.text("Properties") + imgui.separator() + + ###### do this so we can be able to move the window after it was collapsed ######### + ####### and we re open it ######### + if self.collapseScenegraphVisualizer: + imgui.set_window_position(self.graph_x,self.graph_y,imgui.FIRST_USE_EVER) + else: + imgui.set_window_position(self.graph_x,self.graph_y, imgui.FIRST_USE_EVER) + + # smallerTRSgui = True + #TRS sample + # if(isinstance(self.selected, BasicTransform)): + + if imgui.tree_node("Translation", imgui.TREE_NODE_LEAF): + imgui.same_line() + changed, value = imgui.drag_float3("X,Y,Z",self.translation["x"],self.translation["y"],self.translation["z"], 0.01, -30, 30, "%.001f", 1); + self.translation["x"],self.translation["y"],self.translation["z"] = value[0],value[1], value[2] + imgui.tree_pop(); + if imgui.tree_node("Rotation ", imgui.TREE_NODE_LEAF): + imgui.same_line() + changed, value = imgui.drag_float3("X,Y,Z",self.rotation["x"],self.rotation["y"],self.rotation["z"], 1, -180, 180, "%.1f", 1); + self.rotation["x"],self.rotation["y"],self.rotation["z"] = value[0],value[1], value[2] + imgui.tree_pop(); + if imgui.tree_node("Scale ", imgui.TREE_NODE_LEAF): + imgui.same_line() + changed, value = imgui.drag_float3("X,Y,Z",self.scale["x"],self.scale["y"],self.scale["z"], 0.01, 0, 4, "%.01f", 1); + self.scale["x"],self.scale["y"],self.scale["z"] = value[0],value[1], value[2] + imgui.tree_pop(); - # smallerTRSgui = True - #TRS sample - # if(isinstance(self.selected, BasicTransform)): - - if imgui.tree_node("Translation", imgui.TREE_NODE_OPEN_ON_ARROW): - # changed, value = imgui.slider_float("X", self.translation["x"], -3, 3, "%.01f", 1); - # self.translation["x"] = value; - # changed, value = imgui.slider_float("Y", self.translation["y"], -3, 3, "%.01f", 1); - # self.translation["y"] = value; - # changed, value = imgui.slider_float("Z", self.translation["z"], -3, 3, "%.01f", 1); - # self.translation["z"] = value; - changed, value = imgui.drag_float3("X,Y,Z",self.translation["x"],self.translation["y"],self.translation["z"], 0.01, -30, 30, "%.001f", 1); - self.translation["x"],self.translation["y"],self.translation["z"] = value[0],value[1], value[2] - imgui.tree_pop(); - if imgui.tree_node("Rotation", imgui.TREE_NODE_OPEN_ON_ARROW): - # changed, value = imgui.slider_float("X", self.rotation["x"], -90, 90, "%.1f", 1); - # self.rotation["x"] = value; - # changed, value = imgui.slider_float("Y", self.rotation["y"], -90, 90, "%.1f", 1); - # self.rotation["y"] = value; - # changed, value = imgui.slider_float("Z", self.rotation["z"], -90, 90, "%.1f", 1); - # self.rotation["z"] = value; - changed, value = imgui.drag_float3("X,Y,Z",self.rotation["x"],self.rotation["y"],self.rotation["z"], 1, -180, 180, "%.1f", 1); - self.rotation["x"],self.rotation["y"],self.rotation["z"] = value[0],value[1], value[2] - imgui.tree_pop(); - if imgui.tree_node("Scale", imgui.TREE_NODE_OPEN_ON_ARROW): - # changed, value = imgui.slider_float("X", self.scale["x"], 0, 3, "%.01f", 1); - # self.scale["x"] = value; - # changed, value = imgui.slider_float("Y", self.scale["y"], 0, 3, "%.01f", 1); - # self.scale["y"] = value; - # changed, value = imgui.slider_float("Z", self.scale["z"], 0, 3, "%.01f", 1); - # self.scale["z"] = value; - changed, value = imgui.drag_float3("X,Y,Z",self.scale["x"],self.scale["y"],self.scale["z"], 0.01, 0, 4, "%.01f", 1); - self.scale["x"],self.scale["y"],self.scale["z"] = value[0],value[1], value[2] - imgui.tree_pop(); + + if twoColumn: + pass + else: + imgui.separator() + if imgui.tree_node(sceneRoot, imgui.TREE_NODE_OPEN_ON_ARROW): + self.drawNode(self.wrapeeWindow.scene.world.root) + imgui.tree_pop() - - if twoColumn: - pass - else: - imgui.separator() - if imgui.tree_node(sceneRoot, imgui.TREE_NODE_OPEN_ON_ARROW): - self.drawNode(self.wrapeeWindow.scene.world.root) - imgui.tree_pop() + imgui.end() - imgui.end() - def drawNode(self, component): #create a local iterator of Entity's children if component._children is not None: @@ -1112,9 +1185,8 @@ def drawNode(self, component): imgui.text(comp.name) _, selected = imgui.selectable(comp.__str__(), True) if selected: - - if comp != self.selected: # First time selecting it. Set trs values to GUI; - self.selected = comp; + if ( comp != self.selected ): # First time selecting it. Set trs values to GUI; + self.selected = comp if isinstance(comp, BasicTransform): [x, y, z] = comp.translation; self.translation["x"] = x; @@ -1144,16 +1216,10 @@ def drawNode(self, component): comp.drawSelfGui(imgui); imgui.tree_pop() - - self.drawNode(comp) # recursive call of this method to traverse hierarchy - imgui.unindent(10) # Corrent placement of unindent - def event_input_process(self): - """ - process SDL2 basic events and input - """ - return super().event_input_process() - + self.drawNode( comp ) # recursive call of this method to traverse hierarchy + imgui.unindent(10) # Corrent placement of unindent + class RenderGLStateSystem(System): """ @@ -1181,12 +1247,11 @@ def apply2ImGUIDecorator(self, imGUIDecorator, event = None): """ pass - - - def apply2SDLWindow(self, sdlWindow, event = None): - """method for behavioral or logic computation - when visits Components. - + + def apply2SDLWindow(self, sdlWindow, event=None): + """method for behavioral or logic computation + when visits Components. + In this case update GL State from SDLWindow :param sdlWindow: [description] @@ -1201,8 +1266,6 @@ def apply2SDLWindow(self, sdlWindow, event = None): if event.name == "OnUpdateCamera": # print(f"OnUpdateCamera: RenderGLStateSystem():apply2SDLWindow() actuator system for: {event}") sdlWindow._myCamera = event.value - - if __name__ == "__main__": @@ -1214,8 +1277,6 @@ def apply2SDLWindow(self, sdlWindow, event = None): # MAIN RENDERING LOOP while running: gWindow.display() - running = gWindow.event_input_process() - windowaspect = gWindow._windowWidth/gWindow._windowHeight - print("windowaspect : ", windowaspect) + running = gWindow.event_input_process(running) gWindow.display_post() gWindow.shutdown() \ No newline at end of file diff --git a/Elements/pyGLV/tests/test_Scene.py b/Elements/pyGLV/tests/test_Scene.py index d5db255e..978d190a 100644 --- a/Elements/pyGLV/tests/test_Scene.py +++ b/Elements/pyGLV/tests/test_Scene.py @@ -31,7 +31,7 @@ import OpenGL.GL as gl -from Elements.utils.helper_function import displayGUI_text +from Elements.utils.Shortcuts import displayGUI_text import imgui diff --git a/Elements/pyGLV/tests/test_Texture.py b/Elements/pyGLV/tests/test_Texture.py index c876f5c8..19567a3b 100644 --- a/Elements/pyGLV/tests/test_Texture.py +++ b/Elements/pyGLV/tests/test_Texture.py @@ -27,7 +27,7 @@ from Elements.pyGLV.GL.VertexArray import VertexArray import Elements.utils.normals as norm from Elements.pyGLV.GL.Textures import Texture -from Elements.utils.helper_function import displayGUI_text +from Elements.utils.Shortcuts import displayGUI_text from Elements.definitions import TEXTURE_DIR from OpenGL.GL import GL_LINES diff --git a/Elements/utils/Shortcuts.py b/Elements/utils/Shortcuts.py new file mode 100644 index 00000000..82919e0c --- /dev/null +++ b/Elements/utils/Shortcuts.py @@ -0,0 +1,80 @@ +import imgui,sdl2 + +leftAlt_Key = sdl2.SDLK_LALT +rightAlt_Key = sdl2.SDLK_RALT +leftShift_Key = sdl2.SDLK_LSHIFT +rightShift_Key = sdl2.SDLK_RSHIFT +ctrl_Key = sdl2.SDLK_LCTRL + +#change this to one of the above for the shortcuts +shortcut_hotKey = leftAlt_Key + +showGUI_text = True +collapseGUI_text = True +GUItext_x = 10 +GUItext_y = 100 +def displayGUI_text(text:str): + displayShortcutsGUI() # hack to ensure function is always on the main loop without calling it from the example + global showGUI_text, collapseGUI_text + # imgui.set_next_window_size(100.0, 200.0) + # imgui.new_frame() + if showGUI_text: + imgui.core.set_next_window_collapsed(collapseGUI_text, imgui.FIRST_USE_EVER) + collapseGUI_text,showGUI_text = imgui.begin("Example Description", True) + ###### do this so we can be able to move the window after it was collapsed ######### + ####### and we re open it ######### + if collapseGUI_text: + imgui.set_window_position(GUItext_x,GUItext_y,imgui.FIRST_USE_EVER) + else: + imgui.set_window_position(GUItext_x,GUItext_y,imgui.FIRST_USE_EVER) + imgui.text(text) + imgui.end() + + +show_shortcuts_window = False +shortcuts_x = 10 +shortcuts_y = 140 +collapseShortcutsWindow = True +def displayShortcutsGUI(): + + global show_shortcuts_window,shortcuts_x,shortcuts_y,collapseShortcutsWindow + if show_shortcuts_window: + imgui.core.set_next_window_collapsed(collapseShortcutsWindow, imgui.FIRST_USE_EVER) + collapseShortcutsWindow, show_shortcuts_window = imgui.begin("Shortcuts", True) + + ###### do this so we can be able to move the window after it was collapsed ######### + ####### and we re open it ######### + if collapseShortcutsWindow: + imgui.set_window_position(shortcuts_x,shortcuts_y,imgui.FIRST_USE_EVER) + else: + imgui.set_window_position(shortcuts_x,shortcuts_y,imgui.FIRST_USE_EVER) + + imgui.text("List of shortcuts:") + + imgui.bullet_text("Toggle Wireframe Alt+F") + imgui.bullet_text("Vertical Scroll: Vertical camera translate") + imgui.bullet_text("Horizontal Scroll: Vertical camera translate") + + imgui.text("When node is selected:") + #with imgui.indent(): + imgui.bullet_text("Positive translation on x-axis W") + imgui.bullet_text("Negative translation on x-axis Alt+W") + imgui.bullet_text("Positive translation on y-axis E") + imgui.bullet_text("Negative translation on y-axis Alt+E") + imgui.bullet_text("Positive translation on z-axis R") + imgui.bullet_text("Negative translation on z-axis Alt+R") + + imgui.bullet_text("Positive rotation on x-axis T") + imgui.bullet_text("Negative rotation on x-axis Alt+T") + imgui.bullet_text("Positive rotation on y-axis Y") + imgui.bullet_text("Negative rotation on y-axis Alt+Y") + imgui.bullet_text("Positive rotation on z-axis U") + imgui.bullet_text("Negative rotation on z-axis Alt+U") + + imgui.bullet_text("Scale up on x-axis I") + imgui.bullet_text("Scale down on x-axis Alt+I") + imgui.bullet_text("Scale up on y-axis O") + imgui.bullet_text("Scale down on y-axis Alt+O") + imgui.bullet_text("Scale up on z-axis P") + imgui.bullet_text("Scale down on z-axis Alt+P") + imgui.end() \ No newline at end of file diff --git a/README.md b/README.md index 97174a13..39f60c72 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ To dive in the details of the project check [its detailed developer documentatio ## Getting Started - Installation Instructions -Begin by following the installation instructions, found [HERE](https://elementsproject.readthedocs.io/en/latest/source/getting_started/installation.html). For HY358 students, the instructions are [HERE](https://github.com/papagiannakis/Elements/wiki/Installation-Instructions-for-HY358-Students). +Begin by following the installation instructions, found [HERE](https://elementsproject.readthedocs.io/en/latest/source/getting_started/installation.html). For **Computer Graphics Course students**, the instructions are [HERE](https://github.com/papagiannakis/Elements/wiki/Installation-Instructions-for-Computer-Graphics-Course-students). > [!NOTE] > We strongly recommend using: @@ -38,7 +38,7 @@ Begin by following the installation instructions, found [HERE](https://elementsp > * [Visual Studio Code](https://code.visualstudio.com) as your IDE, and > * [Fork](https://git-fork.com)/[Sourcetree](https://www.sourcetreeapp.com) for version control. -The main steps are: +The main steps summarize as follows: * Install Anaconda, VSCode, Git and a optionally a version control app * Clone (or download) this repo (or your forked repo) * Create a python 3.8 environment, by running @@ -60,7 +60,7 @@ The main steps are: * [examples](./Elements/examples): Example files related to pyECSS * [features](./Elements/features): Features extending basic functionality of Elements * [BasicShapes](./Elements/features/BasicShapes): Quickly add basic shapes (cubes, spheres, cones) to the scene with helper functions - * [GA](./Elements/features/GA): Use Geometric Algebra in the context of Elements + * [GA](./Elements/features/GA): Files related to Geometric Algebra(GA) and GA-based components-systems * [Gizmos](./Elements/features/Gizmos): Introducing Unity-like Gizmos to the Elements, for object manipulation * [SkinnedMesh](./Elements/features/SkinnedMesh): Visualize skinned meshes by applying the animation equation * [Slicing](./Elements/features/Slicing): Visualize sliced version of a 3D object @@ -71,20 +71,18 @@ The main steps are: * [rigid_body_animation](./Elements/features/rigid_body_animation): Animate a skinned mesh (preliminary version) * [usd](./Elements/features/usd): Enable loading/saving using Pixar's Universal Scene Descriptor (USD) format * [files](./Elements/files): Static files required - * [atlas_files](./Elements/files/atlas_files): Required for the [atlas](./Elements/features/atlas) feature + * [atlas_files](./Elements/files/atlas_files): Required for the Classification and Generative AI examples/notebooks * [models](./Elements/files/models): Various 3D models, static or rigged * [scenes](./Elements/files/scenes): Scenes in USD format * [scv](./Elements/files/scv): Various SCV files * [shaders](./Elements/files/shaders): Various shader files * [textures](./Elements/files/textures): Various texture files * [pyECSS](./Elements/pyECSS): Contains all the source code for pyECSS - Entity, Component, System, Scenegraph functionality - * [GA](./Elements/pyECSS/GA): Files related to Geometric Algebra(GA) and GA-based components-systems * [tests](./Elements/pyECSS/tests): Test files for pyECSS * [pyGLV](./Elements/pyGLV): Contains all the source code for pyGLV - graphics, shading, imgui functionality * [tests](./Elements/pyGLV/tests): Test files for pyGLV * [GL](./Elements/pyGLV/GL): The basic Graphics Library files (Scene, Shader, Texture, VertexArray) * [GUI](./Elements/pyGLV/GUI): Files related to the window and GUI instantiation. - * [utils](./Elements/pyGLV/utils): Utility files and functions for pyGLV. * [pyEEL](./Elements/pyEEL): The pyEEL learning hub * [notebooks](./Elements/pyEEL/notebooks): Contains all the jupyter notebooks of pyEEL * [SciCom](./Elements/pyEEL/notebooks/SciCom): Scientific Computation related notebooks diff --git a/setup.py b/setup.py index fe569e6b..2b4c2a34 100755 --- a/setup.py +++ b/setup.py @@ -45,7 +45,9 @@ 'bezier', 'clifford', 'trimesh', - 'pyganja' + 'pyganja', + 'open3d', + 'pyassimp==4.1.3' ],