From b18c13895c91d91f0cc060ebee5295c72881b384 Mon Sep 17 00:00:00 2001 From: Shuying Feng Date: Fri, 13 Aug 2010 18:05:53 +0000 Subject: [PATCH] Added animation system --- direct/src/leveleditor/AnimControlUI.py | 936 ++++++++++++++++++++ direct/src/leveleditor/AnimGlobals.py | 53 ++ direct/src/leveleditor/AnimMgr.py | 10 + direct/src/leveleditor/AnimMgrBase.py | 367 ++++++++ direct/src/leveleditor/CurveAnimUI.py | 159 ++++ direct/src/leveleditor/FileMgr.py | 3 + direct/src/leveleditor/GraphEditorUI.py | 918 +++++++++++++++++++ direct/src/leveleditor/LevelEditor.py | 2 + direct/src/leveleditor/LevelEditorBase.py | 4 + direct/src/leveleditor/LevelEditorUIBase.py | 54 +- direct/src/leveleditor/ObjectMgrBase.py | 51 +- direct/src/leveleditor/ObjectPropertyUI.py | 63 ++ 12 files changed, 2616 insertions(+), 4 deletions(-) create mode 100755 direct/src/leveleditor/AnimControlUI.py create mode 100755 direct/src/leveleditor/AnimGlobals.py create mode 100755 direct/src/leveleditor/AnimMgr.py create mode 100755 direct/src/leveleditor/AnimMgrBase.py create mode 100755 direct/src/leveleditor/CurveAnimUI.py create mode 100755 direct/src/leveleditor/GraphEditorUI.py diff --git a/direct/src/leveleditor/AnimControlUI.py b/direct/src/leveleditor/AnimControlUI.py new file mode 100755 index 0000000000..99f9cd880a --- /dev/null +++ b/direct/src/leveleditor/AnimControlUI.py @@ -0,0 +1,936 @@ +""" + This is the GUI for the Animation Control Panel +""" +from direct.interval.IntervalGlobal import * +from direct.actor.Actor import * +from panda3d.core import VBase3,VBase4 +import ObjectGlobals as OG +import AnimGlobals as AG + +import os,wx, time +from wx.lib.embeddedimage import PyEmbeddedImage + +#---------------------------------------------------------------------- +FirstFrame = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACh0lE" + "QVRIie2Uz08aQRTHd368CZzYGGNjPWkTlB7EK0QSewSSamL9R+upXmpko/LLskgqhrDGyqb4" + "Y40CB3dnd6aHMYBIjSZe2vhOk/2+93nf93Z3UOvsTHvtwK9OfIP+f1CEEKUUITRWJYQQQl4A" + "RQgBpQihVqvV6/UwflBCCGGM3XY6tm33H9JhGWPMOR9xFwhhnZzs7e21bPvL+rqu60KIe3eU" + "3tzcVCqVQqHwYW5ubW0tCIIBlDFmmmbTsjLptBoEADjnjUajWCodHx/3er2JiQklqWEdxzGr" + "1R8HB7/bbU3TotHoqFOMcfv8vFarZdJpxpjruvV6vVAs1ut1z/MopQBACAEAAGi32xXTLJfL" + "l5eXGGMAUJ3Gj88Y45w3LSu/v9+0LNd1AYAxpmlaEASE0uvr659HR+Vy2XEcla9pmpRS0zQy" + "tOsBFCEkhPi2tZXP533fD4VCqqY/iue6Xzc3r66uGGPDUr98kNw/SSkJIclE4tPKiq7rnHNl" + "QYUQIhwOp5aXF+bnlfERKKUDf3RYkFJOTk5ms9n40lJuZ6d6eOh5HgDcu5AyFoslk8liqWQY" + "xsXFxcjnOQYqhPB9PwgCzvn76emNjY3FeNzI5ZrNpjKMCQmCAACSicR8NLqfz5dLpU63q9Dk" + "sVMhRCQSmZmZQQhJKTnnCKGPsdjc7KxpmoZhtGxbCIEwVr11Xc9mMvHFxZ1crlarqfzBfp++" + "+THGlNLbTmd3d/fX6enn1dV3U1O+7ysVAIQQjUbj+/Z2bGEhlUqpXT+AKpuP0WpxjuOEw+FQ" + "KDScgxACgG63e3d3F4lEnuV0uFi1HNtVXQjq99VG3v4T8Tecij7uvsczoS+KN+g/Av0D3hpG" + "5dYDHHkAAAAASUVORK5CYII=") + + +#---------------------------------------------------------------------- +PreFrame = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACQ0lE" + "QVRIieWVzW4aMRSFbV+DCHTDJlkAYZMKAek2fQHYddElu/SteA+eoNk0iPA7BBBQgtQJI6Gi" + "ZoQKM2PP2F24P2RmKFGVRauenSX707nH99r4XtfRc4s8O/G/ghKMMcaHoQAAAIdxhFBKGeeM" + "MR/XDxVCrFYr0zQP4hzH0TStVqvpuk4p3d3waAEA6/W6Wq2m0ul3l5cIISmlDwcAm81mPp83" + "m827+ZwCFAoFn9NHUEXZWpZt20F3GGPLsiaTidbvz2Yzx3EQQvFkkgSy8kPVeUJ+xQIACjcc" + "DjudziddZ4ypBIQQyshh6C4OIWSa5sfZrNVqGYZh2zal9GeCUkq19KUUDsUIUUpN0xwOh71e" + "b2EYnHNCSCQSCe5EgZYKgWKMGefvr64Gt7f3i4XnebvuQhQoP7z5CSG2ZX15ePA8T2W6D+i7" + "gL1OpZQUoFwuF4vFD9fXo9HI2m6B0lA0CZuU8KJUPelM5u3Jyavz80ajMZ1OXdf9vesDUOXX" + "c10gpFAsZk5PB4NBvV5fLpdCiF1rT70oH5ozFj86en1x8fLsrN1ud7vdz6sVAKgo1UPxpJby" + "SQghhEgmk6VSKZ/PN25u+pq2tSzFCsYRMqacc9d1g2jP8xBCqVTqzfFxsVCo1+vj8Zgx9n2u" + "9kGFEIlEolKpJOJxGeg+Jdd1CSG5XC6bzbbabcMwghOFfR8fxjgajQohOOfhWfyQyvTrZgOE" + "xGKxXW5I+er5OSghBEboRSIhpfyTi9oniZAMBIr+0o/vH4d+A5itKwKTfnzPAAAAAElFTkSu" + "QmCC") + + +#---------------------------------------------------------------------- +PreKeyFrame = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACPUlE" + "QVRIib3Vz0/aYBgH8OfHiyTGgAsciJMl6A5j7OLO4+48eHT/qjflSDPlRweNEqPGrTF2hQwW" + "gbR9nx2Yi9Ii1bE9x7b55Pu87/Ok+PXqChZdtHDxv6FEsLS0UJQZXVcdHIBSi0OJ0HXV4aHE" + "QJmZmGOgML99BCAipZTjOPVard/vE00jT2gTEYkIEV3XbZpms9HwfP/T3l4mk9FaPwdVSmmt" + "v7uuaZqmaV5fX2utX66tEbOIPC0pIjIzANi23basRqPR7XaDIGBmRAQACImPoZNmtda2bbda" + "raZpOo4zeaiUAgARYebwgUajiJhIJPzR6Jtt12u1k5OTG8f5Y90vVgrjoIQ4Ho/Pz87ax8df" + "TLM/GABAIpGIbkckVvtE9PP2dn9//8KyiFkp9fvsogoRI99Ohw+CIJ1K7e7ulsvllZWVIAim" + "xuVBIqVinakAIFE+n8/t7LwtlarVauf0dDgaRUeeMQDRt+95HiG+3tx8lc+32m2jWr24vASA" + "yfDPCj4HBQAR8X2fmd9vbW0UCs1m8/PRkXNzowH4buUnIxW+qTnDLyKe56VSqQ/l8pti0TCM" + "Rr3+o9+fuDwjeKw1DYIAAbKZzMft7WKxaBiGZVnD4XDWHcbdfZnQiBuFwvr6eqfTqVQqAhBe" + "/Blo1Hd3b8T3fSZ6Vyrlcrler7eUTIbzhlBEmLU/92jP816srqbTaREJh8UHv2hEGI9xMJBs" + "9pG8c+thUhFIJmV5GXz/2SJE/E5E/lKMQhdR/wT9BR51CSZZ1VE7AAAAAElFTkSuQmCC") + +#---------------------------------------------------------------------- +PrePlay = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACV0lE" + "QVRIic2Vz1PaUBDHs5tkH9jROv4LDhUHrLXOlOml/p3+H/VYg6YJQz3UWkDDAJlob8J4AUxq" + "3tse4lBKEIV6cI9vdz7vuz/eW/h1daU9t+GzE18kFBERpxAWhAJAJpO5u7sbDodp7iJQIjIM" + "o16v7+/vtzsdk2giwJgLZxqGYZqXl5eWZf04O4ui6NPeHqTCngpFRCFEr9f76jjVarXf7wsh" + "hBBKqXTw41BEFETD21vbtstHR9fX10SUzWaZmZmllHNDiYiZz2q1Q8vyfV9HzGazIy9r2nxQ" + "0zR1XQ+CwLKsn7WaUkoQAUwWUEqpMT8O1XWdiLrdrm3b305OBoOBEMI0zSk3M7NSWuqmf6BJ" + "NwaDgeO69vFxt9dLyvdQNolSnqEUAKSU309PLcsKgkDX9dm4ETR9aIyIiFgulz8fHEgpl5eX" + "ETEtYdyS+iYjBZo2HnoPZWal1IdS6fXqarVaDYKAmWlaZyYsTpQCjLfrb/rM/Gpp6WOp9G57" + "u1GvO67rBwErNQP9pDlVSt2Goa7r73d3C8Vio9GoVCrtTkc9gAYAfuKLklJKKRFxZ2enUChc" + "eJ7rOK12W0ppEuEYmpnlXM9UKRWGISK+3drazOc9z6tUKl6z+TuOiWj03ck4ngM6jgaAYrG4" + "sbHRarVc1/WazTAMiUjTNKnUrDmdYcycqN7M59/kcu1Ox3Hd8/PzMAzjOF7867tXHUUAkMvl" + "1tfXfd//cniIACqlFBZb0QBARHEc39zcrKysTHgX3FHMHEWRUmptbS3t/a9tyszxtO6/vL3/" + "kP0BhtFQnDqk9wIAAAAASUVORK5CYII=") + +#---------------------------------------------------------------------- +Play = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACTUlE" + "QVRIidXWzU7bQBAH8JmxLW8SbglXLCchTsKFElEUu4aaHnhSnoInoBeEihoSwodEz5wKya67" + "Oz24UAiOgQhV6p5sy/r5v7OztvHH9TW896B3F/8hats2Ir4zenNzQ0Su6y5M249PENG27YOD" + "g5+3t192d33fJyKlFDMvjgIAIRrmo6Oj8/PzTqcThWGjXrcsS6WpMWZBlAFs2xZCAMDx8fFg" + "MAharTAMm82mEEJK+ZrUsygiWkQAQERCCGPMt5OTwelp0Gr1+/0gCBzHUUoVp55FAcCy/178" + "QzN/HwyGo1Gz0QijqB0EQogCOg8lmll3QnRdl5mHo9HZeNyo1/v9frfbFUKkaaq1fhlFotzC" + "4T19Nh6PLy58z4vCsLu2VhJCPk2dl9SyCjr0gb64vLy8uvI8b2trKwiCcqn0EOVp8zMDgG1Z" + "88THtBDCcZzhcLi/v//18JAeFe1J0uxBRAQAzFy8o5RSWuvV1dUkSYJWyxjzkDR/+sUxtdZK" + "qVq1Gm9vf9zcrFQqUsqimiJiAWqMkVJWKpUoDOM4rtVqSqnJZDJz27OkzEgEzybOzEopIvqw" + "vp4kied5Wuvn3Bw0L2maptoY3/d3k6Tb6SDidDqdN5s8NGup++OsfMvLy593dnq9XrlUki/t" + "0fkoIjNPp9OlpaU4jj9FUbValVJOCgMWoUQkpXRdt7exkSTJysrKrzSdV75XoQyAiHXf39vb" + "a7fbiPgmLhs484kmoru7O8dxsu576zs/P6kxplwuA0Dx+r4NzdyFuWz8Pz8TvwFpng4ClVur" + "2QAAAABJRU5ErkJggg==") + +#---------------------------------------------------------------------- +NextKeyFrame = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACNUlE" + "QVRIic2Vy27aQBSGj+3xBQNRlSJlQWhwSKPUSMkQmcu+Sl8zW56AVfMCTQgLVEQSbxCELhFi" + "wcUzc7qwihQ8NiTNImdny/P5/89tlNFwCO8d6rsTPyAUEXQdCAHE7VBFUXaCmqZ2d6c9PIBh" + "bIEi4nK5VFVVVbeYQMMg9/fq4yNGoGRDI2Os2+0yziuVim3bnDGMuFv/P87+JhQBRs/PnU7H" + "9/1Go+E4jqHrnPNYtCzIxrMCYOg6IPZ6vcFgUHbdWr1+mM8DAOf8jdC1ZELIYrH4dXv75PtV" + "z6OUfs7lBOdCiFdDETFgLLQalms6nf68ufnd63med3F+nk6nhRDJaEn18eUBTdNUVR2Px61W" + "q9ls9vt9xhghJKHzJDmNRpgNRHzy/eFo5LpurVr9Ui4rMW0XsQ8QZy1Er1ardrvt+75bq32f" + "TD4dHES/lNgPGJNC12wAmM/nf8bj2WwmFSuvvjTC+pim+bVUopSeXl5mr6+5TMFO0DXupFSi" + "lJaOj9OZDLMsBUA6ErKWWq3g31oRQnDObdt2HKdRrxcKBdu2OedBEERHPklpOJGhumwmUzo5" + "oZQWj44syxJCBEGw1ZkEyjhnjNm2/e3s7ILSYrFo6LoQgiUXMBmaSqUqlHrV6mE+b1kWY2x3" + "nASKiLqu/7i6yu7tWaYZ5i7pNGJ070mgmqblcrldzRICmhZ9rbz9ilZVZTIBQjCbhZdD+Irm" + "3wwhcH8fECEy1v8BBYCYtf3R7v34+AuNHRpDa7trrwAAAABJRU5ErkJggg==") + +#---------------------------------------------------------------------- +NextFrame = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACOUlE" + "QVRIidWVS2/aQBSFZ8ZjYxkTFmSBRFAgpk1CJTIgE/orq/wMJFbNAjbNLqsgxAKVh+INgrhL" + "HkK8PI8urCKCx01bsWju+vrTuef6noHj0Qgcu9DRie8dCiE8MlQIsdlsEEIIvT0EQkhV1WAn" + "PtBIKe10OpSxYrFoGAajVAghJUIIF4vFdDqNx+OxWIxzLlcKIRQAjF9eGo1GrVYbDAaMc4yx" + "1BBN03q93pe7u3a7HdG0UKUAAAiApqpAiG63OxwOP+Xzt5XKWSoFAGCMHTRzzj3P29coh+4k" + "Y4zX6/VTs/nsOGXbJoQkTk85Y/sICCFCKDjHIVQI4VHqu+ivazabfXt4+N7t2rZ9UyhEo1HO" + "eVDdfkm2L15/oCgKQsh13Xq9Xq1W+/0+pTTMaLlSaaPvhhDi2XFG43E+n78tlz9eXob9doHx" + "AQgbzUdvt9tWq+U4TqlUAkJIuXJPpdAdGwCwWq1+uK5pmkhmgnz70vL3E4lEPlgWIaRQKDw1" + "m/Tx8R+hO1zOsggh1sVF1DQxxmGbko2/3YJfscI5Z4wZhpHNZj9XKul02jAMxpjneb8JB4lS" + "/9h9dTHTtHI5Qkjm/FzXdf+Egs1vQyljlFLDMK6vrm4IyWQymqpyzmlggUIIznmQC/ffKITQ" + "crn8en+PFcUul89SKV3XaUhQIYQmk4nruslkMpFIvDrffagfffP5PHZyokcijLGw3NtxMcaM" + "sYOsgcHXVFEU6VB/XhJPgxH3t/WuX9P/FPoTFHYunoRw1IsAAAAASUVORK5CYII=") + +#---------------------------------------------------------------------- +LastFrame = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAAClElE" + "QVRIie2VT0sbQRjG950/Gw8mW4QgCWxaMSdjwkq60YDmYD+A/QBiP5keq2IuHuKxxUNFqoI2" + "UuOlInahHpYkHhLdmXmnh5VNiLFS8CL4nhZm3t/zzvMsM/D76sp47iLPTnyFvkQoABDyqAYh" + "BAD+DwoA3W632WwyxiilA5u01r7vCyE454+B+iXvoZTSVru9Va1++fq10+2aphltAgAA+H5w" + "sL6xcXl5yRhjjD2Eaq0HoWHzzc1NrVZbXV09OjoytOacR2iNeHp6ura2tr297ft+LBaLvOKc" + "Hx4efl5fD+UNw+hpIiIhhHPued5WtXpycjI/P5+dnARChBBaa9M0AyG+7e01zs/nZmeLxWIi" + "kZBSEkJarZbneRGqB9WIiAgAjDGt9c+zs18XF47jVBYWkskkIhqGQQgxTbPZatV2dn7U65VK" + "JTc1xTkf8GSIO6EVpmlKKff39xuNhuu6t7e3kRWMUoNSz/M2Nzez2eyHxUWlVH9QLKIopcJx" + "en4TwhjzfX93d/eNZVFK+9NgjAkh6vX63d2dlUjQvt+x96W17u9BxCAIGGMzMzPLy8uZTEZK" + "Ga1KKYUQ4+PjH5eWPq2sJJNJqdSQ4yOi1toAQEQp5cjISCGfL5VK7yYmCMDx8XEoHKLTqZTr" + "uvl83rIsxphSKgiCYVCtpZQiCOLxuOM4ruu+zWQopUqpQIhwNM65bdvvi8VcLmdZllQqCAJE" + "TKVS07ncEKhSKmaaJdedK5cztg0AIQgAtNaolG3b5XI5Pz09OjoqhIhGk1IWCgXHcYQQ9wmF" + "zwkhpN1udzqddDodhjbg75/r67GxsUQ8LqUcyPNhQfRGEUIIIQO43okYQ8QncQ88/WdPf/RP" + "1ku8pF+hz1V/AVdHWFTfbzsRAAAAAElFTkSuQmCC") + +#---------------------------------------------------------------------- +Key = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACl0lE" + "QVRIie2Uv0sbYRjH3x+X9/JGk9wlYkg6icbJMf0LHCxo/QuKUCfR0TFqB/dKHSztYKHBYHCp" + "kIIFqZM6ODho3Y2tQ44EMRffu3tz79vhlcNGC5I4+h2/L3yen+8Df19cgKcWenLiM7RbKITw" + "vql1g6OUAikhQowxIUTw1GGmEEKdkEKh8GZq6v3KCgAAY9wttKen58Pqan5hwTCMcrm8sLhI" + "COkKSgg5Ozv79PlzcWPjy/r6j50du9HY3d2llHYO1XV9s1R6NTY2OjpqWRbGeG5u7uDwMGjr" + "LRRCiB4tz/N+nZ6OT0y4rgsAcBwnm82GNK1WqyGEQDB9IYTv+wAAhBDGmHOuaZqUUpl3hRBq" + "NBp2szk0OOh5HgBASkkpTSSTVcsyTVMIoakeHR8fb5ZKGsYN2242m5l0ulqtYozNROLurqia" + "GGPX19eGYUgplSmljEQiruOotdUAAJzz4eHh+fl5Sun3cvnn3t7y8nI+n0/29c3MzDRtuz1T" + "256dnXUcJxKJBJG45yGEVBhNxSGE6LpOKTUMIxqNJpPJWCwWi8USpklCoTZoPB4nhPy5vOzv" + "71f98X2/Vq8bhqHKQkH+QgghBOfccRwhhOu6nucFfiDf9ymlqVTqYH8/HA4DADDGlmVdXV2l" + "Uql/oEq+72cymZe5HGNsZGQkOzTEOW8blCpwYnz8a6FQr9ej0ahpmltbWy8ymd7eXvUK2y4/" + "xhhj7LouIURKyTm/fzIQQhDC15OTmqa9W1o6Ojr6tr1dLBaNeLzVaj0AfaR0Xa9UKm+np09O" + "TgYGBj6ureVyOcbY7dw6g0opw+Fw8+amcn6eTqdN02SMBTV1CFVCCIVCoVar1fZHOr+nAAC1" + "JA8E6wb6Pz1Dn15/AQsQSZkYzgBNAAAAAElFTkSuQmCC") + +#---------------------------------------------------------------------- +Stop = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAAAvklE" + "QVRIie3VMQ6DMAwF0G9CEUxMzAwch4NwCEbuxyXoygiBkMQd2KoUM7Cg4tFSnixZ/qH3MODq" + "ii4XHzQO9IiQpvJTIlgLY06gRFgW3/dwDkRHqHMoiqiq4L2ExjGPo+k6TBOUOkK1VnUdtS2M" + "AfMhug+bZfBeQAEkSbB9n+0/6IPeAQ2dKTO0htbi7QcjKoQ6hzx/NQ22TUgpa6ksYe1XmgCg" + "wMe3B4pYe56u64lJATBjnmX0d91n+3+OfgDGM0GplgNFhQAAAABJRU5ErkJggg==") +#---------------------------------------------------------------------- +DeleteKey = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAAEXUlE" + "QVRIibWWX0hUWRzHf+ece4/3TjPjvTMOrqM+lH9KmQhEszCC2OifLRRF+LKwb8X66Eug0UOP" + "Cz22+7JIGwxmEGmTWUahkhapLQSbwQZFm6iDOubM3DPn3nvOPozT6jhKQft7u/f+zuf3O7/v" + "+f3ORf98+ADf2vA3JxaC4q8Mg9DGJeufpUSWBYryxSlhcBy0spLHXQ9VFOXxY/z2LWjal+Qo" + "CaGxGI7HgZDNoQDOwYPqyAianQVV3ZopNY0+eCBDIXfXLrDtzaGuK02THztGYzFIp/PiryPq" + "Oh0f34ax0tq6jRC81fYBgHNRWens30/v3AEorJvUNPXNG/39+98TiR/b2q5evQoAZE0GBdYg" + "xpxIRFRV0f5+qaqA0LrPqorn5oqnpn55966zq8vw+WL37nVdukQp3QoKAMiy7JYW6fHQoSG5" + "VjRCgDHfw4d/VlT81tMTvX69u7v7weBgcmXl0aNHuq5vBc3my48eRcvLyvPnMuuNEGBM+/pw" + "S0t0bOz4oUPfHz4cj8cJIe3t7WPj40KIdVCEEM4zhLAQzqlT6vS0Oj0Nug6aVjQ4iCoqUvX1" + "f01Otp48mclkAIAxVlNToyrKwsJCVrFVqBCCc845dxxHSsk5F0II2+aOk2xtxcPD7uwsfvbM" + "tizrwIHEx4/JVKq6qopzDgBSSk3TAsHgfDyelUsBAErpy5cve27eVAhZSSZTqVS4rGx+fp4Q" + "YgYCLsZFyeQPg4NzlD7dvh2mpqx0+tOnT4ZhSClXz4OUHo8nwxhCaBVq23ZtbW1HR4eu6/di" + "scdPnly5cqWzszNYUnLh/Pkk57C0FIhGeSjUdOYMwXhlefnn9nbGmMfj+Vw9m3OMcTYMzsah" + "lAZMM2CahmH4fL5gMOj3+/1+fyAUCup6eHQULlzQmpu/GxkxQqGKykpK6ceZGSU3JVzXXVhc" + "NAwjqxX+nL8Q2TLajDEhRCaT4bYtAZTbtzMNDSIUshsaXK8XDwxohlFaWjr29KmmaQBACInH" + "44lEorS0dB30c8BwONzU2GhZViQSqY5E0N27orzc2bMHGEOM8SNHUCqljI+3nj37x40bi4uL" + "Pp/PNM1bt26Vh8Nerze7fZQ3+QkhhJBMJkOLi8noqDszw0+fRozlegUDgNbbK5qaTly8qNj2" + "5cuXX7x4caevLxqNGsXFjuMUgK5Wo6hIef1amZxkbW1ICMipvNpUluXv6/u7vv6nrq5XExPb" + "d+z49dq1xsZGy7JWdSsAVVU0N0cHBjLnzoGug+sWcJif9w4NJU6ceL+0VFZSYpqmZVkoNyU2" + "tCkhkE7T+/f58ePS6y1ABADbFmVlqX37vAMDddXVXr+f5U5oIShCAED7+53mZlFejjgvQMw6" + "Mubu3GnX1cneXjfX8ptAVVUdHhaVlc7u3f+JsxnXsuy9e2VJiTIxAWvmHuTXFCG0vCwNAzYE" + "39QwRum01PW1Yubfpl9HBAAh8ogboABfR8ylkvfif/lD+Rcv7QbV/D7nwgAAAABJRU5ErkJg" + "gg==") + +class TimeSlider(wx.Window): + """ + This is the Time Slider Panel. + """ + def __init__(self, parent, slidersize, sliderStartFrame, sliderEndFrame, curFrame): + wx.Window.__init__(self, parent, size = slidersize, style = wx.SUNKEN_BORDER) + + self._mainDialog = wx.GetTopLevelParent(self) + self._mouseIn = False + self.points = [] + self.numbers = [] + + self.curFrame = curFrame + self.sliderStartFrame = sliderStartFrame + self.sliderEndFrame = sliderEndFrame + self.frameNum = self.sliderEndFrame-self.sliderStartFrame+1 + + self.InitBuffer() + + self.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_SIZE, self.OnSize) + self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) + self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) + self.Bind(wx.EVT_MOTION, self.OnMotion) + + def InitBuffer(self): + self.w,self.h = self.GetClientSize() + self.buffer = wx.EmptyBitmap(self.w, self.h) + dc = wx.BufferedDC(wx.ClientDC(self), self.buffer) + self.DrawTimeSlider(dc) + self.DrawNumber(dc) + self.DrawFrame(dc) + self.DrawKeys(dc) + + def SetTimeSliderData(self, sliderStartFrame = 1, sliderEndFrame = 24, curFrame = 0): + self.curFrame = curFrame + self.sliderStartFrame = sliderStartFrame + self.sliderEndFrame = sliderEndFrame + self.frameNum = self.sliderEndFrame-self.sliderStartFrame+1 + + self.points = [] + self.numbers = [] + + self.InitBuffer() + self.Refresh() + + def OnPaint(self, evt): + dc = wx.BufferedPaintDC(self, self.buffer) + + def DrawTimeSlider(self,dc): + + dc.SetBackground(wx.Brush(self.GetBackgroundColour())) + dc.Clear() + + dc.SetPen(wx.BLACK_PEN) + dc.SetBrush(wx.BLACK_BRUSH) + dc.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL)) + + self.unitWidth = self.w/float(self.frameNum) + + if self.frameNum <= 20: + + self.points.append(((float(0),self.h),(float(0),self.h-15))) + + for i in range(1,self.frameNum): + temp = self.points[i-1][0][0]+self.unitWidth + self.points.append(((temp,self.h),(temp,self.h-15))) + + for i in range(self.frameNum): + self.numbers.append(self.sliderStartFrame+i) + + for i in range(self.frameNum): + dc.DrawLine(self.points[i][0][0], self.points[i][0][1], self.points[i][1][0], self.points[i][1][1]) + st = str(self.numbers[i]) + tw,th = dc.GetTextExtent(st) + dc.DrawText(st, self.points[i][0][0]+2, 0.5) + + elif self.frameNum <= 70: + + self.points.append(((self.unitWidth,self.h),(self.unitWidth,self.h-15))) + + for i in range(1,int((self.frameNum+1)/2)): + temp = self.points[i-1][0][0]+2*self.unitWidth + self.points.append(((temp,self.h),(temp,self.h-15))) + + for i in range(1,self.frameNum/2+1): + self.numbers.append(self.sliderStartFrame-1+i*2) + + for i in range(int((self.frameNum+1)/2)): + dc.DrawLine(self.points[i][0][0], self.points[i][0][1], self.points[i][1][0], self.points[i][1][1]) + + for i in range(self.frameNum/2): + st = str(self.numbers[i]) + tw,th = dc.GetTextExtent(st) + dc.DrawText(st, self.points[i][0][0]+2, 0.5) + + elif self.frameNum <= 150: + + self.points.append(((self.unitWidth*4.0,self.h),(self.unitWidth*4.0,self.h-15))) + + for i in range(1,int(self.frameNum/5)): + temp = self.points[i-1][0][0]+5*self.unitWidth + self.points.append(((temp,self.h),(temp,self.h-15))) + + for i in range(1,self.frameNum/5+1): + self.numbers.append(self.sliderStartFrame-1+i*5) + + for i in range(int(self.frameNum/5)): + dc.DrawLine(self.points[i][0][0], self.points[i][0][1], self.points[i][1][0], self.points[i][1][1]) + + for i in range(self.frameNum/5): + st = str(self.numbers[i]) + tw,th = dc.GetTextExtent(st) + dc.DrawText(st, self.points[i][0][0]+2, 0.5) + + elif self.frameNum <= 250: + + self.points.append(((self.unitWidth*9.0,self.h),(self.unitWidth*9.0,self.h-15))) + + for i in range(1,int(self.frameNum/10)): + temp = self.points[i-1][0][0]+10*self.unitWidth + self.points.append(((temp,self.h),(temp,self.h-15))) + + for i in range(1,self.frameNum/10+1): + self.numbers.append(self.sliderStartFrame+i*10) + + for i in range(int(self.frameNum/10)): + dc.DrawLine(self.points[i][0][0], self.points[i][0][1], self.points[i][1][0], self.points[i][1][1]) + + for i in range(self.frameNum/10): + st = str(self.numbers[i]) + tw,th = dc.GetTextExtent(st) + dc.DrawText(st, self.points[i][0][0]+2, 0.5) + + elif self.frameNum <= 1000: + + self.points.append(((self.unitWidth*49.0,self.h),(self.unitWidth*49.0,self.h-15))) + + for i in range(1,int(self.frameNum/50)): + temp = self.points[i-1][0][0]+50*self.unitWidth + self.points.append(((temp,self.h),(temp,self.h-15))) + + for i in range(1,self.frameNum/50+1): + self.numbers.append(self.sliderStartFrame-1+i*50) + + for i in range(int(self.frameNum/50)): + dc.DrawLine(self.points[i][0][0], self.points[i][0][1], self.points[i][1][0], self.points[i][1][1]) + + for i in range(self.frameNum/50): + st = str(self.numbers[i]) + tw,th = dc.GetTextExtent(st) + dc.DrawText(st, self.points[i][0][0]+2, 0.5) + + elif self.frameNum <= 2000: + + self.points.append(((self.unitWidth*99.0,self.h),(self.unitWidth*99.0,self.h-15))) + + for i in range(1,int(self.frameNum/100)): + temp = self.points[i-1][0][0]+100*self.unitWidth + self.points.append(((temp,self.h),(temp,self.h-15))) + + for i in range(1,self.frameNum/100+1): + self.numbers.append(self.sliderStartFrame-1+i*100) + + for i in range(int(self.frameNum/100)): + dc.DrawLine(self.points[i][0][0], self.points[i][0][1], self.points[i][1][0], self.points[i][1][1]) + + for i in range(self.frameNum/100): + st = str(self.numbers[i]) + tw,th = dc.GetTextExtent(st) + dc.DrawText(st, self.points[i][0][0]+2, 0.5) + + elif self.frameNum <= 10000: + + self.points.append(((self.unitWidth*999.0,self.h),(self.unitWidth*999.0,self.h-15))) + + for i in range(1,int(self.frameNum/1000)): + temp = self.points[i-1][0][0]+1000*self.unitWidth + self.points.append(((temp,self.h),(temp,self.h-15))) + + for i in range(1,self.frameNum/1000+1): + self.numbers.append(self.sliderStartFrame-1+i*1000) + + for i in range(int(self.frameNum/1000)): + dc.DrawLine(self.points[i][0][0], self.points[i][0][1], self.points[i][1][0], self.points[i][1][1]) + + for i in range(self.frameNum/1000): + st = str(self.numbers[i]) + tw,th = dc.GetTextExtent(st) + dc.DrawText(st, self.points[i][0][0]+2, 0.5) + + else: + pass + + + def DrawNumber(self, dc): + dc.SetPen(wx.BLACK_PEN) + dc.SetBrush(wx.BLACK_BRUSH) + dc.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL)) + + i = self.curFrame-self.sliderStartFrame + st = str(self.curFrame) + tw,th = dc.GetTextExtent(st) + dc.DrawText(st, float(self.unitWidth)*float(i)+2, self.h-th-0.5) + + def DrawFrame(self, dc): + i = self.curFrame-self.sliderStartFrame + pos = float(self.unitWidth)*float(i) + self.curRect = wx.Rect(pos, float(0), self.unitWidth, self.h) + + oldPen, oldBrush, oldMode = dc.GetPen(), dc.GetBrush(), dc.GetLogicalFunction() + + gray = wx.Colour(200, 200, 200) + grayPen = wx.Pen(gray) + grayBrush = wx.Brush(gray) + dc.SetPen(grayPen) + dc.SetBrush(grayBrush) + dc.SetLogicalFunction(wx.XOR) + dc.DrawRectangleRect(self.curRect) + + dc.SetPen(oldPen) + dc.SetBrush(oldBrush) + dc.SetLogicalFunction(oldMode) + + def DrawKeys(self, dc): + if len(self._mainDialog.keys) != 0: + for key in self._mainDialog.keys: + keyFrame = key + i = keyFrame-self.sliderStartFrame + pos = float(self.unitWidth)*float(i) + + oldPen, oldBrush, oldMode = dc.GetPen(), dc.GetBrush(), dc.GetLogicalFunction() + + dc.SetPen(wx.Pen("red")) + dc.SetBrush(wx.Brush("red")) + dc.SetLogicalFunction(wx.AND) + dc.DrawLine(pos, float(0), pos, self.h) + + dc.SetPen(oldPen) + dc.SetBrush(oldBrush) + dc.SetLogicalFunction(oldMode) + else: + pass + + def OnSize(self,evt): + self.InitBuffer() + + def OnLeftDown(self,evt): + point = (evt.GetX(), evt.GetY()) + + if point[1]>= float(0) and point[1]<= (float(self.h)-2.0): + if point[0]>= float(0) and point[0]<= float(self.w): + self._mouseIn = True + + if self._mouseIn: + self.CaptureMouse() + self.curFrame = int(float(point[0])/self.unitWidth)+self.sliderStartFrame + self._mainDialog.curFrame = self.curFrame + self._mainDialog.curFrameSpin.SetValue(self.curFrame) + self._mainDialog.OnAnimation(self.curFrame) + self.SetTimeSliderData(self.sliderStartFrame, self.sliderEndFrame, self.curFrame) + + def OnLeftUp(self,evt): + if self.GetCapture(): + self.ReleaseMouse() + self._mouseIn = False + + def OnMotion(self,evt): + self._mouseIn = False + if evt.Dragging() and evt.LeftIsDown(): + point = (evt.GetX(), evt.GetY()) + if point[1]>= float(0) and point[1]<= (float(self.h)-2.0): + if point[0]>= float(0) and point[0]<= float(self.w): + self._mouseIn = True + + if self._mouseIn: + self.curFrame = int(float(point[0])/self.unitWidth)+self.sliderStartFrame + self._mainDialog.curFrame = self.curFrame + self._mainDialog.curFrameSpin.SetValue(self.curFrame) + self._mainDialog.OnAnimation(self.curFrame) + self.SetTimeSliderData(self.sliderStartFrame, self.sliderEndFrame, self.curFrame) + + evt.Skip() + self._mouseIn = False + +class TimeRange(wx.Window): + """ + This is the Time Range Panel. + """ + def __init__(self, parent, rangesize, startFrame, endFrame, sliderStartFrame, sliderEndFrame): + wx.Window.__init__(self, parent, size = rangesize, style = wx.SUNKEN_BORDER) + + self._mainDialog = wx.GetTopLevelParent(self) + + self._mouseIn = False + + self.startFrame = startFrame + self.endFrame = endFrame + self.sliderStartFrame = sliderStartFrame + self.sliderEndFrame = sliderEndFrame + self.frameNum = self.endFrame-self.startFrame+1 + + self.InitBuffer() + + self.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_SIZE, self.OnSize) + self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) + self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) + self.Bind(wx.EVT_MOTION, self.OnMotion) + + def InitBuffer(self): + self.w,self.h = self.GetClientSize() + self.buffer = wx.EmptyBitmap(self.w, self.h) + dc = wx.BufferedDC(wx.ClientDC(self), self.buffer) + self.DrawTimeRange(dc) + + def SetTimeRangeData(self, startFrame = 1, endFrame = 24, sliderStartFrame = 1, sliderEndFrame = 24): + self.startFrame = startFrame + self.endFrame = endFrame + self.sliderStartFrame = sliderStartFrame + self.sliderEndFrame = sliderEndFrame + self.frameNum = self.endFrame-self.startFrame+1 + + self.InitBuffer() + self.Refresh() + + def OnPaint(self, evt): + dc = wx.BufferedPaintDC(self, self.buffer) + + def DrawTimeRange(self,dc): + dc.SetBackground(wx.Brush(wx.Colour(150, 150, 150))) + dc.Clear() + + dc.SetPen(wx.Pen(self.GetBackgroundColour())) + dc.SetBrush(wx.Brush(self.GetBackgroundColour())) + + self.unitWidth = (self.w-6.0)/float(self.frameNum) + self.rangePosX = 3.0+(float(self.sliderStartFrame-self.startFrame))*self.unitWidth + self.rangePosY = 2.0 + self.rangeWidth = float(self.sliderEndFrame-self.sliderStartFrame+1)*self.unitWidth + self.rangeHeight = self.h-4.0 + self.curRect = wx.Rect(self.rangePosX, self.rangePosY, self.rangeWidth, self.rangeHeight) + + dc.DrawRoundedRectangleRect(self.curRect, radius = 2) + + def OnSize(self,evt): + self.InitBuffer() + + def OnLeftDown(self,evt): + point = (evt.GetX(), evt.GetY()) + self.pos = 0 + + if point[1]>= self.rangePosY and point[1]<= self.rangePosY+self.rangeHeight: + if point[0]>= self.rangePosX and point[0]<= self.rangePosX+self.rangeWidth: + self._mouseIn = True + + if self._mouseIn: + self.CaptureMouse() + self.pos = point + + def OnLeftUp(self,evt): + if self.GetCapture(): + self.ReleaseMouse() + self._mouseIn = False + + def OnMotion(self,evt): + self._mouseIn = False + if evt.Dragging() and evt.LeftIsDown(): + newPos = (evt.GetX(), evt.GetY()) + if newPos[1]>= self.rangePosY and newPos[1]<= self.rangePosY+self.rangeHeight: + if newPos[0]>= self.rangePosX and newPos[0]<= self.rangePosX+self.rangeWidth: + self._mouseIn = True + + if self._mouseIn: + if newPos[0] == self.pos[0]: + evt.Skip() + self._mouseIn = False + if newPos[0] > self.pos[0]: + if float(newPos[0]-self.pos[0]) >= self.unitWidth: + if self.sliderEndFrame < self.endFrame: + self.sliderStartFrame += 1 + self.sliderEndFrame += 1 + self.SetTimeRangeData(self.startFrame, self.endFrame, self.sliderStartFrame, self.sliderEndFrame) + self.MainPanelUpdate() + self.pos = newPos + evt.Skip() + self._mouseIn = False + if newPos[0] < self.pos[0]: + if float(self.pos[0]-newPos[0]) >= self.unitWidth: + if self.sliderStartFrame > self.startFrame: + self.sliderStartFrame -=1 + self.sliderEndFrame -=1 + self.SetTimeRangeData(self.startFrame, self.endFrame, self.sliderStartFrame, self.sliderEndFrame) + self.MainPanelUpdate() + self.pos = newPos + evt.Skip() + self._mouseIn = False + evt.Skip() + self._mouseIn = False + + def MainPanelUpdate(self): + self._mainDialog.sliderStartFrame = self.sliderStartFrame + self._mainDialog.sliderEndFrame = self.sliderEndFrame + self._mainDialog.timeSliderStartSpin.SetValue(self.sliderStartFrame) + self._mainDialog.timeSliderEndSpin.SetValue(self.sliderEndFrame) + self._mainDialog.timeSlider.SetTimeSliderData(self._mainDialog.sliderStartFrame, self._mainDialog.sliderEndFrame, self._mainDialog.curFrame) + + +class AnimControlUI(wx.Dialog): + """ + This is the Animation Control main class implementation. + """ + def __init__(self, parent, editor): + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Animation Controller", + pos=wx.DefaultPosition, size=(920, 110)) + + self.editor = editor + self._initOver = False + + self.parallel = [] + if self.editor.animMgr.keyFramesInfo != []: + self.editor.animMgr.generateKeyFrames() + self.keys = self.editor.animMgr.keyFrames + self.editor.objectMgr.findActors(render) + self.editor.objectMgr.findNodes(render) + + self.prePlay = False + self.play = False + self.stop = True + + self.curFrame = 1 + self.startFrame = 1 + self.sliderStartFrame = 1 + self.endFrame = 24 + self.sliderEndFrame = 24 + + self.mainPanel1 = wx.Panel(self, -1) + + self.timeSlider = TimeSlider(self.mainPanel1, wx.Size(560, 35), self.sliderStartFrame, self.sliderEndFrame, self.curFrame) + self.curFrameSpin = wx.SpinCtrl(self.mainPanel1, -1, "",size = (70,25),min=self.startFrame, max=self.endFrame) + + bmpFirstFrame = FirstFrame.GetBitmap() + bmpPreFrame = PreFrame.GetBitmap() + bmpPreKeyFrame = PreKeyFrame.GetBitmap() + self.bmpPrePlay = PrePlay.GetBitmap() + self.bmpPlay = Play.GetBitmap() + bmpNextKeyFrame = NextKeyFrame.GetBitmap() + bmpNextFrame = NextFrame.GetBitmap() + bmpLastFrame = LastFrame.GetBitmap() + bmpKey = Key.GetBitmap() + self.bmpStop = Stop.GetBitmap() + bmpDeleteKey = DeleteKey.GetBitmap() + + self.buttonFirstFrame = wx.BitmapButton(self.mainPanel1, -1, bmpFirstFrame, size = (30,30),style = wx.BU_AUTODRAW) + self.buttonPreFrame = wx.BitmapButton(self.mainPanel1, -1, bmpPreFrame, size = (30,30),style = wx.BU_AUTODRAW) + self.buttonPreKeyFrame = wx.BitmapButton(self.mainPanel1, -1, bmpPreKeyFrame, size = (30,30),style = wx.BU_AUTODRAW) + self.buttonPrePlay = wx.BitmapButton(self.mainPanel1, -1, self.bmpPrePlay, size = (30,30),style = wx.BU_AUTODRAW) + self.buttonPlay = wx.BitmapButton(self.mainPanel1, -1, self.bmpPlay, size = (30,30),style = wx.BU_AUTODRAW) + self.buttonNextKeyFrame = wx.BitmapButton(self.mainPanel1, -1, bmpNextKeyFrame, size = (30,30),style = wx.BU_AUTODRAW) + self.buttonNextFrame = wx.BitmapButton(self.mainPanel1, -1, bmpNextFrame, size = (30,30),style = wx.BU_AUTODRAW) + self.buttonLastFrame = wx.BitmapButton(self.mainPanel1, -1, bmpLastFrame, size = (30,30),style = wx.BU_AUTODRAW) + + self.mainPanel2 = wx.Panel(self, -1) + + self.timeStartSpin = wx.SpinCtrl(self.mainPanel2, -1, "",size = (70,25),min=0, max=self.sliderEndFrame) + self.timeSliderStartSpin = wx.SpinCtrl(self.mainPanel2, -1, "",size = (70,25),min=self.startFrame, max=self.sliderEndFrame) + self.timeRange = TimeRange(self.mainPanel2, wx.Size(450, 25), self.startFrame, self.endFrame, self.sliderStartFrame, self.sliderEndFrame) + self.timeSliderEndSpin = wx.SpinCtrl(self.mainPanel2, -1, "",size = (70,25),min=self.sliderStartFrame, max=self.endFrame) + self.timeEndSpin = wx.SpinCtrl(self.mainPanel2, -1, "",size = (70,25),min=self.sliderStartFrame, max=10000) + self.buttonDeleteKey = wx.BitmapButton(self.mainPanel2, -1, bmpDeleteKey, size = (30,30),style = wx.BU_AUTODRAW) + + self.SetProperties() + self.DoLayout() + + self.Bind(wx.EVT_SPINCTRL, self.OnCurrentTime, self.curFrameSpin) + + self.Bind(wx.EVT_BUTTON, self.OnFirstFrame, self.buttonFirstFrame) + self.Bind(wx.EVT_BUTTON, self.OnPreFrame, self.buttonPreFrame) + self.Bind(wx.EVT_BUTTON, self.OnPreKeyFrame, self.buttonPreKeyFrame) + self.Bind(wx.EVT_BUTTON, self.OnPrePlay, self.buttonPrePlay) + self.Bind(wx.EVT_BUTTON, self.OnPlay, self.buttonPlay) + self.Bind(wx.EVT_BUTTON, self.OnNextKeyFrame, self.buttonNextKeyFrame) + self.Bind(wx.EVT_BUTTON, self.OnNextFrame, self.buttonNextFrame) + self.Bind(wx.EVT_BUTTON, self.OnLastFrame, self.buttonLastFrame) + + self.Bind(wx.EVT_SPINCTRL, self.OnTimeStartSpin, self.timeStartSpin) + self.Bind(wx.EVT_SPINCTRL, self.OnTimeSliderStartSpin, self.timeSliderStartSpin) + self.Bind(wx.EVT_SPINCTRL, self.OnTimeSliderEndSpin, self.timeSliderEndSpin) + self.Bind(wx.EVT_SPINCTRL, self.OnTimeEndSpin, self.timeEndSpin) + + self.Bind(wx.EVT_BUTTON, self.OnDeleteKey, self.buttonDeleteKey) + + self.Bind(wx.EVT_CLOSE, self.OnExit) + + self.OnPropKey() + self.OnAnimation(self.curFrame) + + self.timeUnit = float(1)/float(24)*float(1000) + self.timer = wx.Timer(self) + self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer) + + self._initOver = True + + def SetProperties(self): + self.curFrameSpin.SetValue(self.curFrame) + self.timeStartSpin.SetValue(self.startFrame) + self.timeSliderStartSpin.SetValue(self.sliderStartFrame) + self.timeSliderEndSpin.SetValue(self.sliderEndFrame) + self.timeEndSpin.SetValue(self.endFrame) + + def DoLayout(self): + dialogSizer = wx.BoxSizer(wx.VERTICAL) + mainSizer1 = wx.FlexGridSizer(1, 10, 0, 0) + mainSizer2 = wx.FlexGridSizer(1, 6, 0, 0) + + mainSizer1.Add(self.timeSlider, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) + mainSizer1.Add(self.curFrameSpin, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 3) + + mainSizer1.Add(self.buttonFirstFrame, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 3) + mainSizer1.Add(self.buttonPreFrame, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 3) + mainSizer1.Add(self.buttonPreKeyFrame, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 3) + mainSizer1.Add(self.buttonPrePlay, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 3) + mainSizer1.Add(self.buttonPlay, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 3) + mainSizer1.Add(self.buttonNextKeyFrame, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 3) + mainSizer1.Add(self.buttonNextFrame, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 3) + mainSizer1.Add(self.buttonLastFrame, 0, wx.ALIGN_CENTER_VERTICAL) + + mainSizer2.Add(self.timeStartSpin, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5) + mainSizer2.Add(self.timeSliderStartSpin, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5) + mainSizer2.Add(self.timeRange, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5) + mainSizer2.Add(self.timeSliderEndSpin, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5) + mainSizer2.Add(self.timeEndSpin, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5) + mainSizer2.Add(self.buttonDeleteKey, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 3) + + self.mainPanel1.SetSizerAndFit(mainSizer1) + self.mainPanel2.SetSizerAndFit(mainSizer2) + + dialogSizer.Add(self.mainPanel1, 1, wx.ALIGN_CENTER_VERTICAL|wx.TOP, 5) + dialogSizer.Add(self.mainPanel2, 1, wx.ALIGN_CENTER_VERTICAL|wx.TOP, 7) + + self.SetSizer(dialogSizer) + self.Layout() + + self.dialogSizer = dialogSizer + + def OnCurrentTime(self, evt): + self.curFrame = evt.GetInt() + self.timeSlider.SetTimeSliderData(self.sliderStartFrame, self.sliderEndFrame, self.curFrame) + self.OnAnimation(self.curFrame) + + def OnControl(self): + self.curFrameSpin.SetValue(self.curFrame) + self.timeSlider.SetTimeSliderData(self.sliderStartFrame, self.sliderEndFrame, self.curFrame) + self.OnAnimation(self.curFrame) + + def OnFirstFrame(self,evt): + self.curFrame = self.sliderStartFrame + self.OnControl() + + def OnPreFrame(self,evt): + if self.curFrame-1 >= self.startFrame: + self.curFrame -= 1 + self.OnControl() + else: + evt.Skip() + + def sortKey(self): + for i in range(0, len(self.keys)-1): + for j in range(i+1, len(self.keys)): + if self.keys[i]>self.keys[j]: + temp = self.keys[i] + self.keys[i] = self.keys[j] + self.keys[j] = temp + + def OnPreKeyFrame(self,evt): + self.sortKey() + if self.curFrame <= self.keys[0] or self.curFrame > self.keys[len(self.keys)-1]: + self.curFrame = self.keys[len(self.keys)-1] + else: + for i in range(1, len(self.keys)): + if self.curFrame <= self.keys[i] and self.curFrame > self.keys[i-1]: + self.curFrame = self.keys[i-1] + break + self.OnControl() + + def OnTimer(self,evt): + if self.prePlay == True and self.stop == False and self.play == False: + if self.curFrame-1>=self.sliderStartFrame: + self.curFrame -= 1 + self.OnControl() + elif self.curFrame == self.sliderStartFrame: + self.curFrame = self.sliderEndFrame + self.OnControl() + + if self.play == True and self.stop == False and self.prePlay == False: + if self.curFrame+1<=self.sliderEndFrame: + self.curFrame += 1 + self.OnControl() + elif self.curFrame == self.sliderEndFrame: + self.curFrame = self.sliderStartFrame + self.OnControl() + + def OnPrePlay(self,evt): + if self.prePlay == False and self.stop == True and self.play == False: + self.buttonPrePlay = wx.BitmapButton(self.mainPanel1, -1, self.bmpStop, size = (30,30),style = wx.BU_AUTODRAW) + self.DoLayout() + self.prePlay = True + self.stop = False + self.timer.Start(self.timeUnit) + evt.Skip() + + elif self.prePlay == True and self.stop == False and self.play == False: + self.buttonPrePlay = wx.BitmapButton(self.mainPanel1, -1, self.bmpPrePlay, size = (30,30),style = wx.BU_AUTODRAW) + self.DoLayout() + self.prePlay = False + self.stop = True + self.timer.Stop() + evt.Skip() + else: + evt.Skip() + + def OnPlay(self,evt): + if self.play == False and self.stop == True and self.prePlay == False: + self.buttonPlay = wx.BitmapButton(self.mainPanel1, -1, self.bmpStop, size = (30,30),style = wx.BU_AUTODRAW) + self.DoLayout() + self.play = True + self.stop = False + self.timer.Start(self.timeUnit) + evt.Skip() + + elif self.play == True and self.stop == False and self.prePlay == False: + self.buttonPlay = wx.BitmapButton(self.mainPanel1, -1, self.bmpPlay, size = (30,30),style = wx.BU_AUTODRAW) + self.DoLayout() + self.play = False + self.stop = True + self.timer.Stop() + evt.Skip() + else: + evt.Skip() + + def OnNextKeyFrame(self,evt): + self.sortKey() + if self.curFrame < self.keys[0] or self.curFrame >= self.keys[len(self.keys)-1]: + self.curFrame = self.keys[0] + else: + for i in range(0, len(self.keys)-1): + if self.curFrame >= self.keys[i] and self.curFrame < self.keys[i+1]: + self.curFrame = self.keys[i+1] + break + self.OnControl() + + def OnNextFrame(self,evt): + if self.curFrame+1 <= self.endFrame: + self.curFrame += 1 + self.OnControl() + else: + evt.Skip() + + def OnLastFrame(self,evt): + self.curFrame = self.sliderEndFrame + self.OnControl() + + def OnTime(self): + preFrame = self.curFrame + self.curFrameSpin.SetRange(self.startFrame,self.endFrame) + self.curFrame = preFrame + self.timeSlider.SetTimeSliderData(self.sliderStartFrame, self.sliderEndFrame, self.curFrame) + self.timeRange.SetTimeRangeData(self.startFrame, self.endFrame, self.sliderStartFrame, self.sliderEndFrame) + self.parallel = self.editor.animMgr.createParallel(self.startFrame,self.endFrame) + + def OnTimeStartSpin(self,evt): + self.startFrame = evt.GetInt() + self.timeSliderStartSpin.SetRange(self.startFrame, self.sliderEndFrame) + if self.startFrame >= self.sliderStartFrame: + self.sliderStartFrame = self.startFrame + self.timeSliderStartSpin.SetValue(self.sliderStartFrame) + self.OnTime() + else: + self.OnTime() + + def OnTimeSliderStartSpin(self,evt): + self.sliderStartFrame = evt.GetInt() + self.timeEndSpin.SetRange(self.sliderStartFrame, 10000) + self.OnTime() + + def OnTimeSliderEndSpin(self,evt): + self.sliderEndFrame = evt.GetInt() + self.timeStartSpin.SetRange(0, self.sliderEndFrame) + self.OnTime() + + def OnTimeEndSpin(self,evt): + self.endFrame = evt.GetInt() + self.timeSliderEndSpin.SetRange(self.sliderStartFrame, self.endFrame) + if self.endFrame <= self.sliderEndFrame: + self.sliderEndFrame = self.endFrame + self.timeSliderEndSpin.SetValue(self.sliderEndFrame) + self.OnTime() + else: + self.OnTime() + + def OnDeleteKey(self,evt): + for i in range(0,len(self.keys)): + if self.curFrame == self.keys[i]: + del self.keys[i] + break + + for j in self.editor.animMgr.keyFramesInfo.keys(): + for k in range(0,len(self.editor.animMgr.keyFramesInfo[j])): + if self.curFrame == self.editor.animMgr.keyFramesInfo[j][k][0]: + del self.editor.animMgr.keyFramesInfo[j][k] + break + + for l in self.editor.animMgr.keyFramesInfo.keys(): + if len(self.editor.animMgr.keyFramesInfo[l]) == 0: + del self.editor.animMgr.keyFramesInfo[l] + + self.OnPropKey() + self.OnAnimation(self.curFrame) + + def OnPropKey(self): + self.parallel = self.editor.animMgr.createParallel(self.startFrame, self.endFrame) + self.timeSlider.SetTimeSliderData(self.sliderStartFrame, self.sliderEndFrame, self.curFrame) + + + def OnAnimation(self, curFrame): + time = float(curFrame-1)/float(24) + self.parallel.setT(time) + if self.editor.GRAPH_EDITOR == True: + self.editor.ui.graphEditorUI.curFrameChange() + + def OnExit(self,evt): + for actor in self.editor.objectMgr.Actor: + actorAnim = os.path.basename(actor[OG.OBJ_ANIM]) + actor[OG.OBJ_NP].loop(actorAnim) + self.parallel = None + self.Destroy() + self.editor.ui.editAnimMenuItem.Check(False) + self.editor.mode = self.editor.BASE_MODE + + + + + + + \ No newline at end of file diff --git a/direct/src/leveleditor/AnimGlobals.py b/direct/src/leveleditor/AnimGlobals.py new file mode 100755 index 0000000000..4decd4d5af --- /dev/null +++ b/direct/src/leveleditor/AnimGlobals.py @@ -0,0 +1,53 @@ +""" +This contains data structure and constants related with animation handling. +""" + +# index for keyFramesInfo list structure +# data strucrure: {[nodeUID, propertyName] : [frameNum, +# value, +# [inSlopeX, inSlopeY], +# [outSlopeX, outSlopeY]]} +UID = 0 +PROP_NAME = 1 + +FRAME = 0 +VALUE = 1 +INSLOPE = 2 +OUTSLOPE = 3 + +# index for curveAnimation list structure +# data strucrure: {[nodeUID, curveUID] : [nodeUID, +# curveUID, +# time]} +NODE = 0 +CURVE = 1 + +NODE = 0 +CURVE = 1 +TIME = 2 + +# index for animation curve generation information list structure(self.X, self.Y, self.Z in GraphEditorUI) +# data structur: [key, +# i , +# [[keyFrameX, keyFrameY], keyFrame_select], +# [[inTangentX, inTangentY], inTangent_select], +# [[outTangentX, outTangentY], outTangent_select], +# [inSlopeX, inSlopeY], +# [outSlopeX, outSlopeY]] + +KEY = 0 +I = 1 +KEYFRAME = 2 +IN_TANGENT = 3 +OUT_TANGENT = 4 +IN_SLOPE = 5 +OUT_SLOPE = 6 + +LOCAL_VALUE = 0 +SELECT = 1 + +#index for coordinate +X = 0 +Y = 1 +Z = 2 + diff --git a/direct/src/leveleditor/AnimMgr.py b/direct/src/leveleditor/AnimMgr.py new file mode 100755 index 0000000000..ffa69a6b16 --- /dev/null +++ b/direct/src/leveleditor/AnimMgr.py @@ -0,0 +1,10 @@ +""" +Defines AnimMgr +""" +from AnimMgrBase import * + +class AnimMgr(AnimMgrBase): + """ Animation will create, manage, update animations in the scene """ + + def __init__(self, editor): + AnimMgrBase.__init__(self, editor) diff --git a/direct/src/leveleditor/AnimMgrBase.py b/direct/src/leveleditor/AnimMgrBase.py new file mode 100755 index 0000000000..157836337c --- /dev/null +++ b/direct/src/leveleditor/AnimMgrBase.py @@ -0,0 +1,367 @@ +""" +Defines AnimMgrBase +""" + +import os, wx, math + +from direct.interval.IntervalGlobal import * +from panda3d.core import VBase3,VBase4 +import ObjectGlobals as OG +import AnimGlobals as AG + +class AnimMgrBase: + """ AnimMgr will create, manage, update animations in the scene """ + + def __init__(self, editor): + self.editor = editor + self.graphEditorCounter = 0 + + self.keyFramesInfo = {} + self.curveAnimation = {} + + #normal properties + self.lerpFuncs={ + 'H' : self.lerpFuncH, + 'P' : self.lerpFuncP, + 'R' : self.lerpFuncR, + 'SX' : self.lerpFuncSX, + 'SY' : self.lerpFuncSY, + 'SZ' : self.lerpFuncSZ, + 'CR' : self.lerpFuncCR, + 'CG' : self.lerpFuncCG, + 'CB' : self.lerpFuncCB, + 'CA' : self.lerpFuncCA + } + + #Properties which has animation curves + self.curveLerpFuncs={ + 'X' : [ self.lerpFuncX, self.lerpCurveFuncX ], + 'Y' : [ self.lerpFuncY, self.lerpCurveFuncY ], + 'Z' : [ self.lerpFuncZ, self.lerpCurveFuncZ ] + } + + def reset(self): + self.keyFramesInfo = {} + self.curveAnimation = {} + + def generateKeyFrames(self): + #generate keyFrame list + self.keyFrames = [] + for property in self.keyFramesInfo.keys(): + for frameInfo in self.keyFramesInfo[property]: + frame = frameInfo[AG.FRAME] + exist = False + for keyFrame in self.keyFrames: + if frame == keyFrame: + exist = True + break + if exist == False: + self.keyFrames.append(frame) + + def generateSlope(self, list): + #generate handler slope of every keyframe for animation curve + listLen = len(list) + if listLen == 2: + slope =[float(list[1][AG.FRAME]-list[0][AG.FRAME]),(float(list[1][AG.VALUE])-float(list[0][AG.VALUE]))] + list[0][AG.INSLOPE] = slope + list[1][AG.INSLOPE] = slope + list[0][AG.OUTSLOPE] = list[0][AG.INSLOPE] + list[1][AG.OUTSLOPE] = list[1][AG.INSLOPE] + return + + if listLen >= 3: + list[0][AG.INSLOPE] = [float(list[1][AG.FRAME] - list[0][AG.FRAME]),(float(list[1][AG.VALUE]) - float(list[0][AG.VALUE]))] + list[0][AG.OUTSLOPE] = list[0][AG.INSLOPE] + for i in range(1, listLen-1): + list[i][AG.INSLOPE] = [float(list[i+1][AG.FRAME] - list[i-1][AG.FRAME]),(float(list[i+1][AG.VALUE]) - float(list[i-1][AG.VALUE]))] + list[i][AG.OUTSLOPE] = list[i][AG.INSLOPE] + list[listLen-1][AG.INSLOPE] = [float(list[listLen-1][AG.FRAME] - list[listLen-2][AG.FRAME]),(float(list[listLen-1][AG.VALUE]) - float(list[listLen-2][AG.VALUE]))] + list[listLen-1][AG.OUTSLOPE] = list[listLen-1][AG.INSLOPE] + return + + def removeAnimInfo(self, uid): + for property in self.keyFramesInfo.keys(): + if property[AG.UID] == uid: + del self.keyFramesInfo[property] + self.generateKeyFrames() + if self.editor.mode == self.editor.ANIM_MODE: + self.editor.ui.animUI.OnPropKey() + + def singleCurveAnimation(self, nodePath, curve, time): + rope = curve[OG.OBJ_NP] + self.points = rope.getPoints(time) + self.hprs = [] + temp = render.attachNewNode("temp") + temp.setHpr(0,0,0) + for i in range(len(self.points)-1): + temp.setPos(self.points[i]) + temp.lookAt(self.points[i+1]) + hpr = temp.getHpr() + ## self.hprs.append(hpr) + self.hprs.append(VBase3(hpr[0]+180,hpr[1],hpr[2])) + self.hprs.append(self.hprs[len(self.points)-2]) + + curveSequenceName = str(nodePath[OG.OBJ_UID])+' '+str(curve[OG.OBJ_UID])+' '+str(time) + self.curveSequence = Sequence(name = curveSequenceName) + + for i in range(len(self.points)-1): + myLerp = LerpPosHprInterval(nodePath[OG.OBJ_NP], float(1)/float(24), self.points[i+1], self.hprs[i+1], self.points[i], self.hprs[i]) + self.curveSequence.append(myLerp) + + return self.curveSequence + + def createParallel(self, startFrame, endFrame): + self.parallel = [] + self.parallel = Parallel(name="Current Parallel") + + self.createCurveAnimation(self.parallel) + self.createActorAnimation(self.parallel, startFrame, endFrame) + self.createKeyFrameAnimation(self.parallel, startFrame, endFrame) + self.createCurveKeyFrameAnimation(self.parallel, startFrame, endFrame) + + return self.parallel + + def createCurveAnimation(self, parallel): + for key in self.curveAnimation: + curveInfo = self.curveAnimation[key] + nodePath = self.editor.objectMgr.findObjectById(curveInfo[AG.NODE]) + curve = self.editor.objectMgr.findObjectById(curveInfo[AG.CURVE]) + time = curveInfo[AG.TIME] + sequence = self.singleCurveAnimation(nodePath, curve, time) + parallel.append(sequence) + + def createActorAnimation(self, parallel, startFrame, endFrame): + self.editor.objectMgr.findActors(render) + for actor in self.editor.objectMgr.Actor: + actorAnim = os.path.basename(actor[OG.OBJ_ANIM]) + myInterval = ActorInterval(actor[OG.OBJ_NP], actorAnim, loop=1, duration = float(endFrame-startFrame+1)/float(24)) + parallel.append(myInterval) + + def createKeyFrameAnimation(self, parallel, startFrame, endFrame): + #generate key frame animation for normal property + self.editor.objectMgr.findNodes(render) + for node in self.editor.objectMgr.Nodes: + for property in self.keyFramesInfo.keys(): + if property[AG.UID] == node[OG.OBJ_UID] and property[AG.PROP_NAME] != 'X' and property[AG.PROP_NAME] != 'Y' and property[AG.PROP_NAME] != 'Z': + mysequence = Sequence(name = node[OG.OBJ_UID]) + keyFramesInfo = self.keyFramesInfo[property] + if len(keyFramesInfo) == 1: + myLerp = LerpFunc(self.lerpFuncs[property[AG.PROP_NAME]],fromData=float(keyFramesInfo[0][AG.VALUE]),toData=float(keyFramesInfo[0][AG.VALUE]),duration = float(endFrame-startFrame)/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]]) + mysequence.append(myLerp) + parallel.append(mysequence) + + if len(keyFramesInfo) != 1: + myLerp = LerpFunc(self.lerpFuncs[property[AG.PROP_NAME]],fromData=float(keyFramesInfo[0][AG.VALUE]),toData=float(keyFramesInfo[0][AG.VALUE]),duration = float(keyFramesInfo[0][AG.FRAME]-startFrame)/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]]) + mysequence.append(myLerp) + + for key in range(0,len(keyFramesInfo)-1): + myLerp = LerpFunc(self.lerpFuncs[property[AG.PROP_NAME]],fromData=float(keyFramesInfo[key][AG.VALUE]),toData=float(keyFramesInfo[key+1][AG.VALUE]),duration = float(keyFramesInfo[key+1][AG.FRAME]-keyFramesInfo[key][AG.FRAME])/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]]) + mysequence.append(myLerp) + + myLerp = LerpFunc(self.lerpFuncs[property[AG.PROP_NAME]],fromData=float(keyFramesInfo[len(keyFramesInfo)-1][AG.VALUE]),toData=float(keyFramesInfo[len(keyFramesInfo)-1][AG.VALUE]),duration = float(endFrame-keyFramesInfo[len(keyFramesInfo)-1][AG.FRAME])/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]]) + mysequence.append(myLerp) + parallel.append(mysequence) + + def createCurveKeyFrameAnimation(self, parallel, startFrame, endFrame): + #generate key frame animation for the property which is controled by animation curve + self.editor.objectMgr.findNodes(render) + for node in self.editor.objectMgr.Nodes: + for property in self.keyFramesInfo.keys(): + if property[AG.UID] == node[OG.OBJ_UID]: + if property[AG.PROP_NAME] == 'X' or property[AG.PROP_NAME] == 'Y' or property[AG.PROP_NAME] == 'Z': + mysequence = Sequence(name = node[OG.OBJ_UID]) + keyFramesInfo = self.keyFramesInfo[property] + if len(keyFramesInfo) == 1: + myLerp = LerpFunc(self.curveLerpFuncs[property[AG.PROP_NAME]][0],fromData=float(keyFramesInfo[0][AG.VALUE]),toData=float(keyFramesInfo[0][AG.VALUE]),duration = float(endFrame-startFrame)/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]]) + mysequence.append(myLerp) + parallel.append(mysequence) + + if len(keyFramesInfo) == 2: + myLerp = LerpFunc(self.curveLerpFuncs[property[AG.PROP_NAME]][0],fromData=float(keyFramesInfo[0][AG.VALUE]),toData=float(keyFramesInfo[0][AG.VALUE]),duration = float(keyFramesInfo[0][AG.FRAME]-startFrame)/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]]) + mysequence.append(myLerp) + + for key in range(0,len(keyFramesInfo)-1): + self.keyFrameInfoForSingleLerp = keyFramesInfo + self.keyInfoForSingleLerp = key + myLerp = LerpFunc(self.curveLerpFuncs[property[AG.PROP_NAME]][0],fromData=float(keyFramesInfo[key][AG.VALUE]),toData=float(keyFramesInfo[key+1][AG.VALUE]),duration = float(keyFramesInfo[key+1][AG.FRAME]-keyFramesInfo[key][AG.FRAME])/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]]) + mysequence.append(myLerp) + + myLerp = LerpFunc(self.curveLerpFuncs[property[AG.PROP_NAME]][0],fromData=float(keyFramesInfo[len(keyFramesInfo)-1][AG.VALUE]),toData=float(keyFramesInfo[len(keyFramesInfo)-1][AG.VALUE]),duration = float(endFrame-keyFramesInfo[len(keyFramesInfo)-1][AG.FRAME])/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]]) + mysequence.append(myLerp) + parallel.append(mysequence) + + if len(keyFramesInfo) > 2: + myLerp = LerpFunc(self.curveLerpFuncs[property[AG.PROP_NAME]][0],fromData=float(keyFramesInfo[0][AG.VALUE]),toData=float(keyFramesInfo[0][1]),duration = float(keyFramesInfo[0][AG.FRAME]-startFrame)/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]]) + mysequence.append(myLerp) + + for key in range(0,len(keyFramesInfo)-1): + myLerp = LerpFunc(self.curveLerpFuncs[property[AG.PROP_NAME]][1],fromData=float(keyFramesInfo[key][AG.FRAME]),toData=float(keyFramesInfo[key+1][AG.FRAME]),duration = float(keyFramesInfo[key+1][AG.FRAME]-keyFramesInfo[key][AG.FRAME])/float(24),blendType = 'noBlend',extraArgs = [[node[OG.OBJ_NP], keyFramesInfo, key]]) + mysequence.append(myLerp) + + myLerp = LerpFunc(self.curveLerpFuncs[property[AG.PROP_NAME]][0],fromData=float(keyFramesInfo[len(keyFramesInfo)-1][AG.VALUE]),toData=float(keyFramesInfo[len(keyFramesInfo)-1][AG.VALUE]),duration = float(endFrame-keyFramesInfo[len(keyFramesInfo)-1][AG.FRAME])/float(24),blendType = 'noBlend',extraArgs = [node[OG.OBJ_NP]]) + mysequence.append(myLerp) + parallel.append(mysequence) + + def getPos(self, x, list, i): + #get the value from animation curve + x1 = float(list[i][AG.FRAME]) + y1 = float(list[i][AG.VALUE]) + + x4 = float(list[i+1][AG.FRAME]) + y4 = float(list[i+1][AG.VALUE]) + + t1x = list[i][AG.OUTSLOPE][0] + t1y = list[i][AG.OUTSLOPE][1] + + t2x = list[i+1][AG.INSLOPE][0] + t2y = list[i+1][AG.INSLOPE][1] + + x2 = x1 + (x4 - x1) / float(3) + scale1 = (x2 - x1) / t1x + y2 = y1 + t1y * scale1 + + x3 = x4 - (x4 - x1) / float(3) + scale2 = (x4 - x3) / t2x + y3 = y4 - t2y * scale2 + + ax = - float(1) * x1 + float(3) * x2 - float(3) * x3 + float(1) * x4 + bx = float(3) * x1 - float(6) * x2 + float(3) * x3 + float(0) * x4 + cx = - float(3) * x1 + float(3) * x2 + float(0) * x3 + float(0) * x4 + dx = float(1) * x1 + float(0) * x2 - float(0) * x3 + float(0) * x4 + + ay = - float(1) * y1 + float(3) * y2 - float(3) * y3 + float(1) * y4 + by = float(3) * y1 - float(6) * y2 + float(3) * y3 + float(0) * y4 + cy = - float(3) * y1 + float(3) * y2 + float(0) * y3 + float(0) * y4 + dy = float(1) * y1 + float(0) * y2 - float(0) * y3 + float(0) * y4 + + if ax == 0 and bx == 0 and cx == 0: + return 0 + + if ax == 0 and bx == 0 and cx != 0: + a = cx + b = dx-x + t = -b/a + y = ay * t*t*t + by * t*t + cy * t + dy + return y + + if ax == 0 and bx!= 0: + a=bx + b=cx + c=dx-x + t=(-b+math.sqrt(b**2-4.0*a*c))/2*a + if t>=0 and t<=1: + y = ay * t*t*t + by * t*t + cy * t + dy + return y + else: + t=(-b-math.sqrt(b**2-4.0*a*c))/2*a + y = ay * t*t*t + by * t*t + cy * t + dy + return y + + if ax != 0: + a = ax + b = bx + c = cx + d = dx - float(x) + t = self.calculateT(a, b, c, d, x) + y = ay * t*t*t + by * t*t + cy * t + dy + return y + + def calculateT(self, a, b, c, d, x): + #Newton EQUATION + t = float(1) + t2 = t + t -= (a*t*t*t+b*t*t+c*t+d)/(float(3)*a*t*t+float(2)*b*t+c) + if abs(t-t2) <= 0.000001: + return t + else: + while abs(t - t2) > 0.000001: + t2 = t + t -= (a*t*t*t+b*t*t+c*t+d)/(float(3)*a*t*t+float(2)*b*t+c) + return t + + def lerpFuncX(self,pos,np): + np.setX(pos) + + def lerpFuncY(self,pos,np): + np.setY(pos) + + def lerpFuncZ(self,pos,np): + np.setZ(pos) + + def lerpCurveFuncX(self,t,extraArgs): + np = extraArgs[0] + pos = self.getPos(t, extraArgs[1], extraArgs[2]) + np.setX(pos) + + def lerpCurveFuncY(self,t,extraArgs): + np = extraArgs[0] + pos = self.getPos(t, extraArgs[1], extraArgs[2]) + np.setY(pos) + + def lerpCurveFuncZ(self,t,extraArgs): + np = extraArgs[0] + pos = self.getPos(t, extraArgs[1], extraArgs[2]) + np.setZ(pos) + + def lerpFuncH(self,angle,np): + np.setH(angle) + + def lerpFuncP(self,angle,np): + np.setP(angle) + + def lerpFuncR(self,angle,np): + np.setR(angle) + + def lerpFuncSX(self,scale,np): + np.setSx(scale) + + def lerpFuncSY(self,scale,np): + np.setSy(scale) + + def lerpFuncSZ(self,scale,np): + np.setSz(scale) + + def lerpFuncCR(self,R,np): + obj = self.editor.objectMgr.findObjectByNodePath(np) + r = obj[OG.OBJ_RGBA][0] + g = obj[OG.OBJ_RGBA][1] + b = obj[OG.OBJ_RGBA][2] + a = obj[OG.OBJ_RGBA][3] + self.colorUpdate(R,g,b,a,np) + + def lerpFuncCG(self,G,np): + obj = self.editor.objectMgr.findObjectByNodePath(np) + r = obj[OG.OBJ_RGBA][0] + g = obj[OG.OBJ_RGBA][1] + b = obj[OG.OBJ_RGBA][2] + a = obj[OG.OBJ_RGBA][3] + self.colorUpdate(r,G,b,a,np) + + def lerpFuncCB(self,B,np): + obj = self.editor.objectMgr.findObjectByNodePath(np) + r = obj[OG.OBJ_RGBA][0] + g = obj[OG.OBJ_RGBA][1] + b = obj[OG.OBJ_RGBA][2] + a = obj[OG.OBJ_RGBA][3] + self.colorUpdate(r,g,B,a,np) + + def lerpFuncCA(self,A,np): + obj = self.editor.objectMgr.findObjectByNodePath(np) + r = obj[OG.OBJ_RGBA][0] + g = obj[OG.OBJ_RGBA][1] + b = obj[OG.OBJ_RGBA][2] + a = obj[OG.OBJ_RGBA][3] + self.colorUpdate(r,g,b,A,np) + + def colorUpdate(self, r, g, b, a, np): + if base.direct.selected.last == None: + self.editor.objectMgr.updateObjectColor(r, g, b, a, np) + elif self.editor.objectMgr.findObjectByNodePath(np) == self.editor.objectMgr.findObjectByNodePath(base.direct.selected.last): + self.editor.ui.objectPropertyUI.propCR.setValue(r) + self.editor.ui.objectPropertyUI.propCG.setValue(g) + self.editor.ui.objectPropertyUI.propCB.setValue(b) + self.editor.ui.objectPropertyUI.propCA.setValue(a) + self.editor.objectMgr.updateObjectColor(r, g, b, a, np) + else: + self.editor.objectMgr.updateObjectColor(r, g, b, a, np) + \ No newline at end of file diff --git a/direct/src/leveleditor/CurveAnimUI.py b/direct/src/leveleditor/CurveAnimUI.py new file mode 100755 index 0000000000..7afea3879d --- /dev/null +++ b/direct/src/leveleditor/CurveAnimUI.py @@ -0,0 +1,159 @@ +""" + This is the GUI for the Curve Animation +""" +import os, wx, time + +from direct.interval.IntervalGlobal import * +from direct.actor.Actor import * +from direct.showutil.Rope import Rope +import ObjectGlobals as OG + +class CurveAnimUI(wx.Dialog): + """ + This is the Curve Animation Panel implementation. + """ + def __init__(self, parent, editor): + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Curve Animation", + pos=wx.DefaultPosition, size=(430, 140)) + + self.editor = editor + self.nodePath = None + self.curve = None + + self.mainPanel = wx.Panel(self, -1) + + self.chooseNode = wx.StaticText( self.mainPanel, -1, "Choose NodePath:") + self.chooseNodeTxt = wx.TextCtrl( self.mainPanel, -1, "") + self.chooseNodeButton = wx.Button( self.mainPanel, -1, "Choose..") + + self.chooseCurve = wx.StaticText( self.mainPanel, -1, "Choose attch Curve:") + self.chooseCurveTxt = wx.TextCtrl( self.mainPanel, -1, "") + self.chooseCurveButton = wx.Button( self.mainPanel, -1, "Choose..") + + self.duritionTime = wx.StaticText( self.mainPanel, -1, "Durition(Frame):") + self.duritionTimeSpin = wx.SpinCtrl( self.mainPanel, -1, "",size = (70,25), min=24, max=10000) + + self.createAnimButton = wx.Button( self.mainPanel, -1, "Creat") + self.saveAnimButton = wx.Button( self.mainPanel, -1, "Save Animation") + + self.SetProperties() + self.DoLayout() + + self.Bind(wx.EVT_BUTTON, self.OnChooseNode, self.chooseNodeButton) + self.Bind(wx.EVT_BUTTON, self.OnChooseCurve, self.chooseCurveButton) + self.Bind(wx.EVT_BUTTON, self.OnCreateAnim, self.createAnimButton) + self.Bind(wx.EVT_BUTTON, self.OnSaveAnim, self.saveAnimButton) + + self.Bind(wx.EVT_CLOSE, self.OnExit) + + def SetProperties(self): + self.duritionTimeSpin.SetValue(24) + self.chooseNodeTxt.SetMinSize((200,21)) + self.chooseCurveTxt.SetMinSize((200,21)) + self.saveAnimButton.SetToolTipString("Save the animation to the global animation control") + + def DoLayout(self): + dialogSizer = wx.BoxSizer(wx.VERTICAL) + mainSizer = wx.FlexGridSizer(4, 3, 0, 0) + + mainSizer.Add(self.chooseNode, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 10) + mainSizer.Add(self.chooseNodeTxt, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5) + mainSizer.Add(self.chooseNodeButton, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5) + + mainSizer.Add(self.chooseCurve, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 10) + mainSizer.Add(self.chooseCurveTxt, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5) + mainSizer.Add(self.chooseCurveButton, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5) + + mainSizer.Add(self.duritionTime, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 10) + mainSizer.Add(self.duritionTimeSpin, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5) + mainSizer.Add(self.createAnimButton, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5) + + mainSizer.Add(self.saveAnimButton, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5) + + self.mainPanel.SetSizerAndFit(mainSizer) + + dialogSizer.Add(self.mainPanel, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) + + self.SetSizer(dialogSizer) + self.Layout() + + def OnChooseNode(self, evt): + if base.direct.selected.last == None or base.direct.selected.last.hasTag('Controller') or not base.direct.selected.last.hasTag('OBJRoot'): + dlg = wx.MessageDialog(None, 'Please select an object.', 'NOTICE', wx.OK ) + dlg.ShowModal() + dlg.Destroy() + else: + obj = self.editor.objectMgr.findObjectByNodePath(base.direct.selected.last) + if obj[OG.OBJ_DEF].name == '__Curve__': + dlg = wx.MessageDialog(None, 'Please select an object, not a curve.', 'NOTICE', wx.OK ) + dlg.ShowModal() + dlg.Destroy() + else: + self.nodePath = obj + self.chooseNodeTxt.SetValue(str(self.nodePath[OG.OBJ_UID])) + + def OnChooseCurve(self, evt): + if base.direct.selected.last == None or base.direct.selected.last.hasTag('Controller') or not base.direct.selected.last.hasTag('OBJRoot'): + dlg = wx.MessageDialog(None, 'Please select a curve.', 'NOTICE', wx.OK ) + dlg.ShowModal() + dlg.Destroy() + else: + obj = self.editor.objectMgr.findObjectByNodePath(base.direct.selected.last) + if obj[OG.OBJ_DEF].name != '__Curve__': + dlg = wx.MessageDialog(None, 'Please select a curve, not an object.', 'NOTICE', wx.OK ) + dlg.ShowModal() + dlg.Destroy() + elif obj[OG.OBJ_DEF].name == '__Curve__': + self.curve = obj + self.chooseCurveTxt.SetValue(str(self.curve[OG.OBJ_UID])) + + def OnCreateAnim(self, evt): + self.time = self.duritionTimeSpin.GetValue() + if self.nodePath == None or self.curve == None: + dlg = wx.MessageDialog(None, 'Please select an object and a curve first.', 'NOTICE', wx.OK ) + dlg.ShowModal() + dlg.Destroy() + else: + self.curveSequence = self.editor.animMgr.singleCurveAnimation(self.nodePath, self.curve, self.time) + self.curveSequence.start() + + def OnSaveAnim(self,evt): + if not self.curveSequence: + dlg = wx.MessageDialog(None, 'Please create a animation first.', 'NOTICE', wx.OK ) + dlg.ShowModal() + dlg.Destroy() + else: + if self.editor.animMgr.curveAnimation == {}: + self.editor.animMgr.curveAnimation[(self.nodePath[OG.OBJ_UID],self.curve[OG.OBJ_UID])] = (self.nodePath[OG.OBJ_UID],self.curve[OG.OBJ_UID],self.time) + self.editor.updateStatusReadout('Sucessfully saved to global animation list') + return + + hasKey = False + for key in self.editor.animMgr.curveAnimation.keys(): + if key == (self.nodePath[OG.OBJ_UID],self.curve[OG.OBJ_UID]): + dlg = wx.MessageDialog(None, 'Already have the animation for this object attach to this curve.', 'NOTICE', wx.OK ) + dlg.ShowModal() + dlg.Destroy() + hasKey = True + return + elif self.nodePath[OG.OBJ_UID] == key[0]: + dlg = wx.MessageDialog(None, 'This object is already attached to a curve.', 'NOTICE', wx.OK ) + dlg.ShowModal() + dlg.Destroy() + hasKey = True + return + + if hasKey == False and self.editor.animMgr.curveAnimation != {}: + self.editor.animMgr.curveAnimation[(self.nodePath[OG.OBJ_UID],self.curve[OG.OBJ_UID])] = (self.nodePath[OG.OBJ_UID],self.curve[OG.OBJ_UID],self.time) + self.editor.updateStatusReadout('Sucessfully saved to global animation list') + + def OnExit(self,evt): + self.Destroy() + self.editor.ui.curveAnimMenuItem.Check(False) + + + + + + + \ No newline at end of file diff --git a/direct/src/leveleditor/FileMgr.py b/direct/src/leveleditor/FileMgr.py index 46b184fe9f..7baee7d25b 100755 --- a/direct/src/leveleditor/FileMgr.py +++ b/direct/src/leveleditor/FileMgr.py @@ -19,12 +19,15 @@ class FileMgr: f.write("from pandac.PandaModules import *\n") f.write("\nif hasattr(base, 'le'):\n") f.write(" objectMgr = base.le.objectMgr\n") + f.write(" animMgr = base.le.animMgr\n") f.write(" ui = base.le.ui\n") f.write(" ui.sceneGraphUI.reset()\n\n") f.write("else:\n") f.write(" objectMgr = base.objectMgr\n") f.write("# temporary place holder for nodepath\n") f.write("objects = {}\n") + f.write("animMgr.keyFramesInfo = "+str(self.editor.animMgr.keyFramesInfo)+"\n") + f.write("animMgr.curveAnimation = "+str(self.editor.animMgr.curveAnimation)+"\n") saveData = self.editor.objectMgr.getSaveData() for data in saveData: f.write(data) diff --git a/direct/src/leveleditor/GraphEditorUI.py b/direct/src/leveleditor/GraphEditorUI.py new file mode 100755 index 0000000000..c8990495af --- /dev/null +++ b/direct/src/leveleditor/GraphEditorUI.py @@ -0,0 +1,918 @@ +""" +Defines Graph Editor +""" +import os,wx +import math +import cPickle as pickle +from PaletteTreeCtrl import * +import ObjectGlobals as OG +import AnimGlobals as AG +from wx.lib.embeddedimage import PyEmbeddedImage + +property = [ + "translateX", + "translateY", + "translateZ" + ] + +#---------------------------------------------------------------------- +ZoomIn = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACgklE" + "QVRIid3VW0vjQBQA4DlJMziZlCKCSB/9Bf4a3wRtNU6SHyXK6pay2nVbQbasskXFO15SqmwL" + "LtYnn7Zg20zSyT50wZikXsCHxXk9Z74ZZuacgebtLXrvIb27+PFQjDEADIoqiiLL8htQAMAY" + "Hx4dPTw8SHEuIaRWqzWbzUQi8SoUAIaGhgqFgn15SSn1Iwmqqh4fH5c2NpLJpO8/xhORzEeR" + "EJLL5VqtViaT8TxPCBFMoJTu7e1tb28zxpLJpOu6L6D9PS6vrHQ6nWw267puVNzZ2alUKqZh" + "UE0LivFoX/y0vOxynpmZ4ZxHxZ+Vyu7urmmaqqqGxBhUkiSM8eLiIgDMTE87EVHTtB9bW4cH" + "B5ZpEkKiYhjt3/XCwoKiKFNTU47jRPf4vVw+PTmxLAtjHCs+QQEAAJaWljRNm5yc7Ha7wQtF" + "CGGMy+WybduWZSmK4nlerIiCTwoAOOfX19cTExMSgPDDT0iSJNu2x8fHU6nUM+ITVAhBCLFM" + "83MuV63VKCGhVM65ruv1RqNYLFJKX4UihFzXTafTc7Oz+XzerlZVVQ1GhRCyLJuGcXV1VSqV" + "QtGBKEKo6zhjY2Pzur66tnZxcRGa2ev1AMA0zXqj8W3wfmPK1HGc0dFRNj//dX397Ows6iKE" + "DMZ+39wUCgVKabTXxNe+4zgjIyMGY8VS6eT0NOr6vs8Yu7u7+7K6SggJuQMbCud8eHjYYGxz" + "c3N/f19RlJDb6/V0Xb+/v8/n829ofZzzVCqlz82dn5//abUk6UmyEEIIkc1k2u32r3o9uCq8" + "+PHJsuz7vud5IfTffABZljnnwejLnb9/grEiQih2vVd9J36kup4fH+w3/S/QvwejQg8ibHgo" + "AAAAAElFTkSuQmCC") + +#---------------------------------------------------------------------- +ZoomOut = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACmUlE" + "QVRIieXVz0sbQRQH8NmdH8smh3jwEIIlB+tNEW1NVps0WxPBv0BQXKQx4N2/xR2xTYQ9iIHc" + "/A+shyqevHiQVBGLeqgoxM1kNjHTQ8DayWb14KX0Hfe9+fDd4bGr/Ly4AK9d6quL/y6qKIqm" + "aQHThBBVfT7HnwlFUTjn3/f3MUKKoviMqurh4WG9XkcIvRQVQoRCodPT0+1yWdO0bhchxD2v" + "WCxyzjHGL0IBAA8PD9biIgDAcRxN06Q39Tzvk2kahmHbNmMswP3rmBCi0WhYlkUI2dzcxBhL" + "ruu6mUwmnU6v2bbrur1c+dY77sLCQjgcLpVKGKFuN5VKmaZpU1qr1Qghz6OP7vz8fF9f39di" + "Efq5H6amcrkcpfTu7q7b9d8PIQRjbG5urr+//8vGBoSw200mErOzs3R9/fb2VnLh6uqqrwsA" + "aLVao6OjV9fX3/b2RkZGJLfZbMbj8UgkUqlU3g4NhcNhIURQ0k612+12u/1ufPxHtcoY6157" + "z/OGh4fv7++vLi8hhI/Pg9ZY1/Xz83PHcVZWViKRSLPZfNpFCHmeRynNZrNjY2ONRuOx1TOp" + "rutnZ2eO41iWNTg4KIkYY875mm2/n5iYnp5+KvZMqut6tVrd2tpaWlqKx+OMMUmsM0YpnZqc" + "NE3TdV3puA+q6/rJycl2ufw5n38zMCClwBjXXdem9GMmk06lukUfNBQKHR8fVyqVwvJyLBaT" + "REJIrVbr3KNhGL6ijGKMj46OdnZ2CoVCNBqVRAjhr5ubUqmUm5kxksleIpA+fYyxg4ODfD4f" + "jUY559IohHB3dzebzSYTiQARAKA8/fEJIVRVhRC2Wi3faSEEIUTahKCknbAAgF4iAEBV1WdF" + "Ge1kCZgO7vZEX6X+c/Q3Qy8zmmCLWqIAAAAASUVORK5CYII=") + +#---------------------------------------------------------------------- +OneTangent = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACKUlE" + "QVRIia2Wz2saURDH5/3Yt6aVsE3SomArGO0phxCSU6A2EFgSklP+qPwrXkOQkBCoaG8eQrBQ" + "KRYVU1tabUij3V1337weAtZI1FX3e9qF2c/OzJv9zpKbRgMGRAhRSsF8oo9uOCeUMk0LDEo1" + "rVUuF46Pv2SzhDEgJBhoLZer5/OViwu32yWUjnlsvHj/SrluPJ222m3GORMC5ugsGTwoyjkT" + "4nuxeFetvj06ko4zG/RRjeh5rmVFNjeZrt8UCnxhIQAoAIBSnm0n9vb+1Gq/y2Wm60FAAUAp" + "UGr18LCRz9vtNuH8iZipoQAopW4Yb3Z2vmazgDjtJIyMlo7zIpUyksnq2RkTIhgoAHiWFdve" + "RsTmx4IuNP/ZToiUvV7CNNt3natb61b57cKEMIXIOeu+N0uof5IaEl8f7+R3K1SG8sKc0FqF" + "oPTjCZOhEiDF0Vyk8da3+occC4UCgAKAAhBe73X6Xa/T+VEs8klcv0eKCtD1kgcHP6+v7xsN" + "OtZzp5hqhUh1PbG/Xz0/l5ZFGAsACgDous+j0cjWVuX0lI428qmdWNr2q/X10NJS/fJyVHNn" + "sXfPtuO7u39brV+lkgiHmRBDKZOhbepThDHpOJ8zmZ7jLMZiq6YJhPSXxYyLSEkZMgx9ebl8" + "clLKZO6bTTbgkFN7ZV+e47xcW4tubIQjkWcrKyjl/zpmK/9BlDH0vIfZUogBZAoAKCWhVCk1" + "tHrnggLAk/9I/wC0eekA1bLbIQAAAABJRU5ErkJggg==") + +#---------------------------------------------------------------------- +TwoTangents = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAAA3NCSVQICAjb4U/gAAACL0lE" + "QVRIie3UzU8TQRQA8DczO1vatRgh1YhWD/WD+kG91FgOXLhx4+CpiYnE9L8g/gVwJHhp4lUP" + "pPHgQRMOGiEaDabV1LRAtdjUAG23m3W/Znc8YEo/trRNOGjCO+7M++3MezODdopFOO7Axy6e" + "oC0oAqAEY4QGyCcE4faVtaII7dQUy7b7dDEheq3GdB0R4o76KH2+kZ158uzxyzcO56iXS0Rx" + "N5tdnZ9fW1w0FaXZPUQxRl/Le7+q8mruh2paQsem2kLweCq5XGVrq7K5adTruGk+ahx+AeOS" + "rLz4ktctdtYvPYje0i3Gu22cUr1a3VheHg6FhoPBC9EoBwDO21EOQDH2igIALL39NOrz3r8z" + "rppWp3jQmQ8LC5enp4NTU0zTmGG0/PJwKgBzHEU3VcN6eHfie7X++ltBEmknSoaG0snkSDg8" + "Njmp12ptIrieU4dzAEjEIu8KPz8Wy14qNI9SScqvrHCA67Ozlqq6F8f1K3McL6WPYpFUOre9" + "L3uEv50VfL7S+vpuJjMxN8cMo1HEvlAAMG37nF+KR28+fZ/e/62JBAuiWC8U8qlUJJFAhHDH" + "6ZZ71LnRLXY1MDJzI5Rc+6wwR5PlTDIZjsd9gYBjuTSwEajneypR4dV2qXxq1F/dG5fLodg9" + "U9OOThGOHgYAg7FrF8fKKqjng/4rl+xeYl+ozeEMsNsiwmCftnnXQg6EcgABICxyAGAcut2x" + "wdADt+uFdYv/9OU/Qf9d9A9dhOgRU8MmewAAAABJRU5ErkJggg==") + +#---------------------------------------------------------------------- + +class GraphEditorWindow(wx.Window): + """ + This is the main graph editor window. + """ + def __init__(self, parent, windowSize, property, xRange, yRange, curFrame, object): + wx.Window.__init__(self, parent, size = windowSize, style = wx.SUNKEN_BORDER) + + self._mainDialog = wx.GetTopLevelParent(self) + self.w,self.h = self.GetClientSize() + + self.zoom = float(2) + self._mouseIn = False + self._selectRec = False + self._selectHandler = False + self._OneTangent = True + + self.object = object + self.curFrame = curFrame + self.property = property + + self.zeroPos = (float(0), self.h/float(2)) + self.zero = 0 + self.unitWidth = self.w/float(xRange) + self.unitHeight = self.h/float(yRange) + + self.generateInfo() + self.InitBuffer() + + self.Bind(wx.EVT_PAINT, self.OnPaint) + self.Bind(wx.EVT_SIZE, self.OnSize) + self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) + self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) + self.Bind(wx.EVT_MIDDLE_DOWN, self.OnMiddleDown) + self.Bind(wx.EVT_MIDDLE_UP, self.OnMiddleUp) + self.Bind(wx.EVT_MOTION, self.OnMotion) + + def refresh(self): + self._mouseIn = False + self._selectRec = False + self._selectHandler = False + self.generateInfo() + + def generateInfo(self): + #generate the information for animation curve generation + self.X = [] + self.Y = [] + self.Z = [] + + if self._mainDialog.editor.animMgr.keyFramesInfo != {}: + self.keyFramesInfo = self._mainDialog.editor.animMgr.keyFramesInfo + for key in self.keyFramesInfo.keys(): + if key == (self.object[OG.OBJ_UID], 'X'): + for i in range(len(self.keyFramesInfo[key])): + item = self.keyFramesInfo[key][i] + handler = self.generateHandler(item) + self.X.append([key, i, handler[0], handler[1], handler[2], handler[3], handler[4]]) + if key == (self.object[OG.OBJ_UID], 'Y'): + for i in range(len(self.keyFramesInfo[key])): + item = self.keyFramesInfo[key][i] + handler = self.generateHandler(item) + self.Y.append([key, i, handler[0], handler[1], handler[2], handler[3], handler[4]]) + if key == (self.object[OG.OBJ_UID], 'Z'): + for i in range(len(self.keyFramesInfo[key])): + item = self.keyFramesInfo[key][i] + handler = self.generateHandler(item) + self.Z.append([key, i, handler[0], handler[1], handler[2], handler[3], handler[4]]) + + def generateHandler(self, item): + #generate the position for the control handler + x1 = self.zeroPos[0] + float(item[AG.FRAME])*self.unitWidth + y1 = self.zeroPos[1] - float(item[AG.VALUE])*self.unitHeight + + t1x = item[AG.INSLOPE][0]*self.unitWidth + t1y = item[AG.INSLOPE][1]*self.unitHeight + + t2x = item[AG.OUTSLOPE][0]*self.unitWidth + t2y = item[AG.OUTSLOPE][1]*self.unitHeight + + tanA = t1y/t1x + temp1 = float(1)/(tanA**2+1) + if t1x <0 : + cosA = -math.sqrt(abs(temp1)) + if t1x >=0: + cosA = math.sqrt(abs(temp1)) + temp2 = (tanA**2)*temp1 + if t1y <0 : + sinA = -math.sqrt(abs(temp2)) + if t1y >=0: + sinA = math.sqrt(abs(temp2)) + + x2 = x1-float(self.unitWidth*self.zoom)*cosA + y2 = y1+float(self.unitWidth*self.zoom)*sinA + + tanA = t2y/t2x + temp1 = float(1)/(tanA**2+1) + if t2x <0 : + cosA = -math.sqrt(abs(temp1)) + if t2x >=0: + cosA = math.sqrt(abs(temp1)) + temp2 = (tanA**2)*temp1 + if t2y <0 : + sinA = -math.sqrt(abs(temp2)) + if t2y >=0: + sinA = math.sqrt(abs(temp2)) + + x3 = x1+float(self.unitWidth*self.zoom)*cosA + y3 = y1-float(self.unitWidth*self.zoom)*sinA + + return [[(x1,y1),0],[(x2,y2),0],[(x3,y3),0],[t1x,t1y],[t2x,t2y]] + + def InitBuffer(self): + self.buffer = wx.EmptyBitmap(self.w, self.h) + dc = wx.BufferedDC(wx.ClientDC(self), self.buffer) + self.DrawXCoord(dc) + self.DrawYCoord(dc) + self.DrawFrame(dc) + self.DrawCurve(dc) + self.DrawSelectRec(dc) + + def SetGraphEditorData(self, property, curFrame = 1): + self.curFrame = curFrame + self.property = property + + self.InitBuffer() + + def OnPaint(self, evt): + dc = wx.BufferedPaintDC(self, self.buffer) + + def DrawXCoord(self,dc): + dc.SetBackground(wx.Brush(wx.Colour(200, 200, 200))) + dc.Clear() + + dc.SetPen(wx.BLACK_PEN) + dc.SetBrush(wx.BLACK_BRUSH) + dc.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL)) + + dc.DrawLine(self.zeroPos[0], float(0), self.zeroPos[0], self.h) + st = str(self.zero) + self.tw,self.th = dc.GetTextExtent(st) + dc.DrawText(st, self.zeroPos[0]+1.0, self.h-self.th-0.5) + + dc.SetPen(wx.Pen(wx.Colour(150, 150, 150))) + dc.SetBrush(wx.Brush(wx.Colour(150, 150, 150))) + + if self.unitWidth >= 25: + posPos = self.zeroPos[0]+self.unitWidth + posNum = self.zero + 1 + while posPos <= self.w: + dc.DrawLine(posPos, float(0), posPos, self.h) + st = str(posNum) + self.drawXNumber(dc, st, posPos) + posPos += self.unitWidth + posNum += 1 + + negPos = self.zeroPos[0]-self.unitWidth + negNum = self.zero - 1 + while negPos >= float(0): + dc.DrawLine(negPos, float(0), negPos, self.h) + st = str(negNum) + self.drawXNumber(dc, st, negPos) + negPos -= self.unitWidth + posNum -= 1 + + elif self.unitWidth >= 10 and self.unitWidth <= 25: + posPos = self.zeroPos[0]+self.unitWidth*float(2) + posNum = self.zero + 2 + while posPos <= self.w: + dc.DrawLine(posPos, float(0), posPos, self.h) + st = str(posNum) + self.drawXNumber(dc, st, posPos) + posPos += self.unitWidth*float(2) + posNum += 2 + + negPos = self.zeroPos[0]-self.unitWidth*float(2) + negNum = self.zero - 2 + while negPos >= float(0): + dc.DrawLine(negPos, float(0), negPos, self.h) + st = str(negNum) + self.drawXNumber(dc, st, negPos) + negPos -= self.unitWidth*float(2) + posNum -= 2 + + elif self.unitWidth >= 2 and self.unitWidth <= 10: + posPos = self.zeroPos[0]+self.unitWidth*float(5) + posNum = self.zero + 5 + while posPos <= self.w: + dc.DrawLine(posPos, float(0), posPos, self.h) + st = str(posNum) + self.drawXNumber(dc, st, posPos) + posPos += self.unitWidth*float(5) + posNum += 5 + + negPos = self.zeroPos[0]-self.unitWidth*float(5) + negNum = self.zero - 5 + while negPos >= float(0): + dc.DrawLine(negPos, float(0), negPos, self.h) + st = str(negNum) + self.drawXNumber(dc, st, negPos) + negPos -= self.unitWidth*float(5) + posNum -= 5 + + def DrawYCoord(self,dc): + dc.SetPen(wx.BLACK_PEN) + dc.SetBrush(wx.BLACK_BRUSH) + dc.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL)) + + dc.DrawLine(float(0), self.zeroPos[1], self.w, self.zeroPos[1]) + st = str(self.zero) + dc.DrawText(st, 5.0, self.zeroPos[1]-1.0) + + dc.SetPen(wx.Pen(wx.Colour(150, 150, 150))) + dc.SetBrush(wx.Brush(wx.Colour(150, 150, 150))) + dc.SetLogicalFunction(wx.AND) + + posPos = self.zeroPos[1]-self.unitHeight*float(5) + posNum = self.zero + 5 + while posPos >= float(0): + dc.DrawLine(float(0), posPos, self.w, posPos) + st = str(posNum) + self.drawYNumber(dc, st, posPos) + posPos -= self.unitHeight*float(5) + posNum += 5 + + negPos = self.zeroPos[1]+self.unitHeight*float(5) + negNum = self.zero - 5 + while negPos <= self.h: + dc.DrawLine(float(0), negPos, self.w, negPos) + st = str(negNum) + self.drawYNumber(dc, st, negPos) + negPos += self.unitHeight*float(5) + negNum -= 5 + + def drawXNumber(self, dc, st, pos): + oldPen, oldBrush, oldMode = dc.GetPen(), dc.GetBrush(), dc.GetLogicalFunction() + + dc.SetPen(wx.BLACK_PEN) + dc.SetBrush(wx.BLACK_BRUSH) + dc.DrawText(st, pos+1.0, self.h-self.th-0.5) + + dc.SetPen(oldPen) + dc.SetBrush(oldBrush) + dc.SetLogicalFunction(oldMode) + + def drawYNumber(self, dc, st, pos): + oldPen, oldBrush, oldMode = dc.GetPen(), dc.GetBrush(), dc.GetLogicalFunction() + + dc.SetPen(wx.BLACK_PEN) + dc.SetBrush(wx.BLACK_BRUSH) + dc.DrawText(st, 5.0, pos-1.0) + + dc.SetPen(oldPen) + dc.SetBrush(oldBrush) + dc.SetLogicalFunction(oldMode) + + def DrawFrame(self, dc): + if self._mainDialog.editor.mode == self._mainDialog.editor.ANIM_MODE: + curFramePos = self.zeroPos[0]+self.curFrame*self.unitWidth + dc.SetPen(wx.Pen("red")) + dc.SetBrush(wx.Brush("red")) + dc.DrawLine(curFramePos, float(0), curFramePos, self.h) + else: + pass + + def drawX(self, dc): + dc.SetPen(wx.Pen("red")) + dc.SetBrush(wx.Brush("red")) + self.drawSingleCurve(self.X, dc) + self.drawKeys(self.X, dc) + self.drawHandler(self.X, dc) + + def drawY(self, dc): + dc.SetPen(wx.Pen("green")) + dc.SetBrush(wx.Brush("green")) + self.drawSingleCurve(self.Y, dc) + self.drawKeys(self.Y, dc) + self.drawHandler(self.Y, dc) + + def drawZ(self, dc): + dc.SetPen(wx.Pen("blue")) + dc.SetBrush(wx.Brush("blue")) + self.drawSingleCurve(self.Z, dc) + self.drawKeys(self.Z, dc) + self.drawHandler(self.Z, dc) + + def DrawCurve(self, dc): + if self.property == self._mainDialog.namestr: + self.drawX(dc) + self.drawY(dc) + self.drawZ(dc) + return + if self.property == property[AG.X]: + self.drawX(dc) + return + if self.property == property[AG.Y]: + self.drawY(dc) + return + if self.property == property[AG.Z]: + self.drawZ(dc) + return + + def drawSingleCurve(self, list, dc): + if len(list) == 1: + dc.DrawPoint(list[0][AG.KEYFRAME][AG.LOCAL_VALUE][0], list[0][AG.KEYFRAME][AG.LOCAL_VALUE][1]) + return + + if len(list) == 2: + dc.DrawLine(list[0][AG.KEYFRAME][AG.LOCAL_VALUE][0], list[0][AG.KEYFRAME][AG.LOCAL_VALUE][1], list[1][AG.KEYFRAME][AG.LOCAL_VALUE][0], list[1][AG.KEYFRAME][AG.LOCAL_VALUE][1]) + return + + if len(list)>=3 : + for i in range(len(list)-1): + x1 = list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0] + y1 = list[i][AG.KEYFRAME][AG.LOCAL_VALUE][1] + + x4 = list[i+1][AG.KEYFRAME][AG.LOCAL_VALUE][0] + y4 = list[i+1][AG.KEYFRAME][AG.LOCAL_VALUE][1] + + t1x = list[i][AG.OUT_SLOPE][0] + t1y = list[i][AG.OUT_SLOPE][1] + + t2x = list[i+1][AG.IN_SLOPE][0] + t2y = list[i+1][AG.IN_SLOPE][1] + + x2 = x1 + (x4 - x1) / float(3); + scale1 = (x2 - x1) / t1x; + y2 = y1 - t1y * scale1; + + + x3 = x4 - (x4 - x1) / float(3); + scale2 = (x4 - x3) / t2x; + y3 = y4 + t2y * scale2; + + ax = - float(1) * x1 + float(3) * x2 - float(3) * x3 + float(1) * x4; + bx = float(3) * x1 - float(6) * x2 + float(3) * x3 + float(0) * x4; + cx = - float(3) * x1 + float(3) * x2 + float(0) * x3 + float(0) * x4; + dx = float(1) * x1 + float(0) * x2 - float(0) * x3 + float(0) * x4; + + ay = - float(1) * y1 + float(3) * y2 - float(3) * y3 + float(1) * y4; + by = float(3) * y1 - float(6) * y2 + float(3) * y3 + float(0) * y4; + cy = - float(3) * y1 + float(3) * y2 + float(0) * y3 + float(0) * y4; + dy = float(1) * y1 + float(0) * y2 - float(0) * y3 + float(0) * y4; + + preX = x1 + preY = y1 + t = 0.001 + + while t<=float(1): + x = ax * t*t*t + bx * t*t + cx * t + dx; + y = ay * t*t*t + by * t*t + cy * t + dy; + + curX = x + curY = y + + dc.DrawLine(preX, preY, curX, curY) + + preX = curX + preY = curY + + t += 0.001 + + def drawKeys(self, list, dc): + for i in range(len(list)): + pointX = list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0] + pointY = list[i][AG.KEYFRAME][AG.LOCAL_VALUE][1] + + if list[i][AG.KEYFRAME][AG.SELECT] == 0: + dc.SetPen(wx.Pen("black",3)) + dc.SetBrush(wx.Brush("black")) + dc.DrawCircle(pointX, pointY, 2) + + if list[i][AG.KEYFRAME][AG.SELECT] == 1: + dc.SetPen(wx.Pen("cyan",3)) + dc.SetBrush(wx.Brush("cyan")) + dc.DrawCircle(pointX, pointY, 2) + + def drawHandler(self, list, dc): + for i in range(len(list)): + if list[i][AG.KEYFRAME][AG.SELECT] == 1: + X1 = list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0] + Y1 = list[i][AG.KEYFRAME][AG.LOCAL_VALUE][1] + if self._OneTangent == True: + for j in range(3,5): + X = list[i][j][AG.LOCAL_VALUE][0] + Y = list[i][j][AG.LOCAL_VALUE][1] + if list[i][j][AG.SELECT] == 1: + dc.SetPen(wx.Pen("cyan",3)) + dc.SetBrush(wx.Brush("cyan")) + dc.DrawCircle(X, Y, 2) + + dc.SetPen(wx.Pen("cyan",1)) + dc.DrawLine(X1, Y1, X, Y) + + if list[i][j][AG.SELECT] == 0: + dc.SetPen(wx.Pen("brown",3)) + dc.SetBrush(wx.Brush("brown")) + dc.DrawCircle(X, Y, 2) + + dc.SetPen(wx.Pen("brown",1)) + dc.DrawLine(X1, Y1, X, Y) + + if self._OneTangent == False: + if list[i][AG.IN_TANGENT][AG.SELECT] == 1: + X = list[i][AG.IN_TANGENT][AG.LOCAL_VALUE][0] + Y = list[i][AG.IN_TANGENT][AG.LOCAL_VALUE][1] + dc.SetPen(wx.Pen("cyan",3)) + dc.SetBrush(wx.Brush("cyan")) + dc.DrawCircle(X, Y, 2) + + dc.SetPen(wx.Pen("cyan",1)) + dc.DrawLine(X1, Y1, X, Y) + + if list[i][AG.IN_TANGENT][AG.SELECT] == 0: + X = list[i][AG.IN_TANGENT][AG.LOCAL_VALUE][0] + Y = list[i][AG.IN_TANGENT][AG.LOCAL_VALUE][1] + dc.SetPen(wx.Pen("navy",3)) + dc.SetBrush(wx.Brush("navy")) + dc.DrawCircle(X, Y, 2) + + dc.SetPen(wx.Pen("navy",1)) + dc.DrawLine(X1, Y1, X, Y) + + if list[i][AG.OUT_TANGENT][AG.SELECT] == 1: + X = list[i][AG.OUT_TANGENT][AG.LOCAL_VALUE][0] + Y = list[i][AG.OUT_TANGENT][AG.LOCAL_VALUE][1] + dc.SetPen(wx.Pen("cyan",3)) + dc.SetBrush(wx.Brush("cyan")) + dc.DrawCircle(X, Y, 2) + + dc.SetPen(wx.Pen("cyan",1)) + dc.DrawLine(X1, Y1, X, Y) + + if list[i][AG.OUT_TANGENT][AG.SELECT] == 0: + X = list[i][AG.OUT_TANGENT][AG.LOCAL_VALUE][0] + Y = list[i][AG.OUT_TANGENT][AG.LOCAL_VALUE][1] + dc.SetPen(wx.Pen("brown",3)) + dc.SetBrush(wx.Brush("brown")) + dc.DrawCircle(X, Y, 2) + + dc.SetPen(wx.Pen("brown",1)) + dc.DrawLine(X1, Y1, X, Y) + + def DrawSelectRec(self, dc): + if self._selectRec == True: + dc.SetPen(wx.Pen("navy", 1)) + dc.SetBrush(wx.Brush("navy")) + ## dc.SetLogicalFunction(wx.AND) + dc.DrawLine(self.pos[0], self.pos[1], self.pos[0], self.newPos[1]) + dc.DrawLine(self.pos[0], self.pos[1], self.newPos[0], self.pos[1]) + dc.DrawLine(self.newPos[0], self.newPos[1], self.pos[0], self.newPos[1]) + dc.DrawLine(self.newPos[0], self.newPos[1], self.newPos[0], self.pos[1]) + + def OnSize(self,evt): + self.InitBuffer() + + def OnLeftDown(self,evt): + point = (evt.GetX(), evt.GetY()) + + if point[1]>= float(0) and point[1]<= float(self.h): + if point[0]>= float(0) and point[0]<= float(self.w): + self._mouseIn = True + + if self._mouseIn: + self.CaptureMouse() + self.pos = point + + def OnLeftUp(self,evt): + if self.GetCapture(): + self.ReleaseMouse() + self._mouseIn = False + self._selectRec = False + self.setSelection() + self.SetGraphEditorData(self.property, self.curFrame) + + def OnMiddleDown(self,evt): + point = (evt.GetX(), evt.GetY()) + + if point[1]>= float(0) and point[1]<= float(self.h): + if point[0]>= float(0) and point[0]<= float(self.w): + self._mouseIn = True + + if self._mouseIn: + self.CaptureMouse() + self.midPos = point + + def OnMiddleUp(self, evt): + if self.GetCapture(): + self.ReleaseMouse() + + def OnMotion(self,evt): + self._mouseIn = False + if evt.Dragging() and evt.LeftIsDown(): + self.newPos = (evt.GetX(), evt.GetY()) + if self.newPos[1]>= float(0) and self.newPos[1]<= float(self.h): + if self.newPos[0]>= float(0) and self.newPos[0]<= float(self.w): + self._mouseIn = True + + if self._mouseIn: + if self.newPos == self.pos: + evt.Skip() + self._mouseIn = False + else: + self._selectRec = True + self.SetGraphEditorData(self.property, self.curFrame) + + if evt.Dragging() and evt.MiddleIsDown(): + self.newMidPos = (evt.GetX(), evt.GetY()) + if self.newMidPos[1]>= float(0) and self.newMidPos[1]<= float(self.h): + if self.newMidPos[0]>= float(0) and self.newMidPos[0]<= float(self.w): + self._mouseIn = True + + if self._mouseIn: + if self.newMidPos == self.midPos: + evt.Skip() + self._mouseIn = False + else: + self.recalculateSlope() + self.onAnimation() + self.midPos = self.newMidPos + + evt.Skip() + self._mouseIn = False + self._selectRec = False + + def setExistKey(self, list): + flag = False + for i in range(len(list)): + if list[i][AG.KEYFRAME][AG.SELECT] == 1: + inside = self.inside(self.pos, self.newPos, (list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0], list[i][AG.KEYFRAME][AG.LOCAL_VALUE][1])) + if inside == True: + list[i][AG.KEYFRAME][AG.SELECT] = 0 + if inside == False: + find = False + for j in range(3,5): + inside = self.inside(self.pos, self.newPos, (list[i][j][AG.LOCAL_VALUE][0], list[i][j][AG.LOCAL_VALUE][1])) + if inside == False: + list[i][j][AG.SELECT] = 0 + if inside == True: + list[i][j][AG.SELECT] = 1 + find = True + flag = True + if find == False: + list[i][AG.KEYFRAME][AG.SELECT] == 0 + + return flag + + def setNewKey(self, list): + for i in range(len(list)): + inside = self.inside(self.pos, self.newPos, (list[i][2][0][0], list[i][2][0][1])) + if inside == True: + list[i][AG.KEYFRAME][AG.SELECT] = 1 + if inside == False: + list[i][AG.KEYFRAME][AG.SELECT] = 0 + + def setSelection(self): + if self.property == self._mainDialog.namestr: + self.setSelectionBase(self.X) + self.setSelectionBase(self.Y) + self.setSelectionBase(self.Z) + return + if self.property == property[AG.X]: + self.setSelectionBase(self.X) + return + if self.property == property[AG.Y]: + self.setSelectionBase(self.Y) + return + if self.property == property[AG.Z]: + self.setSelectionBase(self.Z) + return + + def setSelectionBase(self, list): + self.setExistKey(list) + if self.setExistKey(list) == True: + return + else: + self.setNewKey(list) + + def inside(self, point0, point1, point): + if point0[0]<=point1[0] and point0[1]<=point1[1]: + if point0[0]= list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0]: + self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.INSLOPE][0] = temp0 + self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.INSLOPE][1] = temp1 + return + if handler[1][0][0] < list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0]: + if self._OneTangent == False: + list[i][AG.IN_TANGENT][0] = handler[1][0] + list[i][AG.IN_SLOPE][0] = handler[3][0] + list[i][AG.IN_SLOPE][1] = handler[3][1] + + if self._OneTangent == True: + self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][0] = newSlope[0]/self.unitWidth + self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][1] = newSlope[1]/self.unitHeight + handler = self.generateHandler(self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]]) + list[i][AG.IN_TANGENT][0] = handler[1][0] + list[i][AG.OUT_TANGENT][0] = handler[2][0] + list[i][AG.IN_SLOPE][0] = handler[3][0] + list[i][AG.IN_SLOPE][1] = handler[3][1] + list[i][AG.OUT_SLOPE][0] = handler[4][0] + list[i][AG.OUT_SLOPE][1] = handler[4][1] + + self.SetGraphEditorData(self.property, self.curFrame) + + if list[i][AG.OUT_TANGENT][AG.SELECT] == 1: + newPointX = list[i][AG.OUT_TANGENT][AG.LOCAL_VALUE][0] + moveX + newPointY = list[i][AG.OUT_TANGENT][AG.LOCAL_VALUE][1] + moveY + + newSlope = [newPointX - list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0] , list[i][AG.KEYFRAME][AG.LOCAL_VALUE][1] - newPointY] + + temp0 = self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][0] + temp1 = self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][1] + self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][0] = newSlope[0]/self.unitWidth + self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][1] = newSlope[1]/self.unitHeight + + handler = self.generateHandler(self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]]) + if handler[2][0][0] <= list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0]: + self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][0] = temp0 + self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.OUTSLOPE][1] = temp1 + return + if handler[2][0][0] > list[i][AG.KEYFRAME][AG.LOCAL_VALUE][0]: + if self._OneTangent == False: + list[i][AG.OUT_TANGENT][0] = handler[2][0] + list[i][AG.OUT_SLOPE][0] = handler[4][0] + list[i][AG.OUT_SLOPE][1] = handler[4][1] + + if self._OneTangent == True: + self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.INSLOPE][0] = newSlope[0]/self.unitWidth + self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]][AG.INSLOPE][1] = newSlope[1]/self.unitHeight + handler = self.generateHandler(self._mainDialog.editor.animMgr.keyFramesInfo[list[i][AG.KEY]][list[i][AG.I]]) + list[i][AG.IN_TANGENT][0] = handler[1][0] + list[i][AG.OUT_TANGENT][0] = handler[2][0] + list[i][AG.IN_SLOPE][0] = handler[3][0] + list[i][AG.IN_SLOPE][1] = handler[3][1] + list[i][AG.OUT_SLOPE][0] = handler[4][0] + list[i][AG.OUT_SLOPE][1] = handler[4][1] + + self.SetGraphEditorData(self.property, self.curFrame) + + def selectHandler(self): + self._selectHandler = False + + def onAnimation(self): + if self._mainDialog.editor.mode == self._mainDialog.editor.ANIM_MODE: + self._mainDialog.editor.ui.animUI.OnAnimation(self._mainDialog.editor.ui.animUI.curFrame) + else: + pass + + +class GraphEditorUI(wx.Dialog): + """ + This is the graph editor main class implementation. + """ + def __init__(self, parent, editor, object): + wx.Dialog.__init__(self, parent, id=wx.ID_ANY, title="Graph Editor", + pos=wx.DefaultPosition, size=(735, 535)) + + self.editor = editor + self.editor.GRAPH_EDITOR = True + self.object = object + self.xRange = 24+1 + self.yRange = 50 + if self.editor.mode == self.editor.ANIM_MODE: + self.curFrame = self.editor.ui.animUI.curFrame + self.curFrame = 1 + + self.mainPanel1 = wx.Panel(self, -1) + + bmpZoomIn = ZoomIn.GetBitmap() + bmpZoomOut = ZoomOut.GetBitmap() + bmpOneTangent = OneTangent.GetBitmap() + bmpTwoTangents = TwoTangents.GetBitmap() + + self.buttonZoomIn = wx.BitmapButton(self.mainPanel1, -1, bmpZoomIn, size = (30,30),style = wx.BU_AUTODRAW) + self.buttonZoomOut = wx.BitmapButton(self.mainPanel1, -1, bmpZoomOut, size = (30,30),style = wx.BU_AUTODRAW) + self.buttonOneTangent = wx.BitmapButton(self.mainPanel1, -1, bmpOneTangent, size = (30,30),style = wx.BU_AUTODRAW) + self.buttonTwoTangents = wx.BitmapButton(self.mainPanel1, -1, bmpTwoTangents, size = (30,30),style = wx.BU_AUTODRAW) + + self.mainPanel2 = wx.Panel(self, -1) + + self.tree = self.tree = wx.TreeCtrl(self.mainPanel2, id=-1, pos=wx.DefaultPosition,size=wx.Size(200, 450), style=wx.TR_MULTIPLE|wx.TR_DEFAULT_STYLE,validator=wx.DefaultValidator, name="treeCtrl") + self.namestr = "%s"%(object[OG.OBJ_DEF].name) + self.root = self.tree.AddRoot(self.namestr) + self.AddTreeNodes(self.root, property) + self.tree.Expand(self.root) + self.tree.SelectItem(self.root,select=True) + self.str = self.tree.GetItemText(self.root) + + self.graphEditorWindow =GraphEditorWindow(self.mainPanel2, wx.Size(500, 450), str(object[OG.OBJ_DEF].name), self.xRange, self.yRange, self.curFrame, self.object) + + self.SetProperties() + self.DoLayout() + + self.Bind(wx.EVT_BUTTON, self.OnZoomIn, self.buttonZoomIn) + self.Bind(wx.EVT_BUTTON, self.OnZoomOut, self.buttonZoomOut) + self.Bind(wx.EVT_BUTTON, self.OnOneTangent, self.buttonOneTangent) + self.Bind(wx.EVT_BUTTON, self.OnTwoTangents, self.buttonTwoTangents) + + self.Bind(wx.EVT_CLOSE, self.OnExit) + self.tree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged) + + def SetProperties(self): + pass + + def DoLayout(self): + dialogSizer = wx.BoxSizer(wx.VERTICAL) + mainSizer1 = wx.FlexGridSizer(1, 4, 0, 0) + mainSizer2 = wx.FlexGridSizer(1, 2, 0, 0) + + mainSizer1.Add(self.buttonOneTangent, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 570) + mainSizer1.Add(self.buttonTwoTangents, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 10) + mainSizer1.Add(self.buttonZoomIn, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 10) + mainSizer1.Add(self.buttonZoomOut, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 10) + + mainSizer2.Add(self.tree, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 10) + mainSizer2.Add(self.graphEditorWindow, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 10) + + self.mainPanel1.SetSizerAndFit(mainSizer1) + self.mainPanel2.SetSizer(mainSizer2) + + dialogSizer.Add(self.mainPanel2, 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, 10) + dialogSizer.Add(self.mainPanel1, 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, 5) + + self.SetSizer(dialogSizer) + self.Layout() + + self.dialogSizer = dialogSizer + + def AddTreeNodes(self, parentItem, items): + for item in items: + if type(item) == str: + self.tree.AppendItem(parentItem, item) + + def OnSelChanged(self, evt): + item = evt.GetItem() + if item: + self.str = self.tree.GetItemText(item) + self.graphEditorWindow.refresh() + self.graphEditorWindow.SetGraphEditorData(self.str, self.curFrame) + + def OnZoomIn(self,evt): + self.graphEditorWindow.zoom = self.graphEditorWindow.zoom/float(1.2) + self.graphEditorWindow.unitWidth = self.graphEditorWindow.unitWidth*float(1.2) + self.graphEditorWindow.unitHeight = self.graphEditorWindow.unitHeight*float(1.2) + self.graphEditorWindow.generateInfo() + self.graphEditorWindow.SetGraphEditorData(self.str, self.curFrame) + + def OnZoomOut(self,evt): + self.graphEditorWindow.zoom = self.graphEditorWindow.zoom*float(1.2) + self.graphEditorWindow.unitWidth = self.graphEditorWindow.unitWidth/float(1.2) + self.graphEditorWindow.unitHeight = self.graphEditorWindow.unitHeight/float(1.2) + self.graphEditorWindow.generateInfo() + self.graphEditorWindow.SetGraphEditorData(self.str, self.curFrame) + + def OnOneTangent(self,evt): + self.graphEditorWindow._OneTangent = True + self.graphEditorWindow.SetGraphEditorData(self.str, self.curFrame) + + def OnTwoTangents(self,evt): + self.graphEditorWindow._OneTangent = False + self.graphEditorWindow.SetGraphEditorData(self.str, self.curFrame) + + def curFrameChange(self): + if self.editor.mode == self.editor.ANIM_MODE: + self.curFrame = self.editor.ui.animUI.curFrame + self.graphEditorWindow.SetGraphEditorData(self.str, self.curFrame) + else: + pass + + def OnExit(self,evt): + self.Destroy() + self.editor.ui.graphEditorMenuItem.Check(False) + self.object = None + self.editor.GRAPH_EDITOR = False + + + + + + + + diff --git a/direct/src/leveleditor/LevelEditor.py b/direct/src/leveleditor/LevelEditor.py index ffd0773795..42707ee34a 100644 --- a/direct/src/leveleditor/LevelEditor.py +++ b/direct/src/leveleditor/LevelEditor.py @@ -8,6 +8,7 @@ to be game specific. from LevelEditorUI import * from LevelEditorBase import * from ObjectMgr import * +from AnimMgr import * from ObjectHandler import * from ObjectPalette import * from ProtoPalette import * @@ -23,6 +24,7 @@ class LevelEditor(LevelEditorBase): # If you have your own ObjectPalette and ObjectHandler # connect them in your own LevelEditor class self.objectMgr = ObjectMgr(self) + self.animMgr = AnimMgr(self) self.objectPalette = ObjectPalette() self.objectHandler = ObjectHandler(self) self.protoPalette = ProtoPalette() diff --git a/direct/src/leveleditor/LevelEditorBase.py b/direct/src/leveleditor/LevelEditorBase.py index 6e6ea64789..01c6ec23a9 100755 --- a/direct/src/leveleditor/LevelEditorBase.py +++ b/direct/src/leveleditor/LevelEditorBase.py @@ -37,6 +37,8 @@ class LevelEditorBase(DirectObject): self.BASE_MODE = BitMask32.bit(0) self.CREATE_CURVE_MODE = BitMask32.bit(2) self.EDIT_CURVE_MODE = BitMask32.bit(3) + self.ANIM_MODE = BitMask32.bit(4) + self.GRAPH_EDITOR = False self.mode = self.BASE_MODE self.preMode = None @@ -245,8 +247,10 @@ class LevelEditorBase(DirectObject): return base.direct.deselectAll() + base.direct.selected.last = None self.ui.reset() self.objectMgr.reset() + self.animMgr.reset() self.actionMgr.reset() self.ui.perspView.camera.setPos(-19, -19, 19) self.ui.perspView.camera.lookAt(Point3(0, 0, 0)) diff --git a/direct/src/leveleditor/LevelEditorUIBase.py b/direct/src/leveleditor/LevelEditorUIBase.py index bd53ee8c2d..1d739ed507 100755 --- a/direct/src/leveleditor/LevelEditorUIBase.py +++ b/direct/src/leveleditor/LevelEditorUIBase.py @@ -14,6 +14,9 @@ from LayerEditorUI import * from HotKeyUI import * from ProtoPaletteUI import * from ActionMgr import * +from AnimControlUI import * +from CurveAnimUI import * +from GraphEditorUI import * class PandaTextDropTarget(wx.TextDropTarget): def __init__(self, editor, view): @@ -118,6 +121,10 @@ ID_PARENT_TO_SELECTED = 306 ID_CREATE_CURVE = 601 ID_EDIT_CURVE = 602 +ID_CURVE_ANIM = 603 + +ID_ANIM = 701 +ID_GRAPH = 702 class LevelEditorUIBase(WxPandaShell): """ Class for Panda3D LevelEditor """ @@ -140,7 +147,10 @@ class LevelEditorUIBase(WxPandaShell): ID_HOT_KEYS : ("&Hot Keys", None), ID_PARENT_TO_SELECTED : ("&Parent To Selected", None), ID_CREATE_CURVE : ("&Create Curve", None), - ID_EDIT_CURVE : ("&Edit Curve", None) + ID_EDIT_CURVE : ("&Edit Curve", None), + ID_CURVE_ANIM : ("&Curve Animation", None), + ID_ANIM : ("&Edit Animation", None), + ID_GRAPH : ("&Graph Editor", None) }) self.editor = editor @@ -217,8 +227,45 @@ class LevelEditorUIBase(WxPandaShell): self.editCurveMenuItem = self.menuCurve.Append(ID_EDIT_CURVE, self.MENU_TEXTS[ID_EDIT_CURVE][0], kind = wx.ITEM_CHECK) self.Bind(wx.EVT_MENU, self.onEditCurve, self.editCurveMenuItem) - + + self.curveAnimMenuItem = self.menuCurve.Append(ID_CURVE_ANIM, self.MENU_TEXTS[ID_CURVE_ANIM][0], kind = wx.ITEM_CHECK) + self.Bind(wx.EVT_MENU, self.onCurveAnim, self.curveAnimMenuItem) + + self.menuAnim = wx.Menu() + self.menuBar.Insert(4, self.menuAnim, "&AnimationMode") + + self.editAnimMenuItem = self.menuAnim.Append(ID_ANIM, self.MENU_TEXTS[ID_ANIM][0], kind = wx.ITEM_CHECK) + self.Bind(wx.EVT_MENU, self.onAnimation, self.editAnimMenuItem) + + self.graphEditorMenuItem = self.menuAnim.Append(ID_GRAPH, self.MENU_TEXTS[ID_GRAPH][0], kind = wx.ITEM_CHECK) + self.Bind(wx.EVT_MENU, self.onGraphEditor, self.graphEditorMenuItem) + WxPandaShell.createMenu(self) + + def onGraphEditor(self,e): + if base.direct.selected.last == None: + dlg = wx.MessageDialog(None, 'Please select a object first.', 'NOTICE', wx.OK ) + dlg.ShowModal() + dlg.Destroy() + self.graphEditorMenuItem.Check(False) + else: + currentObj = self.editor.objectMgr.findObjectByNodePath(base.direct.selected.last) + self.graphEditorUI = GraphEditorUI(self, self.editor, currentObj) + self.graphEditorUI.Show() + self.graphEditorMenuItem.Check(True) + + def onAnimation(self,e): + if self.editor.mode != self.editor.ANIM_MODE: + self.animUI = AnimControlUI(self, self.editor) + self.animUI.Show() + self.editor.mode = self.editor.ANIM_MODE + if self.editor.mode == self.editor.ANIM_MODE: + self.editAnimMenuItem.Check(True) + + def onCurveAnim(self,e): + self.curveAnimUI = CurveAnimUI(self, self.editor) + self.curveAnimUI.Show() + self.curveAnimMenuItem.Check(True) def onCreateCurve(self,e): """Function to invoke curve creating, need to check previous mode""" @@ -248,7 +295,7 @@ class LevelEditorUIBase(WxPandaShell): self.editCurveMenuItem.Check(False) def onEditCurve(self,e): - """Function to invoke curve editing and transfer global information to local information. Need to check previous mode""" + """Function to invoke curve editing and translate global information to local information. Need to check previous mode""" if self.editor.mode == self.editor.EDIT_CURVE_MODE: self.editCurveMenuItem.Check(False) self.editor.curveEditor.onBaseMode() @@ -639,3 +686,4 @@ class CurveDegreeUI(wx.Dialog): if(str(self.degree.GetSelection())=='2'): self.parent.editor.curveEditor.degree = 4 self.Destroy() + diff --git a/direct/src/leveleditor/ObjectMgrBase.py b/direct/src/leveleditor/ObjectMgrBase.py index 8bf01a4421..be93605d4a 100755 --- a/direct/src/leveleditor/ObjectMgrBase.py +++ b/direct/src/leveleditor/ObjectMgrBase.py @@ -31,8 +31,12 @@ class ObjectMgrBase: self.lastUid = '' self.lastUidMode = 0 self.currNodePath = None - self.currLiveNP = None + + self.Actor = [] + self.findActors(render) + self.Nodes = [] + self.findNodes(render) def reset(self): base.direct.deselectAllCB() @@ -50,6 +54,8 @@ class ObjectMgrBase: self.objects = {} self.npIndex = {} self.saveData = [] + self.Actor = [] + self.Nodes = [] def genUniqueId(self): # [gjeon] to solve the problem of unproper $USERNAME @@ -265,6 +271,17 @@ class ObjectMgrBase: def removeObjectById(self, uid): obj = self.findObjectById(uid) nodePath = obj[OG.OBJ_NP] + + for i in range(0,len(self.Actor)): + if self.Actor[i] == obj: + del self.Actor[i] + break + for i in range(0,len(self.Nodes)): + if self.Nodes[i][OG.OBJ_UID] == uid: + del self.Nodes[i] + break + self.editor.animMgr.removeAnimInfo(obj[OG.OBJ_UID]) + del self.objects[uid] del self.npIndex[nodePath] @@ -279,6 +296,16 @@ class ObjectMgrBase: def removeObjectByNodePath(self, nodePath): uid = self.npIndex.get(nodePath) if uid: + for i in range(0,len(self.Actor)): + if self.Actor[i][OG.OBJ_UID] == uid: + del self.Actor[i] + break + for i in range(0,len(self.Nodes)): + if self.Nodes[i][OG.OBJ_UID] == uid: + del self.Nodes[i] + break + self.editor.animMgr.removeAnimInfo(uid) + del self.objects[uid] del self.npIndex[nodePath] @@ -880,5 +907,27 @@ class ObjectMgrBase: def flatten(self, newobjModel, model, objDef, uid): # override this to flatten models pass + + def findActors(self, parent): + for child in parent.getChildren(): + if child.hasTag('OBJRoot') and not child.hasTag('Controller'): + obj = self.findObjectByNodePath(child) + if obj: + if isinstance(obj[OG.OBJ_NP],Actor): + self.Actor.append(obj) + + self.findActors(child) + + def findNodes(self, parent): + for child in parent.getChildren(): + if child.hasTag('OBJRoot') and not child.hasTag('Controller'): + obj = self.findObjectByNodePath(child) + + if obj: + self.Nodes.append(obj) + + self.findActors(child) + + diff --git a/direct/src/leveleditor/ObjectPropertyUI.py b/direct/src/leveleditor/ObjectPropertyUI.py index 5fd6228f8f..f04b21d3ad 100755 --- a/direct/src/leveleditor/ObjectPropertyUI.py +++ b/direct/src/leveleditor/ObjectPropertyUI.py @@ -5,11 +5,24 @@ import wx import os import math +from wx.lib.embeddedimage import PyEmbeddedImage from wx.lib.scrolledpanel import ScrolledPanel from wx.lib.agw.cubecolourdialog import * from direct.wxwidgets.WxSlider import * from pandac.PandaModules import * import ObjectGlobals as OG +import AnimGlobals as AG + +#---------------------------------------------------------------------- +Key = PyEmbeddedImage( + "iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAIAAACQKrqGAAAAA3NCSVQICAjb4U/gAAABIElE" + "QVQokZWSMW7CQBBFZ2Z3sQ02Ni4sOS6QiLgO5yBXIMcJ1KENje8QLESH7F3FVFQIIS3eTWGJ" + "VE7Iq6Z4+tL8GVRSwmPQg94fKiIOBoNer2et/U1FRER8X6+LonBdFwB4l+p53mq1qqRUUsZx" + "nKYpBwDOuRACEQGgaRoAYETn8/l4PL4uFkqp/X6fZRlnjO12u7KqENEa43keADDGvuo6Go0A" + "wPd9YkxrzY0x4/FYKlXX9eVymc1mjIiIgiD43G4BwFprmgYRubU2DMPnySTw/ev1+pSmRISI" + "SZJ8bDan06ksSyLiQmDXCfr9fp7nb8vldDp9mc9d1/1R27XaClscxzkcDlEUhcOhvt06U1uE" + "EMaYtpbOXlu01vf5Hz/wDRuDdIDl5WtQAAAAAElFTkSuQmCC") +#---------------------------------------------------------------------- class AnimFileDrop(wx.FileDropTarget): def __init__(self, editor): @@ -55,13 +68,63 @@ class ObjectPropUI(wx.Panel): self.label = wx.StaticText(self.labelPane, label=label) self.labelSizer = wx.BoxSizer(wx.HORIZONTAL) self.labelSizer.Add(self.label) + bmpKey = Key.GetBitmap() + self.setKeyButton = wx.BitmapButton(self.labelPane, -1, bmpKey, size = (15,15),style = wx.BU_AUTODRAW) + self.labelSizer.Add(self.setKeyButton) self.labelPane.SetSizer(self.labelSizer) self.uiPane = wx.Panel(self) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(self.labelPane) sizer.Add(self.uiPane, 1, wx.EXPAND, 0) self.SetSizer(sizer) + + self.setKeyButton.Bind(wx.EVT_BUTTON, self.onKey) + def onKey(self,evt): + self.parent = wx.GetTopLevelParent(self) + if self.parent.editor.mode == self.parent.editor.ANIM_MODE: + obj= self.parent.editor.objectMgr.findObjectByNodePath(base.direct.selected.last) + + objUID = obj[OG.OBJ_UID] + propertyName = self.label.GetLabelText() + + value = self.getValue() + frame = self.parent.editor.ui.animUI.curFrame + + if self.parent.editor.animMgr.keyFramesInfo.has_key((objUID,propertyName)): + for i in range(len(self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)])): + if self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)][i][AG.FRAME] == frame: + del self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)][i] + self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)].append([frame, value, [], []]) + #sort keyFrameInfo list by the order of frame number + sortKeyList = self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)] + for i in range(0, len(sortKeyList)-1): + for j in range(i+1, len(sortKeyList)): + if sortKeyList[i][AG.FRAME]>sortKeyList[j][AG.FRAME]: + temp = sortKeyList[i] + sortKeyList[i] = sortKeyList[j] + sortKeyList[j] = temp + + self.parent.editor.animMgr.generateSlope(self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)]) + else: + self.parent.editor.animMgr.keyFramesInfo[(objUID,propertyName)] = [[frame, value, [], []]] + + exist = False + for keyFrame in self.parent.editor.animMgr.keyFrames: + if frame == keyFrame: + exist = True + break + + if exist == False: + self.parent.editor.animMgr.keyFrames.append(frame) + self.parent.editor.ui.animUI.OnPropKey() + + else: + self.parent.editor.ui.animUI.OnPropKey() + + else: + evt.Skip() + def setValue(self, value): self.ui.SetValue(value)