浏览代码

added sprite alpha picking

David Catuhe 5 年之前
父节点
当前提交
20b0a8ba53

+ 4 - 0
dist/preview release/what's new.md

@@ -36,6 +36,10 @@
 - Added flag to TargetCamera to invert rotation direction and multiplier to adjust speed ([Exolun](https://github.com/Exolun))
 - Added upwards and downwards keyboard input to `FreeCamera` ([Pheater](https://github.com/pheater))
 
+### Sprites
+
+- Added support for 'sprite.useAlphaForPicking` to enable precise picking using sprite alpha ([Deltakosh](https://github.com/deltakosh) 
+
 ### Physics
 
 - Ammo.js IDL exposed property update and raycast vehicle stablization support ([MackeyK24](https://github.com/MackeyK24))

+ 2 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationPropertyGridComponent.tsx

@@ -170,11 +170,12 @@ export class AnimationGridComponent extends React.Component<IAnimationGridCompon
                     <>
                         <LineContainerComponent globalState={this.props.globalState} title="ANIMATIONS">
                             <TextLineComponent label="Count" value={animations.length.toString()} />
+                            <ButtonLineComponent label="Edit" onClick={() => {}} />
                             {
                                 animations.map((anim, i) => {
                                     return (
                                         <>
-                                            <TextLineComponent label={"#" + i + " >"} value={anim.targetProperty} />
+                                            <TextLineComponent key={i} label={"#" + i + " >"} value={anim.targetProperty} />
                                         </>           
                                     )
                                 })

+ 11 - 0
src/Sprites/sprite.ts

@@ -33,6 +33,17 @@ export class Sprite {
     public animations = new Array<Animation>();
     /** Gets or sets a boolean indicating if the sprite can be picked */
     public isPickable = false;
+    /** Gets or sets a boolean indicating that sprite texture alpha will be used for precise picking (false by default) */
+    public useAlphaForPicking = false;
+
+    /** @hidden */
+    public _xOffset: number;
+    /** @hidden */
+    public _yOffset: number;
+    /** @hidden */
+    public _xSize: number;
+    /** @hidden */
+    public _ySize: number;
 
     /**
      * Gets or sets the associated action manager

+ 66 - 6
src/Sprites/spriteManager.ts

@@ -98,6 +98,8 @@ export class SpriteManager implements ISpriteManager {
     /** True when packed cell data from JSON file is ready*/
     private _packedAndReady: boolean = false;
 
+    private _textureContent: Nullable<Uint8Array>;
+
     /**
     * An event triggered when the manager is disposed.
     */
@@ -138,6 +140,7 @@ export class SpriteManager implements ISpriteManager {
 
     public set texture(value: Texture) {
         this._spriteTexture = value;
+        this._textureContent = null;
     }
 
     private _blendMode = Constants.ALPHA_COMBINE;
@@ -352,10 +355,14 @@ export class SpriteManager implements ISpriteManager {
             if (typeof (num) === "number" && isFinite(num) && Math.floor(num) === num) {
                 sprite.cellRef = this._spriteMap[sprite.cellIndex];
             }
-            this._vertexData[arrayOffset + 10] = this._cellData[sprite.cellRef].frame.x / baseSize.width;
-            this._vertexData[arrayOffset + 11] = this._cellData[sprite.cellRef].frame.y / baseSize.height;
-            this._vertexData[arrayOffset + 12] = this._cellData[sprite.cellRef].frame.w / baseSize.width;
-            this._vertexData[arrayOffset + 13] = this._cellData[sprite.cellRef].frame.h / baseSize.height;
+            sprite._xOffset = this._cellData[sprite.cellRef].frame.x / baseSize.width;
+            sprite._yOffset = this._cellData[sprite.cellRef].frame.y / baseSize.height;
+            sprite._xSize = this._cellData[sprite.cellRef].frame.w;
+            sprite._ySize = this._cellData[sprite.cellRef].frame.h;
+            this._vertexData[arrayOffset + 10] = sprite._xOffset;
+            this._vertexData[arrayOffset + 11] = sprite._yOffset;
+            this._vertexData[arrayOffset + 12] = sprite._xSize / baseSize.width;
+            this._vertexData[arrayOffset + 13] = sprite._ySize / baseSize.height;
         }
         else {
             if (!sprite.cellIndex) {
@@ -363,8 +370,12 @@ export class SpriteManager implements ISpriteManager {
             }
             var rowSize = baseSize.width / this.cellWidth;
             var offset = (sprite.cellIndex / rowSize) >> 0;
-            this._vertexData[arrayOffset + 10] = (sprite.cellIndex - offset * rowSize) * this.cellWidth / baseSize.width;
-            this._vertexData[arrayOffset + 11] = offset * this.cellHeight / baseSize.height;
+            sprite._xOffset = (sprite.cellIndex - offset * rowSize) * this.cellWidth / baseSize.width;
+            sprite._yOffset = offset * this.cellHeight / baseSize.height;
+            sprite._xSize = this.cellWidth;
+            sprite._ySize = this.cellHeight;            
+            this._vertexData[arrayOffset + 10] = sprite._xOffset;
+            this._vertexData[arrayOffset + 11] = sprite._yOffset;
             this._vertexData[arrayOffset + 12] = this.cellWidth / baseSize.width;
             this._vertexData[arrayOffset + 13] = this.cellHeight / baseSize.height;
         }
@@ -375,6 +386,44 @@ export class SpriteManager implements ISpriteManager {
         this._vertexData[arrayOffset + 17] = sprite.color.a;
     }
 
+    private _checkTextureAlpha(sprite: Sprite, ray: Ray, distance: number, min: Vector3, max: Vector3) {
+        if (!sprite.useAlphaForPicking || !this._spriteTexture) {
+            return true;
+        }
+
+        let textureSize = this._spriteTexture.getSize();
+        if (!this._textureContent) {
+            this._textureContent = new Uint8Array(textureSize.width * textureSize.height * 4);
+            this._spriteTexture.readPixels(0, 0, this._textureContent);
+        }
+        
+        let contactPoint = TmpVectors.Vector3[0];
+        
+        contactPoint.copyFrom(ray.direction);
+
+        contactPoint.normalize();
+        contactPoint.scaleInPlace(distance);
+        contactPoint.addInPlace(ray.origin);
+
+        let contactPointU = ((contactPoint.x - min.x) / (max.x - min.x)) - 0.5;
+        let contactPointV = (1.0 - (contactPoint.y - min.y) / (max.y - min.y)) - 0.5;
+
+        // Rotate
+        let angle = sprite.angle;
+        let rotatedU = 0.5 + (contactPointU * Math.cos(angle) - contactPointV * Math.sin(angle));
+        let rotatedV = 0.5 + (contactPointU * Math.sin(angle) + contactPointV * Math.cos(angle));
+        
+        let u = (sprite._xOffset * textureSize.width + rotatedU * sprite._xSize) | 0;
+        let v = (sprite._yOffset * textureSize.height +  rotatedV * sprite._ySize) | 0;
+
+        let alpha = this._textureContent![(u + v * textureSize.width) * 4 + 3];
+
+        console.log((contactPoint.x - min.x) / (max.x - min.x), 1.0 - (contactPoint.y - min.y) / (max.y - min.y))
+        console.log(u, v, alpha)
+
+        return (alpha > 0.5);
+    }
+
     /**
      * Intersects the sprites with a ray
      * @param ray defines the ray to intersect with
@@ -416,6 +465,11 @@ export class SpriteManager implements ISpriteManager {
                 var currentDistance = Vector3.Distance(cameraSpacePosition, ray.origin);
 
                 if (distance > currentDistance) {
+
+                    if (!this._checkTextureAlpha(sprite, ray, distance, min, max)) {
+                        continue;
+                    }   
+
                     distance = currentDistance;
                     currentSprite = sprite;
 
@@ -488,6 +542,10 @@ export class SpriteManager implements ISpriteManager {
             if (ray.intersectsBoxMinMax(min, max)) {
                 distance = Vector3.Distance(cameraSpacePosition, ray.origin);
 
+                if (!this._checkTextureAlpha(sprite, ray, distance, min, max)) {
+                    continue;
+                }
+
                 var result = new PickingInfo();
                 results.push(result);
 
@@ -612,6 +670,8 @@ export class SpriteManager implements ISpriteManager {
             (<any>this._spriteTexture) = null;
         }
 
+        this._textureContent = null;
+
         // Remove from scene
         var index = this._scene.spriteManagers.indexOf(this);
         this._scene.spriteManagers.splice(index, 1);