This commit is contained in:
杨号敬 2024-12-18 02:18:45 +08:00
parent e402949286
commit bcc74f0465
1918 changed files with 319686 additions and 12 deletions

9
xiaofang/Assets/Obi.meta Normal file
View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 36112b76e54ae47b48e48d646aee4ed0
folderAsset: yes
timeCreated: 1435850625
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,525 @@
# Change Log
All notable changes to “Obi - Advanced ropes for Unity” will be documented in this file.
## [7.0.4]
### Fixed
- Bug that caused inactive particles to still be rendered by ObiInstancedParticleRenderer.
- Bug that preventing pin constraints' break threshold from working when using the Compute backend.
## [7.0.3]
### Added
- Object layer support for all renderers.
- New "Synchronous Fixed" solver synchronization mode, similar to Obi 6, offers tighter integration with rigidbody physics.
- New "Tangled Ropes" sample scene.
## [7.0.2]
### Fixed
- Solvers not placed at the scene origin would result in actors having incorrect motion blur in HDRP.
- Fixed issue when disabling all solvers and colliders simultaneously: ipon re-enabling them, the colliders would be ignored.
- Issue withe ElectricalSparks sample scene, ObiRopePrefabPlugger sample component may sometimes thorw an exception dependin on Unity's OnEnable call order.
- Rope would sometimes disappear when using aerodynamic constraints with zero wind in Burst, due to a math.project returning NaN. Replaced with math.projectsafe.
## [7.0.1]
### Added
- BakeMesh functionality to ObiRopeChainRenderer, you can now export baked chain meshes.
- Render layer mask support to all rope renderers and particle renderers.
### Fixed
- Sceneview mouse lookaround in flytrough mode (right click + drag) didn't work in the path editor in Windows.
- Having a ObiParticleRenderer with no material applied resulted in a exception in builds due GetInstanceID() returning 0 in editor but raising a nullref exception in the build.
## [7.0]
### Added
- Compute shader based GPU solver backend.
- More efficient solver update cycle, that also removes the need to have ObiUpdater components.
- Aerodynamic constraint support for ObiRope, ObiRod and ObiBone.
### Changed
- Rendering system is now fully multithreaded, integrated with the solver backend.
### Removed
- Native library based 'Oni' solver backend.
- ObiUpdater and all derived classes have been removed.
## [6.5.1]
### Added
- Support for holes in terrain colliders.
## [6.5]
### Added
- Improved constraint coloring, which results in much faster blueprint generation.
### Fixed
- Memory leak when using collision constraints in the Burst backend.
- Performance drop when using collision constraints in Burst >1.7
- Incorrect lighting on particles in the blueprint editor, when opening the editor without having previously opened any scene.
## [6.4]
### Added
- Support for configurable enter play mode (domain and scene reload disabling).
- Support for in-editor hot reloading.
- Numerical fields to edit control point position and tangents added to the path editor.
### Changed
- Better path editor integration using Unity's editor custom tool API.
- GrapplingHook sample scene now features a hook rope that extends over time, instead of extending instantly.
- Reduced memory allocation in ObiPathSmoother Decimate() method.
- Introduced a job handle pool to avoid runtime allocation of handles by both Burst and Oni backends.
- Constraint padding array in Burst solver has been turned into a member variable instead of allocating anew it every frame.
- Collision and friction batches are pooled instead of allocated every frame.
### Fixed
- InvalidOperationException when performing certain spatial queries with jobs debugger enabled in the Burst backend.
- ObiBone collision category was internally resetting to zero upon pressing play.
- Particle render mode in blueprint editor wasn't updated every frame under specific circumstances.
## [6.3]
### Added
- New ObiBone actor, creates a particle-based representation of a bone hierarchy and simulates it using rod constraints.
- Built-in support for applying solver gravity in world-space.
### Fixed
- Bug in attachments: prefab modifications were not being applied to the component.
- Slight reduction in solver memory allocation.
- Object disposed exception when using contact callbacks with a non-visible solver.
## [6.2]
#IMPORTANT: If updating from an older version, youll need to readjust collision filtering in your scenes.
Phase-based collision filtering has been replaced by mask/categroy based system.
### Added
- New spatial query API, that allows to perform efficient distance, overlap and raycast queries.
### Changed
- Collision phase filtering has been replaced by a category/mask system. If you upgrade existing projects to Obi 6.2, you will have to set up categories and masks appropiately.
## [6.1]
### Added
- RopeCutting sample scene, where screen-space line dragged by the user is used to cut multiple 3D ropes.
- Snake sample scene, where a snake-like character implemented using a rope can be controlled by the user.
### Changed
- Decreased minimum error in distance fields to 1e-07
### Fixed
- Bug in Oni backend: collision stickiness resulted in rapid particle separation from the collision surface.
- Bug that caused NaN velocity assignments to rigidbodies after removing all actors from a solver.
## [6.0.1]
### Fixed
- Bug in Burst backend, resulting in InvalidOperationException when enabling Burst safety checks.
- Bug in ObiSolver that caused particleToActor array entries to be null.
## [6.0]
### Added
- Optional simplex-based surface collion pipeline for more accurate collision detection/response.
- Predictive variable-step constraint solving that makes syncing Unity's physics no longer necessary when using substeps.
- Amount of CCD is now adjustable per-solver.
- Collision margin is now adjustable per-solver.
- Bend and bend/twist constraints now support plasticity.
- One-sided collision detection between particles. Particles with one-sided collision detection will always project penetrating particles to the side defined by their associated normal.
### Fixed
- Bug in dynamic particle attachments that would case colliders parented under a solver to lose their attachments upon enabling/reenabling a solver.
### Changed
- Contacts "particle" and "other" have been renamed to "bodyA" and "bodyB". bodyB might be a simplex or collider index, depending on the contact type.To access
particle indices from simplex indices, use the solver.simplices array.
## [5.6.2]
### Fixed
- Missed collisions using certain meshes for MeshColliders, due to a bug in bounding interval hierarchy generation.
- Corrected orientation error in the first particle of rods, when there's multiple rods in a solver.
- Corrected rendering error in rods, when there's multiple rods in a solver.
## [5.6.1]
### Fixed
- Bug in Burst backend: solver bounding box size was typically underestimated.
- Bug in Burst backend: when using volume constraints: "InvalidOperationException: The NativeContainer ApplyVolumeConstraintsBatchJob.particleIndices has not been assigned or constructed"
- Bug in Burst backend: not releasing native arrays when empty solvers -with zero actors in them- are present in the scene.
- Bug in Oni backend: volume constraints not working properly when multiple actors are present in the solver.
- Fixed crash when using ObiLateUpdater or ObiLateFixedUpdater with the Burst backend.
- Reduced GC allocation in dynamic particle attachments.
- Fixed bug in Skin constraints, that caused them to fail restricting particle movement in certain circumstances.
### Changed
- Updated Oni iOS library to XCode 12.1. Make sure to use XCode 12.1 or up to build for iOS when using the Oni backend.
- ObiKinematicVelocities component has been removed. Its functionality is now built into the engine, and automatically used for kinematic rigidbodies.
### Added
- Sample ObiContactDispatcher component, that will call custom enter/stay/exit contact events.
- Support for multiple solvers in sample script in ObiContactGrabber.
- Added util LookAroundCamera component.
## [5.6]
### Added
- Faster SDF generation.
- New sample scene.
### Fixed
- Bug that causes out of bounds access when dinamically adding/removing colliders at runtime.
- Bug that prevented stitch constraints from working first time they were enabled.
- Offset in particle selection brush on high density screens.
### Changed
- Constraint batches of multiple actors are now merged together. This greatly reduces the amount of dispatched jobs and improves performance on both backends.
- Colliders no longer have a "use distance fields" boolean value, they will always use the SDF as long as they have one.
## [5.5]
### Added
- Backend system: abstracts the underlying particle-based physics engine used. To the native, built-in engine (Oni), we have added a Burst-based implementation that
will run on all platforms supported by Burst.
### Changed
- Improved path smoother, line and extruded renderer performance.
## [5.4]
### Fixed
- Bug that caused a crash when using Obi in conjunction with Mirror networking system (NetworkIdentity component).
- Bug that could cause a crash when disabling an actor under a disabled solver.
- Bug that prevented self-collisions to work correctly between particles created at runtime by a ObiRopeCursor.
### Changed
- Recompiled iOS libraries using XCode 11.3.1, to circumvent bitcode compatibility issues now that XCode 11.4 and up have a bug that cause linking against Obi to fail.
## [5.3]
### Added
- Added adaptive rendering decimation to ObiPathSmoother, controllable using a curvature threshold.
- Greatly increased numerical precision for rigidbody interaction when solvers are far away from the origin.
- 2D colliders now span infinitely in the Z axis.
### Fixed
- Issue in the ObiStitcher editor that picked up incorrect particles when adding stitches.
- Issue that caused a 1-frame delay for collider geometry parented inside a solver.
- Issue in ObiParticleDragger that caused incorrect behavior with multiple solvers.
- Bug in particle advection, that assumed diffuse particle positions to be expreseed in world space. Now advection works correctly
for solvers not positioned at 0,0,0.
## [5.2]
### Added
- Rope tear callback (cloth.OnRopeTorn)
- Function to reset particle positions orientations and and velocities in an actor (actor.ResetParticles())
- Added support for ObiRopeLineRenderer in SRPs.
### Fixed
- Issue with inertia rotations, that caused wrong inertia tensors for complex compound colliders.
- Issue in particle attachments, that forced to call Bind() manually after changing their target or particle group at runtime.
## [5.1]
### Added
-Smooth initial overlap resolution by using clamped depenetration velocity.
-Actors now automatically create a solver root when editing them in prefab mode.
-Brought back rope mesh baking.
### Fixed
- Bug that caused solvers with no actors in their hierarchy to freeze Unity when updated by a fixed updater.
- Bug that prevented multiple colliders in the same object to be notified of transform changes.
### Fixed
-Fixed bug that caused null ref when attempting to edit a rope with no blueprint.
## [5.0]
#IMPORTANT: Youll need to re-create all your 4.x ObiRope components. Data serialization and workflows are significantly different from previous versions.
### Changed
- Introduced blueprints: particle/constraint object representations are now stored in assets called "blueprints" (as opposed to being stored in the actor itself, as in previous versions). This improves
memory consumption, editor performance, completely separates data from the runtime components that operate on it, and enables easier and faster workflow.
- Non-linear, non-destructive rope editing with instant feedback.
- Simplified underlying engine, constraints are grouped in batches using graph coloring for optimal parallelism.
- Unified handles and pin constraints as "attachments".
- Pin and distance constraints now correctly report forces in Newtons.
- Unitless "Stiffness" parameters have been replaced by "Compliance" parameters in most constraint types, expressed in Newtons/meter.
### Added
- Support for multiple cursors in a single rope.
### Removed
- World space/local space simulation. Simulation is now always performed in solver space.
- Solvers no longer have a maxParticles attribute. They dynamically change their capacity at runtime.
### Fixed
- Crash in certain iOS devices using the A12 cpu.
## [4.2]
### Added
- Remade all sample scenes to work in Unity 2019.1 and above.
## [4.1]
#IMPORTANT: Youll need to re-create all your 3.x ObiRope components, as internal data layout of previous versions as well as serialized GameObject data from previous versions is not compatible with 4.x.
### Added
- Now you can bake the rope mesh anytime, saving it as a new mesh asset in your project. Useful for set dressing and generating static geometry. Only works for ObiRopeExtrudedMeshRederer and ObiRopeMeshRenderer.
- More accurate collision/contact model, using staggered projections.
- Approximate shock propagation for particle contacts. This makes particle stacking easier and stabler. A new parameter has been added to the solver that controls the amount of shock propagation.
- Split material friction into static and dynamic friction.
- Added rolling contacts w/ rolling friction. These are slightly more expensive than regular contacts. Can be controlled on a per-collision material basis.
- Added ObiInstancedParticleRenderer, allows to draw particles as instances of an arbitrary mesh (requires GPU instancing).
- Particle-particle collision callbacks (solver.OnParticleCollision)
### Fixed
- Bug that caused mesh colliders with negative thickness to crash.
### Changed
- More reliable, higher precision algorithm for distance field generation. Specially noticeable in sharp corners/crevices.
## [4.0.2]
#IMPORTANT: Youll need to re-create all your 3.x ObiRope components, as internal data layout of previous versions as well as serialized GameObject data from previous versions is not compatible with 4.x.
### Changed
- Switched the Windows compiler to Clang/LLVM, resulting in a huge performance improvement.
### Fixed
- Null ref exception when copying a ObiCloth component.
- Issue with pin constraints overshooting when solver set to world space mode.
- Issue that prevented pin constraints being torn.
## [4.0]
#IMPORTANT: Youll need to re-create all your ObiRope components, as internal data layout of previous versions as well as serialized GameObject data from previous versions is not compatible with 4.x.
### Added
- New ObiRod actor: advanced rope that models torsion, as well as anisotropic bending and shearing.
- Added 3 new constraint types: Shear/Stretch, Bend/Twist and Chain.
- Achieved zero garbage generation trough the use of new Unity API for camera frustum planes.
### Changed
- All particle buffers (positions, velocities, etc). are now shared between C++/C# using pointers to aligned memory. This completely eliminates the need for copying data back and forth, simplifies the API
and improves performance. The entire Oni.Get/SetParticle* family of methods has disappeared, now you can work with the particle buffers directly.
- Rope rendering modes have been replaced by ObiRopeRenderer components.
### Fixed
- Null ref exception when initializing a disabled actor.
- Bug that caused XPBD to be time step dependent.
## [3.5]
### Added
- Support for 32 and 64 bit Linux architectures.
- Two-way rigidbody interaction for local-space solvers.
- Added world inertia scale.
- ObiCollider now takes a reference to a Collider, enabling the use of multiple colliders in the same GameObject.
### Changed
- Separated world velocity scale into linear and angular components.
- World velocity scale is no longer specified per-actor, but per-solver.
- Better ObiProfiler: it now shows a per-thread pyramid diagram of internal jobs, with more human-readable names.
### Removed
- Solvers no longer have a Collision Layers property. All solvers share the same colliders. Note that you can still use phases to ignore collisions with certain colliders.
- Local space solvers no longer need their own copy of each ObiCollider in the scene, they can all share the same colliders. This results in much higher performance for multiple local-space solvers.
### Fixed
- Added (float3x3) typecast to particle shaders, to be GLES friendly.
## [3.4.1]
### Added
- "Thickness from particles" now works in Custom Mesh render mode.
- Custom Mesh mode now has a "volume scaling" parameter that squashes and stretches the mesh together with the rope.
## [3.4]
### Added
- Perspective-correct particle rendering.
- ObiParticleRenderer now supports custom shaders for rendering.
### Fixed
- Bug that required to disable and re-enable ObiParticleRenderer when moving the actor to a new solver.
- Bug that caused twitching when performing more than 1 physics step per frame when using handles.
## [3.3.1]
### Fixed
- Removed unused variable warnings introduced in 3.3
- Fixed null ref exception when creating a new distance field asset.
- Fixed crash when using multiple solvers in different update modes.
- Fixed some sample scenes lacking collision due to missing distance field.
## [3.3]
### Added
- Support for 2D rigidbody coupling has been brought back.
- Added substepping to the core solver. This allows to update each solver at a different effective frequency, and decouple the Obi
physics loop from Unitys.
- New implementation of fine-grained parallel tasks in core solver, that boosts performance up to x1.5.
- Support for a new type of rope rendering, that can deform any mesh to follow the rope curve.
- Support for a new collision primitive: distance fields.
- Support for per-particle coloring of rope.
- ObiCollider automatically creates ObiRigidbody component if needed when reparenting it.
- Helper script (ObiKinematicVelocities) that calculates angular and linear velocities for kinematic rigidbodies that are transformed around. Useful for continuous collision detection and friction effects against objects that are being moved around using their transform instead of forces.
### Changed
- Near-zero garbage generation for OnCollision and ObFluidUpdated solver events.
- Near-zero garbage generation for rope rendering.
- Constraints.GetBatches() now returns an IEnumerable. This means it cannot be accesed by index. Theres a helper method GetFirstBatch() that returns
the correctly typed first batch, or null if theres none.
### Fixed
- Null reference exception in pin constraints when visualization is enabled.
- Bug that caused asleep particles to miss collisions upon reactivation.
- Bug that caused copying a rope to “steal“ the mesh from the original one.
## [3.2]
### Added
- Support for CapsuleCollider2D.
### Changed
- Rope is still rendered (though not simulated) when the ObiRope component is disabled.
- Colliders/rigidbodies are no longer copied over to the C++ library each frame. Instead, only colliders that have their transform or any collision-related property altered are copied to the solver. This greatly improves performance when many colliders are present, and greatly reduces memory allocation and GC activity.
- AllocateParticles() and FreeParticles() have been merged with AddActor() and RemoveActor() respectively. A new per-particle array “particleToActor” makes it faster and easier to know which actor a particle belongs to.
### Removed
- ObiCollisionGroup has disappeared. It is no longer necessary to manually add colliders to a group, instead each ObiSolver automatically picks up all ObiColliders in a given layer.
- MeshColliders are now always treated as two-sided thin concave meshes. Solid and one-sided modes have disappeared.
### Fixed
- Android issue that prevented some devices from finding the Oni library.
- Removed redundant menu items.
## [3.1.1]
### Added
- New “Line” rendering mode for ropes. This will render the rope as a camera-oriented quad strip, similar to what Unitys LineRenderer does. This is useful for lightweight rendering and 2D games.
- Particle renderer is now much faster and also allocates less memory.
- New “hierarchical” method to generate tether constraints: this method generates more constraints than the traditional “anchor to fixed” approach, but works in the general case even if there are no fixed particles.
### Changed
- Installation is no longer required. Obi now works right out of the box, so the installation window has been removed, and the “Editor default resources” and “Gizmos” folders removed.
## [3.1]
### Added
- You can now choose where should the solver be updated: FixedUpdate, AfterFixedUpdate, or LateUpdate.
- Rope rendering now supports variable thickness, based on particle radii. Enabled by default, disable “thickness from particles” to get uniform thickness regardless of particle radii variations.
- Edit-time preview of “smoothness” rope parameter.
- Utility method to calculate actual rope length.
- Support for triggers. A trigger collider will generate contact constraints, but won´t enforce them.
- Contact structs returned by the OnCollision event now include the contact tangent and bitangent vectors.
- Added per-particle layer properties, for finer collision control.
### Changed
- Faster and more accurate rigidbody impulse application, which results in better collision resolution.
- Greatly improved pin constraint stability for large mass ratios.
- ObiColliderGroups Colliders and Colliders2D properties are now plain arrays: “colliders” and “colliders2D”.
- Memory allocation for rope mesh generation has been reduced by 90%.
- ObiParticleRenderer memory allocation has been greatly reduced, and its performance improved.
- Pin constraints are now always drawn in particle edit mode, not only when the particles are selected.
### Fixed
- Got rid of warnings related to obsolete platform enums in sample scripts.
- Potential bug in GCHandle deallocation affecting upcoming Unity versions (thanks to the guys at Unity for pointing me at this)
- Tearable pin constraints now work correctly.
## [3.0.1]
### Added
- Support for iOS simulator.
- Faster collision contact generation.
### Fixed
- Crash in Crane scene due to a bug in pin constraints.
## [3.0]
#IMPORTANT: Youll need to re-generate all your ropes as internal data layout of previous versions is not compatible with this update.
### Added
- Welcome window with automatic installer.
- Upgraded constraint projection to XPBD (extended position-based dynamics). This decouples stiffness and damping from the amount of iterations, resulting in more realistic simulation.
- Solver constraint enforcement order can now be changed. This allows to change the relative importance of constraints.
- The solver now uses a task-based threading system which allows to exploit parallelism between multiple solvers.
- Custom multithreading profiler, that allows to fine-tune performance.
- Optional local-space simulation, for better numerical accuracy in large-scale worlds and greater control.
- ObiStitcher component allows to stitch together separate ropes.
- Added pencil paint mode to particle editor.
- Automatic self-collisions disabling for particles that intersect in rest pose. This allows to set larger particle radii
to ensure better self-collisions, without worrying about constraint fighting.
- Breakable pin constraints.
- Ropes are now tearable, and custom prefabs can be instantiated at both sides of the tear.
- Rope length can be changed at runtime, using the ObiRopeCursor component.
- Procedural curve geometry smoothing.
### Changed
- Actor particle limit is no longer 16384 but 65536, matching Unitys own vertex limit.
- Particle editor paint brush falloff has ben changed from linear to gaussian.
- Distance constraints compression stiffness has been replaced by a slack percentage.
- Performance improvement in mesh colliders and edge colliders.
### Fixed
- Bug in collision detection against terrains.
- Crash in 32-bit windows systems due to memory misalignment.
- Bug that caused slow convergence and excessive jittering for particle-particle sequential contact resolution.
- Bug in hierarchical grid update that caused a crash in some cases when a hash key collision happened.
- Bug in continuous collision detection that caused particles to be pushed to the other side of very thin objects.
- Bug in ray/bounding box intersection test caused by non-IEEE754 compliant SSE division by zero.
- Bug that caused ObiParticleRenderer to ignore camera culling mask, and render in all cameras.
- Bug that caused a crash under certain conditions in 32 bit systems.
- Bug that caused particle property value field to reset to the value of the last particle when painting.
- Fixed collision stabilization bug that caused particles to sink slightly when over a dynamic rigidbody.
## [1.2]
### Added
- Android support.
- Upgraded constraint projection to XPBD (extended position-based dynamics). This decouples stiffness and damping from the amount of iterations.
- Solver constraint enforcement order can now be changed. This allows to change the relative importance of constraints.
- Welcome window with automatic installer.
- Resolution-preserving method to change rope length dynamically.
- Added pencil paint mode to particle editor.
- Optional local-space simulation, for better numerical accuracy in large-scale worlds and greater control.
- Custom multithreading profiler, that allows to fine-tune performance.
- Better particle visualization in editor.
- Breakable pin constraints.
### Changed
- Actor particle limit is no longer 16384 but 65536, matching Unitys own vertex limit.
- Particle editor paint brush falloff has ben changed from linear to gaussian.
- Distance constraints compression stiffness has been replaced by a slack percentage.
### Fixed
- Bug that caused an index out of bounds exception when initializing rope with zero resolution.
- Bug that caused an error message regarding MeshFilter destruction when entering play mode with a rope selected in the hierarchy.
- Bug that prevented the particle editor window from appearing on retina displays.
- 1-frame delay between particle and rigid body physics, which affected pin and collision constraints.
## [1.1]
### Added
- MeshColliders are now fully supported.
- Support for 2D physics, Box2D, Circle2D and Edge2D colliders.
- Chain rendering.
- Sleep threshold that keeps particles fixed in place when their kinetic energy is low.
- Chain constraints, that allow for 100% inextensible ropes.
- Rope thickness, twist, cap sections and section shape can now be changed without the need to re-initialize the rope.
- Required constraint components are automatically removed from the object when removing the rope component in editor.
### Fixed
- Issue with box colliders, that caused incorrect contact generation in corners when using contactOffset.
## [1.0.0] - 2015-07-16
- Initial release.

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 8d9d04260bc994f1b9d444b675212dc6
labels:
- ObiRope
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: d6a0c47fa0afb4e1fb441061faa32d50
folderAsset: yes
timeCreated: 1435569421
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3d5722720e25b4db69e767c6920cf081
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1af54f7feeec0410cada2fc051752b61
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: aa63386a67f904b399175c9931270250
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,40 @@
using UnityEngine;
using UnityEditor;
namespace Obi
{
public abstract class ObiBlueprintEditorTool
{
protected ObiActorBlueprintEditor editor;
protected string m_Name;
protected Texture m_Icon;
public string name
{
get { return m_Name; }
}
public Texture icon
{
get
{
return m_Icon;
}
}
public ObiBlueprintEditorTool(ObiActorBlueprintEditor editor)
{
this.editor = editor;
}
public virtual void OnEnable(){}
public virtual void OnDisable(){}
public virtual void OnDestroy(){}
public virtual string GetHelpString() { return string.Empty; }
public abstract void OnInspectorGUI();
public virtual void OnSceneGUI(SceneView sceneView){}
public virtual bool Editable(int index) { return editor.visible[index]; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ca0c1c4cbbd024d49b15bf3439506500
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,130 @@
using UnityEngine;
using UnityEditor;
namespace Obi
{
public class ObiPaintBrushEditorTool : ObiBlueprintEditorTool
{
public ObiRaycastBrush paintBrush;
public bool selectionMask = false;
public int sourcePropertyIndex = 0; /**<index of the property to copy from*/
public ObiMeshBasedActorBlueprintEditor meshBasedEditor
{
get { return editor as ObiMeshBasedActorBlueprintEditor; }
}
public ObiPaintBrushEditorTool(ObiMeshBasedActorBlueprintEditor editor) : base(editor)
{
m_Icon = Resources.Load<Texture2D>("BrushIcon");
m_Name = "Property painting";
paintBrush = new ObiRaycastBrush(editor.sourceMesh,
() =>
{
// As RecordObject diffs with the end of the current frame,
// and this is a multi-frame operation, we need to use RegisterCompleteObjectUndo instead.
Undo.RegisterCompleteObjectUndo(editor.blueprint, "Paint particles");
},
() =>
{
editor.Refresh();
},
() =>
{
EditorUtility.SetDirty(editor.blueprint);
});
}
public override string GetHelpString()
{
return "Paint particle properties directly on the mesh. Most brushes have an alternate mode, accesed by holding 'shift' while painting.";
}
public override void OnInspectorGUI()
{
EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins);
EditorGUILayout.Space();
// toolbar with available brush modes for the current property:
editor.currentProperty.BrushModes(paintBrush);
EditorGUILayout.Space();
EditorGUI.BeginChangeCheck();
editor.currentPropertyIndex = editor.PropertySelector(editor.currentPropertyIndex);
if (EditorGUI.EndChangeCheck())
{
editor.Refresh();
editor.currentProperty.OnSelect(paintBrush);
}
if (paintBrush.brushMode is ObiFloatCopyBrushMode)
{
EditorGUI.BeginChangeCheck();
sourcePropertyIndex = editor.PropertySelector(sourcePropertyIndex, "Copy from");
var sourceProperty = editor.GetProperty(sourcePropertyIndex) as ObiBlueprintFloatProperty;
if (EditorGUI.EndChangeCheck())
{
(paintBrush.brushMode as ObiFloatCopyBrushMode).source = sourceProperty;
}
if (sourceProperty == null)
EditorGUILayout.HelpBox("You can't copy value from this property.", MessageType.Error);
}
if (paintBrush.brushMode.needsInputValue)
editor.currentProperty.PropertyField();
paintBrush.radius = EditorGUILayout.Slider("Brush size", paintBrush.radius, 0.0001f, 0.5f);
paintBrush.innerRadius = EditorGUILayout.Slider("Brush inner size", paintBrush.innerRadius, 0, 1);
paintBrush.opacity = EditorGUILayout.Slider("Brush opacity", paintBrush.opacity, 0, 1);
paintBrush.mirror.axis = (ObiBrushMirrorSettings.MirrorAxis)EditorGUILayout.EnumPopup("Brush mirror axis", paintBrush.mirror.axis);
paintBrush.mirror.space = (ObiBrushMirrorSettings.MirrorSpace)EditorGUILayout.EnumPopup("Brush mirror space", paintBrush.mirror.space);
EditorGUI.BeginChangeCheck();
meshBasedEditor.particleCulling = (ObiMeshBasedActorBlueprintEditor.ParticleCulling)EditorGUILayout.EnumPopup("Culling", meshBasedEditor.particleCulling);
if (ObiActorBlueprintEditor.selectedCount == 0)
{
EditorGUILayout.HelpBox("Select at least one particle to use selection mask.", MessageType.Info);
selectionMask = false;
GUI.enabled = false;
}
selectionMask = EditorGUILayout.Toggle("Selection mask", selectionMask);
if (EditorGUI.EndChangeCheck())
SceneView.RepaintAll();
GUI.enabled = true;
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
GUILayout.Box(GUIContent.none, ObiEditorUtils.GetSeparatorLineStyle());
EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins);
editor.RenderModeSelector();
editor.currentProperty.VisualizationOptions();
EditorGUILayout.EndVertical();
}
public override bool Editable(int index)
{
return editor.visible[index] && (!selectionMask || ObiActorBlueprintEditor.selectionStatus[index]);
}
public override void OnSceneGUI(SceneView view)
{
if (Camera.current != null)
{
var blueprint = meshBasedEditor.blueprint as ObiMeshBasedActorBlueprint;
paintBrush.raycastTransform = blueprint != null ? Matrix4x4.TRS(Vector3.zero, blueprint.rotation, blueprint.scale) : Matrix4x4.identity;
paintBrush.raycastTarget = meshBasedEditor.sourceMesh;
paintBrush.DoBrush(editor.blueprint.positions);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 332bb5fc94a774291b4c4ebe50f61205
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,336 @@
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
namespace Obi
{
public class ObiParticleSelectionEditorTool : ObiBlueprintEditorTool
{
ObiScreenSpaceBrush selectionBrush;
ObiSelectBrushMode selectMode;
ObiTethersTool tethersTool;
protected ReorderableList particleGroupList;
protected bool mixedPropertyValue = false;
protected float minSelectionValue;
protected float maxSelectionValue;
public ObiParticleSelectionEditorTool(ObiActorBlueprintEditor editor) : base(editor)
{
m_Icon = Resources.Load<Texture2D>("SelectIcon");
m_Name = "Particle selection";
selectionBrush = new ObiScreenSpaceBrush(null, UpdateSelection, null);
selectMode = new ObiSelectBrushMode(new ObiBlueprintSelected(editor));
selectionBrush.brushMode = selectMode;
tethersTool = new ObiTethersTool();
InitializeGroupsList();
}
public override string GetHelpString()
{
if (ObiActorBlueprintEditor.selectedCount > 0)
return "" + ObiActorBlueprintEditor.selectedCount + " selected particles.";
else
return "No particles selected. Click and drag over particles to select them.";
}
private void InitializeGroupsList()
{
particleGroupList = new ReorderableList(editor.serializedObject,
editor.serializedObject.FindProperty("groups"),
false, true, true, true);
particleGroupList.drawHeaderCallback = (Rect rect) =>
{
EditorGUI.LabelField(rect, "Groups");
};
particleGroupList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
{
var element = particleGroupList.serializedProperty.GetArrayElementAtIndex(index);
rect.y += 4;
SerializedObject obj = new SerializedObject(element.objectReferenceValue);
ObiParticleGroup group = obj.targetObject as ObiParticleGroup;
EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight),
obj.FindProperty("m_Name"), new GUIContent("Name"));
rect.y += EditorGUIUtility.singleLineHeight + 2;
if (GUI.Button(new Rect(rect.x, rect.y, rect.width * 0.5f, EditorGUIUtility.singleLineHeight), "Select", EditorStyles.miniButtonLeft))
{
if ((Event.current.modifiers & EventModifiers.Shift) == 0)
{
for (int p = 0; p < ObiActorBlueprintEditor.selectionStatus.Length; p++)
ObiActorBlueprintEditor.selectionStatus[p] = false;
}
foreach (int p in group.particleIndices)
ObiActorBlueprintEditor.selectionStatus[p] = true;
UpdateSelection();
}
if (GUI.Button(new Rect(rect.x + rect.width * 0.5f, rect.y, rect.width * 0.5f, EditorGUIUtility.singleLineHeight), "Set", EditorStyles.miniButtonRight))
{
group.particleIndices.Clear();
for (int p = 0; p < ObiActorBlueprintEditor.selectionStatus.Length; p++)
{
if (ObiActorBlueprintEditor.selectionStatus[p])
group.particleIndices.Add(p);
}
}
obj.ApplyModifiedProperties();
};
particleGroupList.elementHeight = (EditorGUIUtility.singleLineHeight + 2) * 2 + 8;
particleGroupList.onAddCallback = (ReorderableList list) =>
{
var group = editor.blueprint.AppendNewParticleGroup("new group");
for (int i = 0; i < ObiActorBlueprintEditor.selectionStatus.Length; i++)
{
if (ObiActorBlueprintEditor.selectionStatus[i])
group.particleIndices.Add(i);
}
AssetDatabase.SaveAssets();
};
particleGroupList.onRemoveCallback = (ReorderableList list) =>
{
editor.blueprint.RemoveParticleGroupAt(list.index);
};
}
private void SelectionTools()
{
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button(new GUIContent(Resources.Load<Texture2D>("InvertButton"), "Invert selection"), GUILayout.MaxHeight(24), GUILayout.MaxWidth(48)))
{
for (int i = 0; i < ObiActorBlueprintEditor.selectionStatus.Length; i++)
{
if (editor.blueprint.IsParticleActive(i))
ObiActorBlueprintEditor.selectionStatus[i] = !ObiActorBlueprintEditor.selectionStatus[i];
}
UpdateSelection();
}
GUI.enabled = ObiActorBlueprintEditor.selectedCount > 0;
if (GUILayout.Button(new GUIContent(Resources.Load<Texture2D>("ClearButton"), "Clear selection"), GUILayout.MaxHeight(24), GUILayout.MaxWidth(48)))
{
for (int i = 0; i < ObiActorBlueprintEditor.selectionStatus.Length; i++)
ObiActorBlueprintEditor.selectionStatus[i] = false;
UpdateSelection();
}
if (GUILayout.Button(new GUIContent(Resources.Load<Texture2D>("OptimizeButton"), "Optimize selected"), GUILayout.MaxHeight(24), GUILayout.MaxWidth(48)))
{
Undo.RecordObject(editor.blueprint, "Optimize particles away");
editor.blueprint.RemoveSelectedParticles(ref ObiActorBlueprintEditor.selectionStatus);
editor.Refresh();
}
if (GUILayout.Button(new GUIContent(Resources.Load<Texture2D>("RemoveButton"), "Remove selected"), GUILayout.MaxHeight(24), GUILayout.MaxWidth(48)))
{
Undo.RecordObject(editor.blueprint, "Remove particles");
editor.blueprint.RemoveSelectedParticles(ref ObiActorBlueprintEditor.selectionStatus, false);
editor.Refresh();
}
GUI.enabled = true;
if (GUILayout.Button(new GUIContent(Resources.Load<Texture2D>("RestoreButton"), "Restore removed particles"), GUILayout.MaxHeight(24), GUILayout.MaxWidth(48)))
{
Undo.RecordObject(editor.blueprint, "Restore removed particles");
editor.blueprint.RestoreRemovedParticles();
editor.Refresh();
}
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
EditorGUILayout.LabelField("Property-based selection", EditorStyles.boldLabel);
var property = editor.currentProperty as ObiBlueprintFloatProperty;
if (property != null)
{
if (!Mathf.Approximately(property.minVisualizationValue,property.maxVisualizationValue))
{
EditorGUILayout.HelpBox("Drag the slider to select based on " + property.name + ". You can choose a different property in the \"Property\" dropdown below.", MessageType.None);
minSelectionValue = Mathf.Max(minSelectionValue, property.minVisualizationValue);
maxSelectionValue = Mathf.Min(maxSelectionValue, property.maxVisualizationValue);
maxSelectionValue = Mathf.Max(maxSelectionValue, minSelectionValue);
EditorGUI.BeginChangeCheck();
EditorGUILayout.MinMaxSlider("Select by " + property.name, ref minSelectionValue, ref maxSelectionValue, property.minVisualizationValue, property.maxVisualizationValue);
minSelectionValue = EditorGUILayout.FloatField("Minimum " + property.name, minSelectionValue);
maxSelectionValue = EditorGUILayout.FloatField("Maximum " + property.name, maxSelectionValue);
if (EditorGUI.EndChangeCheck())
{
for (int i = 0; i < ObiActorBlueprintEditor.selectionStatus.Length; i++)
{
if (editor.blueprint.IsParticleActive(i))
{
var value = property.Get(i);
ObiActorBlueprintEditor.selectionStatus[i] = value >= minSelectionValue && value <= maxSelectionValue;
}
}
UpdateSelection();
}
}
else
{
EditorGUILayout.HelpBox("All particles have the same " + property.name + " value.", MessageType.Info);
}
}
else
{
EditorGUILayout.HelpBox("Property-based selection only works with scalar properties.",MessageType.Info);
}
}
public override void OnInspectorGUI()
{
// Selection tools:
EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins);
EditorGUILayout.Space();
selectionBrush.radius = EditorGUILayout.Slider("Brush size", selectionBrush.radius, 5, 200);
if (editor is ObiMeshBasedActorBlueprintEditor)
{
EditorGUI.BeginChangeCheck();
(editor as ObiMeshBasedActorBlueprintEditor).particleCulling = (ObiMeshBasedActorBlueprintEditor.ParticleCulling)EditorGUILayout.EnumPopup("Culling", (editor as ObiMeshBasedActorBlueprintEditor).particleCulling);
if (EditorGUI.EndChangeCheck())
SceneView.RepaintAll();
}
EditorGUILayout.Space();
SelectionTools();
EditorGUILayout.EndVertical();
// Properties:
EditorGUILayout.Space();
GUILayout.Box(GUIContent.none, ObiEditorUtils.GetSeparatorLineStyle());
EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins);
EditorGUILayout.Space();
EditorGUILayout.LabelField("Properties", EditorStyles.boldLabel);
EditorGUILayout.HelpBox("Select a property to view and edit. Currently editing " + editor.currentProperty.name+".", MessageType.None);
EditorGUI.BeginChangeCheck();
editor.currentPropertyIndex = editor.PropertySelector(editor.currentPropertyIndex);
if (EditorGUI.EndChangeCheck())
{
editor.Refresh();
UpdateSelection();
}
// Property value:
EditorGUI.showMixedValue = mixedPropertyValue;
EditorGUI.BeginChangeCheck();
editor.currentProperty.PropertyField();
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(editor.blueprint, "Set particle property");
for (int i = 0; i < ObiActorBlueprintEditor.selectionStatus.Length; i++)
{
if (!ObiActorBlueprintEditor.selectionStatus[i]) continue;
editor.currentProperty.SetDefaultToIndex(i);
}
editor.Refresh();
}
EditorGUI.showMixedValue = false;
EditorGUILayout.EndVertical();
// Particle groups:
EditorGUILayout.Space();
GUILayout.Box(GUIContent.none, ObiEditorUtils.GetSeparatorLineStyle());
EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins);
EditorGUILayout.Space();
EditorGUILayout.LabelField("Particle groups", EditorStyles.boldLabel);
particleGroupList.DoLayoutList();
EditorGUILayout.EndVertical();
if (editor.blueprint.usesTethers)
{
EditorGUILayout.Space();
GUILayout.Box(GUIContent.none, ObiEditorUtils.GetSeparatorLineStyle());
EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins);
EditorGUILayout.Space();
tethersTool.DoTethers(editor);
EditorGUILayout.EndVertical();
}
EditorGUILayout.Space();
GUILayout.Box(GUIContent.none, ObiEditorUtils.GetSeparatorLineStyle());
EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins);
editor.RenderModeSelector();
ObiActorBlueprintEditor.dotRadiusScale = EditorGUILayout.Slider(new GUIContent("Particle dot size"), ObiActorBlueprintEditor.dotRadiusScale, 0, 5);
editor.currentProperty.VisualizationOptions();
EditorGUILayout.EndVertical();
}
public override void OnSceneGUI(SceneView sceneView)
{
if (Camera.current != null)
selectionBrush.DoBrush(editor.blueprint.positions);
}
protected void UpdateSelection()
{
ObiActorBlueprintEditor.selectedCount = 0;
mixedPropertyValue = false;
// Find out how many selected particles we have, and whether they all have the same value for the current property:
for (int i = 0; i < ObiActorBlueprintEditor.selectionStatus.Length; i++)
{
if (editor.blueprint.IsParticleActive(i) && ObiActorBlueprintEditor.selectionStatus[i])
{
ObiActorBlueprintEditor.selectedCount++;
if (ObiActorBlueprintEditor.activeParticle >= 0)
{
if (!editor.currentProperty.Equals(ObiActorBlueprintEditor.activeParticle, i))
mixedPropertyValue = true;
}
else
ObiActorBlueprintEditor.activeParticle = i;
}
else if (ObiActorBlueprintEditor.activeParticle == i)
ObiActorBlueprintEditor.activeParticle = -1;
}
// Set initial property value:
if (!mixedPropertyValue && ObiActorBlueprintEditor.activeParticle >= 0)
editor.currentProperty.GetDefaultFromIndex(ObiActorBlueprintEditor.activeParticle);
editor.Repaint();
SceneView.RepaintAll();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b3940c62f9ffe4808afc4d2d70ae5e28
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,166 @@
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
using System.Collections;
using System;
namespace Obi
{
public class ObiPropertyTextureEditorTool : ObiBlueprintEditorTool
{
public enum TextureChannel
{
Red = 0,
Green = 1,
Blue = 2,
Alpha = 3,
}
protected bool selectionMask = false;
protected bool import = true;
protected bool export = true;
protected float minPropertyValue = 0;
protected float maxPropertyValue = 10;
protected int exportWidth = 512;
protected int exportHeight = 512;
protected int padding = 64;
protected Texture2D propertyTexture;
protected TextureChannel textureChannel;
protected ObiBlueprintFloatProperty floatProperty;
protected ObiBlueprintColorProperty colorProperty;
protected Action<int, Color> textureReadCallback;
public ObiMeshBasedActorBlueprintEditor meshBasedEditor
{
get { return editor as ObiMeshBasedActorBlueprintEditor; }
}
public ObiPropertyTextureEditorTool(ObiMeshBasedActorBlueprintEditor editor) : base(editor)
{
m_Icon = Resources.Load<Texture2D>("TextureIcon");
m_Name = "Texture import/export";
}
public override string GetHelpString()
{
return "Import/export particle properties to textures. Assumes that your mesh has non-overlapping UVs.";
}
private void FloatFromTexture(int i, Color color)
{
if (!selectionMask || ObiActorBlueprintEditor.selectionStatus[i])
{
float value = minPropertyValue + color[(int)textureChannel] * (maxPropertyValue - minPropertyValue);
floatProperty.Set(i, value);
}
}
private void ColorFromTexture(int i, Color color)
{
if (!selectionMask || ObiActorBlueprintEditor.selectionStatus[i])
colorProperty.Set(i, color);
}
public override void OnInspectorGUI()
{
EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins);
EditorGUILayout.Space();
EditorGUI.BeginChangeCheck();
editor.currentPropertyIndex = editor.PropertySelector(editor.currentPropertyIndex);
if (EditorGUI.EndChangeCheck())
editor.Refresh();
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
GUILayout.Box(GUIContent.none, ObiEditorUtils.GetSeparatorLineStyle());
EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins);
import = EditorGUILayout.BeginFoldoutHeaderGroup(import, "Import texture");
if (import)
{
propertyTexture = (Texture2D)EditorGUILayout.ObjectField("Source", propertyTexture, typeof(Texture2D), false);
floatProperty = editor.currentProperty as ObiBlueprintFloatProperty;
colorProperty = editor.currentProperty as ObiBlueprintColorProperty;
if (floatProperty != null)
{
textureReadCallback = FloatFromTexture;
textureChannel = (TextureChannel)EditorGUILayout.EnumPopup("Source channel", textureChannel);
minPropertyValue = EditorGUILayout.FloatField("Min value", minPropertyValue);
maxPropertyValue = EditorGUILayout.FloatField("Max value", maxPropertyValue);
}
else if (colorProperty != null)
{
textureReadCallback = ColorFromTexture;
}
if (GUILayout.Button("Import"))
{
Undo.RecordObject(editor.blueprint, "Import particle property");
if (!meshBasedEditor.ReadParticlePropertyFromTexture(propertyTexture, textureReadCallback))
{
EditorUtility.DisplayDialog("Invalid texture", "The texture is either null or not readable.", "Ok");
}
// force automatic range calculation for floating point properties.
if (floatProperty != null)
floatProperty.autoRange = true;
editor.Refresh();
}
}
EditorGUILayout.EndFoldoutHeaderGroup();
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
GUILayout.Box(GUIContent.none, ObiEditorUtils.GetSeparatorLineStyle());
EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins);
export = EditorGUILayout.BeginFoldoutHeaderGroup(export, "Export texture");
if (export)
{
exportWidth = EditorGUILayout.IntField("Texture width", exportWidth);
exportHeight = EditorGUILayout.IntField("Texture height", exportHeight);
padding = EditorGUILayout.IntField("Padding", padding);
if (GUILayout.Button("Export"))
{
var path = EditorUtility.SaveFilePanel("Save texture as PNG",
"",
"property.png",
"png");
if (path.Length > 0)
{
// force automatic range calculation for floating point properties.
if (floatProperty != null)
floatProperty.autoRange = true;
editor.Refresh();
if (!meshBasedEditor.WriteParticlePropertyToTexture(path, exportWidth, exportHeight, padding))
{
EditorUtility.DisplayDialog("Invalid path", "Could not write a texture to that location.", "Ok");
}
}
}
}
EditorGUILayout.EndFoldoutHeaderGroup();
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
GUILayout.Box(GUIContent.none, ObiEditorUtils.GetSeparatorLineStyle());
EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins);
editor.RenderModeSelector();
EditorGUILayout.EndVertical();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7727285b07aa44d6e8f65a8bc1f5d972
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,86 @@
using UnityEngine;
using UnityEditor;
using System;
namespace Obi
{
public class ObiTethersTool
{
protected Rect tetherDropdownRect;
protected bool[] tetheredGroups = new bool[0];
// the GenericMenu.MenuFunction2 event handler for when a menu item is selected
void OnTetherGroupSelected(object index)
{
int i = (int)index;
tetheredGroups[i] = !tetheredGroups[i];
}
public void DoTethers(ObiActorBlueprintEditor editor)
{
EditorGUILayout.LabelField("Tethers", EditorStyles.boldLabel);
var tethers = editor.blueprint.GetConstraintsByType(Oni.ConstraintType.Tether);
int tetherCount = 0;
if (tethers != null)
tetherCount = tethers.GetConstraintCount();
if (tetherCount > 0)
EditorGUILayout.LabelField("" + tetherCount + " tether constraints.", EditorStyles.helpBox);
else
EditorGUILayout.LabelField("No tether constraints. Select at least one particle group in the dropdown, then click 'Generate Tethers'.", EditorStyles.helpBox);
Array.Resize(ref tetheredGroups, editor.blueprint.groups.Count);
// display the GenericMenu when pressing a button
if (GUILayout.Button("Tethered groups", EditorStyles.popup))
{
// create the menu and add items to it
GenericMenu menu = new GenericMenu();
// forward slashes nest menu items under submenus
for (int i = 0; i < editor.blueprint.groups.Count; ++i)
{
menu.AddItem(new GUIContent(editor.blueprint.groups[i].name), tetheredGroups[i], OnTetherGroupSelected, i);
}
// display the menu
menu.DropDown(tetherDropdownRect);
}
if (Event.current.type == EventType.Repaint)
tetherDropdownRect = GUILayoutUtility.GetLastRect();
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Generate tethers",GUILayout.MinHeight(32)))
{
// Select all particles in the tethered groups:
for (int i = 0; i < ObiActorBlueprintEditor.selectionStatus.Length; ++i)
{
ObiActorBlueprintEditor.selectionStatus[i] = false;
for (int j = 0; j < tetheredGroups.Length; ++j)
{
if (tetheredGroups[j] && editor.blueprint.groups[j].ContainsParticle(i))
{
ObiActorBlueprintEditor.selectionStatus[i] = true;
break;
}
}
}
editor.blueprint.GenerateTethers(ObiActorBlueprintEditor.selectionStatus);
editor.Refresh();
}
if (GUILayout.Button("Clear tethers",GUILayout.MinHeight(32)))
{
editor.blueprint.ClearTethers();
editor.Refresh();
}
EditorGUILayout.EndHorizontal();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6aefc9e7e74be461e93ee58f15c5a1dd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 625a6e0c1ba72483190830373b7b45fe
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4fc4d44dc6d334c2985875fc6f7b8cb1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
namespace Obi
{
public interface IObiBrushMode
{
string name{get;}
bool needsInputValue{ get; }
void ApplyStamps(ObiBrushBase brush, bool modified);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4c46364da0f1641c7ab84e12348a6a4b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,38 @@
using UnityEngine;
namespace Obi
{
public class ObiColorPaintBrushMode : IObiBrushMode
{
ObiBlueprintColorProperty property;
public ObiColorPaintBrushMode(ObiBlueprintColorProperty property)
{
this.property = property;
}
public string name
{
get { return "Paint"; }
}
public bool needsInputValue
{
get { return true; }
}
public void ApplyStamps(ObiBrushBase brush, bool modified)
{
for (int i = 0; i < brush.weights.Length; ++i)
{
if (!property.Masked(i) && brush.weights[i] > 0)
{
Color currentValue = property.Get(i);
Color delta = brush.weights[i] * brush.opacity * brush.speed * (property.GetDefault() - currentValue);
property.Set(i, currentValue + delta * (modified ? -1 : 1));
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1fd033c8f79b1494d8b8de5c8311f8c1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,53 @@
using UnityEngine;
namespace Obi
{
public class ObiColorSmoothBrushMode : IObiBrushMode
{
ObiBlueprintColorProperty property;
public ObiColorSmoothBrushMode(ObiBlueprintColorProperty property)
{
this.property = property;
}
public string name
{
get { return "Smooth"; }
}
public bool needsInputValue
{
get { return false; }
}
public void ApplyStamps(ObiBrushBase brush, bool modified)
{
Color averageValue = Color.black;
float totalWeight = 0;
for (int i = 0; i < brush.weights.Length; ++i)
{
if (!property.Masked(i) && brush.weights[i] > 0)
{
averageValue += property.Get(i) * brush.weights[i];
totalWeight += brush.weights[i];
}
}
averageValue /= totalWeight;
for (int i = 0; i < brush.weights.Length; ++i)
{
if (!property.Masked(i) && brush.weights[i] > 0)
{
Color currentValue = property.Get(i);
Color delta = brush.opacity * brush.speed * (Color.Lerp(currentValue, averageValue, brush.weights[i]) - currentValue);
property.Set(i, currentValue + delta * (modified ? -1 : 1));
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a69a8a1614478445cbb770a5e123ab18
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,36 @@
namespace Obi
{
public class ObiFloatAddBrushMode : IObiBrushMode
{
ObiBlueprintFloatProperty property;
public ObiFloatAddBrushMode(ObiBlueprintFloatProperty property)
{
this.property = property;
}
public string name
{
get { return "Add"; }
}
public bool needsInputValue
{
get { return true; }
}
public void ApplyStamps(ObiBrushBase brush, bool modified)
{
for (int i = 0; i < brush.weights.Length; ++i)
{
if (!property.Masked(i) && brush.weights[i] > 0)
{
float currentValue = property.Get(i);
float delta = brush.weights[i] * brush.opacity * brush.speed * property.GetDefault();
property.Set(i, currentValue + delta * (modified ? -1 : 1));
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8b39e10cedc94461083b3b19c3ddb343
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,42 @@
namespace Obi
{
public class ObiFloatCopyBrushMode : IObiBrushMode
{
ObiBlueprintFloatProperty property;
public ObiBlueprintFloatProperty source;
public ObiFloatCopyBrushMode(ObiBlueprintFloatProperty property, ObiBlueprintFloatProperty source)
{
this.property = property;
this.source = source;
}
public string name
{
get { return "Copy"; }
}
public bool needsInputValue
{
get { return false; }
}
public void ApplyStamps(ObiBrushBase brush, bool modified)
{
if (property != null && source != null)
{
for (int i = 0; i < brush.weights.Length; ++i)
{
if (!property.Masked(i) && brush.weights[i] > 0)
{
float currentValue = property.Get(i);
float sourceValue = source.Get(i);
float delta = brush.weights[i] * brush.opacity * brush.speed * (sourceValue - currentValue);
property.Set(i, currentValue + delta * (modified ? -1 : 1));
}
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 95bb470841f42415ab256cd64adb9242
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,37 @@
namespace Obi
{
public class ObiFloatPaintBrushMode : IObiBrushMode
{
ObiBlueprintFloatProperty property;
public ObiFloatPaintBrushMode(ObiBlueprintFloatProperty property)
{
this.property = property;
}
public string name
{
get { return "Paint"; }
}
public bool needsInputValue
{
get { return true; }
}
public void ApplyStamps(ObiBrushBase brush, bool modified)
{
for (int i = 0; i < brush.weights.Length; ++i)
{
if (!property.Masked(i) && brush.weights[i] > 0)
{
float currentValue = property.Get(i);
float delta = brush.weights[i] * brush.opacity * brush.speed * (property.GetDefault() - currentValue);
property.Set(i, currentValue + delta * (modified ? -1 : 1));
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eb3d176006d404a508bef284d2701ab0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,55 @@
using UnityEngine;
namespace Obi
{
public class ObiFloatSmoothBrushMode : IObiBrushMode
{
ObiBlueprintFloatProperty property;
public ObiFloatSmoothBrushMode(ObiBlueprintFloatProperty property)
{
this.property = property;
}
public string name
{
get { return "Smooth"; }
}
public bool needsInputValue
{
get { return false; }
}
public void ApplyStamps(ObiBrushBase brush, bool modified)
{
var floatProperty = (ObiBlueprintFloatProperty)property;
float averageValue = 0;
float totalWeight = 0;
for (int i = 0; i < brush.weights.Length; ++i)
{
if (!property.Masked(i) && brush.weights[i] > 0)
{
averageValue += floatProperty.Get(i) * brush.weights[i];
totalWeight += brush.weights[i];
}
}
averageValue /= totalWeight;
for (int i = 0; i < brush.weights.Length; ++i)
{
if (!property.Masked(i) && brush.weights[i] > 0)
{
float currentValue = floatProperty.Get(i);
float delta = brush.opacity * brush.speed * (Mathf.Lerp(currentValue,averageValue,brush.weights[i]) - currentValue);
floatProperty.Set(i, currentValue + delta * (modified ? -1 : 1));
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 63dad56a994af4db7bb3449382022c54
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,33 @@
namespace Obi
{
public class ObiIntPaintBrushMode : IObiBrushMode
{
ObiBlueprintIntProperty property;
public ObiIntPaintBrushMode(ObiBlueprintIntProperty property)
{
this.property = property;
}
public string name
{
get { return "Paint"; }
}
public bool needsInputValue
{
get { return true; }
}
public void ApplyStamps(ObiBrushBase brush, bool modified)
{
for (int i = 0; i < brush.weights.Length; ++i)
{
if (!property.Masked(i) && brush.weights[i] > (1 - brush.opacity))
{
property.Set(i, property.GetDefault());
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d8882849c81104cef80b3dfdab2021c9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,39 @@
namespace Obi
{
public class ObiMasterSlavePaintBrushMode : IObiBrushMode
{
ObiBlueprintIntProperty property;
public ObiMasterSlavePaintBrushMode(ObiBlueprintIntProperty property)
{
this.property = property;
}
public string name
{
get { return "Master/Slave paint"; }
}
public bool needsInputValue
{
get { return true; }
}
public void ApplyStamps(ObiBrushBase brush, bool modified)
{
for (int i = 0; i < brush.weights.Length; ++i)
{
if (!property.Masked(i) && brush.weights[i] > (1 - brush.opacity))
{
int currentValue = property.Get(i);
if (modified)
currentValue &= ~(int)(1 << property.GetDefault());
else currentValue |= (int)(1 << property.GetDefault());
property.Set(i, currentValue);
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 62f8e9adfc17440359a9b51005cd0948
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,33 @@
namespace Obi
{
public class ObiSelectBrushMode : IObiBrushMode
{
ObiBlueprintSelected property;
string customName;
public ObiSelectBrushMode(ObiBlueprintSelected property, string customName = "Select")
{
this.property = property;
this.customName = customName;
}
public string name
{
get { return customName; }
}
public bool needsInputValue
{
get { return true; }
}
public void ApplyStamps(ObiBrushBase brush, bool modified)
{
for (int i = 0; i < brush.weights.Length; ++i)
{
if (brush.weights[i] > 0 && !property.Masked(i))
property.Set(i,!modified);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9cea7ef42253f4d2b9b5a222521cdee8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,161 @@
using UnityEngine;
using UnityEditor;
using System;
namespace Obi
{
public abstract class ObiBrushBase
{
static int particleBrushHash = "ObiBrushHash".GetHashCode();
public IObiBrushMode brushMode;
public float radius = 1;
public float innerRadius = 0.5f;
public float opacity = 1;
public float[] weights = new float[0];
public bool drag = true;
public float speed = 0.1f;
protected int controlID;
protected Action onStrokeStart;
protected Action onStrokeUpdate;
protected Action onStrokeEnd;
public float SqrRadius
{
get{ return radius * radius; }
}
public ObiBrushBase(Action onStrokeStart, Action onStrokeUpdate, Action onStrokeEnd)
{
this.onStrokeStart = onStrokeStart;
this.onStrokeUpdate = onStrokeUpdate;
this.onStrokeEnd = onStrokeEnd;
}
protected virtual float WeightFromDistance(float distance)
{
// anything outside the brush should have zero weight:
if (distance > radius)
return 0;
float t = Mathf.InverseLerp(innerRadius * radius, radius, distance);
return Mathf.SmoothStep(1, 0, t);
}
protected abstract void GenerateWeights(Vector3[] positions);
protected virtual void OnMouseDown(Vector3[] positions)
{
if (Event.current.button != 0 || (Event.current.modifiers & ~EventModifiers.Shift) != EventModifiers.None)
return;
GUIUtility.hotControl = controlID;
GenerateWeights(positions);
if (onStrokeStart != null)
onStrokeStart();
if (brushMode != null)
brushMode.ApplyStamps(this, (Event.current.modifiers & EventModifiers.Shift) != 0);
if (onStrokeUpdate != null)
onStrokeUpdate();
Event.current.Use();
}
protected virtual void OnMouseMove(Vector3[] positions)
{
}
protected virtual void OnMouseDrag(Vector3[] positions)
{
if (GUIUtility.hotControl == controlID && drag)
{
GenerateWeights(positions);
if (brushMode != null)
brushMode.ApplyStamps(this, (Event.current.modifiers & EventModifiers.Shift) != 0);
if (onStrokeUpdate != null)
onStrokeUpdate();
Event.current.Use();
}
}
protected virtual void OnMouseUp(Vector3[] positions)
{
if (GUIUtility.hotControl == controlID)
{
GUIUtility.hotControl = 0;
Event.current.Use();
if (onStrokeEnd != null)
onStrokeEnd();
}
}
protected virtual void OnRepaint()
{
}
public void DoBrush(Vector3[] positions)
{
Matrix4x4 cachedMatrix = Handles.matrix;
controlID = GUIUtility.GetControlID(particleBrushHash, FocusType.Passive);
Array.Resize(ref weights, positions.Length);
switch (Event.current.GetTypeForControl(controlID))
{
case EventType.MouseDown:
OnMouseDown(positions);
break;
case EventType.MouseMove:
OnMouseMove(positions);
SceneView.RepaintAll();
break;
case EventType.MouseDrag:
OnMouseDrag(positions);
break;
case EventType.MouseUp:
OnMouseUp(positions);
break;
case EventType.Repaint:
Handles.matrix = Matrix4x4.identity;
OnRepaint();
Handles.matrix = cachedMatrix;
break;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 094253962d646436792561e708e62376
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,51 @@
using System;
using UnityEngine;
[Serializable]
public struct ObiBrushMirrorSettings
{
[Flags]
public enum MirrorAxis
{
None = 0x0,
X = 0x1,
Y = 0x2,
Z = 0x4
}
public enum MirrorSpace
{
World = 0,
Camera = 1
}
public MirrorAxis axis;
public MirrorSpace space;
public Vector3 ToAxis()
{
uint m = (uint)axis;
bool xMirror = (m & (uint)MirrorAxis.X) > 0;
bool yMirror = (m & (uint)MirrorAxis.Y) > 0;
bool zMirror = (m & (uint)MirrorAxis.Z) > 0;
if (axis < 0 || ((int)axis > (int)MirrorAxis.X + (int)MirrorAxis.Y + (int)MirrorAxis.Z))
{
return Vector3.one;
}
Vector3 reflection = Vector3.one;
if (xMirror)
reflection.x = -1f;
if (yMirror)
reflection.y = -1f;
if (zMirror)
reflection.z = -1f;
return reflection;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d10a9257ac23248418439eb80e02a611
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,125 @@
using UnityEngine;
using UnityEditor;
using System;
using System.Collections;
using System.Collections.Generic;
namespace Obi
{
public class ObiRaycastBrush : ObiBrushBase
{
public Matrix4x4 raycastTransform = Matrix4x4.identity;
public Mesh raycastTarget = null;
public bool drawVolume = false;
private List<Ray> rays = new List<Ray>();
private List<ObiRaycastHit> hits = new List<ObiRaycastHit>();
public ObiBrushMirrorSettings mirror;
public ObiRaycastBrush(Mesh raycastTarget, Action onStrokeStart, Action onStrokeUpdate, Action onStrokeEnd) : base(onStrokeStart, onStrokeUpdate, onStrokeEnd)
{
radius = 0.1f;
this.raycastTarget = raycastTarget;
rays = new List<Ray>();
}
protected override void GenerateWeights(Vector3[] positions)
{
if (raycastTarget != null)
{
rays.Clear();
hits.Clear();
for (int i = 0; i < positions.Length; i++)
weights[i] = 0;
var vertices = raycastTarget.vertices;
var triangles = raycastTarget.triangles;
Ray mouseRay = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
rays.Add(mouseRay);
ObiBrushMirrorSettings currentAxis = mirror;
if (mirror.axis != ObiBrushMirrorSettings.MirrorAxis.None)
{
for (int i = 0; i < 3; i++)
{
currentAxis.axis = (ObiBrushMirrorSettings.MirrorAxis)(1u << i);
if (((uint)mirror.axis & (1u << i)) < 1)
continue;
Vector3 mirrorVector = currentAxis.ToAxis();
if (currentAxis.space == ObiBrushMirrorSettings.MirrorSpace.World)
{
Vector3 center = raycastTarget.bounds.center;
rays.Add(new Ray(Vector3.Scale(mouseRay.origin - center, mirrorVector) + center,
Vector3.Scale(mouseRay.direction, mirrorVector)));
}
else
{
Transform t = SceneView.lastActiveSceneView.camera.transform;
Vector3 o = t.InverseTransformPoint(mouseRay.origin);
Vector3 d = t.InverseTransformDirection(mouseRay.direction);
rays.Add(new Ray(t.TransformPoint(Vector3.Scale(o, mirrorVector)),
t.TransformDirection(Vector3.Scale(d, mirrorVector))));
}
}
}
foreach (var ray in rays)
{
if (ObiMeshUtils.WorldRaycast(ray, raycastTransform, vertices, triangles, out ObiRaycastHit hit))
{
hit.position = raycastTransform.MultiplyPoint3x4(hit.position);
hit.normal = raycastTransform.MultiplyVector(hit.normal);
hits.Add(hit);
for (int i = 0; i < positions.Length; i++)
{
// get distance from hit position to particle position:
float weight = WeightFromDistance(Vector3.Distance(hit.position, positions[i]));
weights[i] = Mathf.Max(weights[i], weight);
}
}
}
}
}
protected override void OnMouseMove(Vector3[] positions)
{
base.OnMouseMove(positions);
GenerateWeights(positions);
}
protected override void OnRepaint()
{
base.OnRepaint();
if (raycastTarget != null)
{
Color brushColor = ObiEditorSettings.GetOrCreateSettings().brushColor;
foreach (var hit in hits)
{
if (hit != null && hit.triangle >= 0)
{
Handles.color = brushColor;
Handles.DrawLine(hit.position, hit.position + hit.normal.normalized * radius);
Handles.DrawWireDisc(hit.position, hit.normal, radius);
Handles.DrawWireDisc(hit.position, hit.normal, innerRadius * radius);
if (drawVolume)
{
Handles.color = new Color(brushColor.r, brushColor.g, brushColor.b, 0.2f);
Handles.SphereHandleCap(0, hit.position, Quaternion.identity, radius * 2, EventType.Repaint);
}
}
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d925323750dca4dcaa5cca91b18521b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,61 @@
using UnityEngine;
using UnityEditor;
using System;
using System.Collections;
using System.Collections.Generic;
namespace Obi
{
public class ObiScreenSpaceBrush : ObiBrushBase
{
public ObiScreenSpaceBrush(Action onStrokeStart, Action onStrokeUpdate, Action onStrokeEnd) : base(onStrokeStart, onStrokeUpdate, onStrokeEnd)
{
radius = 32;
}
protected override float WeightFromDistance(float distance)
{
// anything outside the brush should have zero weight:
if (distance * EditorGUIUtility.pixelsPerPoint > radius)
return 0;
return 1;
}
protected override void GenerateWeights(Vector3[] positions)
{
for (int i = 0; i < positions.Length; i++)
{
// get particle position in gui space:
Vector2 pos = HandleUtility.WorldToGUIPoint(positions[i]);
// get distance from mouse position to particle position:
weights[i] = WeightFromDistance(Vector3.Distance(Event.current.mousePosition, pos));
}
}
protected override void OnRepaint()
{
base.OnRepaint();
Camera cam = Camera.current;
float depth = (cam.nearClipPlane + cam.farClipPlane) * 0.5f;
float ppp = EditorGUIUtility.pixelsPerPoint;
Vector2 mousePos = new Vector2(Event.current.mousePosition.x * ppp,
cam.pixelHeight - Event.current.mousePosition.y * ppp);
Handles.color = ObiEditorSettings.GetOrCreateSettings().brushColor;
Vector3 point = new Vector3(mousePos.x, mousePos.y, depth);
Vector3 wsPoint = cam.ScreenToWorldPoint(point);
var p1 = cam.ScreenToWorldPoint(new Vector3(1, 0, depth));
var p2 = cam.ScreenToWorldPoint(new Vector3(0, 0, depth));
float units = Vector3.Distance(p1, p2);
Handles.DrawWireDisc(wsPoint, cam.transform.forward, radius * units);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 38fc5288fb3c94ebeb7eaa88a3bc5119
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,477 @@
using UnityEngine;
using UnityEngine.Rendering;
using UnityEditor;
using UnityEngine.SceneManagement;
using UnityEditor.SceneManagement;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Linq;
namespace Obi
{
[CustomEditor(typeof(ObiActorBlueprint), true)]
public class ObiActorBlueprintEditor : Editor, IObiSelectableParticleProvider
{
public List<ObiBlueprintEditorTool> tools = new List<ObiBlueprintEditorTool>();
public int currentToolIndex = 0;
public List<ObiBlueprintPropertyBase> properties = new List<ObiBlueprintPropertyBase>();
public int currentPropertyIndex = 0;
public List<ObiBlueprintRenderMode> renderModes = new List<ObiBlueprintRenderMode>();
public int renderModeFlags = 0;
BooleanPreference showRenderModes;
public bool autoGenerate = false;
public bool editMode = false;
public bool isEditing = false;
protected UnityEngine.Object oldSelection;
//Additional status info for all particles:
public static float dotRadiusScale = 1;
public static int selectedCount = 0;
public static int activeParticle = -1;
public static bool[] selectionStatus = new bool[0];
public bool[] visible = new bool[0];
public Color[] tint = new Color[0];
protected float[] sqrDistanceToCamera = new float[0];
public int[] sortedIndices = new int[0];
public ObiActorBlueprint blueprint
{
get { return target as ObiActorBlueprint; }
}
public ObiBlueprintPropertyBase currentProperty
{
get { return GetProperty(currentPropertyIndex); }
}
public ObiBlueprintEditorTool currentTool
{
get { return GetTool(currentToolIndex); }
}
public override bool UseDefaultMargins()
{
return false;
}
public ObiBlueprintPropertyBase GetProperty(int index)
{
return (properties.Count > index && index >= 0) ? properties[index] : null;
}
public ObiBlueprintEditorTool GetTool(int index)
{
return (tools.Count > index && index >= 0) ? tools[index] : null;
}
#if (UNITY_2019_1_OR_NEWER)
System.Action<ScriptableRenderContext, Camera> renderCallback;
#endif
public virtual void OnEnable()
{
properties.Clear();
renderModes.Clear();
tools.Clear();
properties.Add(new ObiBlueprintMass(this));
properties.Add(new ObiBlueprintRadius(this));
properties.Add(new ObiBlueprintFilterCategory(this));
properties.Add(new ObiBlueprintFilterMask(this));
renderModes.Add(new ObiBlueprintRenderModeParticles(this));
showRenderModes = new BooleanPreference($"{target.GetType()}.showRenderModes", false);
#if (UNITY_2019_1_OR_NEWER)
renderCallback = new System.Action<ScriptableRenderContext, Camera>((cntxt, cam) => { DrawWithCamera(cam); });
RenderPipelineManager.beginCameraRendering += renderCallback;
#endif
Camera.onPreCull += DrawWithCamera;
SceneView.duringSceneGui += OnSceneGUI;
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
}
public virtual void OnDisable()
{
ExitBlueprintEditMode();
#if (UNITY_2019_1_OR_NEWER)
RenderPipelineManager.beginCameraRendering -= renderCallback;
#endif
Camera.onPreCull -= DrawWithCamera;
SceneView.duringSceneGui -= OnSceneGUI;
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
foreach (var tool in tools)
{
tool.OnDisable();
tool.OnDestroy();
}
foreach (var renderMode in renderModes)
{
renderMode.OnDestroy();
}
}
void OnPlayModeStateChanged(PlayModeStateChange playmodeState)
{
if (playmodeState == PlayModeStateChange.ExitingEditMode)
{
if (StageUtility.GetCurrentStage() is ObiActorBlueprintEditorStage)
StageUtility.GoToMainStage();
}
}
protected bool Generate()
{
if (!blueprint.edited)
{
EditorUtility.SetDirty(target);
CoroutineJob job = new CoroutineJob();
IEnumerator routine = job.Start(blueprint.Generate());
EditorCoroutine.ShowCoroutineProgressBar("Generating blueprint...", routine);
Refresh();
EditorGUIUtility.ExitGUI();
}
else
{
if (EditorUtility.DisplayDialog("Blueprint generation", "This blueprint contains manually edited data. If you regenerate the blueprint, these changes will be lost. Are you sure you want to proceed?", "Ok", "Cancel"))
{
EditorUtility.SetDirty(target);
CoroutineJob job = new CoroutineJob();
IEnumerator routine = job.Start(blueprint.Generate());
EditorCoroutine.ShowCoroutineProgressBar("Generating blueprint...", routine);
Refresh();
EditorGUIUtility.ExitGUI();
}
else return false;
}
return true;
}
protected virtual bool ValidateBlueprint() { return true; }
private void DrawGenerationControls()
{
GUILayout.BeginHorizontal();
float originalLabelWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = 72;
autoGenerate = EditorGUILayout.ToggleLeft("Auto Generate", autoGenerate, GUILayout.ExpandWidth(false));
EditorGUIUtility.labelWidth = originalLabelWidth;
GUI.enabled = !autoGenerate;
if (GUILayout.Button("Generate", GUI.skin.FindStyle("LargeButton"), GUILayout.Height(32)))
Generate();
GUILayout.EndHorizontal();
}
public override void OnInspectorGUI()
{
serializedObject.UpdateIfRequiredOrScript();
EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins);
EditorGUI.BeginChangeCheck();
DrawBlueprintProperties();
bool blueprintPropertiesChanged = EditorGUI.EndChangeCheck();
bool blueprintValid = ValidateBlueprint();
GUILayout.Space(10);
GUI.enabled = blueprintValid;
DrawGenerationControls();
GUI.enabled = (blueprint != null && !blueprint.empty && !Application.isPlaying);
EditorGUI.BeginChangeCheck();
editMode = GUILayout.Toggle(editMode, editMode ? "Done" : "Edit", "Button");
if (EditorGUI.EndChangeCheck())
{
if (editMode)
EditorApplication.delayCall += EnterBlueprintEditMode;
else
EditorApplication.delayCall += ExitBlueprintEditMode;
}
EditorGUILayout.EndVertical();
GUI.enabled = true;
if (isEditing)
DrawTools();
if (GUI.changed)
{
serializedObject.ApplyModifiedPropertiesWithoutUndo();
if (autoGenerate && blueprintValid && blueprintPropertiesChanged)
blueprint.GenerateImmediate();
// There might be blueprint editing operations that have no undo entry, so do this to
// ensure changes are serialized to disk by Unity.
EditorUtility.SetDirty(target);
}
}
protected virtual void DrawBlueprintProperties()
{
Editor.DrawPropertiesExcluding(serializedObject, "m_Script");
}
private void DrawWithCamera(Camera camera)
{
if (editMode)
{
for (int i = 0; i < renderModes.Count; ++i)
{
if ((1 << i & renderModeFlags) != 0)
renderModes[i].DrawWithCamera(camera);
}
}
}
void EnterBlueprintEditMode()
{
if (!isEditing)
{
ActiveEditorTracker.sharedTracker.isLocked = true;
string assetPath = AssetDatabase.GetAssetPath(blueprint);
ObiActorBlueprintEditorStage stage = ObiActorBlueprintEditorStage.CreateStage(assetPath, this);
StageUtility.GoToStage(stage, true);
isEditing = true;
}
}
void ExitBlueprintEditMode()
{
if (isEditing)
{
isEditing = false;
AssetDatabase.SaveAssets();
StageUtility.GoToMainStage();
}
}
public void CleanupEditor()
{
ActiveEditorTracker.sharedTracker.isLocked = false;
ObiParticleEditorDrawing.DestroyParticlesMesh();
}
public virtual void OnSceneGUI(SceneView sceneView)
{
if (!isEditing || sceneView.camera == null)
return;
ResizeParticleArrays();
Event e = Event.current;
if (e.type == EventType.Repaint)
{
// Update camera facing status and world space positions array:
UpdateParticleVisibility(sceneView.camera);
// Generate sorted indices for back-to-front rendering:
for (int i = 0; i < sortedIndices.Length; i++)
sortedIndices[i] = i;
Array.Sort<int>(sortedIndices, (a, b) => sqrDistanceToCamera[b].CompareTo(sqrDistanceToCamera[a]));
// render modes OnSceneRepaint:
for (int i = 0; i < renderModes.Count; ++i)
{
if ((1 << i & renderModeFlags) != 0)
renderModes[i].OnSceneRepaint(sceneView);
}
// property OnSceneRepaint:
currentProperty.OnSceneRepaint();
// update particle color based on visiblity, etc.
UpdateTintColor();
// Draw particle handles:
ObiParticleEditorDrawing.DrawParticles(sceneView.camera, blueprint, visible, tint, sortedIndices, dotRadiusScale);
}
if (currentTool != null)
currentTool.OnSceneGUI(sceneView);
}
protected virtual void UpdateTintColor()
{
Color regularColor = ObiEditorSettings.GetOrCreateSettings().particleColor;
Color selectedColor = ObiEditorSettings.GetOrCreateSettings().selectedParticleColor;
Color activeColor = ObiEditorSettings.GetOrCreateSettings().activeParticleColor;
for (int i = 0; i < blueprint.positions.Length; i++)
{
// get particle color:
if (activeParticle == i)
tint[i] = activeColor;
else
tint[i] = selectionStatus[i] ? selectedColor : regularColor;
tint[i].a = visible[i] ? 1 : 0.15f;
}
}
protected void ResizeParticleArrays()
{
if (blueprint.positions != null)
{
activeParticle = Mathf.Min(activeParticle, blueprint.positions.Length - 1);
Array.Resize(ref selectionStatus, blueprint.positions.Length);
Array.Resize(ref visible, blueprint.positions.Length);
Array.Resize(ref tint, blueprint.positions.Length);
Array.Resize(ref sqrDistanceToCamera, blueprint.positions.Length);
Array.Resize(ref sortedIndices, blueprint.positions.Length);
}
}
public int PropertySelector(int propertyIndex, string label = "Property")
{
// get all particle properties:
string[] propertyNames = new string[properties.Count];
for (int i = 0; i < properties.Count; ++i)
propertyNames[i] = properties[i].name;
// Draw a selection dropdown:
return EditorGUILayout.Popup(label, propertyIndex, propertyNames);
}
public virtual void RenderModeSelector()
{
showRenderModes.value = EditorGUILayout.BeginFoldoutHeaderGroup(showRenderModes, "Render modes");
if (showRenderModes)
{
EditorGUI.BeginChangeCheck();
for (int i = 0; i < renderModes.Count; ++i)
{
int value = 1 << i;
if (EditorGUILayout.Toggle(renderModes[i].name, (value & renderModeFlags) != 0))
renderModeFlags |= value;
else
renderModeFlags &= ~value;
}
if (EditorGUI.EndChangeCheck())
Refresh();
}
EditorGUILayout.EndFoldoutHeaderGroup();
}
public void Refresh()
{
// currentProperty might be null after reloading editor during
// asset saving.
currentProperty?.RecalculateMinMax();
// refresh render modes:
for (int i = 0; i < renderModes.Count; ++i)
{
if ((1 << i & renderModeFlags) != 0)
renderModes[i].Refresh();
}
SceneView.RepaintAll();
}
public virtual void UpdateParticleVisibility(Camera cam)
{
for (int i = 0; i < blueprint.positions.Length; i++)
{
if (blueprint.IsParticleActive(i))
{
visible[i] = true;
if (Camera.current != null)
{
Vector3 camToParticle = cam.transform.position - blueprint.positions[i];
sqrDistanceToCamera[i] = camToParticle.sqrMagnitude;
}
}
}
if ((renderModeFlags & 1) != 0)
Refresh();
}
protected void DrawTools()
{
GUIContent[] contents = new GUIContent[tools.Count];
for (int i = 0; i < tools.Count; ++i)
contents[i] = new GUIContent(tools[i].icon, tools[i].name);
EditorGUILayout.Space();
GUILayout.Box(GUIContent.none, ObiEditorUtils.GetSeparatorLineStyle());
EditorGUILayout.Space();
EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins);
EditorGUI.BeginChangeCheck();
int newSelectedTool = ObiEditorUtils.DoToolBar(currentToolIndex, contents);
EditorGUILayout.EndVertical();
if (EditorGUI.EndChangeCheck())
{
if (currentTool != null)
currentTool.OnDisable();
currentToolIndex = newSelectedTool;
if (currentTool != null)
currentTool.OnEnable();
SceneView.RepaintAll();
}
if (currentTool != null)
{
EditorGUILayout.BeginVertical(EditorStyles.inspectorDefaultMargins);
EditorGUILayout.LabelField(currentTool.name, EditorStyles.boldLabel);
string help = currentTool.GetHelpString();
if (!help.Equals(string.Empty))
EditorGUILayout.LabelField(help, EditorStyles.helpBox);
EditorGUILayout.EndVertical();
currentTool.OnInspectorGUI();
}
}
public void SetSelected(int particleIndex, bool selected)
{
selectionStatus[particleIndex] = selected;
}
public bool IsSelected(int particleIndex)
{
return selectionStatus[particleIndex];
}
public bool Editable(int particleIndex)
{
return currentTool.Editable(particleIndex) && blueprint.IsParticleActive(particleIndex);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2e39e6eea226f4ecbbe091613ff79f85
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,72 @@
using System;
using System.IO;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
namespace Obi
{
[Serializable]
class ObiActorBlueprintEditorStage : PreviewSceneStage
{
ObiActorBlueprintEditor m_BlueprintEditor;
string m_AssetPath;
public override string assetPath { get { return m_AssetPath; } }
internal static ObiActorBlueprintEditorStage CreateStage(string assetPath, ObiActorBlueprintEditor avatarEditor)
{
ObiActorBlueprintEditorStage stage = CreateInstance<ObiActorBlueprintEditorStage>();
stage.Init(assetPath, avatarEditor);
return stage;
}
private void Init(string modelAssetPath, ObiActorBlueprintEditor avatarEditor)
{
m_AssetPath = modelAssetPath;
m_BlueprintEditor = avatarEditor;
}
protected override bool OnOpenStage()
{
base.OnOpenStage();
if (!File.Exists(assetPath))
{
Debug.LogError("ActivateStage called on BlueprintStage with an invalid path: Blueprint file not found " + assetPath);
return false;
}
return true;
}
protected override void OnCloseStage()
{
m_BlueprintEditor.CleanupEditor();
base.OnCloseStage();
}
protected override void OnFirstTimeOpenStageInSceneView(SceneView sceneView)
{
// Frame in scene view
sceneView.Frame(m_BlueprintEditor.blueprint.bounds);
// Setup Scene view state
sceneView.sceneViewState.showFlares = false;
sceneView.sceneViewState.alwaysRefresh = false;
sceneView.sceneViewState.showFog = false;
sceneView.sceneViewState.showSkybox = false;
sceneView.sceneViewState.showImageEffects = false;
sceneView.sceneViewState.showParticleSystems = false;
sceneView.sceneLighting = true;
}
protected override GUIContent CreateHeaderContent()
{
return new GUIContent(
"Blueprint Editor",
Resources.Load<Texture2D>("Icons/ObiActorBlueprint Icon"));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4c8d9d8042d49436c89c515b001088ac
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,275 @@
using UnityEngine;
using UnityEditor;
using System;
using System.IO;
namespace Obi
{
[CustomEditor(typeof(ObiMeshBasedActorBlueprint), true)]
public abstract class ObiMeshBasedActorBlueprintEditor : ObiActorBlueprintEditor
{
[Flags]
public enum ParticleCulling
{
Off = 0,
Back = 1 << 0,
Front = 1 << 1
}
protected Mesh visualizationMesh;
protected Mesh visualizationWireMesh;
public ParticleCulling particleCulling = ParticleCulling.Back;
protected Material gradientMaterial;
protected Material textureExportMaterial;
protected Material paddingMaterial;
public override void OnEnable()
{
base.OnEnable();
gradientMaterial = Resources.Load<Material>("PropertyGradientMaterial");
textureExportMaterial = Resources.Load<Material>("UVSpaceColorMaterial");
paddingMaterial = Resources.Load<Material>("PaddingMaterial");
}
public abstract Mesh sourceMesh
{
get;
}
protected void NonReadableMeshWarning(Mesh mesh)
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
Texture2D icon = EditorGUIUtility.Load("icons/console.erroricon.png") as Texture2D;
EditorGUILayout.LabelField(new GUIContent("The input mesh is not readable. Read/Write must be enabled in the mesh import settings.", icon), EditorStyles.wordWrappedMiniLabel);
EditorGUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Fix now", GUILayout.MaxWidth(100), GUILayout.MinHeight(32)))
{
string assetPath = AssetDatabase.GetAssetPath(mesh);
ModelImporter modelImporter = AssetImporter.GetAtPath(assetPath) as ModelImporter;
if (modelImporter != null)
{
modelImporter.isReadable = true;
}
modelImporter.SaveAndReimport();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
protected override bool ValidateBlueprint()
{
if (sourceMesh != null)
{
if (!sourceMesh.isReadable)
{
NonReadableMeshWarning(sourceMesh);
return false;
}
return true;
}
return false;
}
public abstract int VertexToParticle(int vertexIndex);
public override void UpdateParticleVisibility(Camera cam)
{
if (cam != null)
{
for (int i = 0; i < blueprint.positions.Length; i++)
{
if (blueprint.IsParticleActive(i))
{
Vector3 camToParticle = cam.transform.position - blueprint.positions[i];
sqrDistanceToCamera[i] = camToParticle.sqrMagnitude;
Vector3 normal;
switch (particleCulling)
{
case ParticleCulling.Off:
visible[i] = true;
break;
case ParticleCulling.Back:
normal = blueprint.restOrientations[i] * Vector3.forward;
visible[i] = Vector3.Dot(normal, camToParticle) > 0;
break;
case ParticleCulling.Front:
normal = blueprint.restOrientations[i] * Vector3.forward;
visible[i] = Vector3.Dot(normal, camToParticle) <= 0;
break;
}
}
}
if ((renderModeFlags & 1) != 0)
Refresh();
}
}
public void DrawGradientMesh(float[] vertexWeights = null, float[] wireframeWeights = null)
{
// Due to this Unity bug: https://issuetracker.unity3d.com/issues/drawmeshnow-is-not-drawing-mesh-immediately-dx12
// we need to create two meshes insteaf of one :(
if (sourceMesh == null)
return;
visualizationMesh = GameObject.Instantiate(sourceMesh);
visualizationWireMesh = GameObject.Instantiate(sourceMesh);
if (gradientMaterial.SetPass(0))
{
var matrix = Matrix4x4.TRS(Vector3.zero, (blueprint as ObiMeshBasedActorBlueprint).rotation, (blueprint as ObiMeshBasedActorBlueprint).scale);
Color[] colors = new Color[visualizationMesh.vertexCount];
for (int i = 0; i < colors.Length; i++)
{
int particle = VertexToParticle(i);
if (particle >= 0 && particle < blueprint.particleCount)
{
float weight = 1;
if (vertexWeights != null)
weight = vertexWeights[particle];
colors[i] = weight * currentProperty.ToColor(particle);
}
else
colors[i] = Color.gray;
}
visualizationMesh.colors = colors;
Graphics.DrawMeshNow(visualizationMesh, matrix);
Color wireColor = ObiEditorSettings.GetOrCreateSettings().brushWireframeColor;
if (gradientMaterial.SetPass(1))
{
for (int i = 0; i < colors.Length; i++)
{
int particle = VertexToParticle(i);
if (particle >= 0 && particle < blueprint.particleCount)
{
if (wireframeWeights != null)
colors[i] = wireColor * wireframeWeights[particle];
else
colors[i] = wireColor;
}
else
colors[i] = Color.gray;
}
visualizationWireMesh.colors = colors;
GL.wireframe = true;
Graphics.DrawMeshNow(visualizationWireMesh, matrix);
GL.wireframe = false;
}
}
GameObject.DestroyImmediate(visualizationMesh);
GameObject.DestroyImmediate(visualizationWireMesh);
}
/**
* Reads particle data from a 2D texture. Can be used to adjust per particle mass, skin radius, etc. using
* a texture instead of painting it in the editor.
*
* Will call onReadProperty once for each particle, passing the particle index and the bilinearly interpolated
* color of the texture at its coordinate.
*
* Be aware that, if a particle corresponds to more than
* one physical vertex and has multiple uv coordinates,
* onReadProperty will be called multiple times for that particle.
*/
public bool ReadParticlePropertyFromTexture(Texture2D source, Action<int, Color> onReadProperty)
{
if (source == null || onReadProperty == null)
return false;
Vector2[] uvs = sourceMesh.uv;
// Iterate over all vertices in the mesh reading back colors from the texture:
for (int i = 0; i < sourceMesh.vertexCount; ++i)
{
try
{
onReadProperty(VertexToParticle(i), source.GetPixelBilinear(uvs[i].x, uvs[i].y));
}
catch (UnityException e)
{
Debug.LogException(e);
return false;
}
}
return true;
}
public bool WriteParticlePropertyToTexture(string path, int width, int height, int padding)
{
if (path == null || textureExportMaterial == null || !textureExportMaterial.SetPass(0))
return false;
if (visualizationMesh == null)
{
visualizationMesh = GameObject.Instantiate(sourceMesh);
}
RenderTexture tempRT = RenderTexture.GetTemporary(width, height, 0);
RenderTexture paddingRT = RenderTexture.GetTemporary(width, height, 0);
RenderTexture old = RenderTexture.active;
RenderTexture.active = tempRT;
GL.PushMatrix();
var proj = Matrix4x4.Ortho(0, 1, 0, 1, -1, 1); if (Camera.current != null) proj = proj * Camera.current.worldToCameraMatrix.inverse; GL.LoadProjectionMatrix(proj);
Color[] colors = new Color[sourceMesh.vertexCount];
for (int i = 0; i < colors.Length; i++)
colors[i] = currentProperty.ToColor(VertexToParticle(i));
visualizationMesh.colors = colors;
Graphics.DrawMeshNow(visualizationMesh, Matrix4x4.identity);
GL.PopMatrix();
// Perform padding/edge dilation
paddingMaterial.SetFloat("_Padding", padding);
Graphics.Blit(tempRT, paddingRT, paddingMaterial);
// Read result into our Texture2D.
RenderTexture.active = paddingRT;
Texture2D texture = new Texture2D(width, height, TextureFormat.RGBA32, false);
texture.ReadPixels(new Rect(0, 0, width, height), 0, 0);
RenderTexture.active = old;
RenderTexture.ReleaseTemporary(paddingRT);
RenderTexture.ReleaseTemporary(tempRT);
byte[] png = texture.EncodeToPNG();
GameObject.DestroyImmediate(texture);
try
{
File.WriteAllBytes(path, png);
}
catch (Exception e)
{
Debug.LogException(e);
return false;
}
AssetDatabase.Refresh();
return true;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c81a94632ae434014aedb4cc241e73e2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,93 @@
using UnityEngine;
using System.Collections;
namespace Obi
{
public static class ObiMeshUtils
{
// Temporary vector3 values
static Vector3 tv1, tv2, tv3, tv4;
public static bool RayIntersectsTriangle(Vector3 origin,
Vector3 dir,
Vector3 vert0,
Vector3 vert1,
Vector3 vert2,
ref float distance,
ref Vector3 normal)
{
float det;
ObiVectorMath.Subtract(vert0, vert1, ref tv1);
ObiVectorMath.Subtract(vert0, vert2, ref tv2);
ObiVectorMath.Cross(dir, tv2, ref tv4);
det = Vector3.Dot(tv1, tv4);
if (det < Mathf.Epsilon)
return false;
ObiVectorMath.Subtract(vert0, origin, ref tv3);
float u = Vector3.Dot(tv3, tv4);
if (u < 0f || u > det)
return false;
ObiVectorMath.Cross(tv3, tv1, ref tv4);
float v = Vector3.Dot(dir, tv4);
if (v < 0f || u + v > det)
return false;
distance = Vector3.Dot(tv2, tv4) * (1f / det);
ObiVectorMath.Cross(tv1, tv2, ref normal);
return true;
}
/** * Find the nearest triangle intersected by InWorldRay on this mesh. InWorldRay is in world space. * @hit contains information about the hit point. @distance limits how far from @InWorldRay.origin the hit * point may be. @cullingMode determines what face orientations are tested (Culling.Front only tests front * faces, Culling.Back only tests back faces, and Culling.FrontBack tests both). * Ray origin and position values are in local space. */ public static bool WorldRaycast(Ray InWorldRay, Matrix4x4 transform, Vector3[] vertices, int[] triangles, out ObiRaycastHit hit, float distance = Mathf.Infinity) {
Ray ray = InWorldRay;
if (transform != null)
{
Matrix4x4 inv = transform.inverse;
ray.origin = inv.MultiplyPoint3x4(ray.origin);
ray.direction = inv.MultiplyVector(ray.direction);
} return MeshRaycast(ray, vertices, triangles, out hit, distance); } /** * Cast a ray (in model space) against a mesh. */ public static bool MeshRaycast(Ray InRay, Vector3[] vertices, int[] triangles, out ObiRaycastHit hit, float distance = Mathf.Infinity) {
Vector3 hitNormal = Vector3.zero; // vars used in loop
Vector3 vert0, vert1, vert2;
Vector3 origin = InRay.origin, direction = InRay.direction;
hit = new ObiRaycastHit(Mathf.Infinity,
Vector3.zero,
Vector3.zero,
-1);
/**
* Iterate faces, testing for nearest hit to ray origin.
*/
for (int CurTri = 0; CurTri < triangles.Length; CurTri += 3)
{
if (CurTri + 2 >= triangles.Length) continue;
if (triangles[CurTri + 2] >= vertices.Length) continue;
vert0 = vertices[triangles[CurTri + 0]];
vert1 = vertices[triangles[CurTri + 1]];
vert2 = vertices[triangles[CurTri + 2]];
// Second pass, test intersection with triangle
if (RayIntersectsTriangle(origin, direction, vert0, vert1, vert2, ref distance, ref hitNormal))
{
if (distance < hit.distance)
{
hit.distance = distance;
hit.triangle = CurTri / 3;
hit.position = InRay.GetPoint(hit.distance);
hit.normal = hitNormal;
}
}
}
return hit.triangle > -1; }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4d2a4dd4e807c4053b4e4af4d43db45e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,127 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
namespace Obi
{
public class ObiParticleEditorDrawing : MonoBehaviour
{
public static Mesh particlesMesh;
public static Material particleMaterial;
private static void CreateParticlesMesh()
{
if (particlesMesh == null)
{
particlesMesh = new Mesh();
particlesMesh.hideFlags = HideFlags.HideAndDontSave;
}
}
private static void CreateParticleMaterials()
{
if (!particleMaterial)
{
particleMaterial = Resources.Load<Material>("EditorParticle");
}
}
public static void DestroyParticlesMesh()
{
GameObject.DestroyImmediate(particlesMesh);
}
public static void DrawParticles(Camera cam, ObiActorBlueprint blueprint, bool[] visible, Color[] baseColor, int[] sortedIndices, float radiusScale = 1)
{
CreateParticlesMesh();
CreateParticleMaterials();
if (!particleMaterial.SetPass(0))
return;
//because each vertex needs to be drawn as a quad.
int particlesPerDrawcall = Constants.maxVertsPerMesh / 4;
int drawcallCount = blueprint.particleCount / particlesPerDrawcall + 1;
particlesPerDrawcall = Mathf.Min(particlesPerDrawcall, blueprint.particleCount);
List<Vector3> vertices = new List<Vector3>(blueprint.activeParticleCount* 4);
List<Vector3> normals = new List<Vector3>(blueprint.activeParticleCount * 4);
List<Vector4> uvs = new List<Vector4>(blueprint.activeParticleCount * 4);
List<Color> colors = new List<Color>(blueprint.activeParticleCount * 4);
List<int> triangles = new List<int>(blueprint.activeParticleCount * 6);
Vector3 particleOffset0 = new Vector3(1, 1, 0);
Vector3 particleOffset1 = new Vector3(-1, 1, 0);
Vector3 particleOffset2 = new Vector3(-1, -1, 0);
Vector3 particleOffset3 = new Vector3(1, -1, 0);
Vector4 radius = new Vector4(1, 0, 0, 0.005f * radiusScale);
for (int i = 0; i < drawcallCount; ++i)
{
//Draw all cloth vertices:
particlesMesh.Clear();
vertices.Clear();
uvs.Clear();
normals.Clear();
colors.Clear();
triangles.Clear();
int index = 0;
// Run over all particles (not only active ones), since they're reordered based on distance to camera.
// Then test if the sorted index is active or not, and skip inactive ones.
int limit = Mathf.Min((i + 1) * particlesPerDrawcall, blueprint.particleCount);
for (int j = i * particlesPerDrawcall; j < limit; ++j)
{
int sortedIndex = sortedIndices[j];
// skip inactive ones:
if (!blueprint.IsParticleActive(sortedIndex))
continue;
normals.Add(particleOffset0);
normals.Add(particleOffset1);
normals.Add(particleOffset2);
normals.Add(particleOffset3);
uvs.Add(radius);
uvs.Add(radius);
uvs.Add(radius);
uvs.Add(radius);
vertices.Add(blueprint.positions[sortedIndex]);
vertices.Add(blueprint.positions[sortedIndex]);
vertices.Add(blueprint.positions[sortedIndex]);
vertices.Add(blueprint.positions[sortedIndex]);
colors.Add(baseColor[sortedIndex]);
colors.Add(baseColor[sortedIndex]);
colors.Add(baseColor[sortedIndex]);
colors.Add(baseColor[sortedIndex]);
triangles.Add(index + 2);
triangles.Add(index + 1);
triangles.Add(index);
triangles.Add(index + 3);
triangles.Add(index + 2);
triangles.Add(index);
index += 4;
}
particlesMesh.SetVertices(vertices);
particlesMesh.SetNormals(normals);
particlesMesh.SetColors(colors);
particlesMesh.SetUVs(0, uvs);
particlesMesh.SetTriangles(triangles,0, true);
Graphics.DrawMeshNow(particlesMesh, Matrix4x4.identity);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 982f73e0e40c749f09db403624bbd8e1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dfda10b0e82f747998295540d5750108
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7b9ba4ddc644a4bfa8ab44a5826b8007
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,24 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace Obi
{
public abstract class ObiBlueprintBoolProperty : ObiBlueprintProperty<bool>
{
public override bool Equals(int firstIndex, int secondIndex)
{
return Get(firstIndex) == Get(secondIndex);
}
public override void PropertyField()
{
value = EditorGUILayout.Toggle(name, value);
}
public override Color ToColor(int index)
{
return value ? Color.white : Color.gray;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 19cb8e21747094d6dae8dff155654066
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,31 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace Obi
{
public abstract class ObiBlueprintColorProperty : ObiBlueprintProperty<Color>
{
public ObiActorBlueprintEditor editor;
public ObiBlueprintColorProperty(ObiActorBlueprintEditor editor)
{
this.editor = editor;
}
public override bool Equals(int firstIndex, int secondIndex)
{
return Get(firstIndex) == Get(secondIndex);
}
public override void PropertyField()
{
value = EditorGUILayout.ColorField(name, value);
}
public override Color ToColor(int index)
{
return editor.blueprint.colors[index];
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 64ab0b4fad7614808acc3cdb75215c50
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,94 @@
using UnityEngine;
using UnityEditor;
namespace Obi
{
public abstract class ObiBlueprintFloatProperty : ObiBlueprintProperty<float>
{
public float minVisualizationValue = 0;
public float maxVisualizationValue = 10;
protected float minUserVisualizationValue = 0;
protected float maxUserVisualizationValue = 10;
protected float? minValue = null;
protected float? maxValue = null;
public bool autoRange = true;
public ObiActorBlueprintEditor editor;
public ObiBlueprintFloatProperty(ObiActorBlueprintEditor editor, float? minValue = null, float? maxValue = null)
{
this.editor = editor;
this.minValue = minValue;
this.maxValue = maxValue;
}
public override bool Equals(int firstIndex, int secondIndex)
{
float v1 = Get(firstIndex);
float v2 = Get(secondIndex);
if (v1 == v2) return true;
return Mathf.Approximately(v1,v2);
}
public override void PropertyField()
{
EditorGUI.BeginChangeCheck();
value = EditorGUILayout.FloatField(name, value);
if (EditorGUI.EndChangeCheck())
{
if (minValue.HasValue)
value = Mathf.Max(minValue.Value, value);
if (maxValue.HasValue)
value = Mathf.Min(maxValue.Value, value);
}
}
public override void RecalculateMinMax()
{
if (editor != null && autoRange)
{
maxVisualizationValue = float.MinValue;
minVisualizationValue = float.MaxValue;
for (int i = 0; i < editor.blueprint.activeParticleCount; i++)
{
float v = Get(i);
maxVisualizationValue = Mathf.Max(maxVisualizationValue, v);
minVisualizationValue = Mathf.Min(minVisualizationValue, v);
}
}
else
{
maxVisualizationValue = maxUserVisualizationValue;
minVisualizationValue = minUserVisualizationValue;
}
}
public override void VisualizationOptions()
{
EditorGUI.BeginChangeCheck();
autoRange = EditorGUILayout.Toggle("Automatic property range", autoRange);
GUI.enabled = !autoRange;
EditorGUI.indentLevel++;
minUserVisualizationValue = EditorGUILayout.FloatField("Min", minUserVisualizationValue);
maxUserVisualizationValue = EditorGUILayout.FloatField("Max", maxUserVisualizationValue);
EditorGUI.indentLevel--;
GUI.enabled = true;
if (EditorGUI.EndChangeCheck())
{
RecalculateMinMax();
editor.Refresh();
}
}
public override Color ToColor(int index)
{
Gradient gradient = ObiEditorSettings.GetOrCreateSettings().propertyGradient;
if (!Mathf.Approximately(minVisualizationValue, maxVisualizationValue))
return gradient.Evaluate(Mathf.InverseLerp(minVisualizationValue, maxVisualizationValue, Get(index)));
else return gradient.Evaluate(0);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: be67f2576200242f080bbcc074dc598a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,41 @@
using UnityEngine;
using UnityEditor;
namespace Obi
{
public abstract class ObiBlueprintIntProperty : ObiBlueprintProperty<int>
{
protected int? minValue = null;
protected int? maxValue = null;
public ObiBlueprintIntProperty(int? minValue = null, int? maxValue = null)
{
this.minValue = minValue;
this.maxValue = maxValue;
}
public override bool Equals(int firstIndex, int secondIndex)
{
return Get(firstIndex) == Get(secondIndex);
}
public override void PropertyField()
{
EditorGUI.BeginChangeCheck();
value = EditorGUILayout.IntField(name, value);
if (EditorGUI.EndChangeCheck())
{
if (minValue.HasValue)
value = Mathf.Max(minValue.Value, value);
if (maxValue.HasValue)
value = Mathf.Min(maxValue.Value, value);
}
}
public override Color ToColor(int index)
{
int colorIndex = Get(index) % ObiUtils.colorAlphabet.Length;
return ObiUtils.colorAlphabet[colorIndex];
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d8661f9e9cfd044c7bb759145c2a8d73
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,28 @@
using UnityEngine;
using UnityEditor;
namespace Obi
{
public abstract class ObiBlueprintMaskProperty : ObiBlueprintIntProperty
{
public ObiBlueprintMaskProperty() : base(null,null)
{
}
public override void PropertyField()
{
value = EditorGUILayout.MaskField(name, value, ObiUtils.categoryNames);
}
private int MathMod(int a, int b)
{
return (Mathf.Abs(a * b) + a) % b;
}
public override Color ToColor(int index)
{
int colorIndex = MathMod(Get(index),ObiUtils.colorAlphabet.Length);
return ObiUtils.colorAlphabet[colorIndex];
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 165ad17d69adf415ea7609a9373de001
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,79 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
namespace Obi
{
public abstract class ObiBlueprintPropertyBase
{
protected List<IObiBrushMode> brushModes = new List<IObiBrushMode>();
private int selectedBrushMode;
public abstract string name
{
get;
}
public abstract void PropertyField();
public virtual void VisualizationOptions(){}
public virtual void OnSceneRepaint(){}
public abstract bool Equals(int firstIndex, int secondIndex);
public abstract void GetDefaultFromIndex(int index);
public abstract void SetDefaultToIndex(int index);
public virtual bool Masked(int index)
{
return false;
}
public virtual void RecalculateMinMax() { }
public virtual Color ToColor(int index) { return Color.white; }
protected void Initialize(ObiBrushBase paintBrush)
{
// Initialize the brush if there's no brush mode set:
if (paintBrush.brushMode == null && brushModes.Count > 0)
{
selectedBrushMode = 0;
paintBrush.brushMode = brushModes[selectedBrushMode];
}
}
public void OnSelect(ObiBrushBase paintBrush)
{
// Upon selecting the property, change to the last selected brush mode:
if (brushModes.Count > selectedBrushMode)
paintBrush.brushMode = brushModes[selectedBrushMode];
}
public void BrushModes(ObiBrushBase paintBrush)
{
Initialize(paintBrush);
GUIContent[] contents = new GUIContent[brushModes.Count];
for (int i = 0; i < brushModes.Count; ++i)
contents[i] = new GUIContent(brushModes[i].name);
EditorGUI.BeginChangeCheck();
selectedBrushMode = ObiEditorUtils.DoToolBar(selectedBrushMode, contents);
if (EditorGUI.EndChangeCheck())
{
paintBrush.brushMode = brushModes[selectedBrushMode];
}
}
}
public abstract class ObiBlueprintProperty<T> : ObiBlueprintPropertyBase
{
protected T value;
public T GetDefault() { return value; }
public override void GetDefaultFromIndex(int index) { value = Get(index); }
public override void SetDefaultToIndex(int index) { Set(index, value); }
public abstract T Get(int index);
public abstract void Set(int index, T value);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 60a83ba184caf4a1aba4da6754776a1d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,13 @@
using UnityEngine;
using System.Collections;
namespace Obi
{
public interface IObiSelectableParticleProvider
{
void SetSelected(int particleIndex, bool selected);
bool IsSelected(int particleIndex);
bool Editable(int particleIndex);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0c3a2643915854763af75d63fa1ec0c9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,33 @@
using UnityEngine;
namespace Obi
{
public class ObiBlueprintColor : ObiBlueprintColorProperty
{
public ObiBlueprintColor(ObiActorBlueprintEditor editor) : base(editor)
{
brushModes.Add(new ObiColorPaintBrushMode(this));
brushModes.Add(new ObiColorSmoothBrushMode(this));
}
public override string name
{
get { return "Color"; }
}
public override Color Get(int index)
{
return editor.blueprint.colors[index];
}
public override void Set(int index, Color value)
{
editor.blueprint.colors[index] = value;
editor.blueprint.edited = true;
}
public override bool Masked(int index)
{
return !editor.Editable(index);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 810c95482e60a44209c5ece07e287153
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,32 @@
namespace Obi
{
public class ObiBlueprintFilterCategory : ObiBlueprintIntProperty
{
public ObiActorBlueprintEditor editor;
public ObiBlueprintFilterCategory(ObiActorBlueprintEditor editor) : base(ObiUtils.MinCategory, ObiUtils.MaxCategory)
{
this.editor = editor;
brushModes.Add(new ObiIntPaintBrushMode(this));
}
public override string name
{
get { return "Category"; }
}
public override int Get(int index)
{
return ObiUtils.GetCategoryFromFilter(editor.blueprint.filters[index]);
}
public override void Set(int index, int value)
{
editor.blueprint.filters[index] = ObiUtils.MakeFilter(ObiUtils.GetMaskFromFilter(editor.blueprint.filters[index]), value);
editor.blueprint.edited = true;
}
public override bool Masked(int index)
{
return !editor.Editable(index);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 08502999496e54f49bb9dee0e0855794
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,32 @@
namespace Obi
{
public class ObiBlueprintFilterMask : ObiBlueprintMaskProperty
{
public ObiActorBlueprintEditor editor;
public ObiBlueprintFilterMask(ObiActorBlueprintEditor editor)
{
this.editor = editor;
brushModes.Add(new ObiIntPaintBrushMode(this));
}
public override string name
{
get { return "Collides with"; }
}
public override int Get(int index)
{
return ObiUtils.GetMaskFromFilter(editor.blueprint.filters[index]);
}
public override void Set(int index, int value)
{
editor.blueprint.filters[index] = ObiUtils.MakeFilter(value,ObiUtils.GetCategoryFromFilter(editor.blueprint.filters[index]));
editor.blueprint.edited = true;
}
public override bool Masked(int index)
{
return !editor.Editable(index);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ead8a00c964834718a2411be5d7361f3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,33 @@
namespace Obi
{
public class ObiBlueprintMass : ObiBlueprintFloatProperty
{
public ObiBlueprintMass(ObiActorBlueprintEditor editor) : base(editor,0)
{
brushModes.Add(new ObiFloatPaintBrushMode(this));
brushModes.Add(new ObiFloatAddBrushMode(this));
brushModes.Add(new ObiFloatCopyBrushMode(this, this));
brushModes.Add(new ObiFloatSmoothBrushMode(this));
}
public override string name
{
get { return "Mass"; }
}
public override float Get(int index)
{
return ObiUtils.InvMassToMass(editor.blueprint.invMasses[index]);
}
public override void Set(int index, float value)
{
editor.blueprint.invMasses[index] = ObiUtils.MassToInvMass(value);
editor.blueprint.edited = true;
}
public override bool Masked(int index)
{
return !editor.Editable(index);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bd39fbd4c226542e58dc8ecceb5f8f14
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,37 @@
using UnityEngine;
namespace Obi
{
public class ObiBlueprintRadius : ObiBlueprintFloatProperty
{
public ObiBlueprintRadius(ObiActorBlueprintEditor editor) : base(editor,0.0000001f)
{
brushModes.Add(new ObiFloatPaintBrushMode(this));
brushModes.Add(new ObiFloatAddBrushMode(this));
brushModes.Add(new ObiFloatCopyBrushMode(this, this));
brushModes.Add(new ObiFloatSmoothBrushMode(this));
}
public override string name
{
get { return "Radius"; }
}
public override float Get(int index)
{
return editor.blueprint.principalRadii[index][0];
}
public override void Set(int index, float value)
{
value = Mathf.Max(0.0000001f, value);
float ratio = value / Get(index);
editor.blueprint.principalRadii[index] = editor.blueprint.principalRadii[index] * ratio;
editor.blueprint.edited = true;
}
public override bool Masked(int index)
{
return !editor.Editable(index);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b5c61b50f6f4a468aa246f01e5268831
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,29 @@
namespace Obi
{
public class ObiBlueprintSelected : ObiBlueprintBoolProperty
{
public IObiSelectableParticleProvider provider;
public ObiBlueprintSelected(IObiSelectableParticleProvider provider)
{
this.provider = provider;
}
public override string name
{
get { return "Selected"; }
}
public override bool Get(int index)
{
return provider.IsSelected(index);
}
public override void Set(int index, bool value)
{
provider.SetSelected(index,value);
}
public override bool Masked(int index)
{
return !provider.Editable(index);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 270971958421b4fec9e5a5ba95699518
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: be5de82c95ded480d881176f73fc19b7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,25 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace Obi
{
public abstract class ObiBlueprintRenderMode
{
protected ObiActorBlueprintEditor editor;
public abstract string name
{
get;
}
public ObiBlueprintRenderMode(ObiActorBlueprintEditor editor)
{
this.editor = editor;
}
public virtual void DrawWithCamera(Camera camera) {}
public virtual void OnSceneRepaint(SceneView sceneView) {}
public virtual void Refresh(){}
public virtual void OnDestroy() { }
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d60f616a126974e6d8be81fbbe459bac
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,63 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
namespace Obi
{
public class ObiBlueprintRenderModeAerodynamicConstraints : ObiBlueprintRenderMode
{
public override string name
{
get { return "Aerodynamic constraints"; }
}
public ObiMeshBasedActorBlueprintEditor meshBasedEditor
{
get { return editor as ObiMeshBasedActorBlueprintEditor; }
}
public ObiBlueprintRenderModeAerodynamicConstraints(ObiMeshBasedActorBlueprintEditor editor) : base(editor)
{
}
public override void OnSceneRepaint(SceneView sceneView)
{
var meshEditor = editor as ObiMeshBasedActorBlueprintEditor;
if (meshEditor != null)
{
// Get per-particle normals:
Vector3[] normals = meshEditor.sourceMesh.normals;
Vector3[] particleNormals = new Vector3[meshEditor.blueprint.particleCount];
for (int i = 0; i < normals.Length; ++i)
{
int welded = meshEditor.VertexToParticle(i);
particleNormals[welded] = normals[i];
}
using (new Handles.DrawingScope(Color.blue, Matrix4x4.identity))
{
var constraints = editor.blueprint.GetConstraintsByType(Oni.ConstraintType.Aerodynamics) as ObiConstraints<ObiAerodynamicConstraintsBatch>;
if (constraints != null)
{
Vector3[] lines = new Vector3[constraints.GetActiveConstraintCount() * 2];
int lineIndex = 0;
foreach (var batch in constraints.batches)
{
for (int i = 0; i < batch.activeConstraintCount; ++i)
{
int particleIndex = batch.particleIndices[i];
Vector3 position = editor.blueprint.GetParticlePosition(particleIndex);
lines[lineIndex++] = position;
lines[lineIndex++] = position + particleNormals[particleIndex] * 0.025f;
}
}
Handles.DrawLines(lines);
}
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 18881e26e784d47aaa8aa27c93fe0b49
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,44 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
namespace Obi
{
public class ObiBlueprintRenderModeBendConstraints : ObiBlueprintRenderMode
{
public override string name
{
get { return "Bend constraints"; }
}
public ObiBlueprintRenderModeBendConstraints(ObiActorBlueprintEditor editor) : base(editor)
{
}
public override void OnSceneRepaint(SceneView sceneView)
{
using (new Handles.DrawingScope(Color.magenta, Matrix4x4.identity))
{
var constraints = editor.blueprint.GetConstraintsByType(Oni.ConstraintType.Bending) as ObiConstraints<ObiBendConstraintsBatch>;
if (constraints != null)
{
Vector3[] lines = new Vector3[constraints.GetActiveConstraintCount() * 2];
int lineIndex = 0;
foreach (var batch in constraints.batches)
{
for (int i = 0; i < batch.activeConstraintCount; ++i)
{
lines[lineIndex++] = editor.blueprint.GetParticlePosition(batch.particleIndices[i * 3]);
lines[lineIndex++] = editor.blueprint.GetParticlePosition(batch.particleIndices[i * 3 + 1]);
}
}
Handles.DrawLines(lines);
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 15e21bc6bea1c4320948413c8d7334bd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,44 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
namespace Obi
{
public class ObiBlueprintRenderModeDistanceConstraints : ObiBlueprintRenderMode
{
public override string name
{
get { return "Distance constraints"; }
}
public ObiBlueprintRenderModeDistanceConstraints(ObiActorBlueprintEditor editor) : base(editor)
{
}
public override void OnSceneRepaint(SceneView sceneView)
{
using (new Handles.DrawingScope(Color.green, Matrix4x4.identity))
{
var constraints = editor.blueprint.GetConstraintsByType(Oni.ConstraintType.Distance) as ObiConstraints<ObiDistanceConstraintsBatch>;
if (constraints != null)
{
Vector3[] lines = new Vector3[constraints.GetActiveConstraintCount() * 2];
int lineIndex = 0;
foreach (var batch in constraints.batches)
{
for (int i = 0; i < batch.activeConstraintCount; ++i)
{
lines[lineIndex++] = editor.blueprint.GetParticlePosition(batch.particleIndices[i * 2]);
lines[lineIndex++] = editor.blueprint.GetParticlePosition(batch.particleIndices[i * 2 + 1]);
}
}
Handles.DrawLines(lines);
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ac734c05a2b994f148fd43cd5829b1be
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,46 @@
using UnityEditor;
namespace Obi
{
public class ObiBlueprintRenderModeMesh : ObiBlueprintRenderMode
{
public override string name
{
get { return "Mesh"; }
}
public ObiMeshBasedActorBlueprintEditor meshBasedEditor
{
get { return editor as ObiMeshBasedActorBlueprintEditor; }
}
public ObiBlueprintRenderModeMesh(ObiMeshBasedActorBlueprintEditor editor) : base(editor)
{
}
public override void OnSceneRepaint(SceneView sceneView)
{
if (meshBasedEditor.currentTool is ObiPaintBrushEditorTool)
{
ObiPaintBrushEditorTool paintTool = (ObiPaintBrushEditorTool)meshBasedEditor.currentTool;
float[] weights = new float[ObiActorBlueprintEditor.selectionStatus.Length];
for (int i = 0; i < weights.Length; i++)
{
if (paintTool.selectionMask && !ObiActorBlueprintEditor.selectionStatus[i])
weights[i] = 0;
else
weights[i] = 1;
}
float[] wireframeWeights = new float[paintTool.paintBrush.weights.Length];
for (int i = 0; i < wireframeWeights.Length; i++)
wireframeWeights[i] = paintTool.paintBrush.weights[i];
meshBasedEditor.DrawGradientMesh(weights, wireframeWeights);
}
else
meshBasedEditor.DrawGradientMesh();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dbcd302f71d6446cd976f736b365c7ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,65 @@
using UnityEngine;
using System.Collections;
namespace Obi
{
public class ObiBlueprintRenderModeParticles : ObiBlueprintRenderMode
{
public override string name
{
get { return "Particles"; }
}
private Shader shader;
private Material material;
private ParticleImpostorRendering impostorDrawer;
private MaterialPropertyBlock mpb;
public ObiBlueprintRenderModeParticles(ObiActorBlueprintEditor editor) :base(editor)
{
impostorDrawer = new ParticleImpostorRendering();
impostorDrawer.UpdateMeshes(editor.blueprint);
mpb = new MaterialPropertyBlock();
}
void CreateMaterialIfNeeded()
{
if (shader == null)
{
shader = Shader.Find("Obi/EditorParticles");
if (shader != null)
{
if (!shader.isSupported)
Debug.LogWarning("Particle rendering shader not suported.");
if (material == null || material.shader != shader)
{
GameObject.DestroyImmediate(material);
material = new Material(shader);
material.hideFlags = HideFlags.HideAndDontSave;
}
}
}
}
public override void DrawWithCamera(Camera camera)
{
CreateMaterialIfNeeded();
mpb.SetFloat("_RadiusScale", 1);
mpb.SetColor("_ParticleColor", Color.white);
foreach (Mesh mesh in impostorDrawer.Meshes)
Graphics.DrawMesh(mesh, Matrix4x4.identity, material, 0, camera, 0, mpb);
}
public override void Refresh()
{
impostorDrawer.UpdateMeshes(editor.blueprint, editor.visible, editor.tint);
}
public override void OnDestroy()
{
GameObject.DestroyImmediate(material);
impostorDrawer.ClearMeshes();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b51ea2becbebe48ba9d77e9d28403f51
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,54 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
namespace Obi
{
public class ObiBlueprintRenderModeShapeMatchingConstraints : ObiBlueprintRenderMode
{
public override string name
{
get { return "Shape matching clusters"; }
}
public ObiBlueprintRenderModeShapeMatchingConstraints(ObiActorBlueprintEditor editor) : base(editor)
{
}
public override void OnSceneRepaint(SceneView sceneView)
{
using (new Handles.DrawingScope(Color.cyan, Matrix4x4.identity))
{
var constraints = editor.blueprint.GetConstraintsByType(Oni.ConstraintType.ShapeMatching) as ObiConstraints<ObiShapeMatchingConstraintsBatch>;
if (constraints != null)
{
List<Vector3> lines = new List<Vector3>();
foreach (var batch in constraints.batches)
{
for (int i = 0; i < batch.activeConstraintCount; ++i)
{
int first = batch.firstIndex[i];
Vector3 p1 = editor.blueprint.GetParticlePosition(batch.particleIndices[first]);
for (int j = 1; j < batch.numIndices[i]; ++j)
{
int index = first + j;
Vector3 p2 = editor.blueprint.GetParticlePosition(batch.particleIndices[index]);
lines.Add(p1);
lines.Add(p2);
}
}
}
Handles.DrawLines(lines.ToArray());
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e5ff128cfebee45ffb8266eb3e75522e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More