0
\$\begingroup\$

I am using code to generate a mesh to show the firing arcs for a tabletop-style game I am working on. But when applying a material to it, I only get a solid color.

Custom Mesh and Test Material

Here you can see the custom mesh and a Quad with the same test material applied.

Custom Mesh's Properities Custom Mesh's UV Checker

Any help would be appreciated as I am more of a logic coder and am only just dipping my toes into this Custom Mesh coding. I suspect it has something to do with the UVs but not sure how to correct it if it is.

Here is the code I am using to generate this mesh:

using UnityEngine;
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;

public class FiringArcMeshGenerator : MonoBehaviour
{
    /// <summary>
    /// Credit to Knight666 (knight666.com) & Rickz0r 
    /// https://gamedev.stackexchange.com/questions/31170/drawing-a-dynamic-indicator-for-a-field-of-view
    /// Code modified for modular use by ThomFoxx
    /// </summary>
    [SerializeField] private int _quality = 6;
    private Mesh _arcMesh;

    [SerializeField] private bool _isPrimary;
    [SerializeField] private float _primaryAngle = 45, _auxAngle = 180;

    private float _angleOfFire;

    private float _minDist;
    private float _maxDist;
    [SerializeField] private Vector3[] _verts;
    [SerializeField] private int[] _tris;
    private Vector2[] _uv;

    public void MeshSetup(ShipInfo ship)
    {
        WeaponInfo weapon;
        _arcMesh = new Mesh();
        if (_isPrimary)
        {
            _angleOfFire = _primaryAngle / 2;
            weapon = ship.Primary;
            _arcMesh.name = "Primary Arc";
        }
        else
        {
            _angleOfFire = _auxAngle / 2;
            _quality *= 2;
            weapon = ship.Auxiliary;
            _arcMesh.name = "Auxiliary Arc";
        }

        _minDist = weapon.MinRange;
        _maxDist = weapon.MaxRange;

        

        _arcMesh.vertices = new Vector3[4 * _quality];
        _arcMesh.triangles = new int[3 * 2 * _quality];

        Vector3[] normals = new Vector3[4 * _quality];
        

        

        for (int i = 0; i < normals.Length; i++)
            normals[i] = new Vector3(0, 1, 0);

        
        _arcMesh.normals = normals;

        GenerateMesh();

        GetComponent<MeshFilter>().mesh = _arcMesh;
    }

    void GenerateMesh()
    {
        float angle_lookat = 0;

        float angle_start = angle_lookat - _angleOfFire;
        float angle_end = angle_lookat + _angleOfFire;
        float angle_delta = (angle_end - angle_start) / _quality;

        float angle_curr = angle_start;
        float angle_next = angle_start + angle_delta;

        Vector3 pos_curr_min = Vector3.zero;
        Vector3 pos_curr_max = Vector3.zero;

        Vector3 pos_next_min = Vector3.zero;
        Vector3 pos_next_max = Vector3.zero;

        Vector3[] vertices = new Vector3[4 * _quality];
        int[] triangles = new int[3 * 2 * _quality];

        for (int i = 0; i < _quality; i++)
        {
            Vector3 sphere_curr = new Vector3(
                Mathf.Sin(Mathf.Deg2Rad * (angle_curr)), 
                0f,
                Mathf.Cos(Mathf.Deg2Rad * (angle_curr)));

            Vector3 sphere_next = new Vector3(
                Mathf.Sin(Mathf.Deg2Rad * (angle_next)), 
                0f,
                Mathf.Cos(Mathf.Deg2Rad * (angle_next)));

            pos_curr_min = transform.position + sphere_curr * _minDist;
            pos_curr_max = transform.position + sphere_curr * _maxDist;

            pos_next_min = transform.position + sphere_next * _minDist;
            pos_next_max = transform.position + sphere_next * _maxDist;

            int a = 4 * i;
            int b = 4 * i + 1;
            int c = 4 * i + 2;
            int d = 4 * i + 3;

            vertices[a] = pos_curr_min;
            vertices[b] = pos_curr_max;
            vertices[c] = pos_next_max;
            vertices[d] = pos_next_min;            

            angle_curr += angle_delta;
            angle_next += angle_delta;

        }        
        
        _verts = vertices.Distinct().ToArray();
        _tris = new int[3 * 2 * _quality];

        CalculateTrianlges();

        _uv = new Vector2[_verts.Length];
        for (int i = 0; i < _verts.Length; i++)
        {
            _uv[i]=new Vector2(_verts[i].x, _verts[i].z);
        }
        


        _arcMesh.Clear();        
        _arcMesh.vertices = _verts;
        _arcMesh.triangles = _tris;
        _arcMesh.uv = _uv;
        _arcMesh.RecalculateNormals();
        _arcMesh.RecalculateUVDistributionMetrics();
        //_arcMesh.OptimizeReorderVertexBuffer();
    }

    private void CalculateTrianlges()
    {
        if (_minDist >0)
        {
            //takes the initial odd numbering due to Distinct Function
            _tris[0] = 0;
            _tris[1] = 1;
            _tris[2] = 3;

            _tris[3] = 1;
            _tris[4] = 2;
            _tris[5] = 3;

            _tris[6] = 2;
            _tris[7] = 5;
            _tris[8] = 3;

            _tris[9] = 4;
            _tris[10] = 5;
            _tris[11] = 2;

            for (int i = 4; i < _verts.Length - 3; i += 2)
            {
                _tris[i * 3] = i;
                _tris[i * 3 + 1] = i + 2;
                _tris[i * 3 + 2] = i + 3;
            }
            for (int i = 5; i < _verts.Length - 2; i += 2)
            {
                _tris[i * 3] = i;
                _tris[i * 3 + 1] = i - 1;
                _tris[i * 3 + 2] = i + 2;
            }
        }
        else
        {
            //still developing
        }
    }
}

Edit: With the fix of a missing command, Thanks @DMGregory, It's no longer a single color and shows the texture. But how do I get it to deform to the mesh? Right now it tiles, but I want it to stretch the one texture over the single-sided mesh.

Tiling Texture that needs to conform to the Mesh Shape

Sorry for the bad paintbrush art, but this is what I am aiming for. 00 would be in the bottom left corner (Red) with 33 in the top right corner (Gold/Yellow). Bad paintbrush sketch

\$\endgroup\$
8
  • \$\begingroup\$ Have you tried using a shader which colours the mesh based on uv's? something like gl_Color = vec3(v2f.uv.x ,v2f.uv.y, v2f.uv.x / v2f.uv.y); if the colour is solid, then you know that the UV's are all the same. What does your material and shader setup look like? \$\endgroup\$
    – Charly
    Feb 27, 2022 at 22:44
  • \$\begingroup\$ @Charly for testing, I am using the standard URP/Lit shader with a base map texture. Would you be able to point me to a shader that can do what you suggest? As I said, I am just dipping into this stuff and am having trouble understanding the UV stuff.. Currently the UVs are the vertex's x/z coords transposed as x/y. \$\endgroup\$
    – Thom Foxx
    Feb 27, 2022 at 23:58
  • \$\begingroup\$ @DMGregory Did I miss anything else in my post? \$\endgroup\$
    – Thom Foxx
    Feb 28, 2022 at 0:38
  • 1
    \$\begingroup\$ Looks like you're missing a line _arcMesh.uv = _uv; - you go to all the trouble of filling up the _uv array, then never actually tell the mesh about it. \$\endgroup\$
    – DMGregory
    Feb 28, 2022 at 0:45
  • \$\begingroup\$ That solved that problem and opened a new underlying problem. See Edit. \$\endgroup\$
    – Thom Foxx
    Feb 28, 2022 at 1:15

1 Answer 1

0
\$\begingroup\$

Instead of generating all the middle vertices twice each, then having to filter down to only the distinct vertices, let's just not generate duplicates in the first place:

int vertexCount = 2 * (_quality + 1);
int indexCount = 3 * 2 * _quality;

var vertices = new Vector3[vertexCount];
var uv = new Vector2[vertexCount];

var indices = new int[indexCount];

for (int i = 0; i <= _quality; i++)
{        
    float angle = Mathf.Deg2Rad(angle_start + i * angle_delta);

    var direction = new Vector3(Mathf.Sin(angle), 0f, Mathf.Cos(angle));

    int a = 2 * i;
    int b = 2 * i + 1;

    vertices[a] = transform.position + direction * _minDist;
    vertices[b] = transform.position + direction * _minDist;

    // Measure our position in the arc's sweep,
    // from 0 at the start angle to 1 at the other.
    float progress = i / (float)quality;

    // Assign the UVs as we go along.
    // "Inside" vertices get uv.y = 0, "outside" get uv.y = 1.
    uv[a] = new Vector2(progress, 0);
    uv[b] = new Vector2(progress, 1);
}

int p = 0;
for (int i = 0; i < _quality; i++) {
    int quad = 2 * i;
    _indices[p++] = quad + 0;
    _indices[p++] = quad + 1;
    _indices[p++] = quad + 3;
    _indices[p++] = quad + 0;
    _indices[p++] = quad + 3;
    _indices[p++] = quad + 2;
}

Bonus: shorter, simpler code!

One problem though: UV coordinates like this can only support affine texture mapping. That is, mappings that take parallel straight lines to parallel straight lines. But a texture that bends in a wedge or circle is not that. So you'll get visible zig-zag artifacts where lines drawn across your texture visibly crease where they cross the edges between triangles.

(Examples with a trapezoid mesh, and with a sphere)

Increasing your _quality parameter can reduce the artifacts by chopping the mesh into smaller pieces that better approximate the curve. But if you want to completely eliminate the artifacts, then you need to compute your UV coordinates in the pixel shader code - say by taking your vertex positions and converting them to polar coordinates.

\$\endgroup\$
3
  • \$\begingroup\$ Thank you @DMGregory, I will take your code and look it over and try to learn from it. I was aware that the code I am/was using is bulky, but I am very much still in the "brute force" phase of it and trying to understand what it is doing before I try to pair it down to size. I have a couple of pages in my notebook of me working through the code by hand to understand it and see the inefficiencies. The artifacts should not be too much of an issue as I plan to use a texture to mimic an 'edge inward fading glow'. But the _quality being as low as it is was mostly an attempt to limit the work area. \$\endgroup\$
    – Thom Foxx
    Mar 1, 2022 at 5:30
  • \$\begingroup\$ How do I mark this as Solved? i.imgur.com/WLYneYO.png Works with the shader that I originally wrote to get the effect I wanted. \$\endgroup\$
    – Thom Foxx
    Mar 1, 2022 at 6:10
  • \$\begingroup\$ Near the top-left of the answer where the voting buttons are, you should see a checkmark icon. Click that to mark the answer as "Accepted". \$\endgroup\$
    – DMGregory
    Mar 1, 2022 at 12:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .