使用xdim和ydim进行等距简单排序 [英] Isometric simple sorting with xdim and ydim

查看:150
本文介绍了使用xdim和ydim进行等距简单排序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的等距排序系统这个函数(代码在 Typescript / Javascript ):

I have a simple isometric sorting system with this function (code is in Typescript/Javascript) :

public Sort(a: PIXI.Sprite, b: PIXI.Sprite) {
    return ((a.IsoZ - b.IsoZ) == 0 ? (a.TileZ - b.TileZ == 0 ? (a.Tile2Z ? (a.Tile2Z < b.Tile2Z ? -1 : (a.Tile2Z > b.Tile2Z ? 1 : 0)) : 0) : a.TileZ - b.TileZ) : (a.IsoZ - b.IsoZ));
}

这取决于三个参数:


  • IsoZ :第一个用于排序瓷砖的排序变量

  • TileZ :tile
    排序变量,如果 a.IsoZ == b.IsoZ
  • $ b时使用$ b
  • Tile2Z :如果 a.TileZ == b.TileZ
  • $ b $使用b
  • IsoZ: the first sorting variables, used to sort tiles
  • TileZ: the tile sorting variable, used if a.IsoZ == b.IsoZ
  • Tile2Z: used if a.TileZ == b.TileZ

以下是大多数对象的IsoZ基本计算方法:

Here's how IsoZ is basically calculated for most objects:

this.Position 是一个x和y坐标数组

this.Position is an array of x and y coordinates

this.Position[0] + this.Position[1] + 1000;

现在我想支持对象x和y尺寸,所以如何实现像这个表达式中的这个?

now I want to support object x and y dimensions, so how can I implement something like this in this expression?

x和y尺寸值例如(2,2)表示立方体或(2,4)表示立方体

x and y dimensions values are for example (2, 2) for a cube or (2, 4) for a cuboid

this.Position[0] + this.Position[1] + 1000 // + x dimension + y dimension ???


推荐答案

等距视觉遮挡排序(深度排序)



定义深度:
更高的深度值更接近屏幕。与深度距离前平面的3D透视投影不同,此答案使用深度作为屏幕距离。

Isometric visual occlusion sort (depth sort)

Defining depth: Higher depths values are closer to the screen. Unlike 3D perspective projection where depth is distance from the front plane, this answer uses depth as distance towards the screen.

如果您有iso预测

const P2 = (x = 0,y = 0) => ({x, y});
const isoProjMat = {
    xAxis : P2(1   , 0.5),
    yAxis : P2(-0.5, 1  ),
    zAxis : P2(0   , -1 ),
}

这需要3d点并投射到屏幕空间

That takes a 3d point and projects to screen space

const P3 = (x = 0, y = 0, z = 0) => ({x, y, z});
isoProjMat.project = function (p, retP = P2()) { // p is 3D point
    retP.x = p.x * this.xAxis.x + p.y * this.yAxis.x + p.z * this.zAxis.x;
    retP.y = p.x * this.xAxis.y + p.y * this.yAxis.y + p.z * this.zAxis.y;
    return retP;
}

您可以将点的深度添加为2D投影的z值点。您需要为深度添加变换轴。

You can add the depth of a point as the z value of the 2D projected point. You need to add a transform axis for the depth.

isoProjMat.depth = P3(0.5,1, 1 );

对于x移近一半大小,y * 1和z * 1.

For x move closer by half its size, y * 1 and z * 1.

修改后的项目现在将z添加到返回的点。

The modified project now adds z to the returned point.

isoProjMat.project = function (p, retP = P3()) { 
    retP.x = p.x * this.xAxis.x + p.y * this.yAxis.x + p.z * this.zAxis.x;
    retP.y = p.x * this.xAxis.y + p.y * this.yAxis.y + p.z * this.zAxis.y;
    retP.z = p.x * this.depth.x + p.y * this.depth.y + p.z * this.depth.z; 
    return retP;
}

因此,对于投影到2D iso屏幕空间的3D空间中的一组点,您排序z

Thus for a set of points in 3D space projected to 2D iso screen space you sort on the z

const points = mySetOfPoints(); // what ever your points come from
const projected = points.map(p => isoProjMat.project(p));
projected.sort((a,b) => a.z - b.z);

所有积分都有用,但对于占据3D音量的精灵来说这不起作用。

All good for points but for sprites which occupy a 3D volume this does not work.

您需要做的是添加一个边界体积,即一个正方形。如果您的投影是静态的,那么我们可以将边界体积简化为最近的点。对于顶部右上角的顶点框,例如(0,0,0)处的精灵有一个大小(10,10,20),3d中最近的点是(10,10,20)。

What you need to do is add a bounding volume ie a square. If your projection is static then we can simplify the bounding volume to the nearest point. For the box that is the vertex at the top bottom right eg sprite at (0,0,0) has a size (10,10,20) the nearest point in 3d is at (10,10,20).

我无法解决问题,因为问题中没有足够的信息,但我猜sprite。我是sprite和sprite的基本原点.Tile& Tile2代表边界框。

