import { Color, Euler, Material, Object3D, Texture, Vector2, Vector3, Vector4 } from "three";
/**
 * Valid types that can be used as material property overrides
 */
type MaterialPropertyType = number | number[] | Color | Texture | Vector2 | Vector3 | Vector4 | null | Euler;
/**
 * Defines offset and repeat transformations for texture coordinates
 */
export interface TextureTransform {
    /** UV offset applied to the texture */
    offset?: Vector2;
    /** UV repeat/scale applied to the texture */
    repeat?: Vector2;
}
/**
 * Represents a single material property override with optional texture transformation
 * @template T The type of the property value
 */
export interface PropertyBlockOverride<T extends MaterialPropertyType = MaterialPropertyType> {
    /** The name of the material property to override (e.g., "color", "map", "roughness") */
    name: string;
    /** The value to set for this property */
    value: T;
    /** Optional texture coordinate transformation (only used when value is a Texture) */
    textureTransform?: TextureTransform;
}
/**
 * Utility type that extracts only non-function property names from a type
 * @template T The type to extract property names from
 */
type NonFunctionPropertyNames<T> = {
    [K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];
/**
 * MaterialPropertyBlock allows per-object material property overrides without creating new material instances.
 * This is useful for rendering multiple objects with the same base material but different properties
 * (e.g., different colors, textures, or shader parameters).
 *
 * ## How Property Blocks Work
 *
 * **Important**: Overrides are registered on the **Object3D**, not on the material.
 * This means:
 * - If you change the object's material, the overrides will still be applied to the new material
 * - Multiple objects can share the same material but have different property overrides
 * - If you don't want overrides applied after changing a material, you must remove them using {@link removeOveride}, {@link clearAllOverrides}, or {@link dispose}
 *
 * The property block system works by:
 * - Temporarily applying overrides in onBeforeRender
 * - Restoring original values in onAfterRender
 * - Managing shader defines and program cache keys for correct shader compilation
 * - Supporting texture coordinate transforms per object
 *
 * ## Common Use Cases
 *
 * - **Lightmaps**: Apply unique lightmap textures to individual objects sharing the same material
 * - **Reflection Probes**: Apply different environment maps per object for localized reflections
 * - **See-through effects**: Temporarily override transparency/transmission properties for X-ray effects
 *
 * ## Getting a MaterialPropertyBlock
 *
 * **Important**: Do not use the constructor directly. Instead, use the static {@link MaterialPropertyBlock.get} method:
 *
 * ```typescript
 * const block = MaterialPropertyBlock.get(myMesh);
 * ```
 *
 * This method will either return an existing property block or create a new one if it doesn't exist.
 * It automatically:
 * - Creates the property block instance
 * - Registers it in the internal registry
 * - Attaches the necessary render callbacks to the object
 * - Handles Groups by applying overrides to all child meshes
 *
 * @example Basic usage
 * ```typescript
 * // Get or create a property block for an object
 * const block = MaterialPropertyBlock.get(myMesh);
 *
 * // Override the color property
 * block.setOverride("color", new Color(1, 0, 0));
 *
 * // Override a texture with custom UV transform (useful for lightmaps)
 * block.setOverride("lightMap", myLightmapTexture, {
 *   offset: new Vector2(0.5, 0.5),
 *   repeat: new Vector2(2, 2)
 * });
 *
 * // Set a shader define
 * block.setDefine("USE_CUSTOM_FEATURE", 1);
 * ```
 *
 * @example Material swapping behavior
 * ```typescript
 * const mesh = new Mesh(geometry, materialA);
 * const block = MaterialPropertyBlock.get(mesh);
 * block.setOverride("color", new Color(1, 0, 0));
 *
 * // The color override is red for materialA
 *
 * // Swap the material - overrides persist and apply to the new material!
 * mesh.material = materialB;
 * // The color override is now red for materialB too
 *
 * // If you don't want overrides on the new material, remove them:
 * block.clearAllOverrides(); // Remove all overrides
 * // or
 * block.removeOveride("color"); // Remove specific override
 * // or
 * block.dispose(); // Remove the entire property block
 * ```
 *
 * @example Lightmap usage
 * ```typescript
 * const block = MaterialPropertyBlock.get(mesh);
 * block.setOverride("lightMap", lightmapTexture);
 * block.setOverride("lightMapIntensity", 1.5);
 * ```
 *
 * @example See-through effect
 * ```typescript
 * const block = MaterialPropertyBlock.get(mesh);
 * block.setOverride("transparent", true);
 * block.setOverride("opacity", 0.3);
 * ```
 *
 * @template T The material type this property block is associated with
 */
export declare class MaterialPropertyBlock<T extends Material = Material> {
    private _overrides;
    private _defines;
    private _object;
    /** The object this property block is attached to */
    get object(): Object3D | null;
    /**
     * Creates a new MaterialPropertyBlock
     * @param object The object this property block is for (optional)
     */
    protected constructor(object?: Object3D | null);
    /**
     * Gets or creates a MaterialPropertyBlock for the given object.
     * This is the recommended way to obtain a property block instance.
     *
     * @template T The material type
     * @param object The object to get/create a property block for
     * @returns The MaterialPropertyBlock associated with this object
     *
     * @example
     * ```typescript
     * const block = MaterialPropertyBlock.get(myMesh);
     * block.setOverride("roughness", 0.5);
     * ```
     */
    static get<T extends Material = Material>(object: Object3D): MaterialPropertyBlock<T>;
    /**
     * Checks if an object has any property overrides
     * @param object The object to check
     * @returns True if the object has a property block with overrides
     */
    static hasOverrides(object: Object3D): boolean;
    /**
     * Disposes this property block and cleans up associated resources.
     * After calling dispose, this property block should not be used.
     */
    dispose(): void;
    /**
     * Sets or updates a material property override.
     * The override will be applied to the material during rendering.
     *
     * @param name The name of the material property to override (e.g., "color", "map", "roughness")
     * @param value The value to set
     * @param textureTransform Optional UV transform (only used when value is a Texture)
     *
     * @example
     * ```typescript
     * // Override a simple property
     * block.setOverride("roughness", 0.8);
     *
     * // Override a color
     * block.setOverride("color", new Color(0xff0000));
     *
     * // Override a texture with UV transform
     * block.setOverride("map", texture, {
     *   offset: new Vector2(0, 0),
     *   repeat: new Vector2(2, 2)
     * });
     * ```
     */
    setOverride<K extends NonFunctionPropertyNames<T>>(name: K, value: T[K], textureTransform?: TextureTransform): void;
    setOverride(name: string, value: MaterialPropertyType, textureTransform?: TextureTransform): void;
    /**
     * Gets the override for a specific property with type-safe value inference
     * @param name The property name to get
     * @returns The PropertyBlockOverride with correctly typed value if it exists, undefined otherwise
     *
     * @example
     * ```typescript
     * const block = MaterialPropertyBlock.get<MeshStandardMaterial>(mesh);
     *
     * // Value is inferred as number | undefined
     * const roughness = block.getOverride("roughness")?.value;
     *
     * // Value is inferred as Color | undefined
     * const color = block.getOverride("color")?.value;
     *
     * // Value is inferred as Texture | null | undefined
     * const map = block.getOverride("map")?.value;
     *
     * // Explicitly specify the type for properties not on the base material type
     * const transmission = block.getOverride<number>("transmission")?.value;
     *
     * // Or use a more specific material type
     * const physicalBlock = block as MaterialPropertyBlock<MeshPhysicalMaterial>;
     * const transmissionTyped = physicalBlock.getOverride("transmission")?.value; // number
     * ```
     */
    getOverride<K extends NonFunctionPropertyNames<T>>(name: K): PropertyBlockOverride<T[K] & MaterialPropertyType> | undefined;
    getOverride<V extends MaterialPropertyType = MaterialPropertyType>(name: string): PropertyBlockOverride<V> | undefined;
    /**
     * Removes a specific property override.
     * After removal, the material will use its original property value for this property.
     *
     * @param name The property name to remove the override for
     *
     * @example
     * ```typescript
     * const block = MaterialPropertyBlock.get(mesh);
     *
     * // Set some overrides
     * block.setOverride("color", new Color(1, 0, 0));
     * block.setOverride("roughness", 0.5);
     * block.setOverride("lightMap", lightmapTexture);
     *
     * // Remove a specific override - the material will now use its original color
     * block.removeOveride("color");
     *
     * // Other overrides (roughness, lightMap) remain active
     * ```
     */
    removeOveride<K extends NonFunctionPropertyNames<T>>(name: K | ({} & string)): void;
    /**
     * Removes all property overrides from this block.
     * After calling this, the material will use its original values for all properties.
     *
     * **Note**: This does NOT remove shader defines. Use {@link clearDefine} or {@link dispose} for that.
     *
     * @example Remove all overrides but keep the property block
     * ```typescript
     * const block = MaterialPropertyBlock.get(mesh);
     *
     * // Set multiple overrides
     * block.setOverride("color", new Color(1, 0, 0));
     * block.setOverride("roughness", 0.5);
     * block.setOverride("lightMap", lightmapTexture);
     *
     * // Later, remove all overrides at once
     * block.clearAllOverrides();
     *
     * // The material now uses its original values
     * // The property block still exists and can be reused with new overrides
     * ```
     *
     * @example Temporarily disable all overrides
     * ```typescript
     * const block = MaterialPropertyBlock.get(mesh);
     *
     * // Save current overrides if you want to restore them later
     * const savedOverrides = [...block.overrides];
     *
     * // Clear all overrides temporarily
     * block.clearAllOverrides();
     *
     * // Do some rendering without overrides...
     *
     * // Restore overrides
     * savedOverrides.forEach(override => {
     *   block.setOverride(override.name, override.value, override.textureTransform);
     * });
     * ```
     *
     * @see {@link removeOveride} - To remove a single override
     * @see {@link dispose} - To completely remove the property block and clean up resources
     */
    clearAllOverrides(): void;
    /**
     * Gets all property overrides as a readonly array
     * @returns Array of all property overrides
     */
    get overrides(): readonly PropertyBlockOverride[];
    /**
     * Checks if this property block has any overrides
     * @returns True if there are any overrides set
     */
    hasOverrides(): boolean;
    /**
     * Set a shader define that will be included in the program cache key.
     * This allows different objects sharing the same material to have different shader programs.
     *
     * Defines affect shader compilation and are useful for enabling/disabling features per-object.
     *
     * @param name The define name (e.g., "USE_LIGHTMAP", "ENABLE_REFLECTIONS")
     * @param value The define value (typically a boolean, number, or string)
     *
     * @example
     * ```typescript
     * // Enable a feature for this specific object
     * block.setDefine("USE_CUSTOM_SHADER", true);
     * block.setDefine("QUALITY_LEVEL", 2);
     * ```
     */
    setDefine(name: string, value: string | number | boolean): void;
    /**
     * Remove a shader define
     * @param name The define name to remove
     */
    clearDefine(name: string): void;
    /**
     * Get all defines set on this property block
     * @returns A readonly record of all defines
     */
    getDefines(): Readonly<Record<string, string | number | boolean>>;
    /**
     * Generates a cache key based on the current overrides and defines.
     * This key is used internally to ensure correct shader program selection
     * when objects share materials but have different property blocks.
     *
     * @returns A string representing the current state of this property block
     * @internal
     */
    getCacheKey(): string;
}
/**
 * Checks if an object has a MaterialPropertyBlock attached to it.
 *
 * @param object The object to check
 * @returns True if the object has a property block registered
 *
 * @example
 * ```typescript
 * if (objectHasPropertyBlock(myMesh)) {
 *   console.log("This mesh has property overrides");
 * }
 * ```
 */
export declare function objectHasPropertyBlock(object: Object3D): boolean;
export {};
