// Updates glTF technique tech by adding all relevant attributes. // Pass: // mesh - (optional) the attributes of some mesh that uses this material, for sanity-checking. private void AddAllAttributes( GlTF_Technique tech, IExportableMaterial exportableMaterial, GlTF_Attributes mesh) { GlTF_VertexLayout layout = new GlTF_VertexLayout(G, exportableMaterial.VertexLayout); if (mesh != null) { GlTF_VertexLayout meshLayout = mesh.m_layout; if (layout != meshLayout) { if (meshLayout.GetTexcoordSize(2) > 0) { // We funnel timestamps in through GeometryPool.texcoord2 and write them out as // _TB_TIMESTAMP. Thus, the Pool's layout has a texcoord2 but the material's layout does // not. This is a mismatch between the mesh data and the material props, but: // 1. Timestamp isn't intended to be funneled to the material, so it's correct that the // material layoutdoesn't have texcoord2 // 2. It's fine if material attrs are a subset of the mesh attrs // 3. This only affects gltf1; only materials with techniques need to enum their attrs. // Maybe this check should be layout.IsSubset(meshLayout). /* ignore this mismatch */ } else { Debug.LogWarning($"Layout for {exportableMaterial.DurableName} doesn't match mesh's"); } } } // Materials are things that are shared across multiple meshes. // Material creation shouldn't depend on data specific to a particular mesh. // But it does. It's a hack. // Rather than do something reasonable like this: // // Create material's technique's attributes based on layout // Create mesh and its accessors based on layout // // We do this: // // Create mesh and its accessors based on layout // Lazily create the material used by the mesh // Create material's technique's attributes based on the accessors // of the last mesh we created AddAttribute("position", layout.PositionInfo.techniqueType, GlTF_Technique.Semantic.POSITION, tech); if (layout.NormalInfo != null) { AddAttribute("normal", layout.NormalInfo.Value.techniqueType, GlTF_Technique.Semantic.NORMAL, tech); } if (layout.ColorInfo != null) { AddAttribute("color", layout.ColorInfo.Value.techniqueType, GlTF_Technique.Semantic.COLOR, tech); } if (layout.TangentInfo != null) { AddAttribute("tangent", layout.TangentInfo.Value.techniqueType, GlTF_Technique.Semantic.TANGENT, tech); } // TODO: remove; this accessor isn't used. Instead, shaders use texcoord1.w if (layout.PackVertexIdIntoTexcoord1W) { AddAttribute("vertexId", GlTF_Technique.Type.FLOAT /* hardcoded, but this is gong away */, GlTF_Technique.Semantic.UNKNOWN, tech); } for (int i = 0; i < 4; ++i) { var texcoordInfo = layout.GetTexcoordInfo(i); if (texcoordInfo != null) { GlTF_Technique.Semantic semantic = GlTF_Technique.Semantic.TEXCOORD_0 + i; AddAttribute($"texcoord{i}", texcoordInfo.Value.techniqueType, semantic, tech); } } }
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 } }