ArticulatedModel::Instruction::Identifier::Identifier(const Any& a) {
    switch (a.type()) {
    case Any::NUMBER:
        id = ID(iRound(a.number()));
        a.verify(id >= 0, "Illegal ID");
        break;

    case Any::STRING:
        name = a.string();
        break;

    case Any::ARRAY:
        a.verifySize(0);
        if (a.name() == "root") {
            *this = root();
        } else if (a.name() == "all") {
            *this = all();
        } else {
            a.verify(false, "Illegal function call: " + a.name());
        }
        break;

    default:
        a.verify(false, "Expected a name, integer ID, root(), or all()");
    }
}
Texture::Encoding::Encoding(const Any& a) {
    *this = Encoding();

    if (a.type() == Any::STRING) {
        format = ImageFormat::fromString(a);
    } else if (a.nameBeginsWith("Color4")) {
        readMultiplyFirst = a;
	}
	else if (anyNameIsColor3Variant(a)) {
        readMultiplyFirst = Color4(Color3(a), 1.0f);
    } else if (a.type() == Any::NUMBER) {
        readMultiplyFirst = Color4::one() * float(a.number());
    } else {
        AnyTableReader r(a);

        r.getIfPresent("frame", frame);
        r.getIfPresent("readMultiplyFirst", readMultiplyFirst);
        r.getIfPresent("readAddSecond", readAddSecond);

        String fmt;
        if (r.getIfPresent("format", fmt)) {
            format = ImageFormat::fromString(fmt);
        }
    }
}