I can not work your sort out as there is not enough info in the question but I am guessing sprite.Iso is the base origin of the sprite and sprite.Tile & Tile2 represent bounding box.

因此得到最近的点

const depthProj = P3(0.5,1, 1 ); // depth projection matrix
// get the depth of each sprite adding the property depth
sprites.forEach(spr => {
    const p = {
        x : spr.IsoX + Math.max(spr.TileX,spr.Tile2X),
        y : spr.IsoY + Math.max(spr.TileY,spr.Tile2Y),
        z : spr.IsoZ + Math.max(spr.TileZ,spr.Tile2Z)
    };
    spr.depth = p.x * depthProj.x + p.y * depthProj.y + p.z * depthProj.z; 
})
sprites.sort((a,b) => a.depth - b.depth);

然后从索引0向上渲染。

Then render from index 0 up.

以下不完全适用,因为它按多边形排序并使用多边形平均深度而不是其最大深度(真的应该使用最大但不能打扰ATM)

The following is not fully applicable as it sorts by polygons and uses the polygons mean depth rather than its max depth (really should use max but cant be bothered ATM)

我添加它只是为了说明如何使用 isoProjMat 的上述代码。它从像素alpha和在画布上呈现的颜色绘制堆积的框。

I add it only to show how the above code for the isoProjMat is used. It draws stacked boxes from pixel alpha and color rendered on a canvas.

单击渲染结果以将投影从双变形转换为三变形(因为您没有指定您使用的投影类型显示深度变换如何在两种类型的平行投影之间变化。

Click rendered result to switch projections from bi-morphic to tri-morphic (as you did not specify the type of projection you used this shows how the depth transform changes between two types of parallel projection.

const ctx = canvas.getContext("2d");
var count = 0;
var firstRun = 0;
function doIt(){

  // 3d 2d points
  const P3 = (x=0, y=0, z=0) => ({x,y,z});
  const P2 = (x=0, y=0) => ({x, y});

  // isomorphic projection matrix
  const isoProjMat = {
      xAxis : count ? P2(1 , 0.5) : P2(1 , 0.5) ,
      yAxis : count ? P2(-0.5, 1) : P2(-1 , 0.5) ,
      zAxis : count ? P2(0   , -1) : P2(0 , -1) ,
      depth : count ? P3(0.5,1, 1) : P3(0.5,0.5,1) , // projections have z as depth
      origin : P2(), // (0,0) default 2D point
      project (p, retP = P3()) {
          retP.x = p.x * this.xAxis.x + p.y * this.yAxis.x + p.z * this.zAxis.x + this.origin.x;
          retP.y = p.x * this.xAxis.y + p.y * this.yAxis.y + p.z * this.zAxis.y + this.origin.y;
          retP.z = p.x * this.depth.x + p.y * this.depth.y + p.z * this.depth.z; 
          return retP;
      }
  }

  // isomorphic mesh shape as vertices and polygons
  const isoMesh = (()=>{
      const polygon = {
          inds : null,
          depth : 0,
          fillStyle : "#888",
          lineWidth : 0.5,
          strokeStyle : "#000",
          setStyle(ctx) {
              ctx.fillStyle = this.fillStyle;
              ctx.lineWidth = this.lineWidth;
              ctx.strokeStyle = this.strokeStyle;
          },
      }
      const isoShape = {
          verts : null,
          pVerts : null, // projected verts
          polys : null,
          addVert(p3 = P3()) { this.verts.push(p3); return p3 },
          addPoly(poly = isoShape.createPoly()) { this.polys.push(poly); return poly },
          createPoly(options = {}) { return Object.assign({}, polygon, {inds : []}, options) },
          render(ctx,mat = isoProjMat) {
              var i,j,d;
              const pv = this.pVerts === null ? this.pVerts = [] : this.pVerts;
              const v = this.verts;
              const ps = this.polys;
              for(i = 0; i < v.length; i += 1){  pv[i] = mat.project(v[i], pv[i]) }
              for(i = 0; i < ps.length; i += 1) {
                  const p = ps[i];
                  j = 0; d = 0;
                  while(j < p.inds.length) { d += pv[p.inds[j++]].z }
                  p.depth = d / p.inds.length;
              }
              ps.sort((a,b)=>a.depth - b.depth);
              for(i = 0; i < ps.length; i += 1) {
                  const p = ps[i];
                  p.setStyle(ctx);
                  ctx.beginPath();
                  j = 0;
                  while(j < p.inds.length) { ctx.lineTo(pv[p.inds[j]].x, pv[p.inds[j++]].y) }
                  if (p.fillStyle !== "") { ctx.fill() }
                  if (p.strokeStyle !== "" && p.lineWidth !== 0) {ctx.closePath(); ctx.stroke() }
              }
          }
      }
      return () => Object.assign({},isoShape,{verts : [], polys : []});
  })();

  // Lazy coding I am using Point3 (P3) to hold RGB values
  function createBoxMesh(box = isoMesh(), pos = P3(), size = P3(10,10,10), rgb = P3(128,128,128)){ // x,y,z are sizes in those directions
      const PA3 = (x,y,z) => P3(x + pos.x, y + pos.y, z + pos.z);
      const RGB = (s) => `rgb(${(rgb.x * s) | 0},${(rgb.y * s) | 0},${(rgb.z * s) | 0})`;
      const indA = (inds) => inds.map(ind => ind + i);
      const i = box.verts.length; // get top vert index
      if(typeof size === "number") { size = P3(size,size,size) }
      const x = size.x / 2;
      const y = size.y / 2;
      const z = size.z;
      box.addVert(PA3(-x,-y, 0)); // ind 0
      box.addVert(PA3( x,-y, 0));
      box.addVert(PA3( x, y, 0));
      box.addVert(PA3(-x, y, 0));
      box.addVert(PA3(-x,-y, z)); // ind 4
      box.addVert(PA3( x,-y, z));
      box.addVert(PA3( x, y, z));
      box.addVert(PA3(-x, y, z));
     // box.addPoly(box.createPoly({ inds : indA([0,1,5,4]), fillStyle : RGB(0.5) }));
      box.addPoly(box.createPoly({ inds : indA([1,2,6,5]), fillStyle : RGB(0.7) }));
      box.addPoly(box.createPoly({ inds : indA([2,3,7,6]), fillStyle : RGB(1) }));
     // box.addPoly(box.createPoly({ inds : indA([3,0,4,7]), fillStyle : RGB(0.8) }));
      box.addPoly(box.createPoly({ inds : indA([4,5,6,7]), fillStyle : RGB(1.5) }));
      return box;
  }

  function createDrawable(w,h){
      const c = document.createElement("canvas");
      c.width = w;
      c.height = h;
      c.ctx = c.getContext("2d");
      return c;
  }
  const map = createDrawable(40,30);
  map.ctx.font = "20px arial";
  map.ctx.textAlign = "center";
  map.ctx.textBaseline = "middle";
  map.ctx.fillStyle = "rgba(0,128,0,0.5)";
  map.ctx.strokeStyle = "rgba(255,0,0,0.5)";
  map.ctx.lineWidth = 2;
  map.ctx.fillRect(1,1,map.width - 2, map.height - 2);
  map.ctx.strokeRect(1,1,map.width - 2, map.height - 2);
  map.ctx.fillStyle = "#AAA";
  map.ctx.strokeStyle = "rgba(255,128,0,0.5)";
  map.ctx.strokeText("text",map.width / 2, map.height / 2);
  map.ctx.fillText("text",map.width / 2, map.height / 2);
  var dat = map.ctx.getImageData(0, 0, map.width , map.height).data;

  ctx.setTransform(1,0,0,1,0,0);

  // get total projection area and size canvas so that the iso projection fits
  const boxSize = P3(10,10,5);
  const topLeft = isoProjMat.project(P3(0,0,10 * boxSize.z));
  const botRight = isoProjMat.project(P3(map.width * boxSize.x,map.height * boxSize.y,0));
  const topRight = isoProjMat.project(P3(map.width * boxSize.x,0,0));
  const botLeft = isoProjMat.project(P3(0,map.height * boxSize.y,0));

  canvas.width = ((topRight.x - botLeft.x) + 10)|0;
  canvas.height = ((botRight.y - topLeft.y) + 10)|0;
  ctx.clearRect(0,0,canvas.width,canvas.height);
  ctx.font = "32px arial";
  ctx.textAlign = "center";
  ctx.textBaseline = "middle";
  ctx.fillText("Rendering will take a moment.",Math.min(innerWidth,canvas.width)/2,Math.min(innerHeight,canvas.height)/2)
  setTimeout(function(){
    ctx.clearRect(0,0,canvas.width,canvas.height);
    ctx.setTransform(1,0,0,1,-botLeft.x+10,-topLeft.y+10);

    const alphaThresh = 100;
    const boxes = isoMesh();
    for(var y = 0; y < map.height; y ++){
        for(var x = 0; x < map.width; x ++){
            const ind = (x + y * map.width) * 4;
            if(dat[ind + 3] > alphaThresh){
                const h = (((dat[ind + 3]-alphaThresh)/(255-alphaThresh)) * 10) | 0;
                for(var z = 0; z < h; z++){
                    createBoxMesh(
                        boxes,
                        P3(x * boxSize.x,y * boxSize.y, z * boxSize.z),
                        boxSize,
                        P3(dat[ind],dat[ind+1],dat[ind+2])
                    );
                }
            }
        }
    }

    boxes.render(ctx);
    if(firstRun === 0){
        firstRun = 1;
        ctx.setTransform(1,0,0,1,0,0);
        ctx.font = "24px arial";
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.fillStyle = "black";
        ctx.fillText("Bimorphic projection. Click for Trimorphic projection..",canvas.width/2,30)
        canvas.onclick =()=>{
           count += 1;
           count %= 2;
           doIt();
         };
     }
  },0);

};
doIt();

canvas {
   border : 2px solid black;
}

<canvas id="canvas"></canvas>

这篇关于使用xdim和ydim进行等距简单排序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