public GlTF_Attributes( GlTF_Globals G, ObjectName meshName, GlTF_VertexLayout layout) { // This prefix should be used with possibly-nonconforming gltf data: // - texcoords that are not 2-element or that don't contain texture coordinates // - made-up / mythical semantics like VERTEXID string nonconformingPrefix = (G.GltfCompatibilityMode) ? "_TB_UNITY_" : ""; m_layout = layout; { AttributeInfo positionInfo = layout.PositionInfo; positionAccessor = G.CreateAccessor( GlTF_Accessor.GetNameFromObject(meshName, "position"), positionInfo.accessorType, positionInfo.accessorComponentType); m_accessors.Add("POSITION", positionAccessor); } { if (layout.NormalInfo is AttributeInfo normalInfo) { normalAccessor = G.CreateAccessor( GlTF_Accessor.GetNameFromObject(meshName, "normal"), normalInfo.accessorType, normalInfo.accessorComponentType); // Genius particles put things that don't look like normals into the normal attribute. bool isNonconforming = (layout.m_tbLayout.normalSemantic == Semantic.Position); string prefix = isNonconforming ? nonconformingPrefix : ""; m_accessors.Add(prefix + "NORMAL", normalAccessor); } } { if (layout.ColorInfo is AttributeInfo cInfo) { colorAccessor = G.CreateAccessor( GlTF_Accessor.GetNameFromObject(meshName, "color"), cInfo.accessorType, cInfo.accessorComponentType, normalized: true); m_accessors.Add(G.Gltf2 ? "COLOR_0" : "COLOR", colorAccessor); } } { if (layout.TangentInfo is AttributeInfo tangentInfo) { tangentAccessor = G.CreateAccessor( GlTF_Accessor.GetNameFromObject(meshName, "tangent"), tangentInfo.accessorType, tangentInfo.accessorComponentType); m_accessors.Add("TANGENT", tangentAccessor); } } if (layout.PackVertexIdIntoTexcoord1W) { // The vertexid hack modifies the gl layout to extend texcoord1 so the vertexid // can be stuffed into it Debug.Assert(layout.m_tbLayout.GetTexcoordInfo(1).size == 3); Debug.Assert(layout.GetTexcoordSize(1) == 4); } GlTF_Accessor MakeAccessorFor(int texcoord) { var txcInfo = layout.GetTexcoordInfo(texcoord); if (txcInfo == null) { return(null); } Semantic tbSemantic = layout.m_tbLayout.GetTexcoordInfo(texcoord).semantic; string attrName = $"{nonconformingPrefix}TEXCOORD_{texcoord}"; // Timestamps are tunneled into us via a texcoord because there's not really a better way // due to GeometryPool limitations. But that's an internal implementation detail. I'd like // them to have a better attribute name in the gltf. if (tbSemantic == Semantic.Timestamp) { // For b/141876882; Poly doesn't like _TB_TIMESTAMP if (!G.Gltf2) { return(null); } attrName = "_TB_TIMESTAMP"; } var ret = G.CreateAccessor( GlTF_Accessor.GetNameFromObject(meshName, $"uv{texcoord}"), txcInfo.Value.accessorType, txcInfo.Value.accessorComponentType); m_accessors.Add(attrName, ret); return(ret); } texCoord0Accessor = MakeAccessorFor(0); texCoord1Accessor = MakeAccessorFor(1); texCoord2Accessor = MakeAccessorFor(2); texCoord3Accessor = MakeAccessorFor(3); if (G.GltfCompatibilityMode) { TiltBrush.GeometryPool.VertexLayout tbLayout = layout.m_tbLayout; switch (tbLayout.texcoord0.semantic) { case Semantic.Unspecified when tbLayout.texcoord0.size == 2: case Semantic.XyIsUv: case Semantic.XyIsUvZIsDistance: { GlTF_Accessor accessor = GlTF_Accessor.CloneWithDifferentType( G, texCoord0Accessor, GlTF_Accessor.Type.VEC2); m_accessors.Add("TEXCOORD_0", accessor); break; } } // No need to check the other texcoords because TB only ever puts texture coordinates // in texcoord0 } }
private void PopulateUv( int channel, TiltBrush.GeometryPool pool, GlTF_Accessor accessor, Semantic semantic) { bool packVertId = m_layout.PackVertexIdIntoTexcoord1W && channel == 1; if (packVertId) { // Guaranteed by GlTF_VertexLayout Debug.Assert(m_layout.m_tbLayout.GetTexcoordInfo(channel).size == 3); Debug.Assert(m_layout.GetTexcoordSize(channel) == 4); } if (accessor == null) { return; } if (channel < 0 || channel > 3) { throw new ArgumentException("Invalid channel"); } TiltBrush.GeometryPool.TexcoordData texcoordData = pool.GetTexcoordData(channel); if (semantic == Semantic.XyIsUvZIsDistance && accessor.type != GlTF_Accessor.Type.VEC3) { throw new ArgumentException("XyIsUvZIsDistance semantic can only be applied to VEC3"); } bool flipY; if (semantic == Semantic.Unspecified && channel == 0 && accessor.type == GlTF_Accessor.Type.VEC2) { Debug.LogWarning("Assuming Semantic.XyIsUv"); semantic = Semantic.XyIsUv; } switch (semantic) { case Semantic.Position: case Semantic.Vector: case Semantic.Timestamp: flipY = false; break; case Semantic.XyIsUvZIsDistance: case Semantic.XyIsUv: flipY = true; break; default: throw new ArgumentException("semantic"); } switch (accessor.type) { case GlTF_Accessor.Type.SCALAR: throw new NotImplementedException(); case GlTF_Accessor.Type.VEC2: accessor.Populate(texcoordData.v2, flipY: flipY, calculateMinMax: false); break; case GlTF_Accessor.Type.VEC3: accessor.Populate(texcoordData.v3, flipY: flipY, calculateMinMax: false); break; case GlTF_Accessor.Type.VEC4: if (packVertId) { // In the vertexId case, we actually have a vec3, which needs to be augmented to a vec4. // TODO: this should happen at some higher level. int i = 0; var v4 = texcoordData.v3.ConvertAll <Vector4>((v => new Vector4(v.x, v.y, v.z, i++))); accessor.Populate(v4, flipY: flipY, calculateMinMax: false); } else { accessor.Populate(texcoordData.v4, flipY: flipY, calculateMinMax: false); } break; default: throw new ArgumentException("Unexpected accessor.type"); } }