diff --git a/.idea/AugmentWebviewStateStore.xml b/.idea/AugmentWebviewStateStore.xml index b7b6dff5..4dfb51a6 100644 --- a/.idea/AugmentWebviewStateStore.xml +++ b/.idea/AugmentWebviewStateStore.xml @@ -3,7 +3,7 @@ diff --git a/RenderPipelineFile/config/daytime.yaml b/RenderPipelineFile/config/daytime.yaml index 326b684f..0e1455d6 100644 --- a/RenderPipelineFile/config/daytime.yaml +++ b/RenderPipelineFile/config/daytime.yaml @@ -7,18 +7,18 @@ control_points: clouds: cloud_brightness: [[[0.4558541267,0.9574780059],[0.2744727256,0.8944309678],[0.1938559693,0.5249266862],[0.8905932438,0.2375366569],[0.6429901728,0.9589492375],[0.7581600000,0.8914912023],[0.2226478119,0.7859268915],[0.8406909789,0.4897360704],[0.5451055662,0.9618768328],[0.9462571977,0.0000000000],[1.0000000000,0.0000000000],[0.3646833013,0.9472140762],[0.3186180422,0.9325513197],[0.0823264879,0.0000000000]]] color_correction: - camera_iso: [[[0.4438274549,0.2674094708]]] - camera_shutter: [[[0.5211132438,0.0190100061]]] - camera_aperture: [[[0.5057581574,0.3577712610]]] + camera_iso: [[[0.4708024067,0.2757660168]]] + camera_shutter: [[[0.5134061147,0.0552217053]]] + camera_aperture: [[[0.5076849397,0.3299161078]]] env_probes: - ambient_scale: [[[0.4961464355,0.3175487465],[0.1368015414,0.3621169916],[0.8420038536,0.3983286908]]] + ambient_scale: [[[0.5038535646,0.3091922005],[0.1368015414,0.3621169916],[0.8420038536,0.3983286908]]] sky_ambient_scale: [[[0.4836223507,0.0974930362],[0.8497109827,0.0000000000],[0.0867052023,0.0027855153]]] sun_ambient_scale: [[[0.4913294797,0.0584958217],[0.8612716763,0.0306406685],[0.1425818882,0.0445682451]]] scattering: - sun_intensity: [[[0.0000000000,0.0000000000],[0.0041666667,0.0000000000],[0.0083333333,0.0000000000],[0.0125000000,0.0000000000],[0.0166666667,0.0000000000],[0.0208333333,0.0000000000],[0.0250000000,0.0000000000],[0.0291666667,0.0000000000],[0.0333333333,0.0000000000],[0.0375000000,0.0000000000],[0.0416666667,0.0000000000],[0.0458333333,0.0000000000],[0.0500000000,0.0000000000],[0.0541666667,0.0000000000],[0.0583333333,0.0000000000],[0.0625000000,0.0000000000],[0.0666666667,0.0000000000],[0.0708333333,0.0000000000],[0.0750000000,0.0000000000],[0.0791666667,0.0000000000],[0.0833333333,0.0000000000],[0.0875000000,0.0000000000],[0.0916666667,0.0000000000],[0.0958333333,0.0000000000],[0.1000000000,0.0000000000],[0.1041666667,0.0000000000],[0.1083333333,0.0000000000],[0.1125000000,0.0000000000],[0.1166666667,0.0000000000],[0.1208333333,0.0000000000],[0.1250000000,0.0000000000],[0.1291666667,0.0000000000],[0.1333333333,0.0000000000],[0.1375000000,0.0000000000],[0.1416666667,0.0000000000],[0.1458333333,0.0000000000],[0.1500000000,0.0000000000],[0.1541666667,0.0000000000],[0.1583333333,0.0000028805],[0.1625000000,0.0003577724],[0.1666666667,0.0013331400],[0.1708333333,0.0029671803],[0.1750000000,0.0052963381],[0.1791666667,0.0083550556],[0.1833333333,0.0121755589],[0.1875000000,0.0167876159],[0.1916666667,0.0222183530],[0.1958333333,0.0284919947],[0.2000000000,0.0356297193],[0.2041666667,0.0436494349],[0.2083333333,0.0525656099],[0.2125000000,0.0623891610],[0.2166666667,0.0731272461],[0.2208333333,0.0847831708],[0.2250000000,0.0973563167],[0.2291666667,0.1108419698],[0.2333333333,0.1252313631],[0.2375000000,0.1405115250],[0.2416666667,0.1566653434],[0.2458333333,0.1736715009],[0.2500000000,0.1915046014],[0.2541666667,0.2101350464],[0.2583333333,0.2295292930],[0.2625000000,0.2496498145],[0.2666666667,0.2704552670],[0.2708333333,0.2919006662],[0.2750000000,0.3139375192],[0.2791666667,0.3365139497],[0.2833333333,0.3595750662],[0.2875000000,0.3830630359],[0.2916666667,0.4069173972],[0.2958333333,0.4310753462],[0.3000000000,0.4554720417],[0.3041666667,0.4800408236],[0.3083333333,0.5047136020],[0.3125000000,0.5294212108],[0.3166666667,0.5540936424],[0.3208333333,0.5786605298],[0.3250000000,0.6030514553],[0.3291666667,0.6271963182],[0.3333333333,0.6510256858],[0.3375000000,0.6744711982],[0.3416666667,0.6974659988],[0.3458333333,0.7199450163],[0.3500000000,0.7418453485],[0.3541666667,0.7631067095],[0.3583333333,0.7836717291],[0.3625000000,0.8034862953],[0.3666666667,0.8224999302],[0.3708333333,0.8406661079],[0.3750000000,0.8579425235],[0.3791666667,0.8742914270],[0.3833333333,0.8896799131],[0.3875000000,0.9040801386],[0.3916666667,0.9174695289],[0.3958333333,0.9298310650],[0.4000000000,0.9411533765],[0.4041666667,0.9514309312],[0.4083333333,0.9606641691],[0.4125000000,0.9688595571],[0.4166666667,0.9760296330],[0.4208333333,0.9821930708],[0.4250000000,0.9873746114],[0.4291666667,0.9916050060],[0.4333333333,0.9949209310],[0.4375000000,0.9973647924],[0.4416666667,0.9989845508],[0.4458333333,0.9998334497],[0.4500000000,0.9999696949],[0.4541666667,0.9994560801],[0.4583333333,0.9983595429],[0.4625000000,0.9967506613],[0.4666666667,0.9947030614],[0.4708333333,0.9922927758],[0.4750000000,0.9895975125],[0.4791666667,0.9866958610],[0.4833333333,0.9836664262],[0.4875000000,0.9805868867],[0.4916666667,0.9775330316],[0.4958333333,0.9745777179],[0.5000000000,0.9717898417],[0.5041666667,0.9692332877],[0.5083333333,0.9669658924],[0.5125000000,0.9650384806],[0.5166666667,0.9634939916],[0.5208333333,0.9623666659],[0.5250000000,0.9616814371],[0.5291666667,0.9614534423],[0.5333333333,0.9616877089],[0.5375000000,0.9623790807],[0.5416666667,0.9635123329],[0.5458333333,0.9650624244],[0.5500000000,0.9669949804],[0.5541666667,0.9692669864],[0.5583333333,0.9718275065],[0.5625000000,0.9746185969],[0.5666666667,0.9775762863],[0.5708333333,0.9806315864],[0.5750000000,0.9837115661],[0.5791666667,0.9867403433],[0.5833333333,0.9896401655],[0.5875000000,0.9923323562],[0.5916666667,0.9947382579],[0.5958333333,0.9967800977],[0.6000000000,0.9983817820],[0.6041666667,0.9994696263],[0.6083333333,0.9999730028],[0.6125000000,0.9998249266],[0.6166666667,0.9989625601],[0.6208333333,0.9973276624],[0.6250000000,0.9948669567],[0.6291666667,0.9915324664],[0.6333333333,0.9872817545],[0.6375000000,0.9820781426],[0.6416666667,0.9758908775],[0.6458333333,0.9686952146],[0.6500000000,0.9604725211],[0.6541666667,0.9512102537],[0.6583333333,0.9409019858],[0.6625000000,0.9295473441],[0.6666666667,0.9171518878],[0.6708333333,0.9037270619],[0.6750000000,0.8892899902],[0.6791666667,0.8738633008],[0.6833333333,0.8574749656],[0.6875000000,0.8401579787],[0.6916666667,0.8219502453],[0.6958333333,0.8028941798],[0.7000000000,0.7830364456],[0.7041666667,0.7624277344],[0.7083333333,0.7411222520],[0.7125000000,0.7191776044],[0.7166666667,0.6966542563],[0.7208333333,0.6736152714],[0.7250000000,0.6501259629],[0.7291666667,0.6262533880],[0.7333333333,0.6020661121],[0.7375000000,0.5776338043],[0.7416666667,0.5530267796],[0.7458333333,0.5283156992],[0.7500000000,0.5035711751],[0.7541666667,0.4788634341],[0.7583333333,0.4542618347],[0.7625000000,0.4298347613],[0.7666666667,0.4056490351],[0.7708333333,0.3817697830],[0.7750000000,0.3582600107],[0.7791666667,0.3351803495],[0.7833333333,0.3125888445],[0.7875000000,0.2905406366],[0.7916666667,0.2690876955],[0.7958333333,0.2482787388],[0.8000000000,0.2281588906],[0.8041666667,0.2087696425],[0.8083333333,0.1901486315],[0.8125000000,0.1723295359],[0.8166666667,0.1553419918],[0.8208333333,0.1392115328],[0.8250000000,0.1239595144],[0.8291666667,0.1096030703],[0.8333333333,0.0961551918],[0.8375000000,0.0836246599],[0.8416666667,0.0720161369],[0.8458333333,0.0613302273],[0.8500000000,0.0515635598],[0.8541666667,0.0427088803],[0.8583333333,0.0347551990],[0.8625000000,0.0276878920],[0.8666666667,0.0214889271],[0.8708333333,0.0161369711],[0.8750000000,0.0116076130],[0.8791666667,0.0078735477],[0.8833333333,0.0049047927],[0.8875000000,0.0026688977],[0.8916666667,0.0011311782],[0.8958333333,0.0002549473],[0.9000000000,0.0000000000],[0.9041666667,0.0000000000],[0.9083333333,0.0000000000],[0.9125000000,0.0000000000],[0.9166666667,0.0000000000],[0.9208333333,0.0000000000],[0.9250000000,0.0000000000],[0.9291666667,0.0000000000],[0.9333333333,0.0000000000],[0.9375000000,0.0000000000],[0.9416666667,0.0000000000],[0.9458333333,0.0000000000],[0.9500000000,0.0000000000],[0.9541666667,0.0000000000],[0.9583333333,0.0000000000],[0.9625000000,0.0000000000],[0.9666666667,0.0000000000],[0.9708333333,0.0000000000],[0.9750000000,0.0000000000],[0.9791666667,0.0000000000],[0.9833333333,0.0000000000],[0.9875000000,0.0000000000],[0.9916666667,0.0000000000],[0.9958333333,0.0000000000]]] - sun_color: [[[0.4971900000,0.5763000000],[0.0433100000,0.8999700000],[0.8635787716,0.9130000000],[0.1785000000,0.8973600000],[0.8099800000,0.8651100000],[0.2360800000,0.7712700000],[0.6583432177,0.8485126184],[0.1266806142,0.9648102053],[0.9558541267,0.9090909091],[0.5568400771,0.7353760446]],[[0.5001318426,0.5160300000],[0.0572700000,0.6541600000],[0.2395000000,0.5976800000],[0.8104600000,0.6009000000],[0.6967400000,0.5483900000]],[[0.0862400000,0.4257800000],[0.4955600000,0.4033000000],[0.8234200000,0.4340200000]]] - sun_azimuth: [[[0.5000000002,0.5208913649]]] - sun_altitude: [[[0.5000000000,0.9171309191]]] + sun_intensity: [[[0.0000000000,0.0000000000],[0.0041666667,0.0000000000],[0.0083333333,0.0000000000],[0.0125000000,0.0000000000],[0.0166666667,0.0000000000],[0.0208333333,0.0000000000],[0.0250000000,0.0000000000],[0.0291666667,0.0000000000],[0.0333333333,0.0000000000],[0.0375000000,0.0000000000],[0.0416666667,0.0000000000],[0.0458333333,0.0000000000],[0.0500000000,0.0000000000],[0.0541666667,0.0000000000],[0.0583333333,0.0000000000],[0.0625000000,0.0000000000],[0.0666666667,0.0000000000],[0.0708333333,0.0000000000],[0.0750000000,0.0000000000],[0.0791666667,0.0000000000],[0.0833333333,0.0000000000],[0.0875000000,0.0000000000],[0.0916666667,0.0000000000],[0.0958333333,0.0000000000],[0.1000000000,0.0000000000],[0.1041666667,0.0000000000],[0.1083333333,0.0000000000],[0.1125000000,0.0000000000],[0.1166666667,0.0000000000],[0.1208333333,0.0000000000],[0.1250000000,0.0000000000],[0.1291666667,0.0000000000],[0.1333333333,0.0000000000],[0.1375000000,0.0000000000],[0.1416666667,0.0000000000],[0.1458333333,0.0000000000],[0.1500000000,0.0000000000],[0.1541666667,0.0000000000],[0.1583333333,0.0000028805],[0.1625000000,0.0003577724],[0.1666666667,0.0013331400],[0.1708333333,0.0029671803],[0.1750000000,0.0052963381],[0.1791666667,0.0083550556],[0.1833333333,0.0121755589],[0.1875000000,0.0167876159],[0.1916666667,0.0222183530],[0.1958333333,0.0284919947],[0.2000000000,0.0356297193],[0.2041666667,0.0436494349],[0.2083333333,0.0525656099],[0.2125000000,0.0623891610],[0.2166666667,0.0731272461],[0.2208333333,0.0847831708],[0.2250000000,0.0973563167],[0.2291666667,0.1108419698],[0.2333333333,0.1252313631],[0.2375000000,0.1405115250],[0.2416666667,0.1566653434],[0.2458333333,0.1736715009],[0.2500000000,0.1915046014],[0.2541666667,0.2101350464],[0.2583333333,0.2295292930],[0.2625000000,0.2496498145],[0.2666666667,0.2704552670],[0.2708333333,0.2919006662],[0.2750000000,0.3139375192],[0.2791666667,0.3365139497],[0.2833333333,0.3595750662],[0.2875000000,0.3830630359],[0.2916666667,0.4069173972],[0.2958333333,0.4310753462],[0.3000000000,0.4554720417],[0.3041666667,0.4800408236],[0.3083333333,0.5047136020],[0.3125000000,0.5294212108],[0.3166666667,0.5540936424],[0.3208333333,0.5786605298],[0.3250000000,0.6030514553],[0.3291666667,0.6271963182],[0.3333333333,0.6510256858],[0.3375000000,0.6744711982],[0.3416666667,0.6974659988],[0.3458333333,0.7199450163],[0.3500000000,0.7418453485],[0.3541666667,0.7631067095],[0.3583333333,0.7836717291],[0.3625000000,0.8034862953],[0.3666666667,0.8224999302],[0.3708333333,0.8406661079],[0.3750000000,0.8579425235],[0.3791666667,0.8742914270],[0.3833333333,0.8896799131],[0.3875000000,0.9040801386],[0.3916666667,0.9174695289],[0.3958333333,0.9298310650],[0.4000000000,0.9411533765],[0.4041666667,0.9514309312],[0.4083333333,0.9606641691],[0.4125000000,0.9688595571],[0.4166666667,0.9760296330],[0.4208333333,0.9821930708],[0.4250000000,0.9873746114],[0.4291666667,0.9916050060],[0.4333333333,0.9949209310],[0.4375000000,0.9973647924],[0.4416666667,0.9989845508],[0.4458333333,0.9998334497],[0.4500000000,0.9999696949],[0.4541666667,0.9994560801],[0.4583333333,0.9983595429],[0.4625000000,0.9967506613],[0.4666666667,0.9947030614],[0.4708333333,0.9922927758],[0.4750000000,0.9895975125],[0.4791666667,0.9866958610],[0.4833333333,0.9836664262],[0.4875000000,0.9805868867],[0.4916666667,0.9775330316],[0.4958333333,0.9745777179],[0.5000000000,0.9717898417],[0.5041666667,0.9692332877],[0.5083333333,0.9669658924],[0.5125000000,0.9650384806],[0.5089595376,0.9690650222],[0.5208333333,0.9623666659],[0.5250000000,0.9616814371],[0.5291666667,0.9614534423],[0.5333333333,0.9616877089],[0.5375000000,0.9623790807],[0.5416666667,0.9635123329],[0.5458333333,0.9650624244],[0.5500000000,0.9669949804],[0.5541666667,0.9692669864],[0.5583333333,0.9718275065],[0.5625000000,0.9746185969],[0.5666666667,0.9775762863],[0.5708333333,0.9806315864],[0.5750000000,0.9837115661],[0.5791666667,0.9867403433],[0.5833333333,0.9896401655],[0.5875000000,0.9923323562],[0.5916666667,0.9947382579],[0.5958333333,0.9967800977],[0.6000000000,0.9983817820],[0.6041666667,0.9994696263],[0.6083333333,0.9999730028],[0.6125000000,0.9998249266],[0.6166666667,0.9989625601],[0.6208333333,0.9973276624],[0.6250000000,0.9948669567],[0.6291666667,0.9915324664],[0.6333333333,0.9872817545],[0.6375000000,0.9820781426],[0.6416666667,0.9758908775],[0.6458333333,0.9686952146],[0.6500000000,0.9604725211],[0.6541666667,0.9512102537],[0.6583333333,0.9409019858],[0.6625000000,0.9295473441],[0.6666666667,0.9171518878],[0.6708333333,0.9037270619],[0.6750000000,0.8892899902],[0.6791666667,0.8738633008],[0.6833333333,0.8574749656],[0.6875000000,0.8401579787],[0.6916666667,0.8219502453],[0.6958333333,0.8028941798],[0.7000000000,0.7830364456],[0.7041666667,0.7624277344],[0.7083333333,0.7411222520],[0.7125000000,0.7191776044],[0.7166666667,0.6966542563],[0.7208333333,0.6736152714],[0.7250000000,0.6501259629],[0.7291666667,0.6262533880],[0.7333333333,0.6020661121],[0.7375000000,0.5776338043],[0.7416666667,0.5530267796],[0.7458333333,0.5283156992],[0.7500000000,0.5035711751],[0.7541666667,0.4788634341],[0.7583333333,0.4542618347],[0.7625000000,0.4298347613],[0.7666666667,0.4056490351],[0.7708333333,0.3817697830],[0.7750000000,0.3582600107],[0.7791666667,0.3351803495],[0.7833333333,0.3125888445],[0.7875000000,0.2905406366],[0.7916666667,0.2690876955],[0.7958333333,0.2482787388],[0.8000000000,0.2281588906],[0.8041666667,0.2087696425],[0.8083333333,0.1901486315],[0.8125000000,0.1723295359],[0.8166666667,0.1553419918],[0.8208333333,0.1392115328],[0.8250000000,0.1239595144],[0.8291666667,0.1096030703],[0.8333333333,0.0961551918],[0.8375000000,0.0836246599],[0.8416666667,0.0720161369],[0.8458333333,0.0613302273],[0.8500000000,0.0515635598],[0.8541666667,0.0427088803],[0.8583333333,0.0347551990],[0.8625000000,0.0276878920],[0.8666666667,0.0214889271],[0.8708333333,0.0161369711],[0.8750000000,0.0116076130],[0.8791666667,0.0078735477],[0.8833333333,0.0049047927],[0.8875000000,0.0026688977],[0.8916666667,0.0011311782],[0.8958333333,0.0002549473],[0.9000000000,0.0000000000],[0.9041666667,0.0000000000],[0.9083333333,0.0000000000],[0.9125000000,0.0000000000],[0.9166666667,0.0000000000],[0.9208333333,0.0000000000],[0.9250000000,0.0000000000],[0.9291666667,0.0000000000],[0.9333333333,0.0000000000],[0.9375000000,0.0000000000],[0.9416666667,0.0000000000],[0.9458333333,0.0000000000],[0.9500000000,0.0000000000],[0.9541666667,0.0000000000],[0.9583333333,0.0000000000],[0.9625000000,0.0000000000],[0.9666666667,0.0000000000],[0.9708333333,0.0000000000],[0.9750000000,0.0000000000],[0.9791666667,0.0000000000],[0.9833333333,0.0000000000],[0.9875000000,0.0000000000],[0.9916666667,0.0000000000],[0.9958333333,0.0000000000]]] + sun_color: [[[0.5010435645,0.5818710306],[0.0433100000,0.8999700000],[0.8635787716,0.9130000000],[0.1785000000,0.8973600000],[0.8099800000,0.8651100000],[0.2360800000,0.7712700000],[0.6583432177,0.8485126184],[0.1266806142,0.9648102053],[0.9558541267,0.9090909091],[0.5568400771,0.7353760446]],[[0.5001318426,0.5160300000],[0.0572700000,0.6541600000],[0.2395000000,0.5976800000],[0.8104600000,0.6009000000],[0.6967400000,0.5483900000]],[[0.0862400000,0.4257800000],[0.4955600000,0.4033000000],[0.8234200000,0.4340200000]]] + sun_azimuth: [[[0.5000000000,0.2583333333]]] + sun_altitude: [[[0.5000000000,0.5111111111]]] extinction: [[[0.4913294798,0.6378830084]]] volumetrics: fog_ramp_size: [[[0.5510597303,0.7409470752]]] diff --git a/RenderPipelineFile/data/gui/Default_Rough.png b/RenderPipelineFile/data/gui/Default_Rough.png new file mode 100644 index 00000000..1a4e9d65 Binary files /dev/null and b/RenderPipelineFile/data/gui/Default_Rough.png differ diff --git a/RenderPipelineFile/data/gui/black.jpg b/RenderPipelineFile/data/gui/black.jpg new file mode 100644 index 00000000..cbf1ccb0 Binary files /dev/null and b/RenderPipelineFile/data/gui/black.jpg differ diff --git a/RenderPipelineFile/rpcore/render_pipeline.py b/RenderPipelineFile/rpcore/render_pipeline.py index f5fa7e34..0424783f 100644 --- a/RenderPipelineFile/rpcore/render_pipeline.py +++ b/RenderPipelineFile/rpcore/render_pipeline.py @@ -86,7 +86,8 @@ class RenderPipeline(RPObject): self._applied_effects = [] self._pre_showbase_initialized = False self._first_frame = None - self.set_loading_screen_image("/$$rp/data/gui/loading_screen_bg.txo") + #self.set_loading_screen_image("/$$rp/data/gui/loading_screen_bg.txo") + self.set_loading_screen_image("/$$rp/data/gui/Default_Rough.png") def load_settings(self, path): """ Loads the pipeline configuration from a given filename. Usually diff --git a/RenderPipelineFile/rpcore/util/display_shader_builder.py b/RenderPipelineFile/rpcore/util/display_shader_builder.py index 6f2d7679..4231ee50 100644 --- a/RenderPipelineFile/rpcore/util/display_shader_builder.py +++ b/RenderPipelineFile/rpcore/util/display_shader_builder.py @@ -71,8 +71,8 @@ class DisplayShaderBuilder(object): # pylint: disable=too-few-public-methods # Build actual shader built = """ #version 400 - #extension GL_ARB_shading_language_420pack : enable #pragma include "render_pipeline_base.inc.glsl" + #extension GL_ARB_shading_language_420pack : enable in vec2 texcoord; out vec3 result; uniform int mipmap; diff --git a/RenderPipelineFile/rpcore/util/network_communication.py b/RenderPipelineFile/rpcore/util/network_communication.py index 897293c6..cb1b65f1 100644 --- a/RenderPipelineFile/rpcore/util/network_communication.py +++ b/RenderPipelineFile/rpcore/util/network_communication.py @@ -127,9 +127,20 @@ class NetworkCommunication(RPObject): be an update of a plugin setting """ if cmd.startswith("setval "): parts = cmd.split() + if len(parts) < 3: + self.warn("Invalid setval command format. Expected: setval plugin.setting value") + return + setting_parts = parts[1].split(".") - self._pipeline.plugin_mgr.on_setting_changed( - setting_parts[0], setting_parts[1], parts[2]) + if len(setting_parts) < 2: + self.warn("Invalid setting format. Expected: plugin.setting") + return + + try: + self._pipeline.plugin_mgr.on_setting_changed( + setting_parts[0], setting_parts[1], parts[2]) + except Exception as e: + self.warn("Error handling setting change:", str(e)) else: self.warn("Recieved unkown plugin command:", cmd) diff --git a/RenderPipelineFile/rpplugins/scattering/shader/eric_bruneton/scattering_common.glsl b/RenderPipelineFile/rpplugins/scattering/shader/eric_bruneton/scattering_common.glsl index 01de7b21..d68055ab 100644 --- a/RenderPipelineFile/rpplugins/scattering/shader/eric_bruneton/scattering_common.glsl +++ b/RenderPipelineFile/rpplugins/scattering/shader/eric_bruneton/scattering_common.glsl @@ -1,7 +1,5 @@ #pragma once -#extension GL_ARB_shading_language_420pack : enable - // This file includes defines and functions used for the scattering @@ -9,6 +7,8 @@ #pragma include "render_pipeline_base.inc.glsl" +#extension GL_ARB_shading_language_420pack : enable + // These can be overridden depending on if the shader is a compute shader // or a regular fragment shader diff --git a/RenderPipelineFile/toolkit/plugin_configurator/main.py b/RenderPipelineFile/toolkit/plugin_configurator/main.py index 9ed4dba1..ea560ab2 100644 --- a/RenderPipelineFile/toolkit/plugin_configurator/main.py +++ b/RenderPipelineFile/toolkit/plugin_configurator/main.py @@ -97,6 +97,11 @@ class PluginConfigurator(QMainWindow, Ui_MainWindow): update_thread = Thread(target=self.update_thread, args=()) update_thread.start() + # 启动通信文件监听线程 + comm_thread = Thread(target=self.communication_thread, args=()) + comm_thread.daemon = True # 设置为守护线程,主程序退出时自动结束 + comm_thread.start() + def closeEvent(self, event): # noqa event.accept() import os @@ -486,6 +491,84 @@ class PluginConfigurator(QMainWindow, Ui_MainWindow): self.lst_plugins.setCurrentRow(0) + def communication_thread(self): + """监听来自主程序的通信文件""" + import json + import tempfile + + comm_dir = os.path.join(tempfile.gettempdir(), "daytime_editor_comm") + comm_file = os.path.join(comm_dir, "sun_azimuth_command.json") + + print("🔄 Day Time Editor通信监听已启动") + print(f" 监听文件: {comm_file}") + + last_timestamp = 0 + + while True: + try: + if os.path.exists(comm_file): + with open(comm_file, 'r') as f: + command = json.load(f) + + # 检查是否是新命令(避免重复执行) + if command.get('timestamp', 0) > last_timestamp: + last_timestamp = command.get('timestamp', 0) + + if command.get('command') == 'set_sun_azimuth': + azimuth_value = command.get('value', 0) + print(f"📨 收到Sun Azimuth命令: {azimuth_value}°") + + # 在主线程中执行UI更新 + QTimer.singleShot(0, partial(self.apply_sun_azimuth, azimuth_value)) + + time.sleep(0.1) # 100ms检查一次 + + except Exception as e: + print(f"⚠️ 通信监听错误: {e}") + time.sleep(1) # 出错时等待1秒再重试 + + def apply_sun_azimuth(self, azimuth_degrees): + """应用Sun Azimuth值到Day Time Editor""" + try: + print(f"🌞 正在应用Sun Azimuth: {azimuth_degrees}°") + + # 查找Sun Azimuth相关的控件 + # 这里需要根据实际的UI结构来实现 + # 先尝试找到可能的控件 + + # 方法1:查找包含"azimuth"的控件 + for widget_name in dir(self): + widget = getattr(self, widget_name, None) + if widget and hasattr(widget, 'setValue'): + if 'azimuth' in widget_name.lower() or 'sun' in widget_name.lower(): + print(f" 尝试设置控件: {widget_name}") + try: + widget.setValue(azimuth_degrees) + print(f"✅ 成功设置 {widget_name} = {azimuth_degrees}°") + return + except Exception as e: + print(f" 设置失败: {e}") + + # 方法2:查找表格中的Sun Azimuth设置 + if hasattr(self, 'table_plugin_settings'): + table = self.table_plugin_settings + for row in range(table.rowCount()): + item = table.item(row, 0) # 第一列通常是设置名称 + if item and 'azimuth' in item.text().lower(): + # 找到Sun Azimuth行,设置第二列的值 + value_item = table.item(row, 1) + if value_item: + value_item.setText(str(azimuth_degrees)) + print(f"✅ 在表格中设置Sun Azimuth = {azimuth_degrees}°") + return + + print("⚠️ 未找到Sun Azimuth控件,请检查UI结构") + + except Exception as e: + print(f"❌ 应用Sun Azimuth失败: {e}") + import traceback + traceback.print_exc() + # Start application app = QApplication(sys.argv) qt_register_fonts() diff --git a/__pycache__/main.cpython-312.pyc b/__pycache__/main.cpython-312.pyc index a49581b5..23558b48 100644 Binary files a/__pycache__/main.cpython-312.pyc and b/__pycache__/main.cpython-312.pyc differ diff --git a/core/__pycache__/world.cpython-312.pyc b/core/__pycache__/world.cpython-312.pyc index 4091bb38..de54a1b7 100644 Binary files a/core/__pycache__/world.cpython-312.pyc and b/core/__pycache__/world.cpython-312.pyc differ diff --git a/core/world.py b/core/world.py index 0a5bdc67..3eb4a53e 100644 --- a/core/world.py +++ b/core/world.py @@ -1,13 +1,16 @@ import math import warnings + +from direct.actor.Actor import Actor + warnings.filterwarnings("ignore", category=DeprecationWarning) from QPanda3D.Panda3DWorld import Panda3DWorld from panda3d.core import (CardMaker, Vec4, Vec3, AmbientLight, DirectionalLight, Point3, WindowProperties,Material,LColor) from direct.showbase.ShowBaseGlobal import globalClock - +from scene.scene_manager import SceneManager class CoreWorld(Panda3DWorld): """核心世界功能类 - 负责基础的3D世界设置和核心功能""" @@ -49,6 +52,7 @@ class CoreWorld(Panda3DWorld): # #self.createDirectionalLight() #self._setYCModel() + #self._setArm1() # 创建示例材质 #self.load_test_models_with_materials() @@ -70,6 +74,19 @@ class CoreWorld(Panda3DWorld): model.setPos(-8, 42, 0) model.setHpr(0, 90, 0) + + def _setArm1(self): + model1 = self.loader.loadModel("/home/tiger/下载/JQB1.fbx") + model2 = self.loader.loadModel("/home/tiger/下载/JQB2.fbx") + model1.reparentTo(self.render) + model1.setPos(0, 5, 0) + model1.setHpr(0, 90, 0) + model1.setScale(0.01) + model2.reparentTo(self.render) + model2.setPos(0, -5, 0) + model2.setHpr(0, 90, 0) + model2.setScale(0.01) + def _setupCamera(self): """设置相机位置和朝向""" self.cam.setPos(0, -50, 20) @@ -288,8 +305,19 @@ class CoreWorld(Panda3DWorld): def set_daytime(self, time_str): - self.render_pipeline.daytime_mgr.time = time_str - print(f"当前时间设置为: {time_str}") + """设置时间""" + try: + if hasattr(self, 'render_pipeline') and self.render_pipeline: + self.render_pipeline.daytime_mgr.time = time_str + print(f"当前时间设置为: {time_str}") + else: + print(f"⚠️ RenderPipeline 不可用,无法设置时间: {time_str}") + except Exception as e: + print(f"设置时间失败: {e}") + + def get_render_pipeline(self): + """获取 RenderPipeline 实例""" + return getattr(self, 'render_pipeline', None) def _setupSkybox(self): # 加载天空盒模型 diff --git a/main.py b/main.py index ea59bfef..31226662 100644 --- a/main.py +++ b/main.py @@ -59,7 +59,7 @@ class MyWorld(CoreWorld): # 初始化脚本管理系统 self.script_manager = ScriptManager(self) - + # 初始化GUI管理系统 self.gui_manager = GUIManager(self) @@ -509,7 +509,6 @@ class MyWorld(CoreWorld): def listAllScripts(self): """列出所有脚本信息""" return self.script_manager.list_all_scripts() - # ==================== VR系统功能代理 ==================== # VR系统控制方法 - 代理到vr_manager diff --git a/scene/__pycache__/scene_manager.cpython-312.pyc b/scene/__pycache__/scene_manager.cpython-312.pyc index bcc493fd..456f3fef 100644 Binary files a/scene/__pycache__/scene_manager.cpython-312.pyc and b/scene/__pycache__/scene_manager.cpython-312.pyc differ diff --git a/scene/scene_manager.py b/scene/scene_manager.py index 55a936a7..d77f5211 100644 --- a/scene/scene_manager.py +++ b/scene/scene_manager.py @@ -15,13 +15,14 @@ from panda3d.core import ( from panda3d.egg import EggData, EggVertexPool from direct.actor.Actor import Actor from QPanda3D.Panda3DWorld import get_render_pipeline +from scene.animation_manager import AnimationManager class SceneManager: """场景管理器 - 统一管理场景中的所有元素""" def __init__(self, world): """初始化场景管理器 - + Args: world: 主程序world对象引用 """ @@ -30,7 +31,10 @@ class SceneManager: self.Spotlight = [] self.Pointlight = [] - + + # 初始化动画管理器 + self.animation_manager = AnimationManager(world) + print("✓ 场景管理系统初始化完成") # ==================== 模型导入和处理 ==================== @@ -105,6 +109,64 @@ class SceneManager: except Exception as e: print(f"导入模型失败: {str(e)}") return None + + def importAnimatedFBX(self, filepath, scale=0.01, auto_play=True): + """导入带动画的FBX模型(使用assimp) + + Args: + filepath: FBX文件路径 + scale: 缩放比例(默认0.01,从厘米转换到米) + auto_play: 是否自动播放第一个动画 + + Returns: + Actor对象,如果加载失败返回None + """ + try: + print(f"\n=== 导入动画FBX模型: {filepath} ===") + + # 使用动画管理器加载FBX + actor = self.animation_manager.load_fbx_with_animations(filepath, scale) + + if actor: + # 设置模型名称 + model_name = os.path.basename(filepath) + actor.setName(model_name) + + # 调整模型位置到地面 + self._adjustModelToGround(actor) + + # 设置碰撞检测 + self.setupCollision(actor) + + # 添加文件标签 + actor.setTag("file", model_name) + actor.setTag("is_animated_model", "1") + + # 添加到模型列表 + self.models.append(actor) + + # 自动播放第一个动画 + if auto_play: + available_anims = self.animation_manager.get_available_animations(actor) + if available_anims: + first_anim = available_anims[0] + self.animation_manager.play_animation(actor, first_anim, loop=True) + print(f"🎬 自动播放动画: {first_anim}") + + # 更新场景树 + self.updateSceneTree() + + print(f"=== 动画FBX模型导入成功: {model_name} ===\n") + return actor + else: + print("❌ 动画FBX模型导入失败") + return None + + except Exception as e: + print(f"导入动画FBX模型失败: {str(e)}") + import traceback + traceback.print_exc() + return None def _applyMaterialsToModel(self, model): """递归应用材质到模型的所有GeomNode""" @@ -425,25 +487,59 @@ class SceneManager: except Exception as e: print(f"异步加载模型失败: {str(e)}") - def loadAnimatedModel(self, model_path, anims=None): - """加载带动画的模型""" + def loadAnimatedModel(self, model_path, anims=None, auto_play=True): + """加载带动画的模型 + + Args: + model_path: 模型文件路径 + anims: 动画字典,格式为 {"动画名": "动画文件路径"} + auto_play: 是否自动播放第一个动画 + """ try: + print(f"🎬 加载动画模型: {model_path}") + + # 如果没有指定动画,尝试自动检测 + if anims is None: + anims = self._detectAnimations(model_path) + # 创建Actor对象 actor = Actor(model_path, anims) if actor: actor.reparentTo(self.world.render) - + # 设置碰撞检测 self.setupCollision(actor) - + + # 获取可用的动画列表 + available_anims = actor.getAnimNames() + print(f"📋 检测到动画: {available_anims}") + + # 自动播放第一个动画 + if auto_play and available_anims: + first_anim = available_anims[0] + actor.loop(first_anim) + print(f"▶️ 自动播放动画: {first_anim}") + + # 添加动画控制标签 + actor.setTag("animated", "true") + actor.setTag("current_anim", first_anim) + actor.setTag("available_anims", str(available_anims)) + + # 调整模型位置(让它站在地面上) + self._adjustModelPosition(actor) + self.models.append(actor) # 更新场景树 self.updateSceneTree() + + print(f"✅ 动画模型加载完成: {actor.getName()}") return actor except Exception as e: - print(f"加载动画模型失败: {str(e)}") + print(f"❌ 加载动画模型失败: {str(e)}") + import traceback + traceback.print_exc() return None - + # ==================== 材质和几何体处理 ==================== def processMaterials(self, model): diff --git a/scripts/R_P.py b/scripts/R_P.py new file mode 100644 index 00000000..ba6ae6b9 --- /dev/null +++ b/scripts/R_P.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +旋转脚本 - 让对象持续旋转 +""" + +from core.script_system import ScriptBase + + +class RotatorScript(ScriptBase): + """旋转脚本类""" + + def __init__(self): + super().__init__() + self.rotation_speed_y = 30.0 # Y轴旋转速度 (度/秒) + self.max_angle = 30.0 # 最大旋转角度(相对于初始角度) + self.direction = 1 + self.current_offset = 0.0 # 当前相对于初始角度的偏移 + self.initial_angle = None # 模型的初始角度 + self.is_rotating = True # 是否正在旋转 + + def start(self): + """脚本开始时调用""" + self.log("旋转脚本启动!") + self.log(f"旋转速度: {self.rotation_speed_y}度/秒") + self.log(f"最大旋转角度: ±{self.max_angle}度") + + # 记录模型的初始角度 + if self.gameObject: + initial_hpr = self.gameObject.getHpr() + self.initial_angle = initial_hpr.getZ() # 记录Z轴的初始角度 + self.log(f"模型初始角度: {self.initial_angle}度") + self.log(f"旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°") + else: + self.log("⚠️ 无法获取游戏对象,使用默认初始角度0") + self.initial_angle = 0.0 + + def update(self, dt): + """每帧更新""" + if not self.is_rotating or self.initial_angle is None: + return + + # 计算角度变化量 + delta_angle = self.rotation_speed_y * dt * self.direction + self.current_offset += delta_angle + + # 如果超出角度范围,则反向并限制在边界 + if self.current_offset > self.max_angle: + self.current_offset = self.max_angle + self.direction *= -1 + elif self.current_offset < -self.max_angle: + self.current_offset = -self.max_angle + self.direction *= -1 + + # 计算最终角度(初始角度 + 偏移量) + final_angle = self.initial_angle + self.current_offset + + # 设置新的旋转(只改变Z轴,保持其他不变) + current_hpr = self.gameObject.getHpr() + self.gameObject.setHpr(current_hpr.getX(), final_angle, current_hpr.getZ()) + + # if not self.is_rotating: + # return + # + # # 获取当前旋转并应用增量 + # current_hpr = self.gameObject.getHpr() + # new_r = current_hpr.getZ() + self.rotation_speed_y * dt + # self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), new_r) + + def on_destroy(self): + """脚本销毁时调用""" + self.log("旋转脚本停止") + + # ==================== 控制方法 ==================== + + def set_max_angle(self, new_max_angle): + """ + 设置新的最大旋转角度 + + Args: + new_max_angle: 新的最大角度值 + """ + self.max_angle = new_max_angle + self.log(f"最大旋转角度已设置为: ±{self.max_angle}度") + if self.initial_angle is not None: + self.log(f"新的旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°") + + def set_rotation_speed(self, new_speed): + """ + 设置新的旋转速度 + + Args: + new_speed: 新的旋转速度(度/秒) + """ + self.rotation_speed_y = new_speed + self.log(f"旋转速度已设置为: {self.rotation_speed_y}度/秒") + + def pause_rotation(self): + """暂停旋转""" + self.is_rotating = False + self.log("旋转已暂停") + + def resume_rotation(self): + """恢复旋转""" + self.is_rotating = True + self.log("旋转已恢复") + + def reset_to_initial_angle(self): + """重置到初始角度""" + if self.initial_angle is not None and self.gameObject: + current_hpr = self.gameObject.getHpr() + self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), self.initial_angle) + self.current_offset = 0.0 + self.direction = 1 + self.log(f"已重置到初始角度: {self.initial_angle}度") + + def get_current_info(self): + """获取当前旋转信息""" + if self.gameObject and self.initial_angle is not None: + current_hpr = self.gameObject.getHpr() + current_angle = current_hpr.getZ() + self.log("=== 当前旋转信息 ===") + self.log(f"初始角度: {self.initial_angle}度") + self.log(f"当前角度: {current_angle}度") + self.log(f"偏移量: {self.current_offset}度") + self.log(f"最大角度: ±{self.max_angle}度") + self.log(f"旋转速度: {self.rotation_speed_y}度/秒") + self.log(f"旋转方向: {'正向' if self.direction > 0 else '反向'}") + self.log(f"旋转状态: {'运行中' if self.is_rotating else '已暂停'}") + self.log("=== 信息结束 ===") + else: + self.log("无法获取旋转信息") \ No newline at end of file diff --git a/scripts/R_R.py b/scripts/R_R.py new file mode 100644 index 00000000..c78e88cb --- /dev/null +++ b/scripts/R_R.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +旋转脚本 - 让对象持续旋转 +""" + +from core.script_system import ScriptBase + + +class RotatorScript(ScriptBase): + """旋转脚本类""" + + def __init__(self): + super().__init__() + self.rotation_speed_y = 30.0 # Y轴旋转速度 (度/秒) + self.max_angle = 30.0 # 最大旋转角度(相对于初始角度) + self.direction = 1 + self.current_offset = 0.0 # 当前相对于初始角度的偏移 + self.initial_angle = None # 模型的初始角度 + self.is_rotating = True # 是否正在旋转 + + def start(self): + """脚本开始时调用""" + self.log("旋转脚本启动!") + self.log(f"旋转速度: {self.rotation_speed_y}度/秒") + self.log(f"最大旋转角度: ±{self.max_angle}度") + + # 记录模型的初始角度 + if self.gameObject: + initial_hpr = self.gameObject.getHpr() + self.initial_angle = initial_hpr.getZ() # 记录Z轴的初始角度 + self.log(f"模型初始角度: {self.initial_angle}度") + self.log(f"旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°") + else: + self.log("⚠️ 无法获取游戏对象,使用默认初始角度0") + self.initial_angle = 0.0 + + def update(self, dt): + """每帧更新""" + if not self.is_rotating or self.initial_angle is None: + return + + # 计算角度变化量 + delta_angle = self.rotation_speed_y * dt * self.direction + self.current_offset += delta_angle + + # 如果超出角度范围,则反向并限制在边界 + if self.current_offset > self.max_angle: + self.current_offset = self.max_angle + self.direction *= -1 + elif self.current_offset < -self.max_angle: + self.current_offset = -self.max_angle + self.direction *= -1 + + # 计算最终角度(初始角度 + 偏移量) + final_angle = self.initial_angle + self.current_offset + + # 设置新的旋转(只改变Z轴,保持其他不变) + current_hpr = self.gameObject.getHpr() + self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), final_angle) + + # if not self.is_rotating: + # return + # + # # 获取当前旋转并应用增量 + # current_hpr = self.gameObject.getHpr() + # new_r = current_hpr.getZ() + self.rotation_speed_y * dt + # self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), new_r) + + def on_destroy(self): + """脚本销毁时调用""" + self.log("旋转脚本停止") + + # ==================== 控制方法 ==================== + + def set_max_angle(self, new_max_angle): + """ + 设置新的最大旋转角度 + + Args: + new_max_angle: 新的最大角度值 + """ + self.max_angle = new_max_angle + self.log(f"最大旋转角度已设置为: ±{self.max_angle}度") + if self.initial_angle is not None: + self.log(f"新的旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°") + + def set_rotation_speed(self, new_speed): + """ + 设置新的旋转速度 + + Args: + new_speed: 新的旋转速度(度/秒) + """ + self.rotation_speed_y = new_speed + self.log(f"旋转速度已设置为: {self.rotation_speed_y}度/秒") + + def pause_rotation(self): + """暂停旋转""" + self.is_rotating = False + self.log("旋转已暂停") + + def resume_rotation(self): + """恢复旋转""" + self.is_rotating = True + self.log("旋转已恢复") + + def reset_to_initial_angle(self): + """重置到初始角度""" + if self.initial_angle is not None and self.gameObject: + current_hpr = self.gameObject.getHpr() + self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), self.initial_angle) + self.current_offset = 0.0 + self.direction = 1 + self.log(f"已重置到初始角度: {self.initial_angle}度") + + def get_current_info(self): + """获取当前旋转信息""" + if self.gameObject and self.initial_angle is not None: + current_hpr = self.gameObject.getHpr() + current_angle = current_hpr.getZ() + self.log("=== 当前旋转信息 ===") + self.log(f"初始角度: {self.initial_angle}度") + self.log(f"当前角度: {current_angle}度") + self.log(f"偏移量: {self.current_offset}度") + self.log(f"最大角度: ±{self.max_angle}度") + self.log(f"旋转速度: {self.rotation_speed_y}度/秒") + self.log(f"旋转方向: {'正向' if self.direction > 0 else '反向'}") + self.log(f"旋转状态: {'运行中' if self.is_rotating else '已暂停'}") + self.log("=== 信息结束 ===") + else: + self.log("无法获取旋转信息") \ No newline at end of file diff --git a/scripts/Rotate_H_Script.py b/scripts/Rotate_H_Script.py new file mode 100644 index 00000000..ffe0e294 --- /dev/null +++ b/scripts/Rotate_H_Script.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +旋转脚本 - 让对象持续旋转 +""" + +from core.script_system import ScriptBase + + +class RotatorScript(ScriptBase): + """旋转脚本类""" + + def __init__(self): + super().__init__() + self.rotation_speed_y = 30.0 # Y轴旋转速度 (度/秒) + self.max_angle = 30.0 # 最大旋转角度(相对于初始角度) + self.direction = 1 + self.current_offset = 0.0 # 当前相对于初始角度的偏移 + self.initial_angle = None # 模型的初始角度 + self.is_rotating = True # 是否正在旋转 + + # 机器人式停顿参数 + self.pause_duration = 0.5 # 停顿时间(秒) + self.current_pause_time = 0.0 # 当前停顿计时 + self.is_paused = False # 是否正在停顿 + self.robot_mode = True # 是否启用机器人模式 + + def start(self): + """脚本开始时调用""" + self.log("机器人旋转脚本启动!") + self.log(f"旋转速度: {self.rotation_speed_y}度/秒") + self.log(f"最大旋转角度: ±{self.max_angle}度") + self.log(f"机器人模式: {'开启' if self.robot_mode else '关闭'}") + if self.robot_mode: + self.log(f"停顿时间: {self.pause_duration}秒") + + # 记录模型的初始角度 + if self.gameObject: + initial_hpr = self.gameObject.getHpr() + self.initial_angle = initial_hpr.getY() # 记录Y轴的初始角度(Pitch) + self.log(f"模型初始角度: {self.initial_angle}度") + self.log(f"旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°") + else: + self.log("⚠️ 无法获取游戏对象,使用默认初始角度0") + self.initial_angle = 0.0 + + def update(self, dt): + """每帧更新 - 机器人式旋转""" + if not self.is_rotating or self.initial_angle is None: + return + + # 如果正在停顿中 + if self.is_paused: + self.current_pause_time += dt + if self.current_pause_time >= self.pause_duration: + # 停顿结束,继续旋转 + self.is_paused = False + self.current_pause_time = 0.0 + self.log(f"停顿结束,继续旋转,方向: {'正向' if self.direction > 0 else '反向'}") + return + + # 计算角度变化量 + delta_angle = self.rotation_speed_y * dt * self.direction + self.current_offset += delta_angle + + # 检查是否到达边界 + reached_boundary = False + if self.current_offset > self.max_angle: + self.current_offset = self.max_angle + self.direction *= -1 + reached_boundary = True + elif self.current_offset < -self.max_angle: + self.current_offset = -self.max_angle + self.direction *= -1 + reached_boundary = True + + # 如果到达边界且启用机器人模式,开始停顿 + if reached_boundary and self.robot_mode: + self.is_paused = True + self.current_pause_time = 0.0 + self.log(f"到达边界 ({self.current_offset}°),开始停顿 {self.pause_duration}秒") + + # 计算最终角度(初始角度 + 偏移量) + final_angle = self.initial_angle + self.current_offset + + # 设置新的旋转(只改变Y轴,保持其他不变) + current_hpr = self.gameObject.getHpr() + self.gameObject.setHpr(final_angle, current_hpr.getY(), current_hpr.getZ()) + + # if not self.is_rotating: + # return + # + # # 获取当前旋转并应用增量 + # current_hpr = self.gameObject.getHpr() + # new_r = current_hpr.getZ() + self.rotation_speed_y * dt + # self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), new_r) + + def on_destroy(self): + """脚本销毁时调用""" + self.log("机器人旋转脚本停止") + + # ==================== 机器人模式控制方法 ==================== + + def set_robot_mode(self, enabled=True): + """ + 启用或禁用机器人模式 + + Args: + enabled: 是否启用机器人模式 + """ + self.robot_mode = enabled + self.log(f"机器人模式: {'开启' if enabled else '关闭'}") + if not enabled: + self.is_paused = False # 如果禁用机器人模式,立即结束停顿 + + def set_pause_duration(self, duration): + """ + 设置停顿时间 + + Args: + duration: 停顿时间(秒) + """ + self.pause_duration = max(0.1, duration) # 最小0.1秒 + self.log(f"停顿时间已设置为: {self.pause_duration}秒") + + def set_max_angle(self, new_max_angle): + """ + 设置新的最大旋转角度 + + Args: + new_max_angle: 新的最大角度值 + """ + self.max_angle = new_max_angle + self.log(f"最大旋转角度已设置为: ±{self.max_angle}度") + if self.initial_angle is not None: + self.log(f"新的旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°") + + def set_rotation_speed(self, new_speed): + """ + 设置新的旋转速度 + + Args: + new_speed: 新的旋转速度(度/秒) + """ + self.rotation_speed_y = new_speed + self.log(f"旋转速度已设置为: {self.rotation_speed_y}度/秒") + + def pause_rotation(self): + """暂停旋转""" + self.is_rotating = False + self.log("旋转已暂停") + + def resume_rotation(self): + """恢复旋转""" + self.is_rotating = True + self.is_paused = False # 同时结束停顿状态 + self.log("旋转已恢复") + + def reset_to_initial_angle(self): + """重置到初始角度""" + if self.initial_angle is not None and self.gameObject: + current_hpr = self.gameObject.getHpr() + self.gameObject.setHpr(current_hpr.getX(), self.initial_angle, current_hpr.getZ()) + self.current_offset = 0.0 + self.direction = 1 + self.is_paused = False + self.current_pause_time = 0.0 + self.log(f"已重置到初始角度: {self.initial_angle}度") + + def get_current_info(self): + """获取当前旋转信息""" + if self.gameObject and self.initial_angle is not None: + current_hpr = self.gameObject.getHpr() + current_angle = current_hpr.getY() + self.log("=== 机器人旋转信息 ===") + self.log(f"初始角度: {self.initial_angle}度") + self.log(f"当前角度: {current_angle}度") + self.log(f"偏移量: {self.current_offset}度") + self.log(f"最大角度: ±{self.max_angle}度") + self.log(f"旋转速度: {self.rotation_speed_y}度/秒") + self.log(f"旋转方向: {'正向' if self.direction > 0 else '反向'}") + self.log(f"旋转状态: {'运行中' if self.is_rotating else '已暂停'}") + self.log(f"机器人模式: {'开启' if self.robot_mode else '关闭'}") + if self.robot_mode: + self.log(f"停顿时间: {self.pause_duration}秒") + self.log(f"当前状态: {'停顿中' if self.is_paused else '旋转中'}") + if self.is_paused: + remaining_time = self.pause_duration - self.current_pause_time + self.log(f"剩余停顿时间: {remaining_time:.1f}秒") + self.log("=== 信息结束 ===") + else: + self.log("无法获取旋转信息") + + # ==================== 预设配置方法 ==================== + + def set_slow_robot_mode(self): + """预设:慢速机器人模式""" + self.set_rotation_speed(15.0) + self.set_pause_duration(1.0) + self.set_robot_mode(True) + self.log("已设置为慢速机器人模式") + + def set_fast_robot_mode(self): + """预设:快速机器人模式""" + self.set_rotation_speed(45.0) + self.set_pause_duration(0.3) + self.set_robot_mode(True) + self.log("已设置为快速机器人模式") + + def set_smooth_mode(self): + """预设:平滑模式(非机器人)""" + self.set_robot_mode(False) + self.set_rotation_speed(30.0) + self.log("已设置为平滑旋转模式") \ No newline at end of file diff --git a/scripts/Rotate_P.py b/scripts/Rotate_P.py new file mode 100644 index 00000000..506ddfeb --- /dev/null +++ b/scripts/Rotate_P.py @@ -0,0 +1,740 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Rotate_P - 自定义脚本,支持动态挂载其他脚本到模型或子物体 +""" + +from core.script_system import ScriptBase + +class RotateP(ScriptBase): + """自定义脚本类 - 支持脚本挂载功能""" + + def __init__(self): + super().__init__() + # 在这里初始化您的变量 + self.mounted_scripts = {} # 存储已挂载的脚本 {object_name: [script_names]} + self.world = None # 世界对象引用 + + def start(self): + """脚本开始时调用 - 当此脚本被挂载时自动执行挂载其他脚本""" + self.log("Rotate_P 脚本开始运行!") + + # 获取世界对象引用 + if hasattr(self.script_manager, 'world'): + self.world = self.script_manager.world + self.log("✓ 获取世界对象引用成功") + + # 当此脚本被挂载时,自动执行脚本挂载操作 + self.auto_mount_scripts_on_start() + else: + self.log("⚠️ 无法获取世界对象引用") + + def auto_mount_scripts_on_start(self): + """ + 当Rotate_P脚本被挂载时自动执行的脚本挂载操作 + 在这里定义要自动挂载的脚本和目标对象 + """ + self.log("=== 开始自动挂载脚本 ===") + + # 获取当前脚本所挂载的对象 + current_object = self.gameObject + current_object_name = current_object.getName() if current_object else "Unknown" + self.log(f"当前脚本挂载在对象: {current_object_name}") + + # 显示当前对象信息 + self.get_current_object_info() + + # ==================== 配置区域 ==================== + # 请根据您的需求选择以下配置之一,或自定义配置 + + # 配置1:机器人式旋转演示(有停顿) + self.setup_rotation_demo(max_angle=45.0, rotation_speed=25.0, robot_mode=True, pause_duration=0.8) + + # 配置2:工业机器人演示(取消注释以使用) + # self.setup_industrial_robot_demo() + + # 配置3:伺服电机演示(取消注释以使用) + # self.setup_servo_motor_demo() + + # 配置4:机器人手臂演示(取消注释以使用) + # self.setup_robot_arm_demo() + + # 配置5:为指定对象挂载机器人旋转脚本(取消注释并修改) + # self.mount_robot_rotator("arm_joint", max_angle=60.0, rotation_speed=15.0, pause_duration=1.0) + # self.mount_robot_rotator("gripper", max_angle=30.0, rotation_speed=20.0, pause_duration=0.5) + + # 配置6:平滑旋转(无停顿)(取消注释以使用) + # self.mount_smooth_rotator("wheel", max_angle=180.0, rotation_speed=90.0) + + # 配置5:自定义挂载配置(取消注释并修改) + # auto_mount_configs = [ + # ("your_model_name", "RotatorScript"), # 将RotatorScript挂载到指定模型 + # ("child_object_name", "MoverScript"), # 将MoverScript挂载到子物体 + # ("another_object", "ScalerScript"), # 将ScalerScript挂载到另一个对象 + # ] + # if auto_mount_configs: + # success_count = self.batch_mount_scripts(auto_mount_configs) + # self.log(f"✓ 自定义挂载完成,成功挂载 {success_count} 个脚本") + + # ==================== 配置区域结束 ==================== + + # 显示使用说明 + self.show_usage_examples() + + # 列出已挂载的脚本 + self.list_mounted_scripts() + + def mount_script_to_current_object(self, script_name): + """ + 将脚本挂载到当前对象(即Rotate_P脚本所在的对象) + + Args: + script_name: 要挂载的脚本名称 + + Returns: + bool: 挂载是否成功 + """ + if not self.gameObject: + self.log("错误: 无法获取当前对象") + return False + + return self.mount_script_to_object(self.gameObject, script_name) + + def auto_mount_to_all_children(self, script_name): + """ + 自动将脚本挂载到当前对象的所有子物体 + + Args: + script_name: 要挂载的脚本名称 + + Returns: + int: 成功挂载的子物体数量 + """ + if not self.gameObject: + self.log("错误: 无法获取当前对象") + return 0 + + # 获取所有子对象 + children = self.gameObject.getChildren() + success_count = 0 + + self.log(f"开始为当前对象的 {len(children)} 个子物体挂载脚本 '{script_name}'") + + for child in children: + if self.mount_script_to_object(child, script_name): + success_count += 1 + + self.log(f"✓ 成功为 {success_count}/{len(children)} 个子物体挂载脚本 '{script_name}'") + return success_count + + def update(self, dt): + """每帧更新""" + # 在这里编写更新逻辑 + pass + + def on_destroy(self): + """脚本销毁时调用""" + self.log("脚本被销毁") + + # ==================== 脚本挂载功能 ==================== + + def mount_script_to_object(self, target_object, script_name, script_config=None): + """ + 将脚本挂载到指定对象上 + + Args: + target_object: 目标对象(Panda3D NodePath) + script_name: 要挂载的脚本名称(字符串) + script_config: 脚本配置参数(字典),可选 + + Returns: + bool: 挂载是否成功 + """ + if not self.world: + self.log("错误: 世界对象引用不可用") + return False + + try: + # 使用世界对象的addScript方法挂载脚本 + success = self.world.addScript(target_object, script_name) + + if success: + # 记录挂载信息 + obj_name = target_object.getName() + if obj_name not in self.mounted_scripts: + self.mounted_scripts[obj_name] = [] + self.mounted_scripts[obj_name].append(script_name) + + # 如果提供了配置参数,尝试配置脚本 + if script_config and script_name == "RotatorScript": + self.configure_rotator_script(target_object, script_config) + + self.log(f"✓ 成功将脚本 '{script_name}' 挂载到对象 '{obj_name}'") + return True + else: + self.log(f"✗ 挂载脚本 '{script_name}' 失败") + return False + + except Exception as e: + self.log(f"✗ 挂载脚本时出错: {str(e)}") + return False + + def configure_rotator_script(self, target_object, config): + """ + 配置已挂载的RotatorScript脚本 + + Args: + target_object: 目标对象 + config: 配置字典,可包含 max_angle, rotation_speed, robot_mode, pause_duration 等 + """ + try: + # 获取对象上的RotatorScript + scripts = self.world.getScripts(target_object) + for script_component in scripts: + if script_component.script_name == "RotatorScript": + script_instance = script_component.script_instance + + # 配置最大角度 + if 'max_angle' in config: + script_instance.set_max_angle(config['max_angle']) + + # 配置旋转速度 + if 'rotation_speed' in config: + script_instance.set_rotation_speed(config['rotation_speed']) + + # 配置机器人模式 + if 'robot_mode' in config: + script_instance.set_robot_mode(config['robot_mode']) + + # 配置停顿时间 + if 'pause_duration' in config: + script_instance.set_pause_duration(config['pause_duration']) + + # 预设模式 + if 'preset_mode' in config: + preset = config['preset_mode'] + if preset == 'slow_robot': + script_instance.set_slow_robot_mode() + elif preset == 'fast_robot': + script_instance.set_fast_robot_mode() + elif preset == 'smooth': + script_instance.set_smooth_mode() + + self.log(f"✓ 已配置 RotatorScript: {config}") + break + except Exception as e: + self.log(f"✗ 配置 RotatorScript 时出错: {str(e)}") + + def unmount_script_from_object(self, target_object, script_name): + """ + 从指定对象卸载脚本 + + Args: + target_object: 目标对象(Panda3D NodePath) + script_name: 要卸载的脚本名称(字符串) + + Returns: + bool: 卸载是否成功 + """ + if not self.world: + self.log("错误: 世界对象引用不可用") + return False + + try: + # 使用世界对象的removeScript方法卸载脚本 + success = self.world.removeScript(target_object, script_name) + + if success: + # 更新挂载记录 + obj_name = target_object.getName() + if obj_name in self.mounted_scripts and script_name in self.mounted_scripts[obj_name]: + self.mounted_scripts[obj_name].remove(script_name) + if not self.mounted_scripts[obj_name]: # 如果列表为空,删除键 + del self.mounted_scripts[obj_name] + + self.log(f"✓ 成功从对象 '{obj_name}' 卸载脚本 '{script_name}'") + return True + else: + self.log(f"✗ 卸载脚本 '{script_name}' 失败") + return False + + except Exception as e: + self.log(f"✗ 卸载脚本时出错: {str(e)}") + return False + + def mount_script_by_name(self, object_name, script_name): + """ + 通过对象名称挂载脚本 + + Args: + object_name: 目标对象名称(字符串) + script_name: 要挂载的脚本名称(字符串) + + Returns: + bool: 挂载是否成功 + """ + if not self.world: + self.log("错误: 世界对象引用不可用") + return False + + # 查找对象 + target_object = self.find_object_by_name(object_name) + if not target_object: + self.log(f"✗ 未找到名为 '{object_name}' 的对象") + return False + + return self.mount_script_to_object(target_object, script_name) + + def find_object_by_name(self, object_name): + """ + 通过名称查找对象 + + Args: + object_name: 对象名称(字符串) + + Returns: + NodePath: 找到的对象,如果未找到则返回None + """ + if not self.world: + return None + + # 首先尝试使用世界的查找方法 + if hasattr(self.world, 'findModelByName'): + obj = self.world.findModelByName(object_name) + if obj: + return obj + + # 如果没有找到,尝试在render下搜索 + if hasattr(self.world, 'render'): + return self.world.render.find(f"**/{object_name}") + + return None + + def get_available_scripts(self): + """ + 获取所有可用的脚本列表 + + Returns: + list: 可用脚本名称列表 + """ + if not self.world: + self.log("错误: 世界对象引用不可用") + return [] + + try: + return self.world.getAvailableScripts() + except Exception as e: + self.log(f"✗ 获取可用脚本列表时出错: {str(e)}") + return [] + + def get_scripts_on_object(self, target_object): + """ + 获取对象上已挂载的脚本 + + Args: + target_object: 目标对象(Panda3D NodePath) + + Returns: + list: 脚本组件列表 + """ + if not self.world: + self.log("错误: 世界对象引用不可用") + return [] + + try: + return self.world.getScripts(target_object) + except Exception as e: + self.log(f"✗ 获取对象脚本时出错: {str(e)}") + return [] + + def list_mounted_scripts(self): + """ + 列出所有已挂载的脚本信息 + """ + self.log("=== 已挂载脚本列表 ===") + if not self.mounted_scripts: + self.log("暂无已挂载的脚本") + return + + for obj_name, script_names in self.mounted_scripts.items(): + self.log(f"对象 '{obj_name}': {', '.join(script_names)}") + + # ==================== 便捷方法 ==================== + + def mount_rotator_script(self, object_name): + """ + 便捷方法:挂载旋转脚本到指定对象 + + Args: + object_name: 目标对象名称 + + Returns: + bool: 挂载是否成功 + """ + return self.mount_script_by_name(object_name, "RotatorScript") + + def mount_mover_script(self, object_name): + """ + 便捷方法:挂载移动脚本到指定对象 + + Args: + object_name: 目标对象名称 + + Returns: + bool: 挂载是否成功 + """ + return self.mount_script_by_name(object_name, "MoverScript") + + def mount_scaler_script(self, object_name): + """ + 便捷方法:挂载缩放脚本到指定对象 + + Args: + object_name: 目标对象名称 + + Returns: + bool: 挂载是否成功 + """ + return self.mount_script_by_name(object_name, "ScalerScript") + + # ==================== 示例使用方法 ==================== + + def example_mount_scripts(self): + """ + 示例:如何使用脚本挂载功能 + 在start()方法中调用此方法来演示脚本挂载 + """ + self.log("=== 脚本挂载示例 ===") + + # 示例1:挂载旋转脚本到特定对象 + # self.mount_rotator_script("your_model_name") + + # 示例2:挂载多个脚本到同一个对象 + # self.mount_script_by_name("your_model_name", "RotatorScript") + # self.mount_script_by_name("your_model_name", "MoverScript") + + # 示例3:查找并挂载脚本到子物体 + # child_object = self.find_object_by_name("child_object_name") + # if child_object: + # self.mount_script_to_object(child_object, "RotatorScript") + + # 示例4:列出可用脚本 + available_scripts = self.get_available_scripts() + self.log(f"可用脚本: {available_scripts}") + + # 示例5:列出已挂载的脚本 + self.list_mounted_scripts() + + def auto_mount_to_children(self, parent_object_name, script_name): + """ + 自动将脚本挂载到指定父对象的所有子物体 + + Args: + parent_object_name: 父对象名称 + script_name: 要挂载的脚本名称 + + Returns: + int: 成功挂载的子物体数量 + """ + if not self.world: + self.log("错误: 世界对象引用不可用") + return 0 + + # 查找父对象 + parent_object = self.find_object_by_name(parent_object_name) + if not parent_object: + self.log(f"✗ 未找到父对象 '{parent_object_name}'") + return 0 + + # 获取所有子对象 + children = parent_object.getChildren() + success_count = 0 + + for child in children: + if self.mount_script_to_object(child, script_name): + success_count += 1 + + self.log(f"✓ 成功为 {success_count}/{len(children)} 个子物体挂载脚本 '{script_name}'") + return success_count + + def batch_mount_scripts(self, object_script_pairs): + """ + 批量挂载脚本 + + Args: + object_script_pairs: 对象-脚本对列表,格式:[(object_name, script_name), ...] + + Returns: + int: 成功挂载的数量 + """ + success_count = 0 + + for object_name, script_name in object_script_pairs: + if self.mount_script_by_name(object_name, script_name): + success_count += 1 + + self.log(f"✓ 批量挂载完成: {success_count}/{len(object_script_pairs)} 个脚本挂载成功") + return success_count + + # ==================== 预设配置方法 ==================== + + def setup_rotation_demo(self, max_angle=30.0, rotation_speed=30.0, robot_mode=True, pause_duration=0.5): + """ + 预设配置:旋转演示 + 为当前对象及其子物体挂载旋转脚本 + + Args: + max_angle: 最大旋转角度(相对于初始角度) + rotation_speed: 旋转速度(度/秒) + robot_mode: 是否启用机器人模式 + pause_duration: 停顿时间(秒) + """ + self.log("=== 设置旋转演示 ===") + + # 配置参数 + config = { + 'max_angle': max_angle, + 'rotation_speed': rotation_speed, + 'robot_mode': robot_mode, + 'pause_duration': pause_duration + } + + # 为当前对象挂载旋转脚本 + self.mount_script_to_current_object_with_config("RotatorScript", config) + + # 为所有子物体挂载旋转脚本 + self.auto_mount_to_all_children_with_config("RotatorScript", config) + + def setup_animation_combo(self): + """ + 预设配置:动画组合 + 为对象挂载多种动画脚本 + """ + self.log("=== 设置动画组合 ===") + + # 为当前对象挂载多个脚本 + scripts_to_mount = ["RotatorScript", "MoverScript", "ScalerScript"] + + for script_name in scripts_to_mount: + self.mount_script_to_current_object(script_name) + + def setup_child_animation(self, script_name="RotatorScript"): + """ + 预设配置:子物体动画 + 只为子物体挂载脚本,不影响父对象 + + Args: + script_name: 要挂载的脚本名称,默认为RotatorScript + """ + self.log(f"=== 设置子物体动画 ({script_name}) ===") + self.auto_mount_to_all_children(script_name) + + def setup_specific_objects(self, object_names, script_name="RotatorScript"): + """ + 预设配置:为指定的对象列表挂载脚本 + + Args: + object_names: 对象名称列表 + script_name: 要挂载的脚本名称 + """ + self.log(f"=== 为指定对象挂载脚本 ({script_name}) ===") + + for obj_name in object_names: + self.mount_script_by_name(obj_name, script_name) + + def setup_robot_arm_demo(self): + """ + 预设配置:机器人手臂演示 + 为不同关节设置不同的机器人旋转参数 + """ + self.log("=== 设置机器人手臂演示 ===") + + # 为当前对象设置慢速机器人模式 + config = {'preset_mode': 'slow_robot'} + self.mount_script_to_current_object_with_config("RotatorScript", config) + + # 为子物体设置快速机器人模式 + config = {'preset_mode': 'fast_robot'} + self.auto_mount_to_all_children_with_config("RotatorScript", config) + + def setup_industrial_robot_demo(self): + """ + 预设配置:工业机器人演示 + 模拟工业机器人的精确停顿动作 + """ + self.log("=== 设置工业机器人演示 ===") + + # 工业机器人配置:慢速、长停顿 + config = { + 'max_angle': 45.0, + 'rotation_speed': 15.0, + 'robot_mode': True, + 'pause_duration': 1.2 + } + + self.mount_script_to_current_object_with_config("RotatorScript", config) + self.auto_mount_to_all_children_with_config("RotatorScript", config) + + def setup_servo_motor_demo(self): + """ + 预设配置:伺服电机演示 + 模拟伺服电机的快速精确定位 + """ + self.log("=== 设置伺服电机演示 ===") + + # 伺服电机配置:快速、短停顿 + config = { + 'max_angle': 60.0, + 'rotation_speed': 50.0, + 'robot_mode': True, + 'pause_duration': 0.2 + } + + self.mount_script_to_current_object_with_config("RotatorScript", config) + self.auto_mount_to_all_children_with_config("RotatorScript", config) + + # ==================== 使用示例和说明 ==================== + + def show_usage_examples(self): + """ + 显示使用示例和说明 + """ + self.log("=== Rotate_P 脚本使用说明 ===") + self.log("此脚本被挂载时会自动执行脚本挂载操作") + self.log("") + self.log("可用的预设配置方法:") + self.log("1. setup_rotation_demo() - 为当前对象和子物体挂载旋转脚本") + self.log("2. setup_animation_combo() - 为当前对象挂载多种动画脚本") + self.log("3. setup_child_animation() - 只为子物体挂载脚本") + self.log("4. setup_specific_objects() - 为指定对象挂载脚本") + self.log("") + self.log("修改 auto_mount_scripts_on_start() 方法来自定义自动挂载行为") + self.log("=== 使用说明结束 ===") + + def get_current_object_info(self): + """ + 获取当前对象的详细信息 + """ + if not self.gameObject: + self.log("错误: 无法获取当前对象") + return + + obj_name = self.gameObject.getName() + children = self.gameObject.getChildren() + child_names = [child.getName() for child in children] + + self.log("=== 当前对象信息 ===") + self.log(f"对象名称: {obj_name}") + self.log(f"子物体数量: {len(children)}") + if child_names: + self.log(f"子物体名称: {', '.join(child_names)}") + else: + self.log("无子物体") + self.log("=== 对象信息结束 ===") + + # ==================== 带配置的便捷方法 ==================== + + def mount_script_to_current_object_with_config(self, script_name, config=None): + """ + 将脚本挂载到当前对象并配置参数 + + Args: + script_name: 脚本名称 + config: 配置参数字典 + + Returns: + bool: 挂载是否成功 + """ + if not self.gameObject: + self.log("错误: 无法获取当前对象") + return False + + return self.mount_script_to_object(self.gameObject, script_name, config) + + def auto_mount_to_all_children_with_config(self, script_name, config=None): + """ + 自动将脚本挂载到当前对象的所有子物体并配置参数 + + Args: + script_name: 要挂载的脚本名称 + config: 配置参数字典 + + Returns: + int: 成功挂载的子物体数量 + """ + if not self.gameObject: + self.log("错误: 无法获取当前对象") + return 0 + + # 获取所有子对象 + children = self.gameObject.getChildren() + success_count = 0 + + self.log(f"开始为当前对象的 {len(children)} 个子物体挂载脚本 '{script_name}'") + if config: + self.log(f"使用配置: {config}") + + for child in children: + if self.mount_script_to_object(child, script_name, config): + success_count += 1 + + self.log(f"✓ 成功为 {success_count}/{len(children)} 个子物体挂载脚本 '{script_name}'") + return success_count + + def mount_rotator_with_custom_angle(self, object_name, max_angle=45.0, rotation_speed=20.0, robot_mode=True, pause_duration=0.5): + """ + 便捷方法:挂载自定义角度的旋转脚本 + + Args: + object_name: 目标对象名称 + max_angle: 最大旋转角度 + rotation_speed: 旋转速度 + robot_mode: 是否启用机器人模式 + pause_duration: 停顿时间(秒) + + Returns: + bool: 挂载是否成功 + """ + config = { + 'max_angle': max_angle, + 'rotation_speed': rotation_speed, + 'robot_mode': robot_mode, + 'pause_duration': pause_duration + } + + target_object = self.find_object_by_name(object_name) + if not target_object: + self.log(f"✗ 未找到名为 '{object_name}' 的对象") + return False + + return self.mount_script_to_object(target_object, "RotatorScript", config) + + def mount_robot_rotator(self, object_name, max_angle=30.0, rotation_speed=20.0, pause_duration=0.8): + """ + 便捷方法:挂载机器人式旋转脚本 + + Args: + object_name: 目标对象名称 + max_angle: 最大旋转角度 + rotation_speed: 旋转速度 + pause_duration: 停顿时间 + + Returns: + bool: 挂载是否成功 + """ + return self.mount_rotator_with_custom_angle(object_name, max_angle, rotation_speed, True, pause_duration) + + def mount_smooth_rotator(self, object_name, max_angle=45.0, rotation_speed=30.0): + """ + 便捷方法:挂载平滑旋转脚本(无停顿) + + Args: + object_name: 目标对象名称 + max_angle: 最大旋转角度 + rotation_speed: 旋转速度 + + Returns: + bool: 挂载是否成功 + """ + return self.mount_rotator_with_custom_angle(object_name, max_angle, rotation_speed, False, 0.0) diff --git a/scripts/Rotate_P_Script.py b/scripts/Rotate_P_Script.py new file mode 100644 index 00000000..98d02cdf --- /dev/null +++ b/scripts/Rotate_P_Script.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +旋转脚本 - 让对象持续旋转 +""" + +from core.script_system import ScriptBase + + +class RotatorScript(ScriptBase): + """旋转脚本类""" + + def __init__(self): + super().__init__() + self.rotation_speed_y = 30.0 # Y轴旋转速度 (度/秒) + self.max_angle = 25.0 # 最大旋转角度(相对于初始角度) + self.direction = 1 + self.current_offset = 0.0 # 当前相对于初始角度的偏移 + self.initial_angle = None # 模型的初始角度 + self.is_rotating = True # 是否正在旋转 + + # 机器人式停顿参数 + self.pause_duration = 0.5 # 停顿时间(秒) + self.current_pause_time = 0.0 # 当前停顿计时 + self.is_paused = False # 是否正在停顿 + self.robot_mode = True # 是否启用机器人模式 + + def start(self): + """脚本开始时调用""" + self.log("机器人旋转脚本启动!") + self.log(f"旋转速度: {self.rotation_speed_y}度/秒") + self.log(f"最大旋转角度: ±{self.max_angle}度") + self.log(f"机器人模式: {'开启' if self.robot_mode else '关闭'}") + if self.robot_mode: + self.log(f"停顿时间: {self.pause_duration}秒") + + # 记录模型的初始角度 + if self.gameObject: + initial_hpr = self.gameObject.getHpr() + self.initial_angle = initial_hpr.getY() # 记录Y轴的初始角度(Pitch) + self.log(f"模型初始角度: {self.initial_angle}度") + self.log(f"旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°") + else: + self.log("⚠️ 无法获取游戏对象,使用默认初始角度0") + self.initial_angle = 0.0 + + def update(self, dt): + """每帧更新 - 机器人式旋转""" + if not self.is_rotating or self.initial_angle is None: + return + + # 如果正在停顿中 + if self.is_paused: + self.current_pause_time += dt + if self.current_pause_time >= self.pause_duration: + # 停顿结束,继续旋转 + self.is_paused = False + self.current_pause_time = 0.0 + self.log(f"停顿结束,继续旋转,方向: {'正向' if self.direction > 0 else '反向'}") + return + + # 计算角度变化量 + delta_angle = self.rotation_speed_y * dt * self.direction + self.current_offset += delta_angle + + # 检查是否到达边界 + reached_boundary = False + if self.current_offset > self.max_angle: + self.current_offset = self.max_angle + self.direction *= -1 + reached_boundary = True + elif self.current_offset < -self.max_angle: + self.current_offset = -self.max_angle + self.direction *= -1 + reached_boundary = True + + # 如果到达边界且启用机器人模式,开始停顿 + if reached_boundary and self.robot_mode: + self.is_paused = True + self.current_pause_time = 0.0 + self.log(f"到达边界 ({self.current_offset}°),开始停顿 {self.pause_duration}秒") + + # 计算最终角度(初始角度 + 偏移量) + final_angle = self.initial_angle + self.current_offset + + # 设置新的旋转(只改变Y轴,保持其他不变) + current_hpr = self.gameObject.getHpr() + self.gameObject.setHpr(current_hpr.getX(), final_angle, current_hpr.getZ()) + + # if not self.is_rotating: + # return + # + # # 获取当前旋转并应用增量 + # current_hpr = self.gameObject.getHpr() + # new_r = current_hpr.getZ() + self.rotation_speed_y * dt + # self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), new_r) + + def on_destroy(self): + """脚本销毁时调用""" + self.log("机器人旋转脚本停止") + + # ==================== 机器人模式控制方法 ==================== + + def set_robot_mode(self, enabled=True): + """ + 启用或禁用机器人模式 + + Args: + enabled: 是否启用机器人模式 + """ + self.robot_mode = enabled + self.log(f"机器人模式: {'开启' if enabled else '关闭'}") + if not enabled: + self.is_paused = False # 如果禁用机器人模式,立即结束停顿 + + def set_pause_duration(self, duration): + """ + 设置停顿时间 + + Args: + duration: 停顿时间(秒) + """ + self.pause_duration = max(0.1, duration) # 最小0.1秒 + self.log(f"停顿时间已设置为: {self.pause_duration}秒") + + def set_max_angle(self, new_max_angle): + """ + 设置新的最大旋转角度 + + Args: + new_max_angle: 新的最大角度值 + """ + self.max_angle = new_max_angle + self.log(f"最大旋转角度已设置为: ±{self.max_angle}度") + if self.initial_angle is not None: + self.log(f"新的旋转范围: {self.initial_angle - self.max_angle}° 到 {self.initial_angle + self.max_angle}°") + + def set_rotation_speed(self, new_speed): + """ + 设置新的旋转速度 + + Args: + new_speed: 新的旋转速度(度/秒) + """ + self.rotation_speed_y = new_speed + self.log(f"旋转速度已设置为: {self.rotation_speed_y}度/秒") + + def pause_rotation(self): + """暂停旋转""" + self.is_rotating = False + self.log("旋转已暂停") + + def resume_rotation(self): + """恢复旋转""" + self.is_rotating = True + self.is_paused = False # 同时结束停顿状态 + self.log("旋转已恢复") + + def reset_to_initial_angle(self): + """重置到初始角度""" + if self.initial_angle is not None and self.gameObject: + current_hpr = self.gameObject.getHpr() + self.gameObject.setHpr(current_hpr.getX(), self.initial_angle, current_hpr.getZ()) + self.current_offset = 0.0 + self.direction = 1 + self.is_paused = False + self.current_pause_time = 0.0 + self.log(f"已重置到初始角度: {self.initial_angle}度") + + def get_current_info(self): + """获取当前旋转信息""" + if self.gameObject and self.initial_angle is not None: + current_hpr = self.gameObject.getHpr() + current_angle = current_hpr.getY() + self.log("=== 机器人旋转信息 ===") + self.log(f"初始角度: {self.initial_angle}度") + self.log(f"当前角度: {current_angle}度") + self.log(f"偏移量: {self.current_offset}度") + self.log(f"最大角度: ±{self.max_angle}度") + self.log(f"旋转速度: {self.rotation_speed_y}度/秒") + self.log(f"旋转方向: {'正向' if self.direction > 0 else '反向'}") + self.log(f"旋转状态: {'运行中' if self.is_rotating else '已暂停'}") + self.log(f"机器人模式: {'开启' if self.robot_mode else '关闭'}") + if self.robot_mode: + self.log(f"停顿时间: {self.pause_duration}秒") + self.log(f"当前状态: {'停顿中' if self.is_paused else '旋转中'}") + if self.is_paused: + remaining_time = self.pause_duration - self.current_pause_time + self.log(f"剩余停顿时间: {remaining_time:.1f}秒") + self.log("=== 信息结束 ===") + else: + self.log("无法获取旋转信息") + + # ==================== 预设配置方法 ==================== + + def set_slow_robot_mode(self): + """预设:慢速机器人模式""" + self.set_rotation_speed(15.0) + self.set_pause_duration(1.0) + self.set_robot_mode(True) + self.log("已设置为慢速机器人模式") + + def set_fast_robot_mode(self): + """预设:快速机器人模式""" + self.set_rotation_speed(45.0) + self.set_pause_duration(0.3) + self.set_robot_mode(True) + self.log("已设置为快速机器人模式") + + def set_smooth_mode(self): + """预设:平滑模式(非机器人)""" + self.set_robot_mode(False) + self.set_rotation_speed(30.0) + self.log("已设置为平滑旋转模式") \ No newline at end of file diff --git a/scripts/Rotate_R_Script.py b/scripts/Rotate_R_Script.py new file mode 100644 index 00000000..4d22edac --- /dev/null +++ b/scripts/Rotate_R_Script.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +旋转脚本 - 让对象持续旋转 +""" + +from core.script_system import ScriptBase + + +class RotatorScript(ScriptBase): + """旋转脚本类""" + + def __init__(self): + super().__init__() + self.rotation_speed_y = 30.0 # Y轴旋转速度 (度/秒) + self.max_angle = 15.0 + self.direction = 1 + self.current_angle = 0.0 + self.is_rotating = True # 是否正在旋转 + + def start(self): + """脚本开始时调用""" + self.log("旋转脚本启动!") + self.log(f"旋转速度: {self.rotation_speed_y}度/秒") + + def update(self, dt): + """每帧更新""" + + delta_angle = self.rotation_speed_y * dt * self.direction + self.current_angle += delta_angle + + # 如果超出角度范围,则反向 + if self.current_angle > self.max_angle: + self.current_angle = self.max_angle + self.direction *= -1 + elif self.current_angle < -self.max_angle: + self.current_angle = -self.max_angle + self.direction *= -1 + + # 设置新的旋转(只改变Z轴,保持其他不变) + base_hpr = self.gameObject.getHpr() + new_r = self.current_angle + self.gameObject.setHpr(base_hpr.getX(), base_hpr.getY(), new_r) + + # if not self.is_rotating: + # return + # + # # 获取当前旋转并应用增量 + # current_hpr = self.gameObject.getHpr() + # new_r = current_hpr.getZ() + self.rotation_speed_y * dt + # self.gameObject.setHpr(current_hpr.getX(), current_hpr.getY(), new_r) + + def on_destroy(self): + """脚本销毁时调用""" + self.log("旋转脚本停止") \ No newline at end of file diff --git a/scripts/__pycache__/BouncerScript.cpython-312.pyc b/scripts/__pycache__/BouncerScript.cpython-312.pyc new file mode 100644 index 00000000..4ba6c380 Binary files /dev/null and b/scripts/__pycache__/BouncerScript.cpython-312.pyc differ diff --git a/scripts/__pycache__/ColorChangerScript.cpython-312.pyc b/scripts/__pycache__/ColorChangerScript.cpython-312.pyc new file mode 100644 index 00000000..a4dbe18b Binary files /dev/null and b/scripts/__pycache__/ColorChangerScript.cpython-312.pyc differ diff --git a/scripts/__pycache__/ComboAnimatorScript.cpython-312.pyc b/scripts/__pycache__/ComboAnimatorScript.cpython-312.pyc new file mode 100644 index 00000000..6e06256a Binary files /dev/null and b/scripts/__pycache__/ComboAnimatorScript.cpython-312.pyc differ diff --git a/scripts/__pycache__/FollowerScript.cpython-312.pyc b/scripts/__pycache__/FollowerScript.cpython-312.pyc new file mode 100644 index 00000000..148e128a Binary files /dev/null and b/scripts/__pycache__/FollowerScript.cpython-312.pyc differ diff --git a/scripts/__pycache__/MoverScript.cpython-312.pyc b/scripts/__pycache__/MoverScript.cpython-312.pyc new file mode 100644 index 00000000..db031c12 Binary files /dev/null and b/scripts/__pycache__/MoverScript.cpython-312.pyc differ diff --git a/scripts/__pycache__/R_P.cpython-312.pyc b/scripts/__pycache__/R_P.cpython-312.pyc new file mode 100644 index 00000000..13e428c4 Binary files /dev/null and b/scripts/__pycache__/R_P.cpython-312.pyc differ diff --git a/scripts/__pycache__/R_R.cpython-312.pyc b/scripts/__pycache__/R_R.cpython-312.pyc new file mode 100644 index 00000000..cb78e9a1 Binary files /dev/null and b/scripts/__pycache__/R_R.cpython-312.pyc differ diff --git a/scripts/__pycache__/Rotate_H_Script.cpython-312.pyc b/scripts/__pycache__/Rotate_H_Script.cpython-312.pyc new file mode 100644 index 00000000..19c94938 Binary files /dev/null and b/scripts/__pycache__/Rotate_H_Script.cpython-312.pyc differ diff --git a/scripts/__pycache__/Rotate_P.cpython-312.pyc b/scripts/__pycache__/Rotate_P.cpython-312.pyc new file mode 100644 index 00000000..7c7e7b98 Binary files /dev/null and b/scripts/__pycache__/Rotate_P.cpython-312.pyc differ diff --git a/scripts/__pycache__/Rotate_P_Script.cpython-312.pyc b/scripts/__pycache__/Rotate_P_Script.cpython-312.pyc new file mode 100644 index 00000000..53889503 Binary files /dev/null and b/scripts/__pycache__/Rotate_P_Script.cpython-312.pyc differ diff --git a/scripts/__pycache__/Rotate_R_Script.cpython-312.pyc b/scripts/__pycache__/Rotate_R_Script.cpython-312.pyc new file mode 100644 index 00000000..433fef48 Binary files /dev/null and b/scripts/__pycache__/Rotate_R_Script.cpython-312.pyc differ diff --git a/scripts/__pycache__/RotatorScript.cpython-312.pyc b/scripts/__pycache__/RotatorScript.cpython-312.pyc new file mode 100644 index 00000000..39213b91 Binary files /dev/null and b/scripts/__pycache__/RotatorScript.cpython-312.pyc differ diff --git a/scripts/__pycache__/ScalerScript.cpython-312.pyc b/scripts/__pycache__/ScalerScript.cpython-312.pyc new file mode 100644 index 00000000..b325ba70 Binary files /dev/null and b/scripts/__pycache__/ScalerScript.cpython-312.pyc differ diff --git a/scripts/__pycache__/TestMover.cpython-312.pyc b/scripts/__pycache__/TestMover.cpython-312.pyc new file mode 100644 index 00000000..7c3275c4 Binary files /dev/null and b/scripts/__pycache__/TestMover.cpython-312.pyc differ diff --git a/scripts/__pycache__/TestRotator.cpython-312.pyc b/scripts/__pycache__/TestRotator.cpython-312.pyc new file mode 100644 index 00000000..a86d722e Binary files /dev/null and b/scripts/__pycache__/TestRotator.cpython-312.pyc differ diff --git a/scripts/__pycache__/TestScaler.cpython-312.pyc b/scripts/__pycache__/TestScaler.cpython-312.pyc new file mode 100644 index 00000000..c41784f9 Binary files /dev/null and b/scripts/__pycache__/TestScaler.cpython-312.pyc differ diff --git a/scripts/__pycache__/example_script.cpython-312.pyc b/scripts/__pycache__/example_script.cpython-312.pyc new file mode 100644 index 00000000..d7825c25 Binary files /dev/null and b/scripts/__pycache__/example_script.cpython-312.pyc differ diff --git a/scripts/__pycache__/test_quick_script.cpython-312.pyc b/scripts/__pycache__/test_quick_script.cpython-312.pyc new file mode 100644 index 00000000..62416a8a Binary files /dev/null and b/scripts/__pycache__/test_quick_script.cpython-312.pyc differ diff --git a/ui/__pycache__/property_panel.cpython-312.pyc b/ui/__pycache__/property_panel.cpython-312.pyc index 4faf4042..995a943e 100644 Binary files a/ui/__pycache__/property_panel.cpython-312.pyc and b/ui/__pycache__/property_panel.cpython-312.pyc differ diff --git a/ui/property_panel.py b/ui/property_panel.py index d1a8a29b..12bdfbee 100644 --- a/ui/property_panel.py +++ b/ui/property_panel.py @@ -5,6 +5,7 @@ from typing import Hashable from PyQt5.QtWidgets import (QLabel, QLineEdit, QDoubleSpinBox, QPushButton, QTreeWidget, QTreeWidgetItem, QMenu,QCheckBox) from PyQt5.QtCore import Qt +from deploy_libs.unicodedata import normalize from panda3d.core import Vec3, Vec4, transpose @@ -175,6 +176,8 @@ class PropertyPanelManager: zScale.valueChanged.connect(lambda v: model.setScale(model.getScale().getX(), model.getScale().getY(), v)) self._propertyLayout.addRow("缩放 Z:", zScale) + self._addSunAzimuthPanel() + material_title = QLabel("材质属性") material_title.setStyleSheet("color: #FF6B6B;font-weight:bold;font-size:14px;margin-top:10px;") self._propertyLayout.addRow(material_title) @@ -398,10 +401,10 @@ class PropertyPanelManager: fovSpinBox.valueChanged.connect(lambda v:self._updateLightFOV(light_object,v)) self._propertyLayout.addRow("视野角度:",fovSpinBox) - shadowCheckBox = QCheckBox() - shadowCheckBox.setChecked(light_object.casts_shadows) - shadowCheckBox.stateChanged.connect(lambda state:self._updateLightCastsShadows(light_object,state==2)) - self._propertyLayout.addRow("投射阴影:",shadowCheckBox) + # shadowCheckBox = QCheckBox() + # shadowCheckBox.setChecked(light_object.casts_shadows) + # shadowCheckBox.stateChanged.connect(lambda state:self._updateLightCastsShadows(light_object,state==2)) + # self._propertyLayout.addRow("投射阴影:",shadowCheckBox) current_scale = model.getScale() @@ -862,6 +865,10 @@ class PropertyPanelManager: self._addMaterialPresetPanel(material) #self._addColorSpacePanel(material) + # 添加太阳方位角控制面板(只在第一个材质时添加,避免重复) + # if i == 0: + # self._addSunAzimuthPanel() + # 分隔线 if i < len(materials) - 1: @@ -1260,22 +1267,35 @@ class PropertyPanelManager: if node and material: print(f"正在为节点 {node.getName()} 应用漫反射贴图") - # 确保启用PBR效果(简单可靠版本) + # 检查是否有金属性贴图,选择合适的PBR效果 + print("🔧 检查金属性贴图并选择合适的PBR效果...") + has_metallic = self._hasMetallicTexture(node) + needs_alpha = self._needsAlphaTesting(node) + + if has_metallic: + print("✅ 检测到金属性贴图,使用支持金属性的PBR效果") + effect_file = "effects/pbr_with_metallic.yaml" + else: + print("✅ 没有金属性贴图,使用默认PBR效果") + effect_file = "effects/default.yaml" + + print(f"🔍 透明度测试: {'需要' if needs_alpha else '不需要'}") + try: self.world.render_pipeline.set_effect( node, - "effects/default.yaml", + effect_file, { "normal_mapping": True, # 启用法线映射支持 "render_gbuffer": True, - "alpha_testing": True, # 启用透明度测试支持透明贴图 + "alpha_testing": needs_alpha, # 根据是否需要透明度决定 "parallax_mapping": False, "render_shadow": True, "render_envmap": True }, 100 ) - print("✅ 默认PBR效果已应用(支持透明度)") + print(f"✅ {effect_file} 效果已应用(支持透明度和金属性)") except Exception as e: print(f"⚠️ PBR效果应用失败: {e}") @@ -1378,23 +1398,35 @@ class PropertyPanelManager: print("⚠️ 没有漫反射贴图,创建白色纹理确保法线映射正常工作...") self._createWhiteDiffuseTexture(node) - # 使用默认PBR效果,强制启用法线映射 - print("🔧 应用默认PBR效果(强制启用法线映射)...") + # 检查是否有金属性贴图,选择合适的PBR效果 + print("🔧 检查金属性贴图并选择合适的PBR效果...") + has_metallic = self._hasMetallicTexture(node) + needs_alpha = self._needsAlphaTesting(node) + + if has_metallic: + print("✅ 检测到金属性贴图,使用支持金属性的PBR效果") + effect_file = "effects/pbr_with_metallic.yaml" + else: + print("✅ 没有金属性贴图,使用默认PBR效果") + effect_file = "effects/default.yaml" + + print(f"🔍 透明度测试: {'需要' if needs_alpha else '不需要'}") + try: self.world.render_pipeline.set_effect( node, - "effects/default.yaml", + effect_file, { "normal_mapping": True, # 强制启用法线映射 "render_gbuffer": True, - "alpha_testing": False, + "alpha_testing": needs_alpha, # 根据是否需要透明度决定 "parallax_mapping": False, "render_shadow": True, "render_envmap": True }, 100 ) - print("✅ 默认PBR效果已应用(法线映射已启用)") + print(f"✅ {effect_file} 效果已应用(法线映射已启用)") except Exception as e: print(f"⚠️ PBR效果应用失败: {e}") @@ -3426,4 +3458,453 @@ class PropertyPanelManager: return False except Exception as e: print(f"⚠️ 检查透明度需求失败: {e}") - return True # 出错时默认启用透明度测试,更安全 \ No newline at end of file + return True # 出错时默认启用透明度测试,更安全 + + def _addSunAzimuthPanel(self): + """添加太阳方位角控制面板""" + try: + from PyQt5.QtWidgets import QLabel, QSlider, QSpinBox, QHBoxLayout, QWidget + from PyQt5.QtCore import Qt + + # 添加分隔线 + separator = QLabel("─" * 30) + separator.setStyleSheet("color: lightgray;") + self._propertyLayout.addRow(separator) + + # 标题 + title_label = QLabel("☀️ 太阳光照控制") + title_label.setStyleSheet("font-weight: bold; color: #FFA500; font-size: 12px;") + self._propertyLayout.addRow(title_label) + + # 太阳方位角控制 + azimuth_widget = QWidget() + azimuth_layout = QHBoxLayout(azimuth_widget) + azimuth_layout.setContentsMargins(0, 0, 0, 0) + + # 滑块控制 (0-360度) + self.sun_azimuth_slider = QSlider(Qt.Horizontal) + self.sun_azimuth_slider.setMinimum(0) + self.sun_azimuth_slider.setMaximum(360) + self.sun_azimuth_slider.setValue(180) # 默认值180度 + self.sun_azimuth_slider.setTickPosition(QSlider.TicksBelow) + self.sun_azimuth_slider.setTickInterval(45) # 每45度一个刻度 + + # 数值显示框 + self.sun_azimuth_spinbox = QSpinBox() + self.sun_azimuth_spinbox.setMinimum(0) + self.sun_azimuth_spinbox.setMaximum(360) + self.sun_azimuth_spinbox.setValue(180) + self.sun_azimuth_spinbox.setSuffix("°") + self.sun_azimuth_spinbox.setFixedWidth(80) + + # 连接信号 + self.sun_azimuth_slider.valueChanged.connect(self._onSunAzimuthSliderChanged) + self.sun_azimuth_spinbox.valueChanged.connect(self._onSunAzimuthSpinboxChanged) + + azimuth_layout.addWidget(self.sun_azimuth_slider) + azimuth_layout.addWidget(self.sun_azimuth_spinbox) + + self._propertyLayout.addRow("太阳方位角:", azimuth_widget) + + # 太阳高度角控制 + altitude_widget = QWidget() + altitude_layout = QHBoxLayout(altitude_widget) + altitude_layout.setContentsMargins(0, 0, 0, 0) + + # 滑块控制 (0-90度) + self.sun_altitude_slider = QSlider(Qt.Horizontal) + self.sun_altitude_slider.setMinimum(0) + self.sun_altitude_slider.setMaximum(90) + self.sun_altitude_slider.setValue(45) # 默认值45度 + self.sun_altitude_slider.setTickPosition(QSlider.TicksBelow) + self.sun_altitude_slider.setTickInterval(15) # 每15度一个刻度 + + # 数值显示框 + self.sun_altitude_spinbox = QSpinBox() + self.sun_altitude_spinbox.setMinimum(0) + self.sun_altitude_spinbox.setMaximum(90) + self.sun_altitude_spinbox.setValue(45) + self.sun_altitude_spinbox.setSuffix("°") + self.sun_altitude_spinbox.setFixedWidth(80) + + # 连接信号 + self.sun_altitude_slider.valueChanged.connect(self._onSunAltitudeSliderChanged) + self.sun_altitude_spinbox.valueChanged.connect(self._onSunAltitudeSpinboxChanged) + + altitude_layout.addWidget(self.sun_altitude_slider) + altitude_layout.addWidget(self.sun_altitude_spinbox) + + self._propertyLayout.addRow("太阳高度角:", altitude_widget) + + # 添加预设按钮 + preset_widget = QWidget() + preset_layout = QHBoxLayout(preset_widget) + preset_layout.setContentsMargins(0, 0, 0, 0) + + # 预设按钮 + sunrise_btn = QPushButton("🌅 日出") + sunrise_btn.setFixedWidth(60) + sunrise_btn.clicked.connect(lambda: self._setSunPreset("sunrise")) + + noon_btn = QPushButton("☀️ 正午") + noon_btn.setFixedWidth(60) + noon_btn.clicked.connect(lambda: self._setSunPreset("noon")) + + sunset_btn = QPushButton("🌇 日落") + sunset_btn.setFixedWidth(60) + sunset_btn.clicked.connect(lambda: self._setSunPreset("sunset")) + + midnight_btn = QPushButton("🌙 午夜") + midnight_btn.setFixedWidth(60) + midnight_btn.clicked.connect(lambda: self._setSunPreset("midnight")) + + preset_layout.addWidget(sunrise_btn) + preset_layout.addWidget(noon_btn) + preset_layout.addWidget(sunset_btn) + preset_layout.addWidget(midnight_btn) + + self._propertyLayout.addRow("快速设置:", preset_widget) + + print("✅ 太阳控制面板已添加") + + except Exception as e: + print(f"❌ 添加太阳方位角控制面板失败: {e}") + import traceback + traceback.print_exc() + + def _onSunAzimuthSliderChanged(self, value): + """滑块值改变时的回调""" + try: + # 同步到数值框 + self.sun_azimuth_spinbox.blockSignals(True) + self.sun_azimuth_spinbox.setValue(value) + self.sun_azimuth_spinbox.blockSignals(False) + + # 应用到Day Time Editor + self._applySunAzimuth_new(value) + + except Exception as e: + print(f"❌ 滑块值改变处理失败: {e}") + + def _onSunAzimuthSpinboxChanged(self, value): + """数值框值改变时的回调""" + try: + # 同步到滑块 + self.sun_azimuth_slider.blockSignals(True) + self.sun_azimuth_slider.setValue(value) + self.sun_azimuth_slider.blockSignals(False) + + # 应用到Day Time Editor + self._applySunAzimuth_new(value) + + except Exception as e: + print(f"❌ 数值框值改变处理失败: {e}") + + def _onSunAltitudeSliderChanged(self, value): + """太阳高度角滑块值改变时的回调""" + try: + # 同步到数值框 + self.sun_altitude_spinbox.blockSignals(True) + self.sun_altitude_spinbox.setValue(value) + self.sun_altitude_spinbox.blockSignals(False) + + # 应用太阳高度角 + self._applySunAltitude(value) + + except Exception as e: + print(f"❌ 太阳高度角滑块值改变处理失败: {e}") + + def _onSunAltitudeSpinboxChanged(self, value): + """太阳高度角数值框值改变时的回调""" + try: + # 同步到滑块 + self.sun_altitude_slider.blockSignals(True) + self.sun_altitude_slider.setValue(value) + self.sun_altitude_slider.blockSignals(False) + + # 应用太阳高度角 + self._applySunAltitude(value) + + except Exception as e: + print(f"❌ 太阳高度角数值框值改变处理失败: {e}") + + def _setSunPreset(self, preset_name): + """设置太阳预设位置""" + try: + presets = { + "sunrise": (90, 45), # 东方,低角度 + "noon": (180, 90), # 南方,天顶 + "sunset": (270, 45), # 西方,低角度 + "midnight": (0, 0) # 北方,地平线 + } + + if preset_name in presets: + azimuth, altitude = presets[preset_name] + + # 更新滑块和数值框 + self.sun_azimuth_slider.blockSignals(True) + self.sun_azimuth_spinbox.blockSignals(True) + self.sun_altitude_slider.blockSignals(True) + self.sun_altitude_spinbox.blockSignals(True) + + self.sun_azimuth_slider.setValue(azimuth) + self.sun_azimuth_spinbox.setValue(azimuth) + self.sun_altitude_slider.setValue(altitude) + self.sun_altitude_spinbox.setValue(altitude) + + self.sun_azimuth_slider.blockSignals(False) + self.sun_azimuth_spinbox.blockSignals(False) + self.sun_altitude_slider.blockSignals(False) + self.sun_altitude_spinbox.blockSignals(False) + + # 应用设置 - 优先使用Day Time Editor + azimuth_success = self._updateDayTimeEditorSetting("scattering", "sun_azimuth", azimuth) + altitude_success = self._updateDayTimeEditorSetting("scattering", "sun_altitude", altitude) + + if azimuth_success and altitude_success: + print(f"✅ 通过Day Time Editor设置太阳预设 {preset_name}: 方位角{azimuth}°, 高度角{altitude}°") + elif hasattr(self.world, 'sun_controller'): + self.world.sun_controller.set_sun_position(azimuth, altitude) + print(f"✅ 通过太阳控制器设置预设 {preset_name}: 方位角{azimuth}°, 高度角{altitude}°") + elif hasattr(self.world, 'setSunPosition'): + self.world.setSunPosition(azimuth, altitude) + print(f"✅ 通过主程序方法设置预设 {preset_name}: 方位角{azimuth}°, 高度角{altitude}°") + else: + # 分别应用方位角和高度角 + self._applySunAzimuth_new(azimuth) + self._applySunAltitude(altitude) + print(f"✅ 设置太阳预设 {preset_name}: 方位角{azimuth}°, 高度角{altitude}°") + + except Exception as e: + print(f"❌ 设置太阳预设失败: {e}") + import traceback + traceback.print_exc() + + def _applySunAzimuth_new(self, azimuth_value): + """应用太阳方位角 - 直接控制Day Time Editor""" + try: + # 方法1: 直接控制Day Time Editor中的sun_azimuth节点 + success = self._updateDayTimeEditorSetting("scattering", "sun_azimuth", azimuth_value) + if success: + print(f"✅ 通过Day Time Editor设置方位角: {azimuth_value}°") + return + + # 方法2: 使用我们的太阳控制器作为备用 + if hasattr(self.world, 'sun_controller'): + self.world.sun_controller.set_sun_azimuth(azimuth_value) + print(f"✅ 通过太阳控制器设置方位角: {azimuth_value}°") + return + + # 方法3: 直接调用主程序的太阳控制方法 + if hasattr(self.world, 'setSunAzimuth'): + self.world.setSunAzimuth(azimuth_value) + print(f"✅ 通过主程序方法设置方位角: {azimuth_value}°") + return + + print("⚠️ 所有方位角设置方法都不可用") + + except Exception as e: + print(f"❌ 应用太阳方位角失败: {e}") + import traceback + traceback.print_exc() + + def _applySunAltitude(self, altitude_value): + """应用太阳高度角 - 直接控制Day Time Editor""" + try: + # 方法1: 直接控制Day Time Editor中的sun_altitude节点 + success = self._updateDayTimeEditorSetting("scattering", "sun_altitude", altitude_value) + if success: + print(f"✅ 通过Day Time Editor设置高度角: {altitude_value}°") + return + + # 方法2: 使用我们的太阳控制器作为备用 + if hasattr(self.world, 'sun_controller'): + self.world.sun_controller.set_sun_altitude(altitude_value) + print(f"✅ 通过太阳控制器设置高度角: {altitude_value}°") + return + + # 方法3: 直接调用主程序的太阳控制方法 + if hasattr(self.world, 'setSunAltitude'): + self.world.setSunAltitude(altitude_value) + print(f"✅ 通过主程序方法设置高度角: {altitude_value}°") + return + + print("⚠️ 所有高度角设置方法都不可用") + + except Exception as e: + print(f"❌ 应用太阳高度角失败: {e}") + import traceback + traceback.print_exc() + + def _updateDayTimeEditorSetting(self, plugin_name, setting_name, value): + """直接更新Day Time Editor中的设置节点值""" + try: + # 检查是否有RenderPipeline和插件管理器 + if not hasattr(self.world, 'render_pipeline') or not self.world.render_pipeline: + print("⚠️ RenderPipeline未初始化") + return False + + pipeline = self.world.render_pipeline + if not hasattr(pipeline, 'plugin_mgr') or not pipeline.plugin_mgr: + print("⚠️ 插件管理器未初始化") + return False + + plugin_mgr = pipeline.plugin_mgr + + # 检查插件是否存在 + if plugin_name not in plugin_mgr.instances: + print(f"⚠️ 插件 '{plugin_name}' 不存在") + return False + + # 检查Day Time设置是否存在 + if plugin_name not in plugin_mgr.day_settings: + print(f"⚠️ 插件 '{plugin_name}' 没有Day Time设置") + return False + + day_settings = plugin_mgr.day_settings[plugin_name] + if setting_name not in day_settings: + print(f"⚠️ 设置 '{setting_name}' 在插件 '{plugin_name}' 中不存在") + return False + + setting_handle = day_settings[setting_name] + + # 根据设置类型转换值 + if setting_name == "sun_azimuth": + # 方位角:度数转换为0-1范围 + normalized_value = (value % 360) / 360.0 + elif setting_name == "sun_altitude": + # 高度角:度数转换为0-1范围 + normalized_value = max(0, min(90, value)) / 90.0 + else: + # 其他设置直接使用原值 + normalized_value = value + + # 获取当前时间(如果Day Time Editor正在运行) + current_time = getattr(self, '_current_daytime', 0.5) # 默认中午12点 + + # 更新设置的曲线值 + if hasattr(setting_handle, 'curves') and setting_handle.curves: + # 清除现有的控制点,设置单一值 + setting_handle.curves[0].set_single_value(normalized_value) + print(f"✅ 更新Day Time设置: {plugin_name}.{setting_name} = {value} (归一化: {normalized_value:.3f})") + + # 保存设置到配置文件 + try: + plugin_mgr.save_daytime_overrides("/$$rpconfig/daytime.yaml") + print("✅ Day Time设置已保存到配置文件") + except Exception as e: + print(f"⚠️ 保存配置文件失败: {e}") + + # 通知Day Time Editor重新加载配置(如果正在运行) + try: + from RenderPipelineFile.rpcore.util.network_communication import NetworkCommunication + NetworkCommunication.send_async(NetworkCommunication.DAYTIME_PORT, "loadconf") + print("✅ 已通知Day Time Editor重新加载配置") + except Exception as e: + print(f"⚠️ 通知Day Time Editor失败: {e}") + + return True + else: + print(f"⚠️ 设置 '{setting_name}' 没有可用的曲线") + return False + + except Exception as e: + print(f"❌ 更新Day Time Editor设置失败: {e}") + import traceback + traceback.print_exc() + return False + + def _applySunAzimuth(self, azimuth_degrees): + """应用太阳方位角到Day Time Editor中的Sun Azimuth节点""" + try: + if not hasattr(self, 'world') or not self.world: + print("⚠️ World对象不存在") + return + + # Day Time Editor是独立进程,需要通过进程间通信控制 + print(f"🌞 尝试控制Day Time Editor中的Sun Azimuth: {azimuth_degrees}°") + + # 方法1:通过文件通信 + success = self._sendSunAzimuthViaFile(azimuth_degrees) + if success: + return + + # 方法2:尝试直接控制RenderPipeline的daytime_mgr(如果存在) + # success = self._controlLocalDaytimeManager(azimuth_degrees) + # if success: + # return + + # 方法3:通过socket通信(如果实现了) + # success = self._sendSunAzimuthViaSocket(azimuth_degrees) + # if success: + # return + + print("⚠️ 无法控制Day Time Editor中的Sun Azimuth节点") + print(" 原因:Day Time Editor运行在独立进程中") + print(" 建议:") + print(" 1. 直接在Day Time Editor窗口中调整Sun Azimuth") + print(" 2. 或者实现进程间通信机制") + + except Exception as e: + print(f"❌ 应用太阳方位角失败: {e}") + import traceback + traceback.print_exc() + + def _sendSunAzimuthViaFile(self, azimuth_degrees): + """通过文件通信发送Sun Azimuth值到Day Time Editor""" + try: + import json + import os + import tempfile + + # 创建通信文件 + comm_dir = os.path.join(tempfile.gettempdir(), "daytime_editor_comm") + os.makedirs(comm_dir, exist_ok=True) + + comm_file = os.path.join(comm_dir, "sun_azimuth_command.json") + + command = { + "command": "set_sun_azimuth", + "value": azimuth_degrees, + "timestamp": __import__('time').time() + } + + with open(comm_file, 'w') as f: + json.dump(command, f) + + print(f"📁 已通过文件发送Sun Azimuth命令: {azimuth_degrees}°") + print(f" 通信文件: {comm_file}") + return True + + except Exception as e: + print(f"⚠️ 文件通信失败: {e}") + return False + + # def _controlLocalDaytimeManager(self, azimuth_degrees): + # """尝试控制本地的RenderPipeline daytime_mgr""" + # try: + # # 检查RenderPipeline的daytime_mgr + # if hasattr(self.world, 'render_pipeline') and hasattr(self.world.render_pipeline, 'daytime_mgr'): + # daytime_mgr = self.world.render_pipeline.daytime_mgr + # print("✅ 找到 render_pipeline.daytime_mgr") + # + # # 尝试设置sun_azimuth + # if hasattr(daytime_mgr, 'sun_azimuth'): + # daytime_mgr.sun_azimuth = azimuth_degrees + # print(f"☀️ 本地Sun Azimuth已设置为: {azimuth_degrees}°") + # return True + # elif hasattr(daytime_mgr, 'set_sun_azimuth'): + # daytime_mgr.set_sun_azimuth(azimuth_degrees) + # print(f"☀️ 本地Sun Azimuth已设置为: {azimuth_degrees}°") + # return True + # else: + # print("🔍 本地daytime_mgr可用属性:") + # for attr in dir(daytime_mgr): + # if 'sun' in attr.lower() or 'azimuth' in attr.lower(): + # print(f" • {attr}") + # + # return False + # + # except Exception as e: + # print(f"⚠️ 控制本地daytime_mgr失败: {e}") + # return False