_prepareForRender (material) {
    const isOutline = material.isTextOutlineMaterial
    material.uniforms.uTroikaIsOutline.value = isOutline

    // Resize the texture to fit in powers of 2
    let texture = this._dataTextures[isOutline ? 'outline' : 'main'];
    const dataLength = Math.pow(2, Math.ceil(Math.log2(this._members.size * floatsPerMember)));
    if (!texture || dataLength !== texture.image.data.length) {
      // console.log(`resizing: ${dataLength}`);
      if (texture) texture.dispose();
      const width = Math.min(dataLength / 4, 1024);
      texture = this._dataTextures[isOutline ? 'outline' : 'main'] = new DataTexture(
        new Float32Array(dataLength),
        width,
        dataLength / 4 / width,
        RGBAFormat,
        FloatType
      );
    }

    const texData = texture.image.data;
    const setTexData = (index, value) => {
      if (value !== texData[index]) {
        texData[index] = value;
        texture.needsUpdate = true;
      }
    }
    this._members.forEach(({ index, dirty }, text) => {
      if (index > -1) {
        const startIndex = index * floatsPerMember

        // Matrix
        const matrix = text.matrix.elements;
        for (let i = 0; i < 16; i++) {
          setTexData(startIndex + i, matrix[i])
        }

        // Let the member populate the uniforms, since that does all the appropriate
        // logic and handling of defaults, and we'll just grab the results from there
        text._prepareForRender(material)
        const {
          uTroikaTotalBounds,
          uTroikaClipRect,
          uTroikaPositionOffset,
          uTroikaEdgeOffset,
          uTroikaBlurRadius,
          uTroikaStrokeWidth,
          uTroikaStrokeColor,
          uTroikaStrokeOpacity,
          uTroikaFillOpacity,
          uTroikaCurveRadius,
        } = material.uniforms;

        // Total bounds for uv
        for (let i = 0; i < 4; i++) {
          setTexData(startIndex + 16 + i, uTroikaTotalBounds.value.getComponent(i));
        }

        // Clip rect
        for (let i = 0; i < 4; i++) {
          setTexData(startIndex + 20 + i, uTroikaClipRect.value.getComponent(i));
        }

        // Color
        let color = isOutline ? (text.outlineColor || 0) : text.color;
        if (color == null) color = this.color;
        if (color == null) color = this.material.color;
        if (color == null) color = 0xffffff;
        setTexData(startIndex + 24, tempColor.set(color).getHex());

        // Fill opacity / outline opacity
        setTexData(startIndex + 25, uTroikaFillOpacity.value)

        // Curve radius
        setTexData(startIndex + 26, uTroikaCurveRadius.value)

        if (isOutline) {
          // Outline properties
          setTexData(startIndex + 28, uTroikaPositionOffset.value.x);
          setTexData(startIndex + 29, uTroikaPositionOffset.value.y);
          setTexData(startIndex + 30, uTroikaEdgeOffset.value);
          setTexData(startIndex + 31, uTroikaBlurRadius.value);
        } else {
          // Stroke properties
          setTexData(startIndex + 28, uTroikaStrokeWidth.value);
          setTexData(startIndex + 29, tempColor.set(uTroikaStrokeColor.value).getHex());
          setTexData(startIndex + 30, uTroikaStrokeOpacity.value);
        }
      }
    });
    material.setMatrixTexture(texture);

    // For the non-member-specific uniforms:
    super._prepareForRender(material);
  }
@override
Copy member matrices and uniform values into the data texture